+2019-08-14 Martin Liska <mliska@suse.cz>
+
+ PR sanitizer/89832
+ PR sanitizer/91325
+ * All source files: Merge from upstream 368656.
+
2019-06-26 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
* sanitizer_common/sanitizer_posix_libcdep.cc: Cherry-pick
-345033
+368656
The first line of this file holds the svn revision number of the
last merge done from the master library sources.
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
echo ' $(SHELL) ./config.status'; \
$(SHELL) ./config.status;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
esac;
$(top_srcdir)/../multilib.am $(am__empty):
-# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.16.1 -*- Autoconf -*-
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
If you have problems, you may need to regenerate the build system entirely.
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
-# Copyright (C) 2002-2017 Free Software Foundation, Inc.
+# Copyright (C) 2002-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.15'
+[am__api_version='1.16'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.15.1], [],
+m4_if([$1], [1.16.1], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.15.1])dnl
+[AM_AUTOMAKE_VERSION([1.16.1])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# Figure out how to run the assembler. -*- Autoconf -*-
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997-2017 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
- case $CONFIG_FILES in
- *\'*) eval set x "$CONFIG_FILES" ;;
- *) set x $CONFIG_FILES ;;
- esac
+ # TODO: see whether this extra hack can be removed once we start
+ # requiring Autoconf 2.70 or later.
+ AS_CASE([$CONFIG_FILES],
+ [*\'*], [eval set x "$CONFIG_FILES"],
+ [*], [set x $CONFIG_FILES])
shift
- for mf
+ # Used to flag and report bootstrapping failures.
+ am_rc=0
+ for am_mf
do
# Strip MF so we end up with the name of the file.
- mf=`echo "$mf" | sed -e 's/:.*$//'`
- # Check whether this is an Automake generated Makefile or not.
- # We used to match only the files named 'Makefile.in', but
- # some people rename them; so instead we look at the file content.
- # Grep'ing the first line is not enough: some people post-process
- # each Makefile.in and add a new line on top of each file to say so.
- # Grep'ing the whole file is not good either: AIX grep has a line
+ am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile which includes
+ # dependency-tracking related rules and includes.
+ # Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
- if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
- dirpart=`AS_DIRNAME("$mf")`
- else
- continue
- fi
- # Extract the definition of DEPDIR, am__include, and am__quote
- # from the Makefile without running 'make'.
- DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
- test -z "$DEPDIR" && continue
- am__include=`sed -n 's/^am__include = //p' < "$mf"`
- test -z "$am__include" && continue
- am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
- # Find all dependency output files, they are included files with
- # $(DEPDIR) in their names. We invoke sed twice because it is the
- # simplest approach to changing $(DEPDIR) to its actual value in the
- # expansion.
- for file in `sed -n "
- s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
- # Make sure the directory exists.
- test -f "$dirpart/$file" && continue
- fdir=`AS_DIRNAME(["$file"])`
- AS_MKDIR_P([$dirpart/$fdir])
- # echo "creating $dirpart/$file"
- echo '# dummy' > "$dirpart/$file"
- done
+ sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+ || continue
+ am_dirpart=`AS_DIRNAME(["$am_mf"])`
+ am_filepart=`AS_BASENAME(["$am_mf"])`
+ AM_RUN_LOG([cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles]) || am_rc=$?
done
+ if test $am_rc -ne 0; then
+ AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments
+ for automatic dependency tracking. Try re-running configure with the
+ '--disable-dependency-tracking' option to at least be able to build
+ the package (albeit without support for automatic dependency tracking).])
+ fi
+ AS_UNSET([am_dirpart])
+ AS_UNSET([am_filepart])
+ AS_UNSET([am_mf])
+ AS_UNSET([am_rc])
+ rm -f conftest-deps.mk
}
])# _AM_OUTPUT_DEPENDENCY_COMMANDS
# -----------------------------
# This macro should only be invoked once -- use via AC_REQUIRE.
#
-# This code is only required when automatic dependency tracking
-# is enabled. FIXME. This creates each '.P' file that we will
-# need in order to bootstrap the dependency handling code.
+# This code is only required when automatic dependency tracking is enabled.
+# This creates each '.Po' and '.Plo' makefile fragment that we'll need in
+# order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
[test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
- [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
-])
+ [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])])
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
# We need awk for the "check" target (and possibly the TAP driver). The
# system "awk" is bad on some platforms.
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
# From Jim Meyering
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# AM_MAKE_INCLUDE()
# -----------------
-# Check to see how make treats includes.
+# Check whether make has an 'include' directive that can support all
+# the idioms we need for our automatic dependency tracking code.
AC_DEFUN([AM_MAKE_INCLUDE],
-[am_make=${MAKE-make}
-cat > confinc << 'END'
+[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive])
+cat > confinc.mk << 'END'
am__doit:
- @echo this is the am__doit target
+ @echo this is the am__doit target >confinc.out
.PHONY: am__doit
END
-# If we don't find an include directive, just comment out the code.
-AC_MSG_CHECKING([for style of include used by $am_make])
am__include="#"
am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# Ignore all kinds of additional output from 'make'.
-case `$am_make -s -f confmf 2> /dev/null` in #(
-*the\ am__doit\ target*)
- am__include=include
- am__quote=
- _am_result=GNU
- ;;
-esac
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
- echo '.include "confinc"' > confmf
- case `$am_make -s -f confmf 2> /dev/null` in #(
- *the\ am__doit\ target*)
- am__include=.include
- am__quote="\""
- _am_result=BSD
- ;;
- esac
-fi
-AC_SUBST([am__include])
-AC_SUBST([am__quote])
-AC_MSG_RESULT([$_am_result])
-rm -f confinc confmf
-])
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+ AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out])
+ AS_CASE([$?:`cat confinc.out 2>/dev/null`],
+ ['0:this is the am__doit target'],
+ [AS_CASE([$s],
+ [BSD], [am__include='.include' am__quote='"'],
+ [am__include='include' am__quote=''])])
+ if test "$am__include" != "#"; then
+ _am_result="yes ($s style)"
+ break
+ fi
+done
+rm -f confinc.* confmf.*
+AC_MSG_RESULT([${_am_result}])
+AC_SUBST([am__include])])
+AC_SUBST([am__quote])])
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997-2017 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# For backward compatibility.
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# Check to make sure that the build environment is sane. -*- Autoconf -*-
-# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Copyright (C) 1996-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
rm -f conftest.file
])
-# Copyright (C) 2009-2017 Free Software Foundation, Inc.
+# Copyright (C) 2009-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001-2017 Free Software Foundation, Inc.
+# Copyright (C) 2001-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006-2017 Free Software Foundation, Inc.
+# Copyright (C) 2006-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004-2017 Free Software Foundation, Inc.
+# Copyright (C) 2004-2018 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
nodist_toolexeclib_HEADERS = libasan_preinit.o
asan_files = \
- asan_activation.cc \
- asan_allocator.cc \
- asan_debugging.cc \
- asan_descriptions.cc \
- asan_errors.cc \
- asan_fake_stack.cc \
- asan_flags.cc \
- asan_globals.cc \
- asan_interceptors.cc \
- asan_interceptors_memintrinsics.cc \
- asan_linux.cc \
- asan_mac.cc \
- asan_malloc_linux.cc \
- asan_malloc_mac.cc \
- asan_malloc_win.cc \
- asan_memory_profile.cc \
- asan_new_delete.cc \
- asan_poisoning.cc \
- asan_posix.cc \
- asan_premap_shadow.cc \
- asan_report.cc \
- asan_rtems.cc \
- asan_rtl.cc \
- asan_shadow_setup.cc \
- asan_stack.cc \
- asan_stats.cc \
- asan_suppressions.cc \
- asan_thread.cc \
- asan_win.cc \
- asan_win_dll_thunk.cc \
- asan_win_dynamic_runtime_thunk.cc
+ asan_activation.cpp \
+ asan_allocator.cpp \
+ asan_debugging.cpp \
+ asan_descriptions.cpp \
+ asan_errors.cpp \
+ asan_fake_stack.cpp \
+ asan_flags.cpp \
+ asan_globals.cpp \
+ asan_interceptors.cpp \
+ asan_interceptors_memintrinsics.cpp \
+ asan_linux.cpp \
+ asan_mac.cpp \
+ asan_malloc_linux.cpp \
+ asan_malloc_mac.cpp \
+ asan_malloc_win.cpp \
+ asan_memory_profile.cpp \
+ asan_new_delete.cpp \
+ asan_poisoning.cpp \
+ asan_posix.cpp \
+ asan_premap_shadow.cpp \
+ asan_report.cpp \
+ asan_rtems.cpp \
+ asan_rtl.cpp \
+ asan_shadow_setup.cpp \
+ asan_stack.cpp \
+ asan_stats.cpp \
+ asan_suppressions.cpp \
+ asan_thread.cpp \
+ asan_win.cpp \
+ asan_win_dll_thunk.cpp \
+ asan_win_dynamic_runtime_thunk.cpp \
+ asan_interceptors_vfork.S
libasan_la_SOURCES = $(asan_files)
libasan_la_LIBADD = $(top_builddir)/sanitizer_common/libsanitizer_common.la $(top_builddir)/lsan/libsanitizer_lsan.la
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
asan_posix.lo asan_premap_shadow.lo asan_report.lo \
asan_rtems.lo asan_rtl.lo asan_shadow_setup.lo asan_stack.lo \
asan_stats.lo asan_suppressions.lo asan_thread.lo asan_win.lo \
- asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo
+ asan_win_dll_thunk.lo asan_win_dynamic_runtime_thunk.lo \
+ asan_interceptors_vfork.lo
am_libasan_la_OBJECTS = $(am__objects_1)
libasan_la_OBJECTS = $(am_libasan_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/asan_activation.Plo \
+ ./$(DEPDIR)/asan_allocator.Plo ./$(DEPDIR)/asan_debugging.Plo \
+ ./$(DEPDIR)/asan_descriptions.Plo ./$(DEPDIR)/asan_errors.Plo \
+ ./$(DEPDIR)/asan_fake_stack.Plo ./$(DEPDIR)/asan_flags.Plo \
+ ./$(DEPDIR)/asan_globals.Plo ./$(DEPDIR)/asan_interceptors.Plo \
+ ./$(DEPDIR)/asan_interceptors_memintrinsics.Plo \
+ ./$(DEPDIR)/asan_interceptors_vfork.Plo \
+ ./$(DEPDIR)/asan_linux.Plo ./$(DEPDIR)/asan_mac.Plo \
+ ./$(DEPDIR)/asan_malloc_linux.Plo \
+ ./$(DEPDIR)/asan_malloc_mac.Plo \
+ ./$(DEPDIR)/asan_malloc_win.Plo \
+ ./$(DEPDIR)/asan_memory_profile.Plo \
+ ./$(DEPDIR)/asan_new_delete.Plo ./$(DEPDIR)/asan_poisoning.Plo \
+ ./$(DEPDIR)/asan_posix.Plo ./$(DEPDIR)/asan_premap_shadow.Plo \
+ ./$(DEPDIR)/asan_report.Plo ./$(DEPDIR)/asan_rtems.Plo \
+ ./$(DEPDIR)/asan_rtl.Plo ./$(DEPDIR)/asan_shadow_setup.Plo \
+ ./$(DEPDIR)/asan_stack.Plo ./$(DEPDIR)/asan_stats.Plo \
+ ./$(DEPDIR)/asan_suppressions.Plo ./$(DEPDIR)/asan_thread.Plo \
+ ./$(DEPDIR)/asan_win.Plo ./$(DEPDIR)/asan_win_dll_thunk.Plo \
+ ./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo
am__mv = mv -f
+CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CCASFLAGS) $(CCASFLAGS)
+AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
+am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
+am__v_CPPAS_0 = @echo " CPPAS " $@;
+am__v_CPPAS_1 =
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
am__v_CXXLD_0 = @echo " CXXLD " $@;
am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libasan_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS \
-D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS \
-DASAN_HAS_EXCEPTIONS=1 -DASAN_NEEDS_SEGV=1 \
- -DCAN_SANITIZE_UB=0 \
- -DASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION=0 $(am__append_1)
+ -DCAN_SANITIZE_UB=0 -DASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION=0 \
+ $(am__append_1)
DEPDIR = @DEPDIR@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
toolexeclib_LTLIBRARIES = libasan.la
nodist_toolexeclib_HEADERS = libasan_preinit.o
asan_files = \
- asan_activation.cc \
- asan_allocator.cc \
- asan_debugging.cc \
- asan_descriptions.cc \
- asan_errors.cc \
- asan_fake_stack.cc \
- asan_flags.cc \
- asan_globals.cc \
- asan_interceptors.cc \
- asan_interceptors_memintrinsics.cc \
- asan_linux.cc \
- asan_mac.cc \
- asan_malloc_linux.cc \
- asan_malloc_mac.cc \
- asan_malloc_win.cc \
- asan_memory_profile.cc \
- asan_new_delete.cc \
- asan_poisoning.cc \
- asan_posix.cc \
- asan_premap_shadow.cc \
- asan_report.cc \
- asan_rtems.cc \
- asan_rtl.cc \
- asan_shadow_setup.cc \
- asan_stack.cc \
- asan_stats.cc \
- asan_suppressions.cc \
- asan_thread.cc \
- asan_win.cc \
- asan_win_dll_thunk.cc \
- asan_win_dynamic_runtime_thunk.cc
+ asan_activation.cpp \
+ asan_allocator.cpp \
+ asan_debugging.cpp \
+ asan_descriptions.cpp \
+ asan_errors.cpp \
+ asan_fake_stack.cpp \
+ asan_flags.cpp \
+ asan_globals.cpp \
+ asan_interceptors.cpp \
+ asan_interceptors_memintrinsics.cpp \
+ asan_linux.cpp \
+ asan_mac.cpp \
+ asan_malloc_linux.cpp \
+ asan_malloc_mac.cpp \
+ asan_malloc_win.cpp \
+ asan_memory_profile.cpp \
+ asan_new_delete.cpp \
+ asan_poisoning.cpp \
+ asan_posix.cpp \
+ asan_premap_shadow.cpp \
+ asan_report.cpp \
+ asan_rtems.cpp \
+ asan_rtl.cpp \
+ asan_shadow_setup.cpp \
+ asan_stack.cpp \
+ asan_stats.cpp \
+ asan_suppressions.cpp \
+ asan_thread.cpp \
+ asan_win.cpp \
+ asan_win_dll_thunk.cpp \
+ asan_win_dynamic_runtime_thunk.cpp \
+ asan_interceptors_vfork.S
libasan_la_SOURCES = $(asan_files)
libasan_la_LIBADD = \
all: all-am
.SUFFIXES:
-.SUFFIXES: .cc .lo .o .obj
+.SUFFIXES: .S .cpp .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_descriptions.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_errors.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_memintrinsics.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_win.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_memory_profile.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_new_delete.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_poisoning.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_premap_shadow.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_report.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtems.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_shadow_setup.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@
-
-.cc.o:
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_activation.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_allocator.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_debugging.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_descriptions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_errors.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_fake_stack.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_globals.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_memintrinsics.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_interceptors_vfork.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_malloc_win.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_memory_profile.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_new_delete.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_poisoning.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_posix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_premap_shadow.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_report.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtems.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_rtl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_shadow_setup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stack.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_stats.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_suppressions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_thread.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dll_thunk.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.S.o:
+@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
+
+.S.obj:
+@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.S.lo:
+@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
+
+.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
-.cc.obj:
+.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-.cc.lo:
+.cpp.lo:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/asan_activation.Plo
+ -rm -f ./$(DEPDIR)/asan_allocator.Plo
+ -rm -f ./$(DEPDIR)/asan_debugging.Plo
+ -rm -f ./$(DEPDIR)/asan_descriptions.Plo
+ -rm -f ./$(DEPDIR)/asan_errors.Plo
+ -rm -f ./$(DEPDIR)/asan_fake_stack.Plo
+ -rm -f ./$(DEPDIR)/asan_flags.Plo
+ -rm -f ./$(DEPDIR)/asan_globals.Plo
+ -rm -f ./$(DEPDIR)/asan_interceptors.Plo
+ -rm -f ./$(DEPDIR)/asan_interceptors_memintrinsics.Plo
+ -rm -f ./$(DEPDIR)/asan_interceptors_vfork.Plo
+ -rm -f ./$(DEPDIR)/asan_linux.Plo
+ -rm -f ./$(DEPDIR)/asan_mac.Plo
+ -rm -f ./$(DEPDIR)/asan_malloc_linux.Plo
+ -rm -f ./$(DEPDIR)/asan_malloc_mac.Plo
+ -rm -f ./$(DEPDIR)/asan_malloc_win.Plo
+ -rm -f ./$(DEPDIR)/asan_memory_profile.Plo
+ -rm -f ./$(DEPDIR)/asan_new_delete.Plo
+ -rm -f ./$(DEPDIR)/asan_poisoning.Plo
+ -rm -f ./$(DEPDIR)/asan_posix.Plo
+ -rm -f ./$(DEPDIR)/asan_premap_shadow.Plo
+ -rm -f ./$(DEPDIR)/asan_report.Plo
+ -rm -f ./$(DEPDIR)/asan_rtems.Plo
+ -rm -f ./$(DEPDIR)/asan_rtl.Plo
+ -rm -f ./$(DEPDIR)/asan_shadow_setup.Plo
+ -rm -f ./$(DEPDIR)/asan_stack.Plo
+ -rm -f ./$(DEPDIR)/asan_stats.Plo
+ -rm -f ./$(DEPDIR)/asan_suppressions.Plo
+ -rm -f ./$(DEPDIR)/asan_thread.Plo
+ -rm -f ./$(DEPDIR)/asan_win.Plo
+ -rm -f ./$(DEPDIR)/asan_win_dll_thunk.Plo
+ -rm -f ./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/asan_activation.Plo
+ -rm -f ./$(DEPDIR)/asan_allocator.Plo
+ -rm -f ./$(DEPDIR)/asan_debugging.Plo
+ -rm -f ./$(DEPDIR)/asan_descriptions.Plo
+ -rm -f ./$(DEPDIR)/asan_errors.Plo
+ -rm -f ./$(DEPDIR)/asan_fake_stack.Plo
+ -rm -f ./$(DEPDIR)/asan_flags.Plo
+ -rm -f ./$(DEPDIR)/asan_globals.Plo
+ -rm -f ./$(DEPDIR)/asan_interceptors.Plo
+ -rm -f ./$(DEPDIR)/asan_interceptors_memintrinsics.Plo
+ -rm -f ./$(DEPDIR)/asan_interceptors_vfork.Plo
+ -rm -f ./$(DEPDIR)/asan_linux.Plo
+ -rm -f ./$(DEPDIR)/asan_mac.Plo
+ -rm -f ./$(DEPDIR)/asan_malloc_linux.Plo
+ -rm -f ./$(DEPDIR)/asan_malloc_mac.Plo
+ -rm -f ./$(DEPDIR)/asan_malloc_win.Plo
+ -rm -f ./$(DEPDIR)/asan_memory_profile.Plo
+ -rm -f ./$(DEPDIR)/asan_new_delete.Plo
+ -rm -f ./$(DEPDIR)/asan_poisoning.Plo
+ -rm -f ./$(DEPDIR)/asan_posix.Plo
+ -rm -f ./$(DEPDIR)/asan_premap_shadow.Plo
+ -rm -f ./$(DEPDIR)/asan_report.Plo
+ -rm -f ./$(DEPDIR)/asan_rtems.Plo
+ -rm -f ./$(DEPDIR)/asan_rtl.Plo
+ -rm -f ./$(DEPDIR)/asan_shadow_setup.Plo
+ -rm -f ./$(DEPDIR)/asan_stack.Plo
+ -rm -f ./$(DEPDIR)/asan_stats.Plo
+ -rm -f ./$(DEPDIR)/asan_suppressions.Plo
+ -rm -f ./$(DEPDIR)/asan_thread.Plo
+ -rm -f ./$(DEPDIR)/asan_win.Plo
+ -rm -f ./$(DEPDIR)/asan_win_dll_thunk.Plo
+ -rm -f ./$(DEPDIR)/asan_win_dynamic_runtime_thunk.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-nodist_toolexeclibHEADERS install-pdf \
- install-pdf-am install-ps install-ps-am install-strip \
- install-toolexeclibLTLIBRARIES installcheck installcheck-am \
- installdirs maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am uninstall-nodist_toolexeclibHEADERS \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags dvi dvi-am \
+ html html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-nodist_toolexeclibHEADERS \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip install-toolexeclibLTLIBRARIES installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-nodist_toolexeclibHEADERS \
uninstall-toolexeclibLTLIBRARIES
.PRECIOUS: Makefile
+++ /dev/null
-//===-- asan_activation.cc --------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// ASan activation/deactivation logic.
-//===----------------------------------------------------------------------===//
-
-#include "asan_activation.h"
-#include "asan_allocator.h"
-#include "asan_flags.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-
-namespace __asan {
-
-static struct AsanDeactivatedFlags {
- AllocatorOptions allocator_options;
- int malloc_context_size;
- bool poison_heap;
- bool coverage;
- const char *coverage_dir;
-
- void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) {
-#define ASAN_ACTIVATION_FLAG(Type, Name) \
- RegisterFlag(parser, #Name, "", &f->Name);
-#define COMMON_ACTIVATION_FLAG(Type, Name) \
- RegisterFlag(parser, #Name, "", &cf->Name);
-#include "asan_activation_flags.inc"
-#undef ASAN_ACTIVATION_FLAG
-#undef COMMON_ACTIVATION_FLAG
-
- RegisterIncludeFlags(parser, cf);
- }
-
- void OverrideFromActivationFlags() {
- Flags f;
- CommonFlags cf;
- FlagParser parser;
- RegisterActivationFlags(&parser, &f, &cf);
-
- cf.SetDefaults();
- // Copy the current activation flags.
- allocator_options.CopyTo(&f, &cf);
- cf.malloc_context_size = malloc_context_size;
- f.poison_heap = poison_heap;
- cf.coverage = coverage;
- cf.coverage_dir = coverage_dir;
- cf.verbosity = Verbosity();
- cf.help = false; // this is activation-specific help
-
- // Check if activation flags need to be overriden.
- if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) {
- parser.ParseString(env);
- }
-
- InitializeCommonFlags(&cf);
-
- if (Verbosity()) ReportUnrecognizedFlags();
-
- if (cf.help) parser.PrintFlagDescriptions();
-
- allocator_options.SetFrom(&f, &cf);
- malloc_context_size = cf.malloc_context_size;
- poison_heap = f.poison_heap;
- coverage = cf.coverage;
- coverage_dir = cf.coverage_dir;
- }
-
- void Print() {
- Report(
- "quarantine_size_mb %d, thread_local_quarantine_size_kb %d, "
- "max_redzone %d, poison_heap %d, malloc_context_size %d, "
- "alloc_dealloc_mismatch %d, allocator_may_return_null %d, coverage %d, "
- "coverage_dir %s, allocator_release_to_os_interval_ms %d\n",
- allocator_options.quarantine_size_mb,
- allocator_options.thread_local_quarantine_size_kb,
- allocator_options.max_redzone, poison_heap, malloc_context_size,
- allocator_options.alloc_dealloc_mismatch,
- allocator_options.may_return_null, coverage, coverage_dir,
- allocator_options.release_to_os_interval_ms);
- }
-} asan_deactivated_flags;
-
-static bool asan_is_deactivated;
-
-void AsanDeactivate() {
- CHECK(!asan_is_deactivated);
- VReport(1, "Deactivating ASan\n");
-
- // Stash runtime state.
- GetAllocatorOptions(&asan_deactivated_flags.allocator_options);
- asan_deactivated_flags.malloc_context_size = GetMallocContextSize();
- asan_deactivated_flags.poison_heap = CanPoisonMemory();
- asan_deactivated_flags.coverage = common_flags()->coverage;
- asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir;
-
- // Deactivate the runtime.
- SetCanPoisonMemory(false);
- SetMallocContextSize(1);
-
- AllocatorOptions disabled = asan_deactivated_flags.allocator_options;
- disabled.quarantine_size_mb = 0;
- disabled.thread_local_quarantine_size_kb = 0;
- // Redzone must be at least Max(16, granularity) bytes long.
- disabled.min_redzone = Max(16, (int)SHADOW_GRANULARITY);
- disabled.max_redzone = disabled.min_redzone;
- disabled.alloc_dealloc_mismatch = false;
- disabled.may_return_null = true;
- ReInitializeAllocator(disabled);
-
- asan_is_deactivated = true;
-}
-
-void AsanActivate() {
- if (!asan_is_deactivated) return;
- VReport(1, "Activating ASan\n");
-
- UpdateProcessName();
-
- asan_deactivated_flags.OverrideFromActivationFlags();
-
- SetCanPoisonMemory(asan_deactivated_flags.poison_heap);
- SetMallocContextSize(asan_deactivated_flags.malloc_context_size);
- ReInitializeAllocator(asan_deactivated_flags.allocator_options);
-
- asan_is_deactivated = false;
- if (Verbosity()) {
- Report("Activated with flags:\n");
- asan_deactivated_flags.Print();
- }
-}
-
-} // namespace __asan
--- /dev/null
+//===-- asan_activation.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan activation/deactivation logic.
+//===----------------------------------------------------------------------===//
+
+#include "asan_activation.h"
+#include "asan_allocator.h"
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+
+namespace __asan {
+
+static struct AsanDeactivatedFlags {
+ AllocatorOptions allocator_options;
+ int malloc_context_size;
+ bool poison_heap;
+ bool coverage;
+ const char *coverage_dir;
+
+ void RegisterActivationFlags(FlagParser *parser, Flags *f, CommonFlags *cf) {
+#define ASAN_ACTIVATION_FLAG(Type, Name) \
+ RegisterFlag(parser, #Name, "", &f->Name);
+#define COMMON_ACTIVATION_FLAG(Type, Name) \
+ RegisterFlag(parser, #Name, "", &cf->Name);
+#include "asan_activation_flags.inc"
+#undef ASAN_ACTIVATION_FLAG
+#undef COMMON_ACTIVATION_FLAG
+
+ RegisterIncludeFlags(parser, cf);
+ }
+
+ void OverrideFromActivationFlags() {
+ Flags f;
+ CommonFlags cf;
+ FlagParser parser;
+ RegisterActivationFlags(&parser, &f, &cf);
+
+ cf.SetDefaults();
+ // Copy the current activation flags.
+ allocator_options.CopyTo(&f, &cf);
+ cf.malloc_context_size = malloc_context_size;
+ f.poison_heap = poison_heap;
+ cf.coverage = coverage;
+ cf.coverage_dir = coverage_dir;
+ cf.verbosity = Verbosity();
+ cf.help = false; // this is activation-specific help
+
+ // Check if activation flags need to be overriden.
+ if (const char *env = GetEnv("ASAN_ACTIVATION_OPTIONS")) {
+ parser.ParseString(env);
+ }
+
+ InitializeCommonFlags(&cf);
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (cf.help) parser.PrintFlagDescriptions();
+
+ allocator_options.SetFrom(&f, &cf);
+ malloc_context_size = cf.malloc_context_size;
+ poison_heap = f.poison_heap;
+ coverage = cf.coverage;
+ coverage_dir = cf.coverage_dir;
+ }
+
+ void Print() {
+ Report(
+ "quarantine_size_mb %d, thread_local_quarantine_size_kb %d, "
+ "max_redzone %d, poison_heap %d, malloc_context_size %d, "
+ "alloc_dealloc_mismatch %d, allocator_may_return_null %d, coverage %d, "
+ "coverage_dir %s, allocator_release_to_os_interval_ms %d\n",
+ allocator_options.quarantine_size_mb,
+ allocator_options.thread_local_quarantine_size_kb,
+ allocator_options.max_redzone, poison_heap, malloc_context_size,
+ allocator_options.alloc_dealloc_mismatch,
+ allocator_options.may_return_null, coverage, coverage_dir,
+ allocator_options.release_to_os_interval_ms);
+ }
+} asan_deactivated_flags;
+
+static bool asan_is_deactivated;
+
+void AsanDeactivate() {
+ CHECK(!asan_is_deactivated);
+ VReport(1, "Deactivating ASan\n");
+
+ // Stash runtime state.
+ GetAllocatorOptions(&asan_deactivated_flags.allocator_options);
+ asan_deactivated_flags.malloc_context_size = GetMallocContextSize();
+ asan_deactivated_flags.poison_heap = CanPoisonMemory();
+ asan_deactivated_flags.coverage = common_flags()->coverage;
+ asan_deactivated_flags.coverage_dir = common_flags()->coverage_dir;
+
+ // Deactivate the runtime.
+ SetCanPoisonMemory(false);
+ SetMallocContextSize(1);
+
+ AllocatorOptions disabled = asan_deactivated_flags.allocator_options;
+ disabled.quarantine_size_mb = 0;
+ disabled.thread_local_quarantine_size_kb = 0;
+ // Redzone must be at least Max(16, granularity) bytes long.
+ disabled.min_redzone = Max(16, (int)SHADOW_GRANULARITY);
+ disabled.max_redzone = disabled.min_redzone;
+ disabled.alloc_dealloc_mismatch = false;
+ disabled.may_return_null = true;
+ ReInitializeAllocator(disabled);
+
+ asan_is_deactivated = true;
+}
+
+void AsanActivate() {
+ if (!asan_is_deactivated) return;
+ VReport(1, "Activating ASan\n");
+
+ UpdateProcessName();
+
+ asan_deactivated_flags.OverrideFromActivationFlags();
+
+ SetCanPoisonMemory(asan_deactivated_flags.poison_heap);
+ SetMallocContextSize(asan_deactivated_flags.malloc_context_size);
+ ReInitializeAllocator(asan_deactivated_flags.allocator_options);
+
+ asan_is_deactivated = false;
+ if (Verbosity()) {
+ Report("Activated with flags:\n");
+ asan_deactivated_flags.Print();
+ }
+}
+
+} // namespace __asan
//===-- asan_activation.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- asan_activation_flags.inc -------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- asan_allocator.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Implementation of ASan's memory allocator, 2-nd version.
-// This variant uses the allocator from sanitizer_common, i.e. the one shared
-// with ThreadSanitizer and MemorySanitizer.
-//
-//===----------------------------------------------------------------------===//
-
-#include "asan_allocator.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_allocator_checks.h"
-#include "sanitizer_common/sanitizer_allocator_interface.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_list.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_quarantine.h"
-#include "lsan/lsan_common.h"
-
-namespace __asan {
-
-// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
-// We use adaptive redzones: for larger allocation larger redzones are used.
-static u32 RZLog2Size(u32 rz_log) {
- CHECK_LT(rz_log, 8);
- return 16 << rz_log;
-}
-
-static u32 RZSize2Log(u32 rz_size) {
- CHECK_GE(rz_size, 16);
- CHECK_LE(rz_size, 2048);
- CHECK(IsPowerOfTwo(rz_size));
- u32 res = Log2(rz_size) - 4;
- CHECK_EQ(rz_size, RZLog2Size(res));
- return res;
-}
-
-static AsanAllocator &get_allocator();
-
-// The memory chunk allocated from the underlying allocator looks like this:
-// L L L L L L H H U U U U U U R R
-// L -- left redzone words (0 or more bytes)
-// H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
-// U -- user memory.
-// R -- right redzone (0 or more bytes)
-// ChunkBase consists of ChunkHeader and other bytes that overlap with user
-// memory.
-
-// If the left redzone is greater than the ChunkHeader size we store a magic
-// value in the first uptr word of the memory block and store the address of
-// ChunkBase in the next uptr.
-// M B L L L L L L L L L H H U U U U U U
-// | ^
-// ---------------------|
-// M -- magic value kAllocBegMagic
-// B -- address of ChunkHeader pointing to the first 'H'
-static const uptr kAllocBegMagic = 0xCC6E96B9;
-
-struct ChunkHeader {
- // 1-st 8 bytes.
- u32 chunk_state : 8; // Must be first.
- u32 alloc_tid : 24;
-
- u32 free_tid : 24;
- u32 from_memalign : 1;
- u32 alloc_type : 2;
- u32 rz_log : 3;
- u32 lsan_tag : 2;
- // 2-nd 8 bytes
- // This field is used for small sizes. For large sizes it is equal to
- // SizeClassMap::kMaxSize and the actual size is stored in the
- // SecondaryAllocator's metadata.
- u32 user_requested_size : 29;
- // align < 8 -> 0
- // else -> log2(min(align, 512)) - 2
- u32 user_requested_alignment_log : 3;
- u32 alloc_context_id;
-};
-
-struct ChunkBase : ChunkHeader {
- // Header2, intersects with user memory.
- u32 free_context_id;
-};
-
-static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
-static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
-COMPILER_CHECK(kChunkHeaderSize == 16);
-COMPILER_CHECK(kChunkHeader2Size <= 16);
-
-// Every chunk of memory allocated by this allocator can be in one of 3 states:
-// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
-// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
-// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
-enum {
- CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it.
- CHUNK_ALLOCATED = 2,
- CHUNK_QUARANTINE = 3
-};
-
-struct AsanChunk: ChunkBase {
- uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
- uptr UsedSize(bool locked_version = false) {
- if (user_requested_size != SizeClassMap::kMaxSize)
- return user_requested_size;
- return *reinterpret_cast<uptr *>(
- get_allocator().GetMetaData(AllocBeg(locked_version)));
- }
- void *AllocBeg(bool locked_version = false) {
- if (from_memalign) {
- if (locked_version)
- return get_allocator().GetBlockBeginFastLocked(
- reinterpret_cast<void *>(this));
- return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this));
- }
- return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
- }
- bool AddrIsInside(uptr addr, bool locked_version = false) {
- return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
- }
-};
-
-struct QuarantineCallback {
- QuarantineCallback(AllocatorCache *cache, BufferedStackTrace *stack)
- : cache_(cache),
- stack_(stack) {
- }
-
- void Recycle(AsanChunk *m) {
- CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
- atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed);
- CHECK_NE(m->alloc_tid, kInvalidTid);
- CHECK_NE(m->free_tid, kInvalidTid);
- PoisonShadow(m->Beg(),
- RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
- kAsanHeapLeftRedzoneMagic);
- void *p = reinterpret_cast<void *>(m->AllocBeg());
- if (p != m) {
- uptr *alloc_magic = reinterpret_cast<uptr *>(p);
- CHECK_EQ(alloc_magic[0], kAllocBegMagic);
- // Clear the magic value, as allocator internals may overwrite the
- // contents of deallocated chunk, confusing GetAsanChunk lookup.
- alloc_magic[0] = 0;
- CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m));
- }
-
- // Statistics.
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.real_frees++;
- thread_stats.really_freed += m->UsedSize();
-
- get_allocator().Deallocate(cache_, p);
- }
-
- void *Allocate(uptr size) {
- void *res = get_allocator().Allocate(cache_, size, 1);
- // TODO(alekseys): Consider making quarantine OOM-friendly.
- if (UNLIKELY(!res))
- ReportOutOfMemory(size, stack_);
- return res;
- }
-
- void Deallocate(void *p) {
- get_allocator().Deallocate(cache_, p);
- }
-
- private:
- AllocatorCache* const cache_;
- BufferedStackTrace* const stack_;
-};
-
-typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
-typedef AsanQuarantine::Cache QuarantineCache;
-
-void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const {
- PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
- // Statistics.
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.mmaps++;
- thread_stats.mmaped += size;
-}
-void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const {
- PoisonShadow(p, size, 0);
- // We are about to unmap a chunk of user memory.
- // Mark the corresponding shadow memory as not needed.
- FlushUnneededASanShadowMemory(p, size);
- // Statistics.
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.munmaps++;
- thread_stats.munmaped += size;
-}
-
-// We can not use THREADLOCAL because it is not supported on some of the
-// platforms we care about (OSX 10.6, Android).
-// static THREADLOCAL AllocatorCache cache;
-AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
- CHECK(ms);
- return &ms->allocator_cache;
-}
-
-QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
- CHECK(ms);
- CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
- return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
-}
-
-void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
- quarantine_size_mb = f->quarantine_size_mb;
- thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb;
- min_redzone = f->redzone;
- max_redzone = f->max_redzone;
- may_return_null = cf->allocator_may_return_null;
- alloc_dealloc_mismatch = f->alloc_dealloc_mismatch;
- release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms;
-}
-
-void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
- f->quarantine_size_mb = quarantine_size_mb;
- f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb;
- f->redzone = min_redzone;
- f->max_redzone = max_redzone;
- cf->allocator_may_return_null = may_return_null;
- f->alloc_dealloc_mismatch = alloc_dealloc_mismatch;
- cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms;
-}
-
-struct Allocator {
- static const uptr kMaxAllowedMallocSize =
- FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
-
- AsanAllocator allocator;
- AsanQuarantine quarantine;
- StaticSpinMutex fallback_mutex;
- AllocatorCache fallback_allocator_cache;
- QuarantineCache fallback_quarantine_cache;
-
- atomic_uint8_t rss_limit_exceeded;
-
- // ------------------- Options --------------------------
- atomic_uint16_t min_redzone;
- atomic_uint16_t max_redzone;
- atomic_uint8_t alloc_dealloc_mismatch;
-
- // ------------------- Initialization ------------------------
- explicit Allocator(LinkerInitialized)
- : quarantine(LINKER_INITIALIZED),
- fallback_quarantine_cache(LINKER_INITIALIZED) {}
-
- void CheckOptions(const AllocatorOptions &options) const {
- CHECK_GE(options.min_redzone, 16);
- CHECK_GE(options.max_redzone, options.min_redzone);
- CHECK_LE(options.max_redzone, 2048);
- CHECK(IsPowerOfTwo(options.min_redzone));
- CHECK(IsPowerOfTwo(options.max_redzone));
- }
-
- void SharedInitCode(const AllocatorOptions &options) {
- CheckOptions(options);
- quarantine.Init((uptr)options.quarantine_size_mb << 20,
- (uptr)options.thread_local_quarantine_size_kb << 10);
- atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
- memory_order_release);
- atomic_store(&min_redzone, options.min_redzone, memory_order_release);
- atomic_store(&max_redzone, options.max_redzone, memory_order_release);
- }
-
- void InitLinkerInitialized(const AllocatorOptions &options) {
- SetAllocatorMayReturnNull(options.may_return_null);
- allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
- SharedInitCode(options);
- }
-
- bool RssLimitExceeded() {
- return atomic_load(&rss_limit_exceeded, memory_order_relaxed);
- }
-
- void SetRssLimitExceeded(bool limit_exceeded) {
- atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed);
- }
-
- void RePoisonChunk(uptr chunk) {
- // This could be a user-facing chunk (with redzones), or some internal
- // housekeeping chunk, like TransferBatch. Start by assuming the former.
- AsanChunk *ac = GetAsanChunk((void *)chunk);
- uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
- uptr beg = ac->Beg();
- uptr end = ac->Beg() + ac->UsedSize(true);
- uptr chunk_end = chunk + allocated_size;
- if (chunk < beg && beg < end && end <= chunk_end &&
- ac->chunk_state == CHUNK_ALLOCATED) {
- // Looks like a valid AsanChunk in use, poison redzones only.
- PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
- uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
- FastPoisonShadowPartialRightRedzone(
- end_aligned_down, end - end_aligned_down,
- chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
- } else {
- // This is either not an AsanChunk or freed or quarantined AsanChunk.
- // In either case, poison everything.
- PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
- }
- }
-
- void ReInitialize(const AllocatorOptions &options) {
- SetAllocatorMayReturnNull(options.may_return_null);
- allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms);
- SharedInitCode(options);
-
- // Poison all existing allocation's redzones.
- if (CanPoisonMemory()) {
- allocator.ForceLock();
- allocator.ForEachChunk(
- [](uptr chunk, void *alloc) {
- ((Allocator *)alloc)->RePoisonChunk(chunk);
- },
- this);
- allocator.ForceUnlock();
- }
- }
-
- void GetOptions(AllocatorOptions *options) const {
- options->quarantine_size_mb = quarantine.GetSize() >> 20;
- options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10;
- options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
- options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
- options->may_return_null = AllocatorMayReturnNull();
- options->alloc_dealloc_mismatch =
- atomic_load(&alloc_dealloc_mismatch, memory_order_acquire);
- options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs();
- }
-
- // -------------------- Helper methods. -------------------------
- uptr ComputeRZLog(uptr user_requested_size) {
- u32 rz_log =
- user_requested_size <= 64 - 16 ? 0 :
- user_requested_size <= 128 - 32 ? 1 :
- user_requested_size <= 512 - 64 ? 2 :
- user_requested_size <= 4096 - 128 ? 3 :
- user_requested_size <= (1 << 14) - 256 ? 4 :
- user_requested_size <= (1 << 15) - 512 ? 5 :
- user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
- u32 min_rz = atomic_load(&min_redzone, memory_order_acquire);
- u32 max_rz = atomic_load(&max_redzone, memory_order_acquire);
- return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz));
- }
-
- static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) {
- if (user_requested_alignment < 8)
- return 0;
- if (user_requested_alignment > 512)
- user_requested_alignment = 512;
- return Log2(user_requested_alignment) - 2;
- }
-
- static uptr ComputeUserAlignment(uptr user_requested_alignment_log) {
- if (user_requested_alignment_log == 0)
- return 0;
- return 1LL << (user_requested_alignment_log + 2);
- }
-
- // We have an address between two chunks, and we want to report just one.
- AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk,
- AsanChunk *right_chunk) {
- // Prefer an allocated chunk over freed chunk and freed chunk
- // over available chunk.
- if (left_chunk->chunk_state != right_chunk->chunk_state) {
- if (left_chunk->chunk_state == CHUNK_ALLOCATED)
- return left_chunk;
- if (right_chunk->chunk_state == CHUNK_ALLOCATED)
- return right_chunk;
- if (left_chunk->chunk_state == CHUNK_QUARANTINE)
- return left_chunk;
- if (right_chunk->chunk_state == CHUNK_QUARANTINE)
- return right_chunk;
- }
- // Same chunk_state: choose based on offset.
- sptr l_offset = 0, r_offset = 0;
- CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
- CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
- if (l_offset < r_offset)
- return left_chunk;
- return right_chunk;
- }
-
- // -------------------- Allocation/Deallocation routines ---------------
- void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
- AllocType alloc_type, bool can_fill) {
- if (UNLIKELY(!asan_inited))
- AsanInitFromRtl();
- if (RssLimitExceeded()) {
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportRssLimitExceeded(stack);
- }
- Flags &fl = *flags();
- CHECK(stack);
- const uptr min_alignment = SHADOW_GRANULARITY;
- const uptr user_requested_alignment_log =
- ComputeUserRequestedAlignmentLog(alignment);
- if (alignment < min_alignment)
- alignment = min_alignment;
- if (size == 0) {
- // We'd be happy to avoid allocating memory for zero-size requests, but
- // some programs/tests depend on this behavior and assume that malloc
- // would not return NULL even for zero-size allocations. Moreover, it
- // looks like operator new should never return NULL, and results of
- // consecutive "new" calls must be different even if the allocated size
- // is zero.
- size = 1;
- }
- CHECK(IsPowerOfTwo(alignment));
- uptr rz_log = ComputeRZLog(size);
- uptr rz_size = RZLog2Size(rz_log);
- uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment);
- uptr needed_size = rounded_size + rz_size;
- if (alignment > min_alignment)
- needed_size += alignment;
- bool using_primary_allocator = true;
- // If we are allocating from the secondary allocator, there will be no
- // automatic right redzone, so add the right redzone manually.
- if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
- needed_size += rz_size;
- using_primary_allocator = false;
- }
- CHECK(IsAligned(needed_size, min_alignment));
- if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
- if (AllocatorMayReturnNull()) {
- Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
- (void*)size);
- return nullptr;
- }
- ReportAllocationSizeTooBig(size, needed_size, kMaxAllowedMallocSize,
- stack);
- }
-
- AsanThread *t = GetCurrentThread();
- void *allocated;
- if (t) {
- AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
- allocated = allocator.Allocate(cache, needed_size, 8);
- } else {
- SpinMutexLock l(&fallback_mutex);
- AllocatorCache *cache = &fallback_allocator_cache;
- allocated = allocator.Allocate(cache, needed_size, 8);
- }
- if (UNLIKELY(!allocated)) {
- SetAllocatorOutOfMemory();
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportOutOfMemory(size, stack);
- }
-
- if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
- // Heap poisoning is enabled, but the allocator provides an unpoisoned
- // chunk. This is possible if CanPoisonMemory() was false for some
- // time, for example, due to flags()->start_disabled.
- // Anyway, poison the block before using it for anything else.
- uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated);
- PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic);
- }
-
- uptr alloc_beg = reinterpret_cast<uptr>(allocated);
- uptr alloc_end = alloc_beg + needed_size;
- uptr beg_plus_redzone = alloc_beg + rz_size;
- uptr user_beg = beg_plus_redzone;
- if (!IsAligned(user_beg, alignment))
- user_beg = RoundUpTo(user_beg, alignment);
- uptr user_end = user_beg + size;
- CHECK_LE(user_end, alloc_end);
- uptr chunk_beg = user_beg - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
- m->alloc_type = alloc_type;
- m->rz_log = rz_log;
- u32 alloc_tid = t ? t->tid() : 0;
- m->alloc_tid = alloc_tid;
- CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
- m->free_tid = kInvalidTid;
- m->from_memalign = user_beg != beg_plus_redzone;
- if (alloc_beg != chunk_beg) {
- CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg);
- reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic;
- reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg;
- }
- if (using_primary_allocator) {
- CHECK(size);
- m->user_requested_size = size;
- CHECK(allocator.FromPrimary(allocated));
- } else {
- CHECK(!allocator.FromPrimary(allocated));
- m->user_requested_size = SizeClassMap::kMaxSize;
- uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
- meta[0] = size;
- meta[1] = chunk_beg;
- }
- m->user_requested_alignment_log = user_requested_alignment_log;
-
- m->alloc_context_id = StackDepotPut(*stack);
-
- uptr size_rounded_down_to_granularity =
- RoundDownTo(size, SHADOW_GRANULARITY);
- // Unpoison the bulk of the memory region.
- if (size_rounded_down_to_granularity)
- PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
- // Deal with the end of the region if size is not aligned to granularity.
- if (size != size_rounded_down_to_granularity && CanPoisonMemory()) {
- u8 *shadow =
- (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity);
- *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0;
- }
-
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.mallocs++;
- thread_stats.malloced += size;
- thread_stats.malloced_redzones += needed_size - size;
- if (needed_size > SizeClassMap::kMaxSize)
- thread_stats.malloc_large++;
- else
- thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
-
- void *res = reinterpret_cast<void *>(user_beg);
- if (can_fill && fl.max_malloc_fill_size) {
- uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
- REAL(memset)(res, fl.malloc_fill_byte, fill_size);
- }
-#if CAN_SANITIZE_LEAKS
- m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored
- : __lsan::kDirectlyLeaked;
-#endif
- // Must be the last mutation of metadata in this function.
- atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
- ASAN_MALLOC_HOOK(res, size);
- return res;
- }
-
- // Set quarantine flag if chunk is allocated, issue ASan error report on
- // available and quarantined chunks. Return true on success, false otherwise.
- bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr,
- BufferedStackTrace *stack) {
- u8 old_chunk_state = CHUNK_ALLOCATED;
- // Flip the chunk_state atomically to avoid race on double-free.
- if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state,
- CHUNK_QUARANTINE,
- memory_order_acquire)) {
- ReportInvalidFree(ptr, old_chunk_state, stack);
- // It's not safe to push a chunk in quarantine on invalid free.
- return false;
- }
- CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
- return true;
- }
-
- // Expects the chunk to already be marked as quarantined by using
- // AtomicallySetQuarantineFlagIfAllocated.
- void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) {
- CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
- CHECK_GE(m->alloc_tid, 0);
- if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
- CHECK_EQ(m->free_tid, kInvalidTid);
- AsanThread *t = GetCurrentThread();
- m->free_tid = t ? t->tid() : 0;
- m->free_context_id = StackDepotPut(*stack);
-
- Flags &fl = *flags();
- if (fl.max_free_fill_size > 0) {
- // We have to skip the chunk header, it contains free_context_id.
- uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size;
- if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area.
- uptr size_to_fill = m->UsedSize() - kChunkHeader2Size;
- size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size);
- REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill);
- }
- }
-
- // Poison the region.
- PoisonShadow(m->Beg(),
- RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
- kAsanHeapFreeMagic);
-
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.frees++;
- thread_stats.freed += m->UsedSize();
-
- // Push into quarantine.
- if (t) {
- AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
- AllocatorCache *ac = GetAllocatorCache(ms);
- quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac, stack), m,
- m->UsedSize());
- } else {
- SpinMutexLock l(&fallback_mutex);
- AllocatorCache *ac = &fallback_allocator_cache;
- quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac, stack),
- m, m->UsedSize());
- }
- }
-
- void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment,
- BufferedStackTrace *stack, AllocType alloc_type) {
- uptr p = reinterpret_cast<uptr>(ptr);
- if (p == 0) return;
-
- uptr chunk_beg = p - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
-
- // On Windows, uninstrumented DLLs may allocate memory before ASan hooks
- // malloc. Don't report an invalid free in this case.
- if (SANITIZER_WINDOWS &&
- !get_allocator().PointerIsMine(ptr)) {
- if (!IsSystemHeapAddress(p))
- ReportFreeNotMalloced(p, stack);
- return;
- }
-
- ASAN_FREE_HOOK(ptr);
-
- // Must mark the chunk as quarantined before any changes to its metadata.
- // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag.
- if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return;
-
- if (m->alloc_type != alloc_type) {
- if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) {
- ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type,
- (AllocType)alloc_type);
- }
- } else {
- if (flags()->new_delete_type_mismatch &&
- (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) &&
- ((delete_size && delete_size != m->UsedSize()) ||
- ComputeUserRequestedAlignmentLog(delete_alignment) !=
- m->user_requested_alignment_log)) {
- ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack);
- }
- }
-
- QuarantineChunk(m, ptr, stack);
- }
-
- void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
- CHECK(old_ptr && new_size);
- uptr p = reinterpret_cast<uptr>(old_ptr);
- uptr chunk_beg = p - kChunkHeaderSize;
- AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
-
- AsanStats &thread_stats = GetCurrentThreadStats();
- thread_stats.reallocs++;
- thread_stats.realloced += new_size;
-
- void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
- if (new_ptr) {
- u8 chunk_state = m->chunk_state;
- if (chunk_state != CHUNK_ALLOCATED)
- ReportInvalidFree(old_ptr, chunk_state, stack);
- CHECK_NE(REAL(memcpy), nullptr);
- uptr memcpy_size = Min(new_size, m->UsedSize());
- // If realloc() races with free(), we may start copying freed memory.
- // However, we will report racy double-free later anyway.
- REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
- Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC);
- }
- return new_ptr;
- }
-
- void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
- if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportCallocOverflow(nmemb, size, stack);
- }
- void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
- // If the memory comes from the secondary allocator no need to clear it
- // as it comes directly from mmap.
- if (ptr && allocator.FromPrimary(ptr))
- REAL(memset)(ptr, 0, nmemb * size);
- return ptr;
- }
-
- void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) {
- if (chunk_state == CHUNK_QUARANTINE)
- ReportDoubleFree((uptr)ptr, stack);
- else
- ReportFreeNotMalloced((uptr)ptr, stack);
- }
-
- void CommitBack(AsanThreadLocalMallocStorage *ms, BufferedStackTrace *stack) {
- AllocatorCache *ac = GetAllocatorCache(ms);
- quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac, stack));
- allocator.SwallowCache(ac);
- }
-
- // -------------------------- Chunk lookup ----------------------
-
- // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
- AsanChunk *GetAsanChunk(void *alloc_beg) {
- if (!alloc_beg) return nullptr;
- if (!allocator.FromPrimary(alloc_beg)) {
- uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg));
- AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
- return m;
- }
- uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg);
- if (alloc_magic[0] == kAllocBegMagic)
- return reinterpret_cast<AsanChunk *>(alloc_magic[1]);
- return reinterpret_cast<AsanChunk *>(alloc_beg);
- }
-
- AsanChunk *GetAsanChunkByAddr(uptr p) {
- void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
- return GetAsanChunk(alloc_beg);
- }
-
- // Allocator must be locked when this function is called.
- AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) {
- void *alloc_beg =
- allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p));
- return GetAsanChunk(alloc_beg);
- }
-
- uptr AllocationSize(uptr p) {
- AsanChunk *m = GetAsanChunkByAddr(p);
- if (!m) return 0;
- if (m->chunk_state != CHUNK_ALLOCATED) return 0;
- if (m->Beg() != p) return 0;
- return m->UsedSize();
- }
-
- AsanChunkView FindHeapChunkByAddress(uptr addr) {
- AsanChunk *m1 = GetAsanChunkByAddr(addr);
- if (!m1) return AsanChunkView(m1);
- sptr offset = 0;
- if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
- // The address is in the chunk's left redzone, so maybe it is actually
- // a right buffer overflow from the other chunk to the left.
- // Search a bit to the left to see if there is another chunk.
- AsanChunk *m2 = nullptr;
- for (uptr l = 1; l < GetPageSizeCached(); l++) {
- m2 = GetAsanChunkByAddr(addr - l);
- if (m2 == m1) continue; // Still the same chunk.
- break;
- }
- if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
- m1 = ChooseChunk(addr, m2, m1);
- }
- return AsanChunkView(m1);
- }
-
- void Purge(BufferedStackTrace *stack) {
- AsanThread *t = GetCurrentThread();
- if (t) {
- AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
- quarantine.DrainAndRecycle(GetQuarantineCache(ms),
- QuarantineCallback(GetAllocatorCache(ms),
- stack));
- }
- {
- SpinMutexLock l(&fallback_mutex);
- quarantine.DrainAndRecycle(&fallback_quarantine_cache,
- QuarantineCallback(&fallback_allocator_cache,
- stack));
- }
-
- allocator.ForceReleaseToOS();
- }
-
- void PrintStats() {
- allocator.PrintStats();
- quarantine.PrintStats();
- }
-
- void ForceLock() {
- allocator.ForceLock();
- fallback_mutex.Lock();
- }
-
- void ForceUnlock() {
- fallback_mutex.Unlock();
- allocator.ForceUnlock();
- }
-};
-
-static Allocator instance(LINKER_INITIALIZED);
-
-static AsanAllocator &get_allocator() {
- return instance.allocator;
-}
-
-bool AsanChunkView::IsValid() const {
- return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE;
-}
-bool AsanChunkView::IsAllocated() const {
- return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED;
-}
-bool AsanChunkView::IsQuarantined() const {
- return chunk_ && chunk_->chunk_state == CHUNK_QUARANTINE;
-}
-uptr AsanChunkView::Beg() const { return chunk_->Beg(); }
-uptr AsanChunkView::End() const { return Beg() + UsedSize(); }
-uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); }
-u32 AsanChunkView::UserRequestedAlignment() const {
- return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log);
-}
-uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; }
-uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; }
-AllocType AsanChunkView::GetAllocType() const {
- return (AllocType)chunk_->alloc_type;
-}
-
-static StackTrace GetStackTraceFromId(u32 id) {
- CHECK(id);
- StackTrace res = StackDepotGet(id);
- CHECK(res.trace);
- return res;
-}
-
-u32 AsanChunkView::GetAllocStackId() const { return chunk_->alloc_context_id; }
-u32 AsanChunkView::GetFreeStackId() const { return chunk_->free_context_id; }
-
-StackTrace AsanChunkView::GetAllocStack() const {
- return GetStackTraceFromId(GetAllocStackId());
-}
-
-StackTrace AsanChunkView::GetFreeStack() const {
- return GetStackTraceFromId(GetFreeStackId());
-}
-
-void InitializeAllocator(const AllocatorOptions &options) {
- instance.InitLinkerInitialized(options);
-}
-
-void ReInitializeAllocator(const AllocatorOptions &options) {
- instance.ReInitialize(options);
-}
-
-void GetAllocatorOptions(AllocatorOptions *options) {
- instance.GetOptions(options);
-}
-
-AsanChunkView FindHeapChunkByAddress(uptr addr) {
- return instance.FindHeapChunkByAddress(addr);
-}
-AsanChunkView FindHeapChunkByAllocBeg(uptr addr) {
- return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr)));
-}
-
-void AsanThreadLocalMallocStorage::CommitBack() {
- GET_STACK_TRACE_MALLOC;
- instance.CommitBack(this, &stack);
-}
-
-void PrintInternalAllocatorStats() {
- instance.PrintStats();
-}
-
-void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
- instance.Deallocate(ptr, 0, 0, stack, alloc_type);
-}
-
-void asan_delete(void *ptr, uptr size, uptr alignment,
- BufferedStackTrace *stack, AllocType alloc_type) {
- instance.Deallocate(ptr, size, alignment, stack, alloc_type);
-}
-
-void *asan_malloc(uptr size, BufferedStackTrace *stack) {
- return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true));
-}
-
-void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
- return SetErrnoOnNull(instance.Calloc(nmemb, size, stack));
-}
-
-void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
- if (!p)
- return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true));
- if (size == 0) {
- if (flags()->allocator_frees_and_returns_null_on_realloc_zero) {
- instance.Deallocate(p, 0, 0, stack, FROM_MALLOC);
- return nullptr;
- }
- // Allocate a size of 1 if we shouldn't free() on Realloc to 0
- size = 1;
- }
- return SetErrnoOnNull(instance.Reallocate(p, size, stack));
-}
-
-void *asan_valloc(uptr size, BufferedStackTrace *stack) {
- return SetErrnoOnNull(
- instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true));
-}
-
-void *asan_pvalloc(uptr size, BufferedStackTrace *stack) {
- uptr PageSize = GetPageSizeCached();
- if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
- errno = errno_ENOMEM;
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportPvallocOverflow(size, stack);
- }
- // pvalloc(0) should allocate one page.
- size = size ? RoundUpTo(size, PageSize) : PageSize;
- return SetErrnoOnNull(
- instance.Allocate(size, PageSize, stack, FROM_MALLOC, true));
-}
-
-void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
- AllocType alloc_type) {
- if (UNLIKELY(!IsPowerOfTwo(alignment))) {
- errno = errno_EINVAL;
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportInvalidAllocationAlignment(alignment, stack);
- }
- return SetErrnoOnNull(
- instance.Allocate(size, alignment, stack, alloc_type, true));
-}
-
-void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) {
- if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
- errno = errno_EINVAL;
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportInvalidAlignedAllocAlignment(size, alignment, stack);
- }
- return SetErrnoOnNull(
- instance.Allocate(size, alignment, stack, FROM_MALLOC, true));
-}
-
-int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
- BufferedStackTrace *stack) {
- if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
- if (AllocatorMayReturnNull())
- return errno_EINVAL;
- ReportInvalidPosixMemalignAlignment(alignment, stack);
- }
- void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true);
- if (UNLIKELY(!ptr))
- // OOM error is already taken care of by Allocate.
- return errno_ENOMEM;
- CHECK(IsAligned((uptr)ptr, alignment));
- *memptr = ptr;
- return 0;
-}
-
-uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
- if (!ptr) return 0;
- uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
- if (flags()->check_malloc_usable_size && (usable_size == 0)) {
- GET_STACK_TRACE_FATAL(pc, bp);
- ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
- }
- return usable_size;
-}
-
-uptr asan_mz_size(const void *ptr) {
- return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
-}
-
-void asan_mz_force_lock() {
- instance.ForceLock();
-}
-
-void asan_mz_force_unlock() {
- instance.ForceUnlock();
-}
-
-void AsanSoftRssLimitExceededCallback(bool limit_exceeded) {
- instance.SetRssLimitExceeded(limit_exceeded);
-}
-
-} // namespace __asan
-
-// --- Implementation of LSan-specific functions --- {{{1
-namespace __lsan {
-void LockAllocator() {
- __asan::get_allocator().ForceLock();
-}
-
-void UnlockAllocator() {
- __asan::get_allocator().ForceUnlock();
-}
-
-void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
- *begin = (uptr)&__asan::get_allocator();
- *end = *begin + sizeof(__asan::get_allocator());
-}
-
-uptr PointsIntoChunk(void* p) {
- uptr addr = reinterpret_cast<uptr>(p);
- __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr);
- if (!m) return 0;
- uptr chunk = m->Beg();
- if (m->chunk_state != __asan::CHUNK_ALLOCATED)
- return 0;
- if (m->AddrIsInside(addr, /*locked_version=*/true))
- return chunk;
- if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true),
- addr))
- return chunk;
- return 0;
-}
-
-uptr GetUserBegin(uptr chunk) {
- __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk);
- CHECK(m);
- return m->Beg();
-}
-
-LsanMetadata::LsanMetadata(uptr chunk) {
- metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize);
-}
-
-bool LsanMetadata::allocated() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return m->chunk_state == __asan::CHUNK_ALLOCATED;
-}
-
-ChunkTag LsanMetadata::tag() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return static_cast<ChunkTag>(m->lsan_tag);
-}
-
-void LsanMetadata::set_tag(ChunkTag value) {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- m->lsan_tag = value;
-}
-
-uptr LsanMetadata::requested_size() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return m->UsedSize(/*locked_version=*/true);
-}
-
-u32 LsanMetadata::stack_trace_id() const {
- __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
- return m->alloc_context_id;
-}
-
-void ForEachChunk(ForEachChunkCallback callback, void *arg) {
- __asan::get_allocator().ForEachChunk(callback, arg);
-}
-
-IgnoreObjectResult IgnoreObjectLocked(const void *p) {
- uptr addr = reinterpret_cast<uptr>(p);
- __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr);
- if (!m) return kIgnoreObjectInvalid;
- if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
- if (m->lsan_tag == kIgnored)
- return kIgnoreObjectAlreadyIgnored;
- m->lsan_tag = __lsan::kIgnored;
- return kIgnoreObjectSuccess;
- } else {
- return kIgnoreObjectInvalid;
- }
-}
-} // namespace __lsan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-// ASan allocator doesn't reserve extra bytes, so normally we would
-// just return "size". We don't want to expose our redzone sizes, etc here.
-uptr __sanitizer_get_estimated_allocated_size(uptr size) {
- return size;
-}
-
-int __sanitizer_get_ownership(const void *p) {
- uptr ptr = reinterpret_cast<uptr>(p);
- return instance.AllocationSize(ptr) > 0;
-}
-
-uptr __sanitizer_get_allocated_size(const void *p) {
- if (!p) return 0;
- uptr ptr = reinterpret_cast<uptr>(p);
- uptr allocated_size = instance.AllocationSize(ptr);
- // Die if p is not malloced or if it is already freed.
- if (allocated_size == 0) {
- GET_STACK_TRACE_FATAL_HERE;
- ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
- }
- return allocated_size;
-}
-
-void __sanitizer_purge_allocator() {
- GET_STACK_TRACE_MALLOC;
- instance.Purge(&stack);
-}
-
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
-// Provide default (no-op) implementation of malloc hooks.
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook,
- void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
-
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) {
- (void)ptr;
-}
-#endif
--- /dev/null
+//===-- asan_allocator.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Implementation of ASan's memory allocator, 2-nd version.
+// This variant uses the allocator from sanitizer_common, i.e. the one shared
+// with ThreadSanitizer and MemorySanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_list.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_quarantine.h"
+#include "lsan/lsan_common.h"
+
+namespace __asan {
+
+// Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits.
+// We use adaptive redzones: for larger allocation larger redzones are used.
+static u32 RZLog2Size(u32 rz_log) {
+ CHECK_LT(rz_log, 8);
+ return 16 << rz_log;
+}
+
+static u32 RZSize2Log(u32 rz_size) {
+ CHECK_GE(rz_size, 16);
+ CHECK_LE(rz_size, 2048);
+ CHECK(IsPowerOfTwo(rz_size));
+ u32 res = Log2(rz_size) - 4;
+ CHECK_EQ(rz_size, RZLog2Size(res));
+ return res;
+}
+
+static AsanAllocator &get_allocator();
+
+// The memory chunk allocated from the underlying allocator looks like this:
+// L L L L L L H H U U U U U U R R
+// L -- left redzone words (0 or more bytes)
+// H -- ChunkHeader (16 bytes), which is also a part of the left redzone.
+// U -- user memory.
+// R -- right redzone (0 or more bytes)
+// ChunkBase consists of ChunkHeader and other bytes that overlap with user
+// memory.
+
+// If the left redzone is greater than the ChunkHeader size we store a magic
+// value in the first uptr word of the memory block and store the address of
+// ChunkBase in the next uptr.
+// M B L L L L L L L L L H H U U U U U U
+// | ^
+// ---------------------|
+// M -- magic value kAllocBegMagic
+// B -- address of ChunkHeader pointing to the first 'H'
+static const uptr kAllocBegMagic = 0xCC6E96B9;
+
+struct ChunkHeader {
+ // 1-st 8 bytes.
+ u32 chunk_state : 8; // Must be first.
+ u32 alloc_tid : 24;
+
+ u32 free_tid : 24;
+ u32 from_memalign : 1;
+ u32 alloc_type : 2;
+ u32 rz_log : 3;
+ u32 lsan_tag : 2;
+ // 2-nd 8 bytes
+ // This field is used for small sizes. For large sizes it is equal to
+ // SizeClassMap::kMaxSize and the actual size is stored in the
+ // SecondaryAllocator's metadata.
+ u32 user_requested_size : 29;
+ // align < 8 -> 0
+ // else -> log2(min(align, 512)) - 2
+ u32 user_requested_alignment_log : 3;
+ u32 alloc_context_id;
+};
+
+struct ChunkBase : ChunkHeader {
+ // Header2, intersects with user memory.
+ u32 free_context_id;
+};
+
+static const uptr kChunkHeaderSize = sizeof(ChunkHeader);
+static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize;
+COMPILER_CHECK(kChunkHeaderSize == 16);
+COMPILER_CHECK(kChunkHeader2Size <= 16);
+
+// Every chunk of memory allocated by this allocator can be in one of 3 states:
+// CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated.
+// CHUNK_ALLOCATED: the chunk is allocated and not yet freed.
+// CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone.
+enum {
+ CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it.
+ CHUNK_ALLOCATED = 2,
+ CHUNK_QUARANTINE = 3
+};
+
+struct AsanChunk: ChunkBase {
+ uptr Beg() { return reinterpret_cast<uptr>(this) + kChunkHeaderSize; }
+ uptr UsedSize(bool locked_version = false) {
+ if (user_requested_size != SizeClassMap::kMaxSize)
+ return user_requested_size;
+ return *reinterpret_cast<uptr *>(
+ get_allocator().GetMetaData(AllocBeg(locked_version)));
+ }
+ void *AllocBeg(bool locked_version = false) {
+ if (from_memalign) {
+ if (locked_version)
+ return get_allocator().GetBlockBeginFastLocked(
+ reinterpret_cast<void *>(this));
+ return get_allocator().GetBlockBegin(reinterpret_cast<void *>(this));
+ }
+ return reinterpret_cast<void*>(Beg() - RZLog2Size(rz_log));
+ }
+ bool AddrIsInside(uptr addr, bool locked_version = false) {
+ return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version));
+ }
+};
+
+struct QuarantineCallback {
+ QuarantineCallback(AllocatorCache *cache, BufferedStackTrace *stack)
+ : cache_(cache),
+ stack_(stack) {
+ }
+
+ void Recycle(AsanChunk *m) {
+ CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
+ atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed);
+ CHECK_NE(m->alloc_tid, kInvalidTid);
+ CHECK_NE(m->free_tid, kInvalidTid);
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapLeftRedzoneMagic);
+ void *p = reinterpret_cast<void *>(m->AllocBeg());
+ if (p != m) {
+ uptr *alloc_magic = reinterpret_cast<uptr *>(p);
+ CHECK_EQ(alloc_magic[0], kAllocBegMagic);
+ // Clear the magic value, as allocator internals may overwrite the
+ // contents of deallocated chunk, confusing GetAsanChunk lookup.
+ alloc_magic[0] = 0;
+ CHECK_EQ(alloc_magic[1], reinterpret_cast<uptr>(m));
+ }
+
+ // Statistics.
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.real_frees++;
+ thread_stats.really_freed += m->UsedSize();
+
+ get_allocator().Deallocate(cache_, p);
+ }
+
+ void *Allocate(uptr size) {
+ void *res = get_allocator().Allocate(cache_, size, 1);
+ // TODO(alekseys): Consider making quarantine OOM-friendly.
+ if (UNLIKELY(!res))
+ ReportOutOfMemory(size, stack_);
+ return res;
+ }
+
+ void Deallocate(void *p) {
+ get_allocator().Deallocate(cache_, p);
+ }
+
+ private:
+ AllocatorCache* const cache_;
+ BufferedStackTrace* const stack_;
+};
+
+typedef Quarantine<QuarantineCallback, AsanChunk> AsanQuarantine;
+typedef AsanQuarantine::Cache QuarantineCache;
+
+void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const {
+ PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic);
+ // Statistics.
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.mmaps++;
+ thread_stats.mmaped += size;
+}
+void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const {
+ PoisonShadow(p, size, 0);
+ // We are about to unmap a chunk of user memory.
+ // Mark the corresponding shadow memory as not needed.
+ FlushUnneededASanShadowMemory(p, size);
+ // Statistics.
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.munmaps++;
+ thread_stats.munmaped += size;
+}
+
+// We can not use THREADLOCAL because it is not supported on some of the
+// platforms we care about (OSX 10.6, Android).
+// static THREADLOCAL AllocatorCache cache;
+AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ return &ms->allocator_cache;
+}
+
+QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) {
+ CHECK(ms);
+ CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache));
+ return reinterpret_cast<QuarantineCache *>(ms->quarantine_cache);
+}
+
+void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) {
+ quarantine_size_mb = f->quarantine_size_mb;
+ thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb;
+ min_redzone = f->redzone;
+ max_redzone = f->max_redzone;
+ may_return_null = cf->allocator_may_return_null;
+ alloc_dealloc_mismatch = f->alloc_dealloc_mismatch;
+ release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms;
+}
+
+void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) {
+ f->quarantine_size_mb = quarantine_size_mb;
+ f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb;
+ f->redzone = min_redzone;
+ f->max_redzone = max_redzone;
+ cf->allocator_may_return_null = may_return_null;
+ f->alloc_dealloc_mismatch = alloc_dealloc_mismatch;
+ cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms;
+}
+
+struct Allocator {
+ static const uptr kMaxAllowedMallocSize =
+ FIRST_32_SECOND_64(3UL << 30, 1ULL << 40);
+
+ AsanAllocator allocator;
+ AsanQuarantine quarantine;
+ StaticSpinMutex fallback_mutex;
+ AllocatorCache fallback_allocator_cache;
+ QuarantineCache fallback_quarantine_cache;
+
+ atomic_uint8_t rss_limit_exceeded;
+
+ // ------------------- Options --------------------------
+ atomic_uint16_t min_redzone;
+ atomic_uint16_t max_redzone;
+ atomic_uint8_t alloc_dealloc_mismatch;
+
+ // ------------------- Initialization ------------------------
+ explicit Allocator(LinkerInitialized)
+ : quarantine(LINKER_INITIALIZED),
+ fallback_quarantine_cache(LINKER_INITIALIZED) {}
+
+ void CheckOptions(const AllocatorOptions &options) const {
+ CHECK_GE(options.min_redzone, 16);
+ CHECK_GE(options.max_redzone, options.min_redzone);
+ CHECK_LE(options.max_redzone, 2048);
+ CHECK(IsPowerOfTwo(options.min_redzone));
+ CHECK(IsPowerOfTwo(options.max_redzone));
+ }
+
+ void SharedInitCode(const AllocatorOptions &options) {
+ CheckOptions(options);
+ quarantine.Init((uptr)options.quarantine_size_mb << 20,
+ (uptr)options.thread_local_quarantine_size_kb << 10);
+ atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch,
+ memory_order_release);
+ atomic_store(&min_redzone, options.min_redzone, memory_order_release);
+ atomic_store(&max_redzone, options.max_redzone, memory_order_release);
+ }
+
+ void InitLinkerInitialized(const AllocatorOptions &options) {
+ SetAllocatorMayReturnNull(options.may_return_null);
+ allocator.InitLinkerInitialized(options.release_to_os_interval_ms);
+ SharedInitCode(options);
+ }
+
+ bool RssLimitExceeded() {
+ return atomic_load(&rss_limit_exceeded, memory_order_relaxed);
+ }
+
+ void SetRssLimitExceeded(bool limit_exceeded) {
+ atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed);
+ }
+
+ void RePoisonChunk(uptr chunk) {
+ // This could be a user-facing chunk (with redzones), or some internal
+ // housekeeping chunk, like TransferBatch. Start by assuming the former.
+ AsanChunk *ac = GetAsanChunk((void *)chunk);
+ uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac);
+ uptr beg = ac->Beg();
+ uptr end = ac->Beg() + ac->UsedSize(true);
+ uptr chunk_end = chunk + allocated_size;
+ if (chunk < beg && beg < end && end <= chunk_end &&
+ ac->chunk_state == CHUNK_ALLOCATED) {
+ // Looks like a valid AsanChunk in use, poison redzones only.
+ PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic);
+ uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY);
+ FastPoisonShadowPartialRightRedzone(
+ end_aligned_down, end - end_aligned_down,
+ chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic);
+ } else {
+ // This is either not an AsanChunk or freed or quarantined AsanChunk.
+ // In either case, poison everything.
+ PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic);
+ }
+ }
+
+ void ReInitialize(const AllocatorOptions &options) {
+ SetAllocatorMayReturnNull(options.may_return_null);
+ allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms);
+ SharedInitCode(options);
+
+ // Poison all existing allocation's redzones.
+ if (CanPoisonMemory()) {
+ allocator.ForceLock();
+ allocator.ForEachChunk(
+ [](uptr chunk, void *alloc) {
+ ((Allocator *)alloc)->RePoisonChunk(chunk);
+ },
+ this);
+ allocator.ForceUnlock();
+ }
+ }
+
+ void GetOptions(AllocatorOptions *options) const {
+ options->quarantine_size_mb = quarantine.GetSize() >> 20;
+ options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10;
+ options->min_redzone = atomic_load(&min_redzone, memory_order_acquire);
+ options->max_redzone = atomic_load(&max_redzone, memory_order_acquire);
+ options->may_return_null = AllocatorMayReturnNull();
+ options->alloc_dealloc_mismatch =
+ atomic_load(&alloc_dealloc_mismatch, memory_order_acquire);
+ options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs();
+ }
+
+ // -------------------- Helper methods. -------------------------
+ uptr ComputeRZLog(uptr user_requested_size) {
+ u32 rz_log =
+ user_requested_size <= 64 - 16 ? 0 :
+ user_requested_size <= 128 - 32 ? 1 :
+ user_requested_size <= 512 - 64 ? 2 :
+ user_requested_size <= 4096 - 128 ? 3 :
+ user_requested_size <= (1 << 14) - 256 ? 4 :
+ user_requested_size <= (1 << 15) - 512 ? 5 :
+ user_requested_size <= (1 << 16) - 1024 ? 6 : 7;
+ u32 min_rz = atomic_load(&min_redzone, memory_order_acquire);
+ u32 max_rz = atomic_load(&max_redzone, memory_order_acquire);
+ return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz));
+ }
+
+ static uptr ComputeUserRequestedAlignmentLog(uptr user_requested_alignment) {
+ if (user_requested_alignment < 8)
+ return 0;
+ if (user_requested_alignment > 512)
+ user_requested_alignment = 512;
+ return Log2(user_requested_alignment) - 2;
+ }
+
+ static uptr ComputeUserAlignment(uptr user_requested_alignment_log) {
+ if (user_requested_alignment_log == 0)
+ return 0;
+ return 1LL << (user_requested_alignment_log + 2);
+ }
+
+ // We have an address between two chunks, and we want to report just one.
+ AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk,
+ AsanChunk *right_chunk) {
+ // Prefer an allocated chunk over freed chunk and freed chunk
+ // over available chunk.
+ if (left_chunk->chunk_state != right_chunk->chunk_state) {
+ if (left_chunk->chunk_state == CHUNK_ALLOCATED)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_ALLOCATED)
+ return right_chunk;
+ if (left_chunk->chunk_state == CHUNK_QUARANTINE)
+ return left_chunk;
+ if (right_chunk->chunk_state == CHUNK_QUARANTINE)
+ return right_chunk;
+ }
+ // Same chunk_state: choose based on offset.
+ sptr l_offset = 0, r_offset = 0;
+ CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset));
+ CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset));
+ if (l_offset < r_offset)
+ return left_chunk;
+ return right_chunk;
+ }
+
+ // -------------------- Allocation/Deallocation routines ---------------
+ void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
+ AllocType alloc_type, bool can_fill) {
+ if (UNLIKELY(!asan_inited))
+ AsanInitFromRtl();
+ if (RssLimitExceeded()) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportRssLimitExceeded(stack);
+ }
+ Flags &fl = *flags();
+ CHECK(stack);
+ const uptr min_alignment = SHADOW_GRANULARITY;
+ const uptr user_requested_alignment_log =
+ ComputeUserRequestedAlignmentLog(alignment);
+ if (alignment < min_alignment)
+ alignment = min_alignment;
+ if (size == 0) {
+ // We'd be happy to avoid allocating memory for zero-size requests, but
+ // some programs/tests depend on this behavior and assume that malloc
+ // would not return NULL even for zero-size allocations. Moreover, it
+ // looks like operator new should never return NULL, and results of
+ // consecutive "new" calls must be different even if the allocated size
+ // is zero.
+ size = 1;
+ }
+ CHECK(IsPowerOfTwo(alignment));
+ uptr rz_log = ComputeRZLog(size);
+ uptr rz_size = RZLog2Size(rz_log);
+ uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment);
+ uptr needed_size = rounded_size + rz_size;
+ if (alignment > min_alignment)
+ needed_size += alignment;
+ bool using_primary_allocator = true;
+ // If we are allocating from the secondary allocator, there will be no
+ // automatic right redzone, so add the right redzone manually.
+ if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) {
+ needed_size += rz_size;
+ using_primary_allocator = false;
+ }
+ CHECK(IsAligned(needed_size, min_alignment));
+ if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) {
+ if (AllocatorMayReturnNull()) {
+ Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n",
+ (void*)size);
+ return nullptr;
+ }
+ ReportAllocationSizeTooBig(size, needed_size, kMaxAllowedMallocSize,
+ stack);
+ }
+
+ AsanThread *t = GetCurrentThread();
+ void *allocated;
+ if (t) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocated = allocator.Allocate(cache, needed_size, 8);
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ allocated = allocator.Allocate(cache, needed_size, 8);
+ }
+ if (UNLIKELY(!allocated)) {
+ SetAllocatorOutOfMemory();
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportOutOfMemory(size, stack);
+ }
+
+ if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) {
+ // Heap poisoning is enabled, but the allocator provides an unpoisoned
+ // chunk. This is possible if CanPoisonMemory() was false for some
+ // time, for example, due to flags()->start_disabled.
+ // Anyway, poison the block before using it for anything else.
+ uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated);
+ PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic);
+ }
+
+ uptr alloc_beg = reinterpret_cast<uptr>(allocated);
+ uptr alloc_end = alloc_beg + needed_size;
+ uptr beg_plus_redzone = alloc_beg + rz_size;
+ uptr user_beg = beg_plus_redzone;
+ if (!IsAligned(user_beg, alignment))
+ user_beg = RoundUpTo(user_beg, alignment);
+ uptr user_end = user_beg + size;
+ CHECK_LE(user_end, alloc_end);
+ uptr chunk_beg = user_beg - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+ m->alloc_type = alloc_type;
+ m->rz_log = rz_log;
+ u32 alloc_tid = t ? t->tid() : 0;
+ m->alloc_tid = alloc_tid;
+ CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield?
+ m->free_tid = kInvalidTid;
+ m->from_memalign = user_beg != beg_plus_redzone;
+ if (alloc_beg != chunk_beg) {
+ CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg);
+ reinterpret_cast<uptr *>(alloc_beg)[0] = kAllocBegMagic;
+ reinterpret_cast<uptr *>(alloc_beg)[1] = chunk_beg;
+ }
+ if (using_primary_allocator) {
+ CHECK(size);
+ m->user_requested_size = size;
+ CHECK(allocator.FromPrimary(allocated));
+ } else {
+ CHECK(!allocator.FromPrimary(allocated));
+ m->user_requested_size = SizeClassMap::kMaxSize;
+ uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(allocated));
+ meta[0] = size;
+ meta[1] = chunk_beg;
+ }
+ m->user_requested_alignment_log = user_requested_alignment_log;
+
+ m->alloc_context_id = StackDepotPut(*stack);
+
+ uptr size_rounded_down_to_granularity =
+ RoundDownTo(size, SHADOW_GRANULARITY);
+ // Unpoison the bulk of the memory region.
+ if (size_rounded_down_to_granularity)
+ PoisonShadow(user_beg, size_rounded_down_to_granularity, 0);
+ // Deal with the end of the region if size is not aligned to granularity.
+ if (size != size_rounded_down_to_granularity && CanPoisonMemory()) {
+ u8 *shadow =
+ (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity);
+ *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0;
+ }
+
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.mallocs++;
+ thread_stats.malloced += size;
+ thread_stats.malloced_redzones += needed_size - size;
+ if (needed_size > SizeClassMap::kMaxSize)
+ thread_stats.malloc_large++;
+ else
+ thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++;
+
+ void *res = reinterpret_cast<void *>(user_beg);
+ if (can_fill && fl.max_malloc_fill_size) {
+ uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size);
+ REAL(memset)(res, fl.malloc_fill_byte, fill_size);
+ }
+#if CAN_SANITIZE_LEAKS
+ m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored
+ : __lsan::kDirectlyLeaked;
+#endif
+ // Must be the last mutation of metadata in this function.
+ atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release);
+ ASAN_MALLOC_HOOK(res, size);
+ return res;
+ }
+
+ // Set quarantine flag if chunk is allocated, issue ASan error report on
+ // available and quarantined chunks. Return true on success, false otherwise.
+ bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr,
+ BufferedStackTrace *stack) {
+ u8 old_chunk_state = CHUNK_ALLOCATED;
+ // Flip the chunk_state atomically to avoid race on double-free.
+ if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state,
+ CHUNK_QUARANTINE,
+ memory_order_acquire)) {
+ ReportInvalidFree(ptr, old_chunk_state, stack);
+ // It's not safe to push a chunk in quarantine on invalid free.
+ return false;
+ }
+ CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state);
+ return true;
+ }
+
+ // Expects the chunk to already be marked as quarantined by using
+ // AtomicallySetQuarantineFlagIfAllocated.
+ void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) {
+ CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
+ CHECK_GE(m->alloc_tid, 0);
+ if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area.
+ CHECK_EQ(m->free_tid, kInvalidTid);
+ AsanThread *t = GetCurrentThread();
+ m->free_tid = t ? t->tid() : 0;
+ m->free_context_id = StackDepotPut(*stack);
+
+ Flags &fl = *flags();
+ if (fl.max_free_fill_size > 0) {
+ // We have to skip the chunk header, it contains free_context_id.
+ uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size;
+ if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area.
+ uptr size_to_fill = m->UsedSize() - kChunkHeader2Size;
+ size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size);
+ REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill);
+ }
+ }
+
+ // Poison the region.
+ PoisonShadow(m->Beg(),
+ RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
+ kAsanHeapFreeMagic);
+
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.frees++;
+ thread_stats.freed += m->UsedSize();
+
+ // Push into quarantine.
+ if (t) {
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac, stack), m,
+ m->UsedSize());
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *ac = &fallback_allocator_cache;
+ quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac, stack),
+ m, m->UsedSize());
+ }
+ }
+
+ void Deallocate(void *ptr, uptr delete_size, uptr delete_alignment,
+ BufferedStackTrace *stack, AllocType alloc_type) {
+ uptr p = reinterpret_cast<uptr>(ptr);
+ if (p == 0) return;
+
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ // On Windows, uninstrumented DLLs may allocate memory before ASan hooks
+ // malloc. Don't report an invalid free in this case.
+ if (SANITIZER_WINDOWS &&
+ !get_allocator().PointerIsMine(ptr)) {
+ if (!IsSystemHeapAddress(p))
+ ReportFreeNotMalloced(p, stack);
+ return;
+ }
+
+ ASAN_FREE_HOOK(ptr);
+
+ // Must mark the chunk as quarantined before any changes to its metadata.
+ // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag.
+ if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return;
+
+ if (m->alloc_type != alloc_type) {
+ if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) {
+ ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type,
+ (AllocType)alloc_type);
+ }
+ } else {
+ if (flags()->new_delete_type_mismatch &&
+ (alloc_type == FROM_NEW || alloc_type == FROM_NEW_BR) &&
+ ((delete_size && delete_size != m->UsedSize()) ||
+ ComputeUserRequestedAlignmentLog(delete_alignment) !=
+ m->user_requested_alignment_log)) {
+ ReportNewDeleteTypeMismatch(p, delete_size, delete_alignment, stack);
+ }
+ }
+
+ QuarantineChunk(m, ptr, stack);
+ }
+
+ void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) {
+ CHECK(old_ptr && new_size);
+ uptr p = reinterpret_cast<uptr>(old_ptr);
+ uptr chunk_beg = p - kChunkHeaderSize;
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(chunk_beg);
+
+ AsanStats &thread_stats = GetCurrentThreadStats();
+ thread_stats.reallocs++;
+ thread_stats.realloced += new_size;
+
+ void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true);
+ if (new_ptr) {
+ u8 chunk_state = m->chunk_state;
+ if (chunk_state != CHUNK_ALLOCATED)
+ ReportInvalidFree(old_ptr, chunk_state, stack);
+ CHECK_NE(REAL(memcpy), nullptr);
+ uptr memcpy_size = Min(new_size, m->UsedSize());
+ // If realloc() races with free(), we may start copying freed memory.
+ // However, we will report racy double-free later anyway.
+ REAL(memcpy)(new_ptr, old_ptr, memcpy_size);
+ Deallocate(old_ptr, 0, 0, stack, FROM_MALLOC);
+ }
+ return new_ptr;
+ }
+
+ void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportCallocOverflow(nmemb, size, stack);
+ }
+ void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
+ // If the memory comes from the secondary allocator no need to clear it
+ // as it comes directly from mmap.
+ if (ptr && allocator.FromPrimary(ptr))
+ REAL(memset)(ptr, 0, nmemb * size);
+ return ptr;
+ }
+
+ void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) {
+ if (chunk_state == CHUNK_QUARANTINE)
+ ReportDoubleFree((uptr)ptr, stack);
+ else
+ ReportFreeNotMalloced((uptr)ptr, stack);
+ }
+
+ void CommitBack(AsanThreadLocalMallocStorage *ms, BufferedStackTrace *stack) {
+ AllocatorCache *ac = GetAllocatorCache(ms);
+ quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac, stack));
+ allocator.SwallowCache(ac);
+ }
+
+ // -------------------------- Chunk lookup ----------------------
+
+ // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
+ AsanChunk *GetAsanChunk(void *alloc_beg) {
+ if (!alloc_beg) return nullptr;
+ if (!allocator.FromPrimary(alloc_beg)) {
+ uptr *meta = reinterpret_cast<uptr *>(allocator.GetMetaData(alloc_beg));
+ AsanChunk *m = reinterpret_cast<AsanChunk *>(meta[1]);
+ return m;
+ }
+ uptr *alloc_magic = reinterpret_cast<uptr *>(alloc_beg);
+ if (alloc_magic[0] == kAllocBegMagic)
+ return reinterpret_cast<AsanChunk *>(alloc_magic[1]);
+ return reinterpret_cast<AsanChunk *>(alloc_beg);
+ }
+
+ AsanChunk *GetAsanChunkByAddr(uptr p) {
+ void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast<void *>(p));
+ return GetAsanChunk(alloc_beg);
+ }
+
+ // Allocator must be locked when this function is called.
+ AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) {
+ void *alloc_beg =
+ allocator.GetBlockBeginFastLocked(reinterpret_cast<void *>(p));
+ return GetAsanChunk(alloc_beg);
+ }
+
+ uptr AllocationSize(uptr p) {
+ AsanChunk *m = GetAsanChunkByAddr(p);
+ if (!m) return 0;
+ if (m->chunk_state != CHUNK_ALLOCATED) return 0;
+ if (m->Beg() != p) return 0;
+ return m->UsedSize();
+ }
+
+ AsanChunkView FindHeapChunkByAddress(uptr addr) {
+ AsanChunk *m1 = GetAsanChunkByAddr(addr);
+ if (!m1) return AsanChunkView(m1);
+ sptr offset = 0;
+ if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) {
+ // The address is in the chunk's left redzone, so maybe it is actually
+ // a right buffer overflow from the other chunk to the left.
+ // Search a bit to the left to see if there is another chunk.
+ AsanChunk *m2 = nullptr;
+ for (uptr l = 1; l < GetPageSizeCached(); l++) {
+ m2 = GetAsanChunkByAddr(addr - l);
+ if (m2 == m1) continue; // Still the same chunk.
+ break;
+ }
+ if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset))
+ m1 = ChooseChunk(addr, m2, m1);
+ }
+ return AsanChunkView(m1);
+ }
+
+ void Purge(BufferedStackTrace *stack) {
+ AsanThread *t = GetCurrentThread();
+ if (t) {
+ AsanThreadLocalMallocStorage *ms = &t->malloc_storage();
+ quarantine.DrainAndRecycle(GetQuarantineCache(ms),
+ QuarantineCallback(GetAllocatorCache(ms),
+ stack));
+ }
+ {
+ SpinMutexLock l(&fallback_mutex);
+ quarantine.DrainAndRecycle(&fallback_quarantine_cache,
+ QuarantineCallback(&fallback_allocator_cache,
+ stack));
+ }
+
+ allocator.ForceReleaseToOS();
+ }
+
+ void PrintStats() {
+ allocator.PrintStats();
+ quarantine.PrintStats();
+ }
+
+ void ForceLock() {
+ allocator.ForceLock();
+ fallback_mutex.Lock();
+ }
+
+ void ForceUnlock() {
+ fallback_mutex.Unlock();
+ allocator.ForceUnlock();
+ }
+};
+
+static Allocator instance(LINKER_INITIALIZED);
+
+static AsanAllocator &get_allocator() {
+ return instance.allocator;
+}
+
+bool AsanChunkView::IsValid() const {
+ return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE;
+}
+bool AsanChunkView::IsAllocated() const {
+ return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED;
+}
+bool AsanChunkView::IsQuarantined() const {
+ return chunk_ && chunk_->chunk_state == CHUNK_QUARANTINE;
+}
+uptr AsanChunkView::Beg() const { return chunk_->Beg(); }
+uptr AsanChunkView::End() const { return Beg() + UsedSize(); }
+uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); }
+u32 AsanChunkView::UserRequestedAlignment() const {
+ return Allocator::ComputeUserAlignment(chunk_->user_requested_alignment_log);
+}
+uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; }
+uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; }
+AllocType AsanChunkView::GetAllocType() const {
+ return (AllocType)chunk_->alloc_type;
+}
+
+static StackTrace GetStackTraceFromId(u32 id) {
+ CHECK(id);
+ StackTrace res = StackDepotGet(id);
+ CHECK(res.trace);
+ return res;
+}
+
+u32 AsanChunkView::GetAllocStackId() const { return chunk_->alloc_context_id; }
+u32 AsanChunkView::GetFreeStackId() const { return chunk_->free_context_id; }
+
+StackTrace AsanChunkView::GetAllocStack() const {
+ return GetStackTraceFromId(GetAllocStackId());
+}
+
+StackTrace AsanChunkView::GetFreeStack() const {
+ return GetStackTraceFromId(GetFreeStackId());
+}
+
+void InitializeAllocator(const AllocatorOptions &options) {
+ instance.InitLinkerInitialized(options);
+}
+
+void ReInitializeAllocator(const AllocatorOptions &options) {
+ instance.ReInitialize(options);
+}
+
+void GetAllocatorOptions(AllocatorOptions *options) {
+ instance.GetOptions(options);
+}
+
+AsanChunkView FindHeapChunkByAddress(uptr addr) {
+ return instance.FindHeapChunkByAddress(addr);
+}
+AsanChunkView FindHeapChunkByAllocBeg(uptr addr) {
+ return AsanChunkView(instance.GetAsanChunk(reinterpret_cast<void*>(addr)));
+}
+
+void AsanThreadLocalMallocStorage::CommitBack() {
+ GET_STACK_TRACE_MALLOC;
+ instance.CommitBack(this, &stack);
+}
+
+void PrintInternalAllocatorStats() {
+ instance.PrintStats();
+}
+
+void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
+ instance.Deallocate(ptr, 0, 0, stack, alloc_type);
+}
+
+void asan_delete(void *ptr, uptr size, uptr alignment,
+ BufferedStackTrace *stack, AllocType alloc_type) {
+ instance.Deallocate(ptr, size, alignment, stack, alloc_type);
+}
+
+void *asan_malloc(uptr size, BufferedStackTrace *stack) {
+ return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true));
+}
+
+void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
+ return SetErrnoOnNull(instance.Calloc(nmemb, size, stack));
+}
+
+void *asan_reallocarray(void *p, uptr nmemb, uptr size,
+ BufferedStackTrace *stack) {
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportReallocArrayOverflow(nmemb, size, stack);
+ }
+ return asan_realloc(p, nmemb * size, stack);
+}
+
+void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
+ if (!p)
+ return SetErrnoOnNull(instance.Allocate(size, 8, stack, FROM_MALLOC, true));
+ if (size == 0) {
+ if (flags()->allocator_frees_and_returns_null_on_realloc_zero) {
+ instance.Deallocate(p, 0, 0, stack, FROM_MALLOC);
+ return nullptr;
+ }
+ // Allocate a size of 1 if we shouldn't free() on Realloc to 0
+ size = 1;
+ }
+ return SetErrnoOnNull(instance.Reallocate(p, size, stack));
+}
+
+void *asan_valloc(uptr size, BufferedStackTrace *stack) {
+ return SetErrnoOnNull(
+ instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true));
+}
+
+void *asan_pvalloc(uptr size, BufferedStackTrace *stack) {
+ uptr PageSize = GetPageSizeCached();
+ if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportPvallocOverflow(size, stack);
+ }
+ // pvalloc(0) should allocate one page.
+ size = size ? RoundUpTo(size, PageSize) : PageSize;
+ return SetErrnoOnNull(
+ instance.Allocate(size, PageSize, stack, FROM_MALLOC, true));
+}
+
+void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
+ AllocType alloc_type) {
+ if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportInvalidAllocationAlignment(alignment, stack);
+ }
+ return SetErrnoOnNull(
+ instance.Allocate(size, alignment, stack, alloc_type, true));
+}
+
+void *asan_aligned_alloc(uptr alignment, uptr size, BufferedStackTrace *stack) {
+ if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportInvalidAlignedAllocAlignment(size, alignment, stack);
+ }
+ return SetErrnoOnNull(
+ instance.Allocate(size, alignment, stack, FROM_MALLOC, true));
+}
+
+int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
+ BufferedStackTrace *stack) {
+ if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
+ if (AllocatorMayReturnNull())
+ return errno_EINVAL;
+ ReportInvalidPosixMemalignAlignment(alignment, stack);
+ }
+ void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true);
+ if (UNLIKELY(!ptr))
+ // OOM error is already taken care of by Allocate.
+ return errno_ENOMEM;
+ CHECK(IsAligned((uptr)ptr, alignment));
+ *memptr = ptr;
+ return 0;
+}
+
+uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) {
+ if (!ptr) return 0;
+ uptr usable_size = instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+ if (flags()->check_malloc_usable_size && (usable_size == 0)) {
+ GET_STACK_TRACE_FATAL(pc, bp);
+ ReportMallocUsableSizeNotOwned((uptr)ptr, &stack);
+ }
+ return usable_size;
+}
+
+uptr asan_mz_size(const void *ptr) {
+ return instance.AllocationSize(reinterpret_cast<uptr>(ptr));
+}
+
+void asan_mz_force_lock() {
+ instance.ForceLock();
+}
+
+void asan_mz_force_unlock() {
+ instance.ForceUnlock();
+}
+
+void AsanSoftRssLimitExceededCallback(bool limit_exceeded) {
+ instance.SetRssLimitExceeded(limit_exceeded);
+}
+
+} // namespace __asan
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+void LockAllocator() {
+ __asan::get_allocator().ForceLock();
+}
+
+void UnlockAllocator() {
+ __asan::get_allocator().ForceUnlock();
+}
+
+void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
+ *begin = (uptr)&__asan::get_allocator();
+ *end = *begin + sizeof(__asan::get_allocator());
+}
+
+uptr PointsIntoChunk(void* p) {
+ uptr addr = reinterpret_cast<uptr>(p);
+ __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr);
+ if (!m) return 0;
+ uptr chunk = m->Beg();
+ if (m->chunk_state != __asan::CHUNK_ALLOCATED)
+ return 0;
+ if (m->AddrIsInside(addr, /*locked_version=*/true))
+ return chunk;
+ if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true),
+ addr))
+ return chunk;
+ return 0;
+}
+
+uptr GetUserBegin(uptr chunk) {
+ __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk);
+ CHECK(m);
+ return m->Beg();
+}
+
+LsanMetadata::LsanMetadata(uptr chunk) {
+ metadata_ = reinterpret_cast<void *>(chunk - __asan::kChunkHeaderSize);
+}
+
+bool LsanMetadata::allocated() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->chunk_state == __asan::CHUNK_ALLOCATED;
+}
+
+ChunkTag LsanMetadata::tag() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return static_cast<ChunkTag>(m->lsan_tag);
+}
+
+void LsanMetadata::set_tag(ChunkTag value) {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ m->lsan_tag = value;
+}
+
+uptr LsanMetadata::requested_size() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->UsedSize(/*locked_version=*/true);
+}
+
+u32 LsanMetadata::stack_trace_id() const {
+ __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_);
+ return m->alloc_context_id;
+}
+
+void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+ __asan::get_allocator().ForEachChunk(callback, arg);
+}
+
+IgnoreObjectResult IgnoreObjectLocked(const void *p) {
+ uptr addr = reinterpret_cast<uptr>(p);
+ __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr);
+ if (!m) return kIgnoreObjectInvalid;
+ if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) {
+ if (m->lsan_tag == kIgnored)
+ return kIgnoreObjectAlreadyIgnored;
+ m->lsan_tag = __lsan::kIgnored;
+ return kIgnoreObjectSuccess;
+ } else {
+ return kIgnoreObjectInvalid;
+ }
+}
+} // namespace __lsan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// ASan allocator doesn't reserve extra bytes, so normally we would
+// just return "size". We don't want to expose our redzone sizes, etc here.
+uptr __sanitizer_get_estimated_allocated_size(uptr size) {
+ return size;
+}
+
+int __sanitizer_get_ownership(const void *p) {
+ uptr ptr = reinterpret_cast<uptr>(p);
+ return instance.AllocationSize(ptr) > 0;
+}
+
+uptr __sanitizer_get_allocated_size(const void *p) {
+ if (!p) return 0;
+ uptr ptr = reinterpret_cast<uptr>(p);
+ uptr allocated_size = instance.AllocationSize(ptr);
+ // Die if p is not malloced or if it is already freed.
+ if (allocated_size == 0) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack);
+ }
+ return allocated_size;
+}
+
+void __sanitizer_purge_allocator() {
+ GET_STACK_TRACE_MALLOC;
+ instance.Purge(&stack);
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook,
+ void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) {
+ (void)ptr;
+}
+#endif
//===-- asan_allocator.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_allocator.cc.
+// ASan-private header for asan_allocator.cpp.
//===----------------------------------------------------------------------===//
#ifndef ASAN_ALLOCATOR_H
const uptr kAllocatorSpace = 0x10000000000ULL;
const uptr kAllocatorSize = 0x10000000000ULL; // 3T.
typedef DefaultSizeClassMap SizeClassMap;
-# elif defined(__sparc__)
+#elif defined(__sparc__)
const uptr kAllocatorSpace = ~(uptr)0;
-const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
+const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
typedef DefaultSizeClassMap SizeClassMap;
# elif SANITIZER_WINDOWS
const uptr kAllocatorSpace = ~(uptr)0;
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
typedef DefaultSizeClassMap SizeClassMap;
# endif
+template <typename AddressSpaceViewTy>
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
typedef __asan::SizeClassMap SizeClassMap;
typedef AsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
-typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#else // Fallback to SizeClassAllocator32.
-static const uptr kRegionSizeLog = 20;
-static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
-# if SANITIZER_WORDSIZE == 32
-typedef FlatByteMap<kNumRegions> ByteMap;
-# elif SANITIZER_WORDSIZE == 64
-typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
-# endif
typedef CompactSizeClassMap SizeClassMap;
+template <typename AddressSpaceViewTy>
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = 16;
typedef __asan::SizeClassMap SizeClassMap;
- static const uptr kRegionSizeLog = __asan::kRegionSizeLog;
- typedef __asan::ByteMap ByteMap;
+ static const uptr kRegionSizeLog = 20;
+ using AddressSpaceView = AddressSpaceViewTy;
typedef AsanMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
-typedef SizeClassAllocator32<AP32> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView> >;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#endif // SANITIZER_CAN_USE_ALLOCATOR64
static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses;
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> AsanAllocator;
+template <typename AddressSpaceView>
+using AsanAllocatorASVT =
+ CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>>;
+using AsanAllocator = AsanAllocatorASVT<LocalAddressSpaceView>;
+using AllocatorCache = AsanAllocator::AllocatorCache;
struct AsanThreadLocalMallocStorage {
uptr quarantine_cache[16];
void *asan_malloc(uptr size, BufferedStackTrace *stack);
void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);
void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack);
+void *asan_reallocarray(void *p, uptr nmemb, uptr size,
+ BufferedStackTrace *stack);
void *asan_valloc(uptr size, BufferedStackTrace *stack);
void *asan_pvalloc(uptr size, BufferedStackTrace *stack);
+++ /dev/null
-//===-- asan_debugging.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// This file contains various functions that are generally useful to call when
-// using a debugger (LLDB, GDB).
-//===----------------------------------------------------------------------===//
-
-#include "asan_allocator.h"
-#include "asan_descriptions.h"
-#include "asan_flags.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_report.h"
-#include "asan_thread.h"
-
-namespace {
-using namespace __asan;
-
-static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset,
- char *name, uptr name_size,
- uptr ®ion_address, uptr ®ion_size) {
- InternalMmapVector<StackVarDescr> vars;
- vars.reserve(16);
- if (!ParseFrameDescription(frame_descr, &vars)) {
- return;
- }
-
- for (uptr i = 0; i < vars.size(); i++) {
- if (offset <= vars[i].beg + vars[i].size) {
- // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so
- // if we're limiting the copy due to name_len, we add 1 to ensure we copy
- // the whole name and then terminate with '\0'.
- internal_strlcpy(name, vars[i].name_pos,
- Min(name_size, vars[i].name_len + 1));
- region_address = addr - (offset - vars[i].beg);
- region_size = vars[i].size;
- return;
- }
- }
-}
-
-uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
- bool alloc_stack) {
- AsanChunkView chunk = FindHeapChunkByAddress(addr);
- if (!chunk.IsValid()) return 0;
-
- StackTrace stack(nullptr, 0);
- if (alloc_stack) {
- if (chunk.AllocTid() == kInvalidTid) return 0;
- stack = chunk.GetAllocStack();
- if (thread_id) *thread_id = chunk.AllocTid();
- } else {
- if (chunk.FreeTid() == kInvalidTid) return 0;
- stack = chunk.GetFreeStack();
- if (thread_id) *thread_id = chunk.FreeTid();
- }
-
- if (trace && size) {
- size = Min(size, Min(stack.size, kStackTraceMax));
- for (uptr i = 0; i < size; i++)
- trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
-
- return size;
- }
-
- return 0;
-}
-
-} // namespace
-
-SANITIZER_INTERFACE_ATTRIBUTE
-const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
- uptr *region_address_ptr,
- uptr *region_size_ptr) {
- AddressDescription descr(addr);
- uptr region_address = 0;
- uptr region_size = 0;
- const char *region_kind = nullptr;
- if (name && name_size > 0) name[0] = 0;
-
- if (auto shadow = descr.AsShadow()) {
- // region_{address,size} are already 0
- switch (shadow->kind) {
- case kShadowKindLow:
- region_kind = "low shadow";
- break;
- case kShadowKindGap:
- region_kind = "shadow gap";
- break;
- case kShadowKindHigh:
- region_kind = "high shadow";
- break;
- }
- } else if (auto heap = descr.AsHeap()) {
- region_kind = "heap";
- region_address = heap->chunk_access.chunk_begin;
- region_size = heap->chunk_access.chunk_size;
- } else if (auto stack = descr.AsStack()) {
- region_kind = "stack";
- if (!stack->frame_descr) {
- // region_{address,size} are already 0
- } else {
- FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name,
- name_size, region_address, region_size);
- }
- } else if (auto global = descr.AsGlobal()) {
- region_kind = "global";
- auto &g = global->globals[0];
- internal_strlcpy(name, g.name, name_size);
- region_address = g.beg;
- region_size = g.size;
- } else {
- // region_{address,size} are already 0
- region_kind = "heap-invalid";
- }
-
- CHECK(region_kind);
- if (region_address_ptr) *region_address_ptr = region_address;
- if (region_size_ptr) *region_size_ptr = region_size;
- return region_kind;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
- return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
- return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) {
- if (shadow_scale)
- *shadow_scale = SHADOW_SCALE;
- if (shadow_offset)
- *shadow_offset = SHADOW_OFFSET;
-}
--- /dev/null
+//===-- asan_debugging.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file contains various functions that are generally useful to call when
+// using a debugger (LLDB, GDB).
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_descriptions.h"
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_thread.h"
+
+namespace {
+using namespace __asan;
+
+static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset,
+ char *name, uptr name_size,
+ uptr ®ion_address, uptr ®ion_size) {
+ InternalMmapVector<StackVarDescr> vars;
+ vars.reserve(16);
+ if (!ParseFrameDescription(frame_descr, &vars)) {
+ return;
+ }
+
+ for (uptr i = 0; i < vars.size(); i++) {
+ if (offset <= vars[i].beg + vars[i].size) {
+ // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so
+ // if we're limiting the copy due to name_len, we add 1 to ensure we copy
+ // the whole name and then terminate with '\0'.
+ internal_strlcpy(name, vars[i].name_pos,
+ Min(name_size, vars[i].name_len + 1));
+ region_address = addr - (offset - vars[i].beg);
+ region_size = vars[i].size;
+ return;
+ }
+ }
+}
+
+uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
+ bool alloc_stack) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+ if (!chunk.IsValid()) return 0;
+
+ StackTrace stack(nullptr, 0);
+ if (alloc_stack) {
+ if (chunk.AllocTid() == kInvalidTid) return 0;
+ stack = chunk.GetAllocStack();
+ if (thread_id) *thread_id = chunk.AllocTid();
+ } else {
+ if (chunk.FreeTid() == kInvalidTid) return 0;
+ stack = chunk.GetFreeStack();
+ if (thread_id) *thread_id = chunk.FreeTid();
+ }
+
+ if (trace && size) {
+ size = Min(size, Min(stack.size, kStackTraceMax));
+ for (uptr i = 0; i < size; i++)
+ trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
+
+ return size;
+ }
+
+ return 0;
+}
+
+} // namespace
+
+SANITIZER_INTERFACE_ATTRIBUTE
+const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
+ uptr *region_address_ptr,
+ uptr *region_size_ptr) {
+ AddressDescription descr(addr);
+ uptr region_address = 0;
+ uptr region_size = 0;
+ const char *region_kind = nullptr;
+ if (name && name_size > 0) name[0] = 0;
+
+ if (auto shadow = descr.AsShadow()) {
+ // region_{address,size} are already 0
+ switch (shadow->kind) {
+ case kShadowKindLow:
+ region_kind = "low shadow";
+ break;
+ case kShadowKindGap:
+ region_kind = "shadow gap";
+ break;
+ case kShadowKindHigh:
+ region_kind = "high shadow";
+ break;
+ }
+ } else if (auto heap = descr.AsHeap()) {
+ region_kind = "heap";
+ region_address = heap->chunk_access.chunk_begin;
+ region_size = heap->chunk_access.chunk_size;
+ } else if (auto stack = descr.AsStack()) {
+ region_kind = "stack";
+ if (!stack->frame_descr) {
+ // region_{address,size} are already 0
+ } else {
+ FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name,
+ name_size, region_address, region_size);
+ }
+ } else if (auto global = descr.AsGlobal()) {
+ region_kind = "global";
+ auto &g = global->globals[0];
+ internal_strlcpy(name, g.name, name_size);
+ region_address = g.beg;
+ region_size = g.size;
+ } else {
+ // region_{address,size} are already 0
+ region_kind = "heap-invalid";
+ }
+
+ CHECK(region_kind);
+ if (region_address_ptr) *region_address_ptr = region_address;
+ if (region_size_ptr) *region_size_ptr = region_size;
+ return region_kind;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+ return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+ return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) {
+ if (shadow_scale)
+ *shadow_scale = SHADOW_SCALE;
+ if (shadow_offset)
+ *shadow_offset = SHADOW_OFFSET;
+}
+++ /dev/null
-//===-- asan_descriptions.cc ------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// ASan functions for getting information about an address and/or printing it.
-//===----------------------------------------------------------------------===//
-
-#include "asan_descriptions.h"
-#include "asan_mapping.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-
-namespace __asan {
-
-AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {
- Init(t->tid, t->name);
-}
-
-AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) {
- if (tid == kInvalidTid) {
- Init(tid, "");
- } else {
- asanThreadRegistry().CheckLocked();
- AsanThreadContext *t = GetThreadContextByTidLocked(tid);
- Init(tid, t->name);
- }
-}
-
-void AsanThreadIdAndName::Init(u32 tid, const char *tname) {
- int len = internal_snprintf(name, sizeof(name), "T%d", tid);
- CHECK(((unsigned int)len) < sizeof(name));
- if (tname[0] != '\0')
- internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname);
-}
-
-void DescribeThread(AsanThreadContext *context) {
- CHECK(context);
- asanThreadRegistry().CheckLocked();
- // No need to announce the main thread.
- if (context->tid == 0 || context->announced) {
- return;
- }
- context->announced = true;
- InternalScopedString str(1024);
- str.append("Thread %s", AsanThreadIdAndName(context).c_str());
- if (context->parent_tid == kInvalidTid) {
- str.append(" created by unknown thread\n");
- Printf("%s", str.data());
- return;
- }
- str.append(" created by %s here:\n",
- AsanThreadIdAndName(context->parent_tid).c_str());
- Printf("%s", str.data());
- StackDepotGet(context->stack_id).Print();
- // Recursively described parent thread if needed.
- if (flags()->print_full_thread_history) {
- AsanThreadContext *parent_context =
- GetThreadContextByTidLocked(context->parent_tid);
- DescribeThread(parent_context);
- }
-}
-
-// Shadow descriptions
-static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {
- CHECK(!AddrIsInMem(addr));
- if (AddrIsInShadowGap(addr)) {
- *shadow_kind = kShadowKindGap;
- } else if (AddrIsInHighShadow(addr)) {
- *shadow_kind = kShadowKindHigh;
- } else if (AddrIsInLowShadow(addr)) {
- *shadow_kind = kShadowKindLow;
- } else {
- CHECK(0 && "Address is not in memory and not in shadow?");
- return false;
- }
- return true;
-}
-
-bool DescribeAddressIfShadow(uptr addr) {
- ShadowAddressDescription descr;
- if (!GetShadowAddressInformation(addr, &descr)) return false;
- descr.Print();
- return true;
-}
-
-bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {
- if (AddrIsInMem(addr)) return false;
- ShadowKind shadow_kind;
- if (!GetShadowKind(addr, &shadow_kind)) return false;
- if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;
- descr->addr = addr;
- descr->kind = shadow_kind;
- return true;
-}
-
-// Heap descriptions
-static void GetAccessToHeapChunkInformation(ChunkAccess *descr,
- AsanChunkView chunk, uptr addr,
- uptr access_size) {
- descr->bad_addr = addr;
- if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {
- descr->access_type = kAccessTypeLeft;
- } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {
- descr->access_type = kAccessTypeRight;
- if (descr->offset < 0) {
- descr->bad_addr -= descr->offset;
- descr->offset = 0;
- }
- } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {
- descr->access_type = kAccessTypeInside;
- } else {
- descr->access_type = kAccessTypeUnknown;
- }
- descr->chunk_begin = chunk.Beg();
- descr->chunk_size = chunk.UsedSize();
- descr->user_requested_alignment = chunk.UserRequestedAlignment();
- descr->alloc_type = chunk.GetAllocType();
-}
-
-static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
- Decorator d;
- InternalScopedString str(4096);
- str.append("%s", d.Location());
- switch (descr.access_type) {
- case kAccessTypeLeft:
- str.append("%p is located %zd bytes to the left of",
- (void *)descr.bad_addr, descr.offset);
- break;
- case kAccessTypeRight:
- str.append("%p is located %zd bytes to the right of",
- (void *)descr.bad_addr, descr.offset);
- break;
- case kAccessTypeInside:
- str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
- descr.offset);
- break;
- case kAccessTypeUnknown:
- str.append(
- "%p is located somewhere around (this is AddressSanitizer bug!)",
- (void *)descr.bad_addr);
- }
- str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size,
- (void *)descr.chunk_begin,
- (void *)(descr.chunk_begin + descr.chunk_size));
- str.append("%s", d.Default());
- Printf("%s", str.data());
-}
-
-bool GetHeapAddressInformation(uptr addr, uptr access_size,
- HeapAddressDescription *descr) {
- AsanChunkView chunk = FindHeapChunkByAddress(addr);
- if (!chunk.IsValid()) {
- return false;
- }
- descr->addr = addr;
- GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,
- access_size);
- CHECK_NE(chunk.AllocTid(), kInvalidTid);
- descr->alloc_tid = chunk.AllocTid();
- descr->alloc_stack_id = chunk.GetAllocStackId();
- descr->free_tid = chunk.FreeTid();
- if (descr->free_tid != kInvalidTid)
- descr->free_stack_id = chunk.GetFreeStackId();
- return true;
-}
-
-static StackTrace GetStackTraceFromId(u32 id) {
- CHECK(id);
- StackTrace res = StackDepotGet(id);
- CHECK(res.trace);
- return res;
-}
-
-bool DescribeAddressIfHeap(uptr addr, uptr access_size) {
- HeapAddressDescription descr;
- if (!GetHeapAddressInformation(addr, access_size, &descr)) {
- Printf(
- "AddressSanitizer can not describe address in more detail "
- "(wild memory access suspected).\n");
- return false;
- }
- descr.Print();
- return true;
-}
-
-// Stack descriptions
-bool GetStackAddressInformation(uptr addr, uptr access_size,
- StackAddressDescription *descr) {
- AsanThread *t = FindThreadByStackAddress(addr);
- if (!t) return false;
-
- descr->addr = addr;
- descr->tid = t->tid();
- // Try to fetch precise stack frame for this access.
- AsanThread::StackFrameAccess access;
- if (!t->GetStackFrameAccessByAddr(addr, &access)) {
- descr->frame_descr = nullptr;
- return true;
- }
-
- descr->offset = access.offset;
- descr->access_size = access_size;
- descr->frame_pc = access.frame_pc;
- descr->frame_descr = access.frame_descr;
-
-#if SANITIZER_PPC64V1
- // On PowerPC64 ELFv1, the address of a function actually points to a
- // three-doubleword data structure with the first field containing
- // the address of the function's code.
- descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);
-#endif
- descr->frame_pc += 16;
-
- return true;
-}
-
-static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
- uptr access_size, uptr prev_var_end,
- uptr next_var_beg) {
- uptr var_end = var.beg + var.size;
- uptr addr_end = addr + access_size;
- const char *pos_descr = nullptr;
- // If the variable [var.beg, var_end) is the nearest variable to the
- // current memory access, indicate it in the log.
- if (addr >= var.beg) {
- if (addr_end <= var_end)
- pos_descr = "is inside"; // May happen if this is a use-after-return.
- else if (addr < var_end)
- pos_descr = "partially overflows";
- else if (addr_end <= next_var_beg &&
- next_var_beg - addr_end >= addr - var_end)
- pos_descr = "overflows";
- } else {
- if (addr_end > var.beg)
- pos_descr = "partially underflows";
- else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)
- pos_descr = "underflows";
- }
- InternalScopedString str(1024);
- str.append(" [%zd, %zd)", var.beg, var_end);
- // Render variable name.
- str.append(" '");
- for (uptr i = 0; i < var.name_len; ++i) {
- str.append("%c", var.name_pos[i]);
- }
- str.append("'");
- if (var.line > 0) {
- str.append(" (line %d)", var.line);
- }
- if (pos_descr) {
- Decorator d;
- // FIXME: we may want to also print the size of the access here,
- // but in case of accesses generated by memset it may be confusing.
- str.append("%s <== Memory access at offset %zd %s this variable%s\n",
- d.Location(), addr, pos_descr, d.Default());
- } else {
- str.append("\n");
- }
- Printf("%s", str.data());
-}
-
-bool DescribeAddressIfStack(uptr addr, uptr access_size) {
- StackAddressDescription descr;
- if (!GetStackAddressInformation(addr, access_size, &descr)) return false;
- descr.Print();
- return true;
-}
-
-// Global descriptions
-static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
- const __asan_global &g) {
- InternalScopedString str(4096);
- Decorator d;
- str.append("%s", d.Location());
- if (addr < g.beg) {
- str.append("%p is located %zd bytes to the left", (void *)addr,
- g.beg - addr);
- } else if (addr + access_size > g.beg + g.size) {
- if (addr < g.beg + g.size) addr = g.beg + g.size;
- str.append("%p is located %zd bytes to the right", (void *)addr,
- addr - (g.beg + g.size));
- } else {
- // Can it happen?
- str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
- }
- str.append(" of global variable '%s' defined in '",
- MaybeDemangleGlobalName(g.name));
- PrintGlobalLocation(&str, g);
- str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
- str.append("%s", d.Default());
- PrintGlobalNameIfASCII(&str, g);
- Printf("%s", str.data());
-}
-
-bool GetGlobalAddressInformation(uptr addr, uptr access_size,
- GlobalAddressDescription *descr) {
- descr->addr = addr;
- int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,
- ARRAY_SIZE(descr->globals));
- descr->size = globals_num;
- descr->access_size = access_size;
- return globals_num != 0;
-}
-
-bool DescribeAddressIfGlobal(uptr addr, uptr access_size,
- const char *bug_type) {
- GlobalAddressDescription descr;
- if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;
-
- descr.Print(bug_type);
- return true;
-}
-
-void ShadowAddressDescription::Print() const {
- Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
-}
-
-void GlobalAddressDescription::Print(const char *bug_type) const {
- for (int i = 0; i < size; i++) {
- DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
- if (bug_type &&
- 0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
- reg_sites[i]) {
- Printf(" registered at:\n");
- StackDepotGet(reg_sites[i]).Print();
- }
- }
-}
-
-bool GlobalAddressDescription::PointsInsideTheSameVariable(
- const GlobalAddressDescription &other) const {
- if (size == 0 || other.size == 0) return false;
-
- for (uptr i = 0; i < size; i++) {
- const __asan_global &a = globals[i];
- for (uptr j = 0; j < other.size; j++) {
- const __asan_global &b = other.globals[j];
- if (a.beg == b.beg &&
- a.beg <= addr &&
- b.beg <= other.addr &&
- (addr + access_size) < (a.beg + a.size) &&
- (other.addr + other.access_size) < (b.beg + b.size))
- return true;
- }
- }
-
- return false;
-}
-
-void StackAddressDescription::Print() const {
- Decorator d;
- Printf("%s", d.Location());
- Printf("Address %p is located in stack of thread %s", addr,
- AsanThreadIdAndName(tid).c_str());
-
- if (!frame_descr) {
- Printf("%s\n", d.Default());
- return;
- }
- Printf(" at offset %zu in frame%s\n", offset, d.Default());
-
- // Now we print the frame where the alloca has happened.
- // We print this frame as a stack trace with one element.
- // The symbolizer may print more than one frame if inlining was involved.
- // The frame numbers may be different than those in the stack trace printed
- // previously. That's unfortunate, but I have no better solution,
- // especially given that the alloca may be from entirely different place
- // (e.g. use-after-scope, or different thread's stack).
- Printf("%s", d.Default());
- StackTrace alloca_stack(&frame_pc, 1);
- alloca_stack.Print();
-
- InternalMmapVector<StackVarDescr> vars;
- vars.reserve(16);
- if (!ParseFrameDescription(frame_descr, &vars)) {
- Printf(
- "AddressSanitizer can't parse the stack frame "
- "descriptor: |%s|\n",
- frame_descr);
- // 'addr' is a stack address, so return true even if we can't parse frame
- return;
- }
- uptr n_objects = vars.size();
- // Report the number of stack objects.
- Printf(" This frame has %zu object(s):\n", n_objects);
-
- // Report all objects in this frame.
- for (uptr i = 0; i < n_objects; i++) {
- uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
- uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
- PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
- next_var_beg);
- }
- Printf(
- "HINT: this may be a false positive if your program uses "
- "some custom stack unwind mechanism, swapcontext or vfork\n");
- if (SANITIZER_WINDOWS)
- Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
- else
- Printf(" (longjmp and C++ exceptions *are* supported)\n");
-
- DescribeThread(GetThreadContextByTidLocked(tid));
-}
-
-void HeapAddressDescription::Print() const {
- PrintHeapChunkAccess(addr, chunk_access);
-
- asanThreadRegistry().CheckLocked();
- AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
- StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
-
- Decorator d;
- AsanThreadContext *free_thread = nullptr;
- if (free_tid != kInvalidTid) {
- free_thread = GetThreadContextByTidLocked(free_tid);
- Printf("%sfreed by thread %s here:%s\n", d.Allocation(),
- AsanThreadIdAndName(free_thread).c_str(), d.Default());
- StackTrace free_stack = GetStackTraceFromId(free_stack_id);
- free_stack.Print();
- Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(),
- AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
- } else {
- Printf("%sallocated by thread %s here:%s\n", d.Allocation(),
- AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
- }
- alloc_stack.Print();
- DescribeThread(GetCurrentThread());
- if (free_thread) DescribeThread(free_thread);
- DescribeThread(alloc_thread);
-}
-
-AddressDescription::AddressDescription(uptr addr, uptr access_size,
- bool shouldLockThreadRegistry) {
- if (GetShadowAddressInformation(addr, &data.shadow)) {
- data.kind = kAddressKindShadow;
- return;
- }
- if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
- data.kind = kAddressKindHeap;
- return;
- }
-
- bool isStackMemory = false;
- if (shouldLockThreadRegistry) {
- ThreadRegistryLock l(&asanThreadRegistry());
- isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
- } else {
- isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
- }
- if (isStackMemory) {
- data.kind = kAddressKindStack;
- return;
- }
-
- if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
- data.kind = kAddressKindGlobal;
- return;
- }
- data.kind = kAddressKindWild;
- addr = 0;
-}
-
-void PrintAddressDescription(uptr addr, uptr access_size,
- const char *bug_type) {
- ShadowAddressDescription shadow_descr;
- if (GetShadowAddressInformation(addr, &shadow_descr)) {
- shadow_descr.Print();
- return;
- }
-
- GlobalAddressDescription global_descr;
- if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
- global_descr.Print(bug_type);
- return;
- }
-
- StackAddressDescription stack_descr;
- if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
- stack_descr.Print();
- return;
- }
-
- HeapAddressDescription heap_descr;
- if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
- heap_descr.Print();
- return;
- }
-
- // We exhausted our possibilities. Bail out.
- Printf(
- "AddressSanitizer can not describe address in more detail "
- "(wild memory access suspected).\n");
-}
-} // namespace __asan
--- /dev/null
+//===-- asan_descriptions.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan functions for getting information about an address and/or printing it.
+//===----------------------------------------------------------------------===//
+
+#include "asan_descriptions.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {
+ Init(t->tid, t->name);
+}
+
+AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) {
+ if (tid == kInvalidTid) {
+ Init(tid, "");
+ } else {
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *t = GetThreadContextByTidLocked(tid);
+ Init(tid, t->name);
+ }
+}
+
+void AsanThreadIdAndName::Init(u32 tid, const char *tname) {
+ int len = internal_snprintf(name, sizeof(name), "T%d", tid);
+ CHECK(((unsigned int)len) < sizeof(name));
+ if (tname[0] != '\0')
+ internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname);
+}
+
+void DescribeThread(AsanThreadContext *context) {
+ CHECK(context);
+ asanThreadRegistry().CheckLocked();
+ // No need to announce the main thread.
+ if (context->tid == 0 || context->announced) {
+ return;
+ }
+ context->announced = true;
+ InternalScopedString str(1024);
+ str.append("Thread %s", AsanThreadIdAndName(context).c_str());
+ if (context->parent_tid == kInvalidTid) {
+ str.append(" created by unknown thread\n");
+ Printf("%s", str.data());
+ return;
+ }
+ str.append(" created by %s here:\n",
+ AsanThreadIdAndName(context->parent_tid).c_str());
+ Printf("%s", str.data());
+ StackDepotGet(context->stack_id).Print();
+ // Recursively described parent thread if needed.
+ if (flags()->print_full_thread_history) {
+ AsanThreadContext *parent_context =
+ GetThreadContextByTidLocked(context->parent_tid);
+ DescribeThread(parent_context);
+ }
+}
+
+// Shadow descriptions
+static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {
+ CHECK(!AddrIsInMem(addr));
+ if (AddrIsInShadowGap(addr)) {
+ *shadow_kind = kShadowKindGap;
+ } else if (AddrIsInHighShadow(addr)) {
+ *shadow_kind = kShadowKindHigh;
+ } else if (AddrIsInLowShadow(addr)) {
+ *shadow_kind = kShadowKindLow;
+ } else {
+ CHECK(0 && "Address is not in memory and not in shadow?");
+ return false;
+ }
+ return true;
+}
+
+bool DescribeAddressIfShadow(uptr addr) {
+ ShadowAddressDescription descr;
+ if (!GetShadowAddressInformation(addr, &descr)) return false;
+ descr.Print();
+ return true;
+}
+
+bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {
+ if (AddrIsInMem(addr)) return false;
+ ShadowKind shadow_kind;
+ if (!GetShadowKind(addr, &shadow_kind)) return false;
+ if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;
+ descr->addr = addr;
+ descr->kind = shadow_kind;
+ return true;
+}
+
+// Heap descriptions
+static void GetAccessToHeapChunkInformation(ChunkAccess *descr,
+ AsanChunkView chunk, uptr addr,
+ uptr access_size) {
+ descr->bad_addr = addr;
+ if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {
+ descr->access_type = kAccessTypeLeft;
+ } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {
+ descr->access_type = kAccessTypeRight;
+ if (descr->offset < 0) {
+ descr->bad_addr -= descr->offset;
+ descr->offset = 0;
+ }
+ } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {
+ descr->access_type = kAccessTypeInside;
+ } else {
+ descr->access_type = kAccessTypeUnknown;
+ }
+ descr->chunk_begin = chunk.Beg();
+ descr->chunk_size = chunk.UsedSize();
+ descr->user_requested_alignment = chunk.UserRequestedAlignment();
+ descr->alloc_type = chunk.GetAllocType();
+}
+
+static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
+ Decorator d;
+ InternalScopedString str(4096);
+ str.append("%s", d.Location());
+ switch (descr.access_type) {
+ case kAccessTypeLeft:
+ str.append("%p is located %zd bytes to the left of",
+ (void *)descr.bad_addr, descr.offset);
+ break;
+ case kAccessTypeRight:
+ str.append("%p is located %zd bytes to the right of",
+ (void *)descr.bad_addr, descr.offset);
+ break;
+ case kAccessTypeInside:
+ str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
+ descr.offset);
+ break;
+ case kAccessTypeUnknown:
+ str.append(
+ "%p is located somewhere around (this is AddressSanitizer bug!)",
+ (void *)descr.bad_addr);
+ }
+ str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size,
+ (void *)descr.chunk_begin,
+ (void *)(descr.chunk_begin + descr.chunk_size));
+ str.append("%s", d.Default());
+ Printf("%s", str.data());
+}
+
+bool GetHeapAddressInformation(uptr addr, uptr access_size,
+ HeapAddressDescription *descr) {
+ AsanChunkView chunk = FindHeapChunkByAddress(addr);
+ if (!chunk.IsValid()) {
+ return false;
+ }
+ descr->addr = addr;
+ GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,
+ access_size);
+ CHECK_NE(chunk.AllocTid(), kInvalidTid);
+ descr->alloc_tid = chunk.AllocTid();
+ descr->alloc_stack_id = chunk.GetAllocStackId();
+ descr->free_tid = chunk.FreeTid();
+ if (descr->free_tid != kInvalidTid)
+ descr->free_stack_id = chunk.GetFreeStackId();
+ return true;
+}
+
+static StackTrace GetStackTraceFromId(u32 id) {
+ CHECK(id);
+ StackTrace res = StackDepotGet(id);
+ CHECK(res.trace);
+ return res;
+}
+
+bool DescribeAddressIfHeap(uptr addr, uptr access_size) {
+ HeapAddressDescription descr;
+ if (!GetHeapAddressInformation(addr, access_size, &descr)) {
+ Printf(
+ "AddressSanitizer can not describe address in more detail "
+ "(wild memory access suspected).\n");
+ return false;
+ }
+ descr.Print();
+ return true;
+}
+
+// Stack descriptions
+bool GetStackAddressInformation(uptr addr, uptr access_size,
+ StackAddressDescription *descr) {
+ AsanThread *t = FindThreadByStackAddress(addr);
+ if (!t) return false;
+
+ descr->addr = addr;
+ descr->tid = t->tid();
+ // Try to fetch precise stack frame for this access.
+ AsanThread::StackFrameAccess access;
+ if (!t->GetStackFrameAccessByAddr(addr, &access)) {
+ descr->frame_descr = nullptr;
+ return true;
+ }
+
+ descr->offset = access.offset;
+ descr->access_size = access_size;
+ descr->frame_pc = access.frame_pc;
+ descr->frame_descr = access.frame_descr;
+
+#if SANITIZER_PPC64V1
+ // On PowerPC64 ELFv1, the address of a function actually points to a
+ // three-doubleword data structure with the first field containing
+ // the address of the function's code.
+ descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);
+#endif
+ descr->frame_pc += 16;
+
+ return true;
+}
+
+static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
+ uptr access_size, uptr prev_var_end,
+ uptr next_var_beg) {
+ uptr var_end = var.beg + var.size;
+ uptr addr_end = addr + access_size;
+ const char *pos_descr = nullptr;
+ // If the variable [var.beg, var_end) is the nearest variable to the
+ // current memory access, indicate it in the log.
+ if (addr >= var.beg) {
+ if (addr_end <= var_end)
+ pos_descr = "is inside"; // May happen if this is a use-after-return.
+ else if (addr < var_end)
+ pos_descr = "partially overflows";
+ else if (addr_end <= next_var_beg &&
+ next_var_beg - addr_end >= addr - var_end)
+ pos_descr = "overflows";
+ } else {
+ if (addr_end > var.beg)
+ pos_descr = "partially underflows";
+ else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)
+ pos_descr = "underflows";
+ }
+ InternalScopedString str(1024);
+ str.append(" [%zd, %zd)", var.beg, var_end);
+ // Render variable name.
+ str.append(" '");
+ for (uptr i = 0; i < var.name_len; ++i) {
+ str.append("%c", var.name_pos[i]);
+ }
+ str.append("'");
+ if (var.line > 0) {
+ str.append(" (line %d)", var.line);
+ }
+ if (pos_descr) {
+ Decorator d;
+ // FIXME: we may want to also print the size of the access here,
+ // but in case of accesses generated by memset it may be confusing.
+ str.append("%s <== Memory access at offset %zd %s this variable%s\n",
+ d.Location(), addr, pos_descr, d.Default());
+ } else {
+ str.append("\n");
+ }
+ Printf("%s", str.data());
+}
+
+bool DescribeAddressIfStack(uptr addr, uptr access_size) {
+ StackAddressDescription descr;
+ if (!GetStackAddressInformation(addr, access_size, &descr)) return false;
+ descr.Print();
+ return true;
+}
+
+// Global descriptions
+static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
+ const __asan_global &g) {
+ InternalScopedString str(4096);
+ Decorator d;
+ str.append("%s", d.Location());
+ if (addr < g.beg) {
+ str.append("%p is located %zd bytes to the left", (void *)addr,
+ g.beg - addr);
+ } else if (addr + access_size > g.beg + g.size) {
+ if (addr < g.beg + g.size) addr = g.beg + g.size;
+ str.append("%p is located %zd bytes to the right", (void *)addr,
+ addr - (g.beg + g.size));
+ } else {
+ // Can it happen?
+ str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
+ }
+ str.append(" of global variable '%s' defined in '",
+ MaybeDemangleGlobalName(g.name));
+ PrintGlobalLocation(&str, g);
+ str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
+ str.append("%s", d.Default());
+ PrintGlobalNameIfASCII(&str, g);
+ Printf("%s", str.data());
+}
+
+bool GetGlobalAddressInformation(uptr addr, uptr access_size,
+ GlobalAddressDescription *descr) {
+ descr->addr = addr;
+ int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,
+ ARRAY_SIZE(descr->globals));
+ descr->size = globals_num;
+ descr->access_size = access_size;
+ return globals_num != 0;
+}
+
+bool DescribeAddressIfGlobal(uptr addr, uptr access_size,
+ const char *bug_type) {
+ GlobalAddressDescription descr;
+ if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;
+
+ descr.Print(bug_type);
+ return true;
+}
+
+void ShadowAddressDescription::Print() const {
+ Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
+}
+
+void GlobalAddressDescription::Print(const char *bug_type) const {
+ for (int i = 0; i < size; i++) {
+ DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
+ if (bug_type &&
+ 0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
+ reg_sites[i]) {
+ Printf(" registered at:\n");
+ StackDepotGet(reg_sites[i]).Print();
+ }
+ }
+}
+
+bool GlobalAddressDescription::PointsInsideTheSameVariable(
+ const GlobalAddressDescription &other) const {
+ if (size == 0 || other.size == 0) return false;
+
+ for (uptr i = 0; i < size; i++) {
+ const __asan_global &a = globals[i];
+ for (uptr j = 0; j < other.size; j++) {
+ const __asan_global &b = other.globals[j];
+ if (a.beg == b.beg &&
+ a.beg <= addr &&
+ b.beg <= other.addr &&
+ (addr + access_size) < (a.beg + a.size) &&
+ (other.addr + other.access_size) < (b.beg + b.size))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void StackAddressDescription::Print() const {
+ Decorator d;
+ Printf("%s", d.Location());
+ Printf("Address %p is located in stack of thread %s", addr,
+ AsanThreadIdAndName(tid).c_str());
+
+ if (!frame_descr) {
+ Printf("%s\n", d.Default());
+ return;
+ }
+ Printf(" at offset %zu in frame%s\n", offset, d.Default());
+
+ // Now we print the frame where the alloca has happened.
+ // We print this frame as a stack trace with one element.
+ // The symbolizer may print more than one frame if inlining was involved.
+ // The frame numbers may be different than those in the stack trace printed
+ // previously. That's unfortunate, but I have no better solution,
+ // especially given that the alloca may be from entirely different place
+ // (e.g. use-after-scope, or different thread's stack).
+ Printf("%s", d.Default());
+ StackTrace alloca_stack(&frame_pc, 1);
+ alloca_stack.Print();
+
+ InternalMmapVector<StackVarDescr> vars;
+ vars.reserve(16);
+ if (!ParseFrameDescription(frame_descr, &vars)) {
+ Printf(
+ "AddressSanitizer can't parse the stack frame "
+ "descriptor: |%s|\n",
+ frame_descr);
+ // 'addr' is a stack address, so return true even if we can't parse frame
+ return;
+ }
+ uptr n_objects = vars.size();
+ // Report the number of stack objects.
+ Printf(" This frame has %zu object(s):\n", n_objects);
+
+ // Report all objects in this frame.
+ for (uptr i = 0; i < n_objects; i++) {
+ uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
+ uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
+ PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
+ next_var_beg);
+ }
+ Printf(
+ "HINT: this may be a false positive if your program uses "
+ "some custom stack unwind mechanism, swapcontext or vfork\n");
+ if (SANITIZER_WINDOWS)
+ Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
+ else
+ Printf(" (longjmp and C++ exceptions *are* supported)\n");
+
+ DescribeThread(GetThreadContextByTidLocked(tid));
+}
+
+void HeapAddressDescription::Print() const {
+ PrintHeapChunkAccess(addr, chunk_access);
+
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
+ StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
+
+ Decorator d;
+ AsanThreadContext *free_thread = nullptr;
+ if (free_tid != kInvalidTid) {
+ free_thread = GetThreadContextByTidLocked(free_tid);
+ Printf("%sfreed by thread %s here:%s\n", d.Allocation(),
+ AsanThreadIdAndName(free_thread).c_str(), d.Default());
+ StackTrace free_stack = GetStackTraceFromId(free_stack_id);
+ free_stack.Print();
+ Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(),
+ AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
+ } else {
+ Printf("%sallocated by thread %s here:%s\n", d.Allocation(),
+ AsanThreadIdAndName(alloc_thread).c_str(), d.Default());
+ }
+ alloc_stack.Print();
+ DescribeThread(GetCurrentThread());
+ if (free_thread) DescribeThread(free_thread);
+ DescribeThread(alloc_thread);
+}
+
+AddressDescription::AddressDescription(uptr addr, uptr access_size,
+ bool shouldLockThreadRegistry) {
+ if (GetShadowAddressInformation(addr, &data.shadow)) {
+ data.kind = kAddressKindShadow;
+ return;
+ }
+ if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
+ data.kind = kAddressKindHeap;
+ return;
+ }
+
+ bool isStackMemory = false;
+ if (shouldLockThreadRegistry) {
+ ThreadRegistryLock l(&asanThreadRegistry());
+ isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
+ } else {
+ isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
+ }
+ if (isStackMemory) {
+ data.kind = kAddressKindStack;
+ return;
+ }
+
+ if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
+ data.kind = kAddressKindGlobal;
+ return;
+ }
+ data.kind = kAddressKindWild;
+ addr = 0;
+}
+
+void PrintAddressDescription(uptr addr, uptr access_size,
+ const char *bug_type) {
+ ShadowAddressDescription shadow_descr;
+ if (GetShadowAddressInformation(addr, &shadow_descr)) {
+ shadow_descr.Print();
+ return;
+ }
+
+ GlobalAddressDescription global_descr;
+ if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
+ global_descr.Print(bug_type);
+ return;
+ }
+
+ StackAddressDescription stack_descr;
+ if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
+ stack_descr.Print();
+ return;
+ }
+
+ HeapAddressDescription heap_descr;
+ if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
+ heap_descr.Print();
+ return;
+ }
+
+ // We exhausted our possibilities. Bail out.
+ Printf(
+ "AddressSanitizer can not describe address in more detail "
+ "(wild memory access suspected).\n");
+}
+} // namespace __asan
//===-- asan_descriptions.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_descriptions.cc.
+// ASan-private header for asan_descriptions.cpp.
// TODO(filcab): Most struct definitions should move to the interface headers.
//===----------------------------------------------------------------------===//
#ifndef ASAN_DESCRIPTIONS_H
+++ /dev/null
-//===-- asan_errors.cc ------------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// ASan implementation for error structures.
-//===----------------------------------------------------------------------===//
-
-#include "asan_errors.h"
-#include "asan_descriptions.h"
-#include "asan_mapping.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-
-namespace __asan {
-
-static void OnStackUnwind(const SignalContext &sig,
- const void *callback_context,
- BufferedStackTrace *stack) {
- bool fast = common_flags()->fast_unwind_on_fatal;
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD
- // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace()
- // yields the call stack of the signal's handler and not of the code
- // that raised the signal (as it does on Linux).
- fast = true;
-#endif
- // Tests and maybe some users expect that scariness is going to be printed
- // just before the stack. As only asan has scariness score we have no
- // corresponding code in the sanitizer_common and we use this callback to
- // print it.
- static_cast<const ScarinessScoreBase *>(callback_context)->Print();
- GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context, fast);
-}
-
-void ErrorDeadlySignal::Print() {
- ReportDeadlySignal(signal, tid, &OnStackUnwind, &scariness);
-}
-
-void ErrorDoubleFree::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: attempting %s on %p in thread %s:\n",
- scariness.GetDescription(), addr_description.addr,
- AsanThreadIdAndName(tid).c_str());
- Printf("%s", d.Default());
- scariness.Print();
- GET_STACK_TRACE_FATAL(second_free_stack->trace[0],
- second_free_stack->top_frame_bp);
- stack.Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), &stack);
-}
-
-void ErrorNewDeleteTypeMismatch::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: %s on %p in thread %s:\n",
- scariness.GetDescription(), addr_description.addr,
- AsanThreadIdAndName(tid).c_str());
- Printf("%s object passed to delete has wrong type:\n", d.Default());
- if (delete_size != 0) {
- Printf(
- " size of the allocated type: %zd bytes;\n"
- " size of the deallocated type: %zd bytes.\n",
- addr_description.chunk_access.chunk_size, delete_size);
- }
- const uptr user_alignment =
- addr_description.chunk_access.user_requested_alignment;
- if (delete_alignment != user_alignment) {
- char user_alignment_str[32];
- char delete_alignment_str[32];
- internal_snprintf(user_alignment_str, sizeof(user_alignment_str),
- "%zd bytes", user_alignment);
- internal_snprintf(delete_alignment_str, sizeof(delete_alignment_str),
- "%zd bytes", delete_alignment);
- static const char *kDefaultAlignment = "default-aligned";
- Printf(
- " alignment of the allocated type: %s;\n"
- " alignment of the deallocated type: %s.\n",
- user_alignment > 0 ? user_alignment_str : kDefaultAlignment,
- delete_alignment > 0 ? delete_alignment_str : kDefaultAlignment);
- }
- CHECK_GT(free_stack->size, 0);
- scariness.Print();
- GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- stack.Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), &stack);
- Report(
- "HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
-}
-
-void ErrorFreeNotMalloced::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: attempting free on address "
- "which was not malloc()-ed: %p in thread %s\n",
- addr_description.Address(), AsanThreadIdAndName(tid).c_str());
- Printf("%s", d.Default());
- CHECK_GT(free_stack->size, 0);
- scariness.Print();
- GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
- stack.Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), &stack);
-}
-
-void ErrorAllocTypeMismatch::Print() {
- static const char *alloc_names[] = {"INVALID", "malloc", "operator new",
- "operator new []"};
- static const char *dealloc_names[] = {"INVALID", "free", "operator delete",
- "operator delete []"};
- CHECK_NE(alloc_type, dealloc_type);
- Decorator d;
- Printf("%s", d.Error());
- Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n",
- scariness.GetDescription(), alloc_names[alloc_type],
- dealloc_names[dealloc_type], addr_description.Address());
- Printf("%s", d.Default());
- CHECK_GT(dealloc_stack->size, 0);
- scariness.Print();
- GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp);
- stack.Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), &stack);
- Report(
- "HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
-}
-
-void ErrorMallocUsableSizeNotOwned::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for "
- "pointer which is not owned: %p\n",
- addr_description.Address());
- Printf("%s", d.Default());
- stack->Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorSanitizerGetAllocatedSizeNotOwned::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: attempting to call "
- "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n",
- addr_description.Address());
- Printf("%s", d.Default());
- stack->Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorCallocOverflow::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: calloc parameters overflow: count * size "
- "(%zd * %zd) cannot be represented in type size_t (thread %s)\n",
- count, size, AsanThreadIdAndName(tid).c_str());
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorPvallocOverflow::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: pvalloc parameters overflow: size 0x%zx "
- "rounded up to system page size 0x%zx cannot be represented in type "
- "size_t (thread %s)\n",
- size, GetPageSizeCached(), AsanThreadIdAndName(tid).c_str());
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorInvalidAllocationAlignment::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: invalid allocation alignment: %zd, "
- "alignment must be a power of two (thread %s)\n",
- alignment, AsanThreadIdAndName(tid).c_str());
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorInvalidAlignedAllocAlignment::Print() {
- Decorator d;
- Printf("%s", d.Error());
-#if SANITIZER_POSIX
- Report("ERROR: AddressSanitizer: invalid alignment requested in "
- "aligned_alloc: %zd, alignment must be a power of two and the "
- "requested size 0x%zx must be a multiple of alignment "
- "(thread %s)\n", alignment, size, AsanThreadIdAndName(tid).c_str());
-#else
- Report("ERROR: AddressSanitizer: invalid alignment requested in "
- "aligned_alloc: %zd, the requested size 0x%zx must be a multiple of "
- "alignment (thread %s)\n", alignment, size,
- AsanThreadIdAndName(tid).c_str());
-#endif
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorInvalidPosixMemalignAlignment::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: invalid alignment requested in posix_memalign: "
- "%zd, alignment must be a power of two and a multiple of sizeof(void*) "
- "== %zd (thread %s)\n",
- alignment, sizeof(void*), AsanThreadIdAndName(tid).c_str()); // NOLINT
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorAllocationSizeTooBig::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: requested allocation size 0x%zx (0x%zx after "
- "adjustments for alignment, red zones etc.) exceeds maximum supported "
- "size of 0x%zx (thread %s)\n",
- user_size, total_size, max_size, AsanThreadIdAndName(tid).c_str());
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorRssLimitExceeded::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: specified RSS limit exceeded, currently set to "
- "soft_rss_limit_mb=%zd\n", common_flags()->soft_rss_limit_mb);
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorOutOfMemory::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: allocator is out of memory trying to allocate "
- "0x%zx bytes\n", requested_size);
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorStringFunctionMemoryRangesOverlap::Print() {
- Decorator d;
- char bug_type[100];
- internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
- Printf("%s", d.Error());
- Report(
- "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) "
- "overlap\n",
- bug_type, addr1_description.Address(),
- addr1_description.Address() + length1, addr2_description.Address(),
- addr2_description.Address() + length2);
- Printf("%s", d.Default());
- scariness.Print();
- stack->Print();
- addr1_description.Print();
- addr2_description.Print();
- ReportErrorSummary(bug_type, stack);
-}
-
-void ErrorStringFunctionSizeOverflow::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report("ERROR: AddressSanitizer: %s: (size=%zd)\n",
- scariness.GetDescription(), size);
- Printf("%s", d.Default());
- scariness.Print();
- stack->Print();
- addr_description.Print();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorBadParamsToAnnotateContiguousContainer::Print() {
- Report(
- "ERROR: AddressSanitizer: bad parameters to "
- "__sanitizer_annotate_contiguous_container:\n"
- " beg : %p\n"
- " end : %p\n"
- " old_mid : %p\n"
- " new_mid : %p\n",
- beg, end, old_mid, new_mid);
- uptr granularity = SHADOW_GRANULARITY;
- if (!IsAligned(beg, granularity))
- Report("ERROR: beg is not aligned by %d\n", granularity);
- stack->Print();
- ReportErrorSummary(scariness.GetDescription(), stack);
-}
-
-void ErrorODRViolation::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(),
- global1.beg);
- Printf("%s", d.Default());
- InternalScopedString g1_loc(256), g2_loc(256);
- PrintGlobalLocation(&g1_loc, global1);
- PrintGlobalLocation(&g2_loc, global2);
- Printf(" [1] size=%zd '%s' %s\n", global1.size,
- MaybeDemangleGlobalName(global1.name), g1_loc.data());
- Printf(" [2] size=%zd '%s' %s\n", global2.size,
- MaybeDemangleGlobalName(global2.name), g2_loc.data());
- if (stack_id1 && stack_id2) {
- Printf("These globals were registered at these points:\n");
- Printf(" [1]:\n");
- StackDepotGet(stack_id1).Print();
- Printf(" [2]:\n");
- StackDepotGet(stack_id2).Print();
- }
- Report(
- "HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=detect_odr_violation=0\n");
- InternalScopedString error_msg(256);
- error_msg.append("%s: global '%s' at %s", scariness.GetDescription(),
- MaybeDemangleGlobalName(global1.name), g1_loc.data());
- ReportErrorSummary(error_msg.data());
-}
-
-void ErrorInvalidPointerPair::Print() {
- Decorator d;
- Printf("%s", d.Error());
- Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(),
- addr1_description.Address(), addr2_description.Address());
- Printf("%s", d.Default());
- GET_STACK_TRACE_FATAL(pc, bp);
- stack.Print();
- addr1_description.Print();
- addr2_description.Print();
- ReportErrorSummary(scariness.GetDescription(), &stack);
-}
-
-static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
- return s[-1] > 127 && s[1] > 127;
-}
-
-ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
- bool is_write_, uptr access_size_)
- : ErrorBase(tid),
- addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false),
- pc(pc_),
- bp(bp_),
- sp(sp_),
- access_size(access_size_),
- is_write(is_write_),
- shadow_val(0) {
- scariness.Clear();
- if (access_size) {
- if (access_size <= 9) {
- char desr[] = "?-byte";
- desr[0] = '0' + access_size;
- scariness.Scare(access_size + access_size / 2, desr);
- } else if (access_size >= 10) {
- scariness.Scare(15, "multi-byte");
- }
- is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read");
-
- // Determine the error type.
- bug_descr = "unknown-crash";
- if (AddrIsInMem(addr)) {
- u8 *shadow_addr = (u8 *)MemToShadow(addr);
- // If we are accessing 16 bytes, look at the second shadow byte.
- if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++;
- // If we are in the partial right redzone, look at the next shadow byte.
- if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
- bool far_from_bounds = false;
- shadow_val = *shadow_addr;
- int bug_type_score = 0;
- // For use-after-frees reads are almost as bad as writes.
- int read_after_free_bonus = 0;
- switch (shadow_val) {
- case kAsanHeapLeftRedzoneMagic:
- case kAsanArrayCookieMagic:
- bug_descr = "heap-buffer-overflow";
- bug_type_score = 10;
- far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
- break;
- case kAsanHeapFreeMagic:
- bug_descr = "heap-use-after-free";
- bug_type_score = 20;
- if (!is_write) read_after_free_bonus = 18;
- break;
- case kAsanStackLeftRedzoneMagic:
- bug_descr = "stack-buffer-underflow";
- bug_type_score = 25;
- far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
- break;
- case kAsanInitializationOrderMagic:
- bug_descr = "initialization-order-fiasco";
- bug_type_score = 1;
- break;
- case kAsanStackMidRedzoneMagic:
- case kAsanStackRightRedzoneMagic:
- bug_descr = "stack-buffer-overflow";
- bug_type_score = 25;
- far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
- break;
- case kAsanStackAfterReturnMagic:
- bug_descr = "stack-use-after-return";
- bug_type_score = 30;
- if (!is_write) read_after_free_bonus = 18;
- break;
- case kAsanUserPoisonedMemoryMagic:
- bug_descr = "use-after-poison";
- bug_type_score = 20;
- break;
- case kAsanContiguousContainerOOBMagic:
- bug_descr = "container-overflow";
- bug_type_score = 10;
- break;
- case kAsanStackUseAfterScopeMagic:
- bug_descr = "stack-use-after-scope";
- bug_type_score = 10;
- break;
- case kAsanGlobalRedzoneMagic:
- bug_descr = "global-buffer-overflow";
- bug_type_score = 10;
- far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
- break;
- case kAsanIntraObjectRedzone:
- bug_descr = "intra-object-overflow";
- bug_type_score = 10;
- break;
- case kAsanAllocaLeftMagic:
- case kAsanAllocaRightMagic:
- bug_descr = "dynamic-stack-buffer-overflow";
- bug_type_score = 25;
- far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
- break;
- }
- scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr);
- if (far_from_bounds) scariness.Scare(10, "far-from-bounds");
- }
- }
-}
-
-static void PrintContainerOverflowHint() {
- Printf("HINT: if you don't care about these errors you may set "
- "ASAN_OPTIONS=detect_container_overflow=0.\n"
- "If you suspect a false positive see also: "
- "https://github.com/google/sanitizers/wiki/"
- "AddressSanitizerContainerOverflow.\n");
-}
-
-static void PrintShadowByte(InternalScopedString *str, const char *before,
- u8 byte, const char *after = "\n") {
- PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
-}
-
-static void PrintLegend(InternalScopedString *str) {
- str->append(
- "Shadow byte legend (one shadow byte represents %d "
- "application bytes):\n",
- (int)SHADOW_GRANULARITY);
- PrintShadowByte(str, " Addressable: ", 0);
- str->append(" Partially addressable: ");
- for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
- str->append("\n");
- PrintShadowByte(str, " Heap left redzone: ",
- kAsanHeapLeftRedzoneMagic);
- PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
- PrintShadowByte(str, " Stack left redzone: ",
- kAsanStackLeftRedzoneMagic);
- PrintShadowByte(str, " Stack mid redzone: ",
- kAsanStackMidRedzoneMagic);
- PrintShadowByte(str, " Stack right redzone: ",
- kAsanStackRightRedzoneMagic);
- PrintShadowByte(str, " Stack after return: ",
- kAsanStackAfterReturnMagic);
- PrintShadowByte(str, " Stack use after scope: ",
- kAsanStackUseAfterScopeMagic);
- PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
- PrintShadowByte(str, " Global init order: ",
- kAsanInitializationOrderMagic);
- PrintShadowByte(str, " Poisoned by user: ",
- kAsanUserPoisonedMemoryMagic);
- PrintShadowByte(str, " Container overflow: ",
- kAsanContiguousContainerOOBMagic);
- PrintShadowByte(str, " Array cookie: ",
- kAsanArrayCookieMagic);
- PrintShadowByte(str, " Intra object redzone: ",
- kAsanIntraObjectRedzone);
- PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
- PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
- PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
- PrintShadowByte(str, " Shadow gap: ", kAsanShadowGap);
-}
-
-static void PrintShadowBytes(InternalScopedString *str, const char *before,
- u8 *bytes, u8 *guilty, uptr n) {
- Decorator d;
- if (before) str->append("%s%p:", before, bytes);
- for (uptr i = 0; i < n; i++) {
- u8 *p = bytes + i;
- const char *before =
- p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
- const char *after = p == guilty ? "]" : "";
- PrintShadowByte(str, before, *p, after);
- }
- str->append("\n");
-}
-
-static void PrintShadowMemoryForAddress(uptr addr) {
- if (!AddrIsInMem(addr)) return;
- uptr shadow_addr = MemToShadow(addr);
- const uptr n_bytes_per_row = 16;
- uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
- InternalScopedString str(4096 * 8);
- str.append("Shadow bytes around the buggy address:\n");
- for (int i = -5; i <= 5; i++) {
- uptr row_shadow_addr = aligned_shadow + i * n_bytes_per_row;
- // Skip rows that would be outside the shadow range. This can happen when
- // the user address is near the bottom, top, or shadow gap of the address
- // space.
- if (!AddrIsInShadow(row_shadow_addr)) continue;
- const char *prefix = (i == 0) ? "=>" : " ";
- PrintShadowBytes(&str, prefix, (u8 *)row_shadow_addr, (u8 *)shadow_addr,
- n_bytes_per_row);
- }
- if (flags()->print_legend) PrintLegend(&str);
- Printf("%s", str.data());
-}
-
-void ErrorGeneric::Print() {
- Decorator d;
- Printf("%s", d.Error());
- uptr addr = addr_description.Address();
- Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n",
- bug_descr, (void *)addr, pc, bp, sp);
- Printf("%s", d.Default());
-
- Printf("%s%s of size %zu at %p thread %s%s\n", d.Access(),
- access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size,
- (void *)addr, AsanThreadIdAndName(tid).c_str(), d.Default());
-
- scariness.Print();
- GET_STACK_TRACE_FATAL(pc, bp);
- stack.Print();
-
- // Pass bug_descr because we have a special case for
- // initialization-order-fiasco
- addr_description.Print(bug_descr);
- if (shadow_val == kAsanContiguousContainerOOBMagic)
- PrintContainerOverflowHint();
- ReportErrorSummary(bug_descr, &stack);
- PrintShadowMemoryForAddress(addr);
-}
-
-} // namespace __asan
--- /dev/null
+//===-- asan_errors.cpp -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan implementation for error structures.
+//===----------------------------------------------------------------------===//
+
+#include "asan_errors.h"
+#include "asan_descriptions.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+static void OnStackUnwind(const SignalContext &sig,
+ const void *callback_context,
+ BufferedStackTrace *stack) {
+ bool fast = common_flags()->fast_unwind_on_fatal;
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD
+ // On FreeBSD the slow unwinding that leverages _Unwind_Backtrace()
+ // yields the call stack of the signal's handler and not of the code
+ // that raised the signal (as it does on Linux).
+ fast = true;
+#endif
+ // Tests and maybe some users expect that scariness is going to be printed
+ // just before the stack. As only asan has scariness score we have no
+ // corresponding code in the sanitizer_common and we use this callback to
+ // print it.
+ static_cast<const ScarinessScoreBase *>(callback_context)->Print();
+ stack->Unwind(sig.pc, sig.bp, sig.context, fast);
+}
+
+void ErrorDeadlySignal::Print() {
+ ReportDeadlySignal(signal, tid, &OnStackUnwind, &scariness);
+}
+
+void ErrorDoubleFree::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: attempting %s on %p in thread %s:\n",
+ scariness.GetDescription(), addr_description.addr,
+ AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(second_free_stack->trace[0],
+ second_free_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), &stack);
+}
+
+void ErrorNewDeleteTypeMismatch::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: %s on %p in thread %s:\n",
+ scariness.GetDescription(), addr_description.addr,
+ AsanThreadIdAndName(tid).c_str());
+ Printf("%s object passed to delete has wrong type:\n", d.Default());
+ if (delete_size != 0) {
+ Printf(
+ " size of the allocated type: %zd bytes;\n"
+ " size of the deallocated type: %zd bytes.\n",
+ addr_description.chunk_access.chunk_size, delete_size);
+ }
+ const uptr user_alignment =
+ addr_description.chunk_access.user_requested_alignment;
+ if (delete_alignment != user_alignment) {
+ char user_alignment_str[32];
+ char delete_alignment_str[32];
+ internal_snprintf(user_alignment_str, sizeof(user_alignment_str),
+ "%zd bytes", user_alignment);
+ internal_snprintf(delete_alignment_str, sizeof(delete_alignment_str),
+ "%zd bytes", delete_alignment);
+ static const char *kDefaultAlignment = "default-aligned";
+ Printf(
+ " alignment of the allocated type: %s;\n"
+ " alignment of the deallocated type: %s.\n",
+ user_alignment > 0 ? user_alignment_str : kDefaultAlignment,
+ delete_alignment > 0 ? delete_alignment_str : kDefaultAlignment);
+ }
+ CHECK_GT(free_stack->size, 0);
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), &stack);
+ Report(
+ "HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+}
+
+void ErrorFreeNotMalloced::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: attempting free on address "
+ "which was not malloc()-ed: %p in thread %s\n",
+ addr_description.Address(), AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ CHECK_GT(free_stack->size, 0);
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), &stack);
+}
+
+void ErrorAllocTypeMismatch::Print() {
+ static const char *alloc_names[] = {"INVALID", "malloc", "operator new",
+ "operator new []"};
+ static const char *dealloc_names[] = {"INVALID", "free", "operator delete",
+ "operator delete []"};
+ CHECK_NE(alloc_type, dealloc_type);
+ Decorator d;
+ Printf("%s", d.Error());
+ Report("ERROR: AddressSanitizer: %s (%s vs %s) on %p\n",
+ scariness.GetDescription(), alloc_names[alloc_type],
+ dealloc_names[dealloc_type], addr_description.Address());
+ Printf("%s", d.Default());
+ CHECK_GT(dealloc_stack->size, 0);
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp);
+ stack.Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), &stack);
+ Report(
+ "HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+}
+
+void ErrorMallocUsableSizeNotOwned::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for "
+ "pointer which is not owned: %p\n",
+ addr_description.Address());
+ Printf("%s", d.Default());
+ stack->Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorSanitizerGetAllocatedSizeNotOwned::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: attempting to call "
+ "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n",
+ addr_description.Address());
+ Printf("%s", d.Default());
+ stack->Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorCallocOverflow::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: calloc parameters overflow: count * size "
+ "(%zd * %zd) cannot be represented in type size_t (thread %s)\n",
+ count, size, AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorReallocArrayOverflow::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: reallocarray parameters overflow: count * size "
+ "(%zd * %zd) cannot be represented in type size_t (thread %s)\n",
+ count, size, AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorPvallocOverflow::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: pvalloc parameters overflow: size 0x%zx "
+ "rounded up to system page size 0x%zx cannot be represented in type "
+ "size_t (thread %s)\n",
+ size, GetPageSizeCached(), AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorInvalidAllocationAlignment::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: invalid allocation alignment: %zd, "
+ "alignment must be a power of two (thread %s)\n",
+ alignment, AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorInvalidAlignedAllocAlignment::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+#if SANITIZER_POSIX
+ Report("ERROR: AddressSanitizer: invalid alignment requested in "
+ "aligned_alloc: %zd, alignment must be a power of two and the "
+ "requested size 0x%zx must be a multiple of alignment "
+ "(thread %s)\n", alignment, size, AsanThreadIdAndName(tid).c_str());
+#else
+ Report("ERROR: AddressSanitizer: invalid alignment requested in "
+ "aligned_alloc: %zd, the requested size 0x%zx must be a multiple of "
+ "alignment (thread %s)\n", alignment, size,
+ AsanThreadIdAndName(tid).c_str());
+#endif
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorInvalidPosixMemalignAlignment::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: invalid alignment requested in posix_memalign: "
+ "%zd, alignment must be a power of two and a multiple of sizeof(void*) "
+ "== %zd (thread %s)\n",
+ alignment, sizeof(void*), AsanThreadIdAndName(tid).c_str()); // NOLINT
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorAllocationSizeTooBig::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: requested allocation size 0x%zx (0x%zx after "
+ "adjustments for alignment, red zones etc.) exceeds maximum supported "
+ "size of 0x%zx (thread %s)\n",
+ user_size, total_size, max_size, AsanThreadIdAndName(tid).c_str());
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorRssLimitExceeded::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: specified RSS limit exceeded, currently set to "
+ "soft_rss_limit_mb=%zd\n", common_flags()->soft_rss_limit_mb);
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorOutOfMemory::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: allocator is out of memory trying to allocate "
+ "0x%zx bytes\n", requested_size);
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorStringFunctionMemoryRangesOverlap::Print() {
+ Decorator d;
+ char bug_type[100];
+ internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
+ Printf("%s", d.Error());
+ Report(
+ "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) "
+ "overlap\n",
+ bug_type, addr1_description.Address(),
+ addr1_description.Address() + length1, addr2_description.Address(),
+ addr2_description.Address() + length2);
+ Printf("%s", d.Default());
+ scariness.Print();
+ stack->Print();
+ addr1_description.Print();
+ addr2_description.Print();
+ ReportErrorSummary(bug_type, stack);
+}
+
+void ErrorStringFunctionSizeOverflow::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report("ERROR: AddressSanitizer: %s: (size=%zd)\n",
+ scariness.GetDescription(), size);
+ Printf("%s", d.Default());
+ scariness.Print();
+ stack->Print();
+ addr_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorBadParamsToAnnotateContiguousContainer::Print() {
+ Report(
+ "ERROR: AddressSanitizer: bad parameters to "
+ "__sanitizer_annotate_contiguous_container:\n"
+ " beg : %p\n"
+ " end : %p\n"
+ " old_mid : %p\n"
+ " new_mid : %p\n",
+ beg, end, old_mid, new_mid);
+ uptr granularity = SHADOW_GRANULARITY;
+ if (!IsAligned(beg, granularity))
+ Report("ERROR: beg is not aligned by %d\n", granularity);
+ stack->Print();
+ ReportErrorSummary(scariness.GetDescription(), stack);
+}
+
+void ErrorODRViolation::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(),
+ global1.beg);
+ Printf("%s", d.Default());
+ InternalScopedString g1_loc(256), g2_loc(256);
+ PrintGlobalLocation(&g1_loc, global1);
+ PrintGlobalLocation(&g2_loc, global2);
+ Printf(" [1] size=%zd '%s' %s\n", global1.size,
+ MaybeDemangleGlobalName(global1.name), g1_loc.data());
+ Printf(" [2] size=%zd '%s' %s\n", global2.size,
+ MaybeDemangleGlobalName(global2.name), g2_loc.data());
+ if (stack_id1 && stack_id2) {
+ Printf("These globals were registered at these points:\n");
+ Printf(" [1]:\n");
+ StackDepotGet(stack_id1).Print();
+ Printf(" [2]:\n");
+ StackDepotGet(stack_id2).Print();
+ }
+ Report(
+ "HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=detect_odr_violation=0\n");
+ InternalScopedString error_msg(256);
+ error_msg.append("%s: global '%s' at %s", scariness.GetDescription(),
+ MaybeDemangleGlobalName(global1.name), g1_loc.data());
+ ReportErrorSummary(error_msg.data());
+}
+
+void ErrorInvalidPointerPair::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ Report("ERROR: AddressSanitizer: %s: %p %p\n", scariness.GetDescription(),
+ addr1_description.Address(), addr2_description.Address());
+ Printf("%s", d.Default());
+ GET_STACK_TRACE_FATAL(pc, bp);
+ stack.Print();
+ addr1_description.Print();
+ addr2_description.Print();
+ ReportErrorSummary(scariness.GetDescription(), &stack);
+}
+
+static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
+ return s[-1] > 127 && s[1] > 127;
+}
+
+ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
+ bool is_write_, uptr access_size_)
+ : ErrorBase(tid),
+ addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false),
+ pc(pc_),
+ bp(bp_),
+ sp(sp_),
+ access_size(access_size_),
+ is_write(is_write_),
+ shadow_val(0) {
+ scariness.Clear();
+ if (access_size) {
+ if (access_size <= 9) {
+ char desr[] = "?-byte";
+ desr[0] = '0' + access_size;
+ scariness.Scare(access_size + access_size / 2, desr);
+ } else if (access_size >= 10) {
+ scariness.Scare(15, "multi-byte");
+ }
+ is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read");
+
+ // Determine the error type.
+ bug_descr = "unknown-crash";
+ if (AddrIsInMem(addr)) {
+ u8 *shadow_addr = (u8 *)MemToShadow(addr);
+ // If we are accessing 16 bytes, look at the second shadow byte.
+ if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++;
+ // If we are in the partial right redzone, look at the next shadow byte.
+ if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
+ bool far_from_bounds = false;
+ shadow_val = *shadow_addr;
+ int bug_type_score = 0;
+ // For use-after-frees reads are almost as bad as writes.
+ int read_after_free_bonus = 0;
+ switch (shadow_val) {
+ case kAsanHeapLeftRedzoneMagic:
+ case kAsanArrayCookieMagic:
+ bug_descr = "heap-buffer-overflow";
+ bug_type_score = 10;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanHeapFreeMagic:
+ bug_descr = "heap-use-after-free";
+ bug_type_score = 20;
+ if (!is_write) read_after_free_bonus = 18;
+ break;
+ case kAsanStackLeftRedzoneMagic:
+ bug_descr = "stack-buffer-underflow";
+ bug_type_score = 25;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanInitializationOrderMagic:
+ bug_descr = "initialization-order-fiasco";
+ bug_type_score = 1;
+ break;
+ case kAsanStackMidRedzoneMagic:
+ case kAsanStackRightRedzoneMagic:
+ bug_descr = "stack-buffer-overflow";
+ bug_type_score = 25;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanStackAfterReturnMagic:
+ bug_descr = "stack-use-after-return";
+ bug_type_score = 30;
+ if (!is_write) read_after_free_bonus = 18;
+ break;
+ case kAsanUserPoisonedMemoryMagic:
+ bug_descr = "use-after-poison";
+ bug_type_score = 20;
+ break;
+ case kAsanContiguousContainerOOBMagic:
+ bug_descr = "container-overflow";
+ bug_type_score = 10;
+ break;
+ case kAsanStackUseAfterScopeMagic:
+ bug_descr = "stack-use-after-scope";
+ bug_type_score = 10;
+ break;
+ case kAsanGlobalRedzoneMagic:
+ bug_descr = "global-buffer-overflow";
+ bug_type_score = 10;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ case kAsanIntraObjectRedzone:
+ bug_descr = "intra-object-overflow";
+ bug_type_score = 10;
+ break;
+ case kAsanAllocaLeftMagic:
+ case kAsanAllocaRightMagic:
+ bug_descr = "dynamic-stack-buffer-overflow";
+ bug_type_score = 25;
+ far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+ break;
+ }
+ scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr);
+ if (far_from_bounds) scariness.Scare(10, "far-from-bounds");
+ }
+ }
+}
+
+static void PrintContainerOverflowHint() {
+ Printf("HINT: if you don't care about these errors you may set "
+ "ASAN_OPTIONS=detect_container_overflow=0.\n"
+ "If you suspect a false positive see also: "
+ "https://github.com/google/sanitizers/wiki/"
+ "AddressSanitizerContainerOverflow.\n");
+}
+
+static void PrintShadowByte(InternalScopedString *str, const char *before,
+ u8 byte, const char *after = "\n") {
+ PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
+}
+
+static void PrintLegend(InternalScopedString *str) {
+ str->append(
+ "Shadow byte legend (one shadow byte represents %d "
+ "application bytes):\n",
+ (int)SHADOW_GRANULARITY);
+ PrintShadowByte(str, " Addressable: ", 0);
+ str->append(" Partially addressable: ");
+ for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
+ str->append("\n");
+ PrintShadowByte(str, " Heap left redzone: ",
+ kAsanHeapLeftRedzoneMagic);
+ PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
+ PrintShadowByte(str, " Stack left redzone: ",
+ kAsanStackLeftRedzoneMagic);
+ PrintShadowByte(str, " Stack mid redzone: ",
+ kAsanStackMidRedzoneMagic);
+ PrintShadowByte(str, " Stack right redzone: ",
+ kAsanStackRightRedzoneMagic);
+ PrintShadowByte(str, " Stack after return: ",
+ kAsanStackAfterReturnMagic);
+ PrintShadowByte(str, " Stack use after scope: ",
+ kAsanStackUseAfterScopeMagic);
+ PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
+ PrintShadowByte(str, " Global init order: ",
+ kAsanInitializationOrderMagic);
+ PrintShadowByte(str, " Poisoned by user: ",
+ kAsanUserPoisonedMemoryMagic);
+ PrintShadowByte(str, " Container overflow: ",
+ kAsanContiguousContainerOOBMagic);
+ PrintShadowByte(str, " Array cookie: ",
+ kAsanArrayCookieMagic);
+ PrintShadowByte(str, " Intra object redzone: ",
+ kAsanIntraObjectRedzone);
+ PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
+ PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
+ PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
+ PrintShadowByte(str, " Shadow gap: ", kAsanShadowGap);
+}
+
+static void PrintShadowBytes(InternalScopedString *str, const char *before,
+ u8 *bytes, u8 *guilty, uptr n) {
+ Decorator d;
+ if (before) str->append("%s%p:", before, bytes);
+ for (uptr i = 0; i < n; i++) {
+ u8 *p = bytes + i;
+ const char *before =
+ p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
+ const char *after = p == guilty ? "]" : "";
+ PrintShadowByte(str, before, *p, after);
+ }
+ str->append("\n");
+}
+
+static void PrintShadowMemoryForAddress(uptr addr) {
+ if (!AddrIsInMem(addr)) return;
+ uptr shadow_addr = MemToShadow(addr);
+ const uptr n_bytes_per_row = 16;
+ uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
+ InternalScopedString str(4096 * 8);
+ str.append("Shadow bytes around the buggy address:\n");
+ for (int i = -5; i <= 5; i++) {
+ uptr row_shadow_addr = aligned_shadow + i * n_bytes_per_row;
+ // Skip rows that would be outside the shadow range. This can happen when
+ // the user address is near the bottom, top, or shadow gap of the address
+ // space.
+ if (!AddrIsInShadow(row_shadow_addr)) continue;
+ const char *prefix = (i == 0) ? "=>" : " ";
+ PrintShadowBytes(&str, prefix, (u8 *)row_shadow_addr, (u8 *)shadow_addr,
+ n_bytes_per_row);
+ }
+ if (flags()->print_legend) PrintLegend(&str);
+ Printf("%s", str.data());
+}
+
+void ErrorGeneric::Print() {
+ Decorator d;
+ Printf("%s", d.Error());
+ uptr addr = addr_description.Address();
+ Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n",
+ bug_descr, (void *)addr, pc, bp, sp);
+ Printf("%s", d.Default());
+
+ Printf("%s%s of size %zu at %p thread %s%s\n", d.Access(),
+ access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size,
+ (void *)addr, AsanThreadIdAndName(tid).c_str(), d.Default());
+
+ scariness.Print();
+ GET_STACK_TRACE_FATAL(pc, bp);
+ stack.Print();
+
+ // Pass bug_descr because we have a special case for
+ // initialization-order-fiasco
+ addr_description.Print(bug_descr);
+ if (shadow_val == kAsanContiguousContainerOOBMagic)
+ PrintContainerOverflowHint();
+ ReportErrorSummary(bug_descr, &stack);
+ PrintShadowMemoryForAddress(addr);
+}
+
+} // namespace __asan
//===-- asan_errors.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void Print();
};
+struct ErrorReallocArrayOverflow : ErrorBase {
+ const BufferedStackTrace *stack;
+ uptr count;
+ uptr size;
+
+ ErrorReallocArrayOverflow() = default; // (*)
+ ErrorReallocArrayOverflow(u32 tid, BufferedStackTrace *stack_, uptr count_,
+ uptr size_)
+ : ErrorBase(tid, 10, "reallocarray-overflow"),
+ stack(stack_),
+ count(count_),
+ size(size_) {}
+ void Print();
+};
+
struct ErrorPvallocOverflow : ErrorBase {
const BufferedStackTrace *stack;
uptr size;
macro(MallocUsableSizeNotOwned) \
macro(SanitizerGetAllocatedSizeNotOwned) \
macro(CallocOverflow) \
+ macro(ReallocArrayOverflow) \
macro(PvallocOverflow) \
macro(InvalidAllocationAlignment) \
macro(InvalidAlignedAllocAlignment) \
#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name,
#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name;
-#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \
- ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {}
+#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \
+ ErrorDescription(Error##name const &e) : kind(kErrorKind##name) { \
+ internal_memcpy(&name, &e, sizeof(name)); \
+ }
#define ASAN_ERROR_DESCRIPTION_PRINT(name) \
case kErrorKind##name: \
return name.Print();
+++ /dev/null
-//===-- asan_fake_stack.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// FakeStack is used to detect use-after-return bugs.
-//===----------------------------------------------------------------------===//
-
-#include "asan_allocator.h"
-#include "asan_poisoning.h"
-#include "asan_thread.h"
-
-namespace __asan {
-
-static const u64 kMagic1 = kAsanStackAfterReturnMagic;
-static const u64 kMagic2 = (kMagic1 << 8) | kMagic1;
-static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;
-static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;
-
-static const u64 kAllocaRedzoneSize = 32UL;
-static const u64 kAllocaRedzoneMask = 31UL;
-
-// For small size classes inline PoisonShadow for better performance.
-ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
- u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr));
- if (SHADOW_SCALE == 3 && class_id <= 6) {
- // This code expects SHADOW_SCALE=3.
- for (uptr i = 0; i < (((uptr)1) << class_id); i++) {
- shadow[i] = magic;
- // Make sure this does not become memset.
- SanitizerBreakOptimization(nullptr);
- }
- } else {
- // The size class is too big, it's cheaper to poison only size bytes.
- PoisonShadow(ptr, size, static_cast<u8>(magic));
- }
-}
-
-FakeStack *FakeStack::Create(uptr stack_size_log) {
- static uptr kMinStackSizeLog = 16;
- static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28);
- if (stack_size_log < kMinStackSizeLog)
- stack_size_log = kMinStackSizeLog;
- if (stack_size_log > kMaxStackSizeLog)
- stack_size_log = kMaxStackSizeLog;
- uptr size = RequiredSize(stack_size_log);
- FakeStack *res = reinterpret_cast<FakeStack *>(
- flags()->uar_noreserve ? MmapNoReserveOrDie(size, "FakeStack")
- : MmapOrDie(size, "FakeStack"));
- res->stack_size_log_ = stack_size_log;
- u8 *p = reinterpret_cast<u8 *>(res);
- VReport(1, "T%d: FakeStack created: %p -- %p stack_size_log: %zd; "
- "mmapped %zdK, noreserve=%d \n",
- GetCurrentTidOrInvalid(), p,
- p + FakeStack::RequiredSize(stack_size_log), stack_size_log,
- size >> 10, flags()->uar_noreserve);
- return res;
-}
-
-void FakeStack::Destroy(int tid) {
- PoisonAll(0);
- if (Verbosity() >= 2) {
- InternalScopedString str(kNumberOfSizeClasses * 50);
- for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++)
- str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id],
- NumberOfFrames(stack_size_log(), class_id));
- Report("T%d: FakeStack destroyed: %s\n", tid, str.data());
- }
- uptr size = RequiredSize(stack_size_log_);
- FlushUnneededASanShadowMemory(reinterpret_cast<uptr>(this), size);
- UnmapOrDie(this, size);
-}
-
-void FakeStack::PoisonAll(u8 magic) {
- PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()),
- magic);
-}
-
-#if !defined(_MSC_VER) || defined(__clang__)
-ALWAYS_INLINE USED
-#endif
-FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
- uptr real_stack) {
- CHECK_LT(class_id, kNumberOfSizeClasses);
- if (needs_gc_)
- GC(real_stack);
- uptr &hint_position = hint_position_[class_id];
- const int num_iter = NumberOfFrames(stack_size_log, class_id);
- u8 *flags = GetFlags(stack_size_log, class_id);
- for (int i = 0; i < num_iter; i++) {
- uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++);
- // This part is tricky. On one hand, checking and setting flags[pos]
- // should be atomic to ensure async-signal safety. But on the other hand,
- // if the signal arrives between checking and setting flags[pos], the
- // signal handler's fake stack will start from a different hint_position
- // and so will not touch this particular byte. So, it is safe to do this
- // with regular non-atomic load and store (at least I was not able to make
- // this code crash).
- if (flags[pos]) continue;
- flags[pos] = 1;
- FakeFrame *res = reinterpret_cast<FakeFrame *>(
- GetFrame(stack_size_log, class_id, pos));
- res->real_stack = real_stack;
- *SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos];
- return res;
- }
- return nullptr; // We are out of fake stack.
-}
-
-uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) {
- uptr stack_size_log = this->stack_size_log();
- uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0));
- uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log);
- if (ptr < beg || ptr >= end) return 0;
- uptr class_id = (ptr - beg) >> stack_size_log;
- uptr base = beg + (class_id << stack_size_log);
- CHECK_LE(base, ptr);
- CHECK_LT(ptr, base + (((uptr)1) << stack_size_log));
- uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);
- uptr res = base + pos * BytesInSizeClass(class_id);
- *frame_end = res + BytesInSizeClass(class_id);
- *frame_beg = res + sizeof(FakeFrame);
- return res;
-}
-
-void FakeStack::HandleNoReturn() {
- needs_gc_ = true;
-}
-
-// When throw, longjmp or some such happens we don't call OnFree() and
-// as the result may leak one or more fake frames, but the good news is that
-// we are notified about all such events by HandleNoReturn().
-// If we recently had such no-return event we need to collect garbage frames.
-// We do it based on their 'real_stack' values -- everything that is lower
-// than the current real_stack is garbage.
-NOINLINE void FakeStack::GC(uptr real_stack) {
- uptr collected = 0;
- for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
- u8 *flags = GetFlags(stack_size_log(), class_id);
- for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
- i++) {
- if (flags[i] == 0) continue; // not allocated.
- FakeFrame *ff = reinterpret_cast<FakeFrame *>(
- GetFrame(stack_size_log(), class_id, i));
- if (ff->real_stack < real_stack) {
- flags[i] = 0;
- collected++;
- }
- }
- }
- needs_gc_ = false;
-}
-
-void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) {
- for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
- u8 *flags = GetFlags(stack_size_log(), class_id);
- for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
- i++) {
- if (flags[i] == 0) continue; // not allocated.
- FakeFrame *ff = reinterpret_cast<FakeFrame *>(
- GetFrame(stack_size_log(), class_id, i));
- uptr begin = reinterpret_cast<uptr>(ff);
- callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg);
- }
- }
-}
-
-#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
-static THREADLOCAL FakeStack *fake_stack_tls;
-
-FakeStack *GetTLSFakeStack() {
- return fake_stack_tls;
-}
-void SetTLSFakeStack(FakeStack *fs) {
- fake_stack_tls = fs;
-}
-#else
-FakeStack *GetTLSFakeStack() { return 0; }
-void SetTLSFakeStack(FakeStack *fs) { }
-#endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
-
-static FakeStack *GetFakeStack() {
- AsanThread *t = GetCurrentThread();
- if (!t) return nullptr;
- return t->fake_stack();
-}
-
-static FakeStack *GetFakeStackFast() {
- if (FakeStack *fs = GetTLSFakeStack())
- return fs;
- if (!__asan_option_detect_stack_use_after_return)
- return nullptr;
- return GetFakeStack();
-}
-
-ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {
- FakeStack *fs = GetFakeStackFast();
- if (!fs) return 0;
- uptr local_stack;
- uptr real_stack = reinterpret_cast<uptr>(&local_stack);
- FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
- if (!ff) return 0; // Out of fake stack.
- uptr ptr = reinterpret_cast<uptr>(ff);
- SetShadow(ptr, size, class_id, 0);
- return ptr;
-}
-
-ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
- FakeStack::Deallocate(ptr, class_id);
- SetShadow(ptr, size, class_id, kMagic8);
-}
-
-} // namespace __asan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan;
-#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \
- extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
- __asan_stack_malloc_##class_id(uptr size) { \
- return OnMalloc(class_id, size); \
- } \
- extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \
- uptr ptr, uptr size) { \
- OnFree(ptr, class_id, size); \
- }
-
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)
-DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
- void **end) {
- FakeStack *fs = reinterpret_cast<FakeStack*>(fake_stack);
- if (!fs) return nullptr;
- uptr frame_beg, frame_end;
- FakeFrame *frame = reinterpret_cast<FakeFrame *>(fs->AddrIsInFakeStack(
- reinterpret_cast<uptr>(addr), &frame_beg, &frame_end));
- if (!frame) return nullptr;
- if (frame->magic != kCurrentStackFrameMagic)
- return nullptr;
- if (beg) *beg = reinterpret_cast<void*>(frame_beg);
- if (end) *end = reinterpret_cast<void*>(frame_end);
- return reinterpret_cast<void*>(frame->real_stack);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_alloca_poison(uptr addr, uptr size) {
- uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize;
- uptr PartialRzAddr = addr + size;
- uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask;
- uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1);
- FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic);
- FastPoisonShadowPartialRightRedzone(
- PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY,
- RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic);
- FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_allocas_unpoison(uptr top, uptr bottom) {
- if ((!top) || (top > bottom)) return;
- REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
- (bottom - top) / SHADOW_GRANULARITY);
-}
-} // extern "C"
--- /dev/null
+//===-- asan_fake_stack.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// FakeStack is used to detect use-after-return bugs.
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_poisoning.h"
+#include "asan_thread.h"
+
+namespace __asan {
+
+static const u64 kMagic1 = kAsanStackAfterReturnMagic;
+static const u64 kMagic2 = (kMagic1 << 8) | kMagic1;
+static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;
+static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;
+
+static const u64 kAllocaRedzoneSize = 32UL;
+static const u64 kAllocaRedzoneMask = 31UL;
+
+// For small size classes inline PoisonShadow for better performance.
+ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
+ u64 *shadow = reinterpret_cast<u64*>(MemToShadow(ptr));
+ if (SHADOW_SCALE == 3 && class_id <= 6) {
+ // This code expects SHADOW_SCALE=3.
+ for (uptr i = 0; i < (((uptr)1) << class_id); i++) {
+ shadow[i] = magic;
+ // Make sure this does not become memset.
+ SanitizerBreakOptimization(nullptr);
+ }
+ } else {
+ // The size class is too big, it's cheaper to poison only size bytes.
+ PoisonShadow(ptr, size, static_cast<u8>(magic));
+ }
+}
+
+FakeStack *FakeStack::Create(uptr stack_size_log) {
+ static uptr kMinStackSizeLog = 16;
+ static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28);
+ if (stack_size_log < kMinStackSizeLog)
+ stack_size_log = kMinStackSizeLog;
+ if (stack_size_log > kMaxStackSizeLog)
+ stack_size_log = kMaxStackSizeLog;
+ uptr size = RequiredSize(stack_size_log);
+ FakeStack *res = reinterpret_cast<FakeStack *>(
+ flags()->uar_noreserve ? MmapNoReserveOrDie(size, "FakeStack")
+ : MmapOrDie(size, "FakeStack"));
+ res->stack_size_log_ = stack_size_log;
+ u8 *p = reinterpret_cast<u8 *>(res);
+ VReport(1, "T%d: FakeStack created: %p -- %p stack_size_log: %zd; "
+ "mmapped %zdK, noreserve=%d \n",
+ GetCurrentTidOrInvalid(), p,
+ p + FakeStack::RequiredSize(stack_size_log), stack_size_log,
+ size >> 10, flags()->uar_noreserve);
+ return res;
+}
+
+void FakeStack::Destroy(int tid) {
+ PoisonAll(0);
+ if (Verbosity() >= 2) {
+ InternalScopedString str(kNumberOfSizeClasses * 50);
+ for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++)
+ str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id],
+ NumberOfFrames(stack_size_log(), class_id));
+ Report("T%d: FakeStack destroyed: %s\n", tid, str.data());
+ }
+ uptr size = RequiredSize(stack_size_log_);
+ FlushUnneededASanShadowMemory(reinterpret_cast<uptr>(this), size);
+ UnmapOrDie(this, size);
+}
+
+void FakeStack::PoisonAll(u8 magic) {
+ PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()),
+ magic);
+}
+
+#if !defined(_MSC_VER) || defined(__clang__)
+ALWAYS_INLINE USED
+#endif
+FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
+ uptr real_stack) {
+ CHECK_LT(class_id, kNumberOfSizeClasses);
+ if (needs_gc_)
+ GC(real_stack);
+ uptr &hint_position = hint_position_[class_id];
+ const int num_iter = NumberOfFrames(stack_size_log, class_id);
+ u8 *flags = GetFlags(stack_size_log, class_id);
+ for (int i = 0; i < num_iter; i++) {
+ uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++);
+ // This part is tricky. On one hand, checking and setting flags[pos]
+ // should be atomic to ensure async-signal safety. But on the other hand,
+ // if the signal arrives between checking and setting flags[pos], the
+ // signal handler's fake stack will start from a different hint_position
+ // and so will not touch this particular byte. So, it is safe to do this
+ // with regular non-atomic load and store (at least I was not able to make
+ // this code crash).
+ if (flags[pos]) continue;
+ flags[pos] = 1;
+ FakeFrame *res = reinterpret_cast<FakeFrame *>(
+ GetFrame(stack_size_log, class_id, pos));
+ res->real_stack = real_stack;
+ *SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos];
+ return res;
+ }
+ return nullptr; // We are out of fake stack.
+}
+
+uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) {
+ uptr stack_size_log = this->stack_size_log();
+ uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0));
+ uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log);
+ if (ptr < beg || ptr >= end) return 0;
+ uptr class_id = (ptr - beg) >> stack_size_log;
+ uptr base = beg + (class_id << stack_size_log);
+ CHECK_LE(base, ptr);
+ CHECK_LT(ptr, base + (((uptr)1) << stack_size_log));
+ uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);
+ uptr res = base + pos * BytesInSizeClass(class_id);
+ *frame_end = res + BytesInSizeClass(class_id);
+ *frame_beg = res + sizeof(FakeFrame);
+ return res;
+}
+
+void FakeStack::HandleNoReturn() {
+ needs_gc_ = true;
+}
+
+// When throw, longjmp or some such happens we don't call OnFree() and
+// as the result may leak one or more fake frames, but the good news is that
+// we are notified about all such events by HandleNoReturn().
+// If we recently had such no-return event we need to collect garbage frames.
+// We do it based on their 'real_stack' values -- everything that is lower
+// than the current real_stack is garbage.
+NOINLINE void FakeStack::GC(uptr real_stack) {
+ uptr collected = 0;
+ for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
+ u8 *flags = GetFlags(stack_size_log(), class_id);
+ for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
+ i++) {
+ if (flags[i] == 0) continue; // not allocated.
+ FakeFrame *ff = reinterpret_cast<FakeFrame *>(
+ GetFrame(stack_size_log(), class_id, i));
+ if (ff->real_stack < real_stack) {
+ flags[i] = 0;
+ collected++;
+ }
+ }
+ }
+ needs_gc_ = false;
+}
+
+void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) {
+ for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
+ u8 *flags = GetFlags(stack_size_log(), class_id);
+ for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
+ i++) {
+ if (flags[i] == 0) continue; // not allocated.
+ FakeFrame *ff = reinterpret_cast<FakeFrame *>(
+ GetFrame(stack_size_log(), class_id, i));
+ uptr begin = reinterpret_cast<uptr>(ff);
+ callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg);
+ }
+ }
+}
+
+#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
+static THREADLOCAL FakeStack *fake_stack_tls;
+
+FakeStack *GetTLSFakeStack() {
+ return fake_stack_tls;
+}
+void SetTLSFakeStack(FakeStack *fs) {
+ fake_stack_tls = fs;
+}
+#else
+FakeStack *GetTLSFakeStack() { return 0; }
+void SetTLSFakeStack(FakeStack *fs) { }
+#endif // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA
+
+static FakeStack *GetFakeStack() {
+ AsanThread *t = GetCurrentThread();
+ if (!t) return nullptr;
+ return t->fake_stack();
+}
+
+static FakeStack *GetFakeStackFast() {
+ if (FakeStack *fs = GetTLSFakeStack())
+ return fs;
+ if (!__asan_option_detect_stack_use_after_return)
+ return nullptr;
+ return GetFakeStack();
+}
+
+ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {
+ FakeStack *fs = GetFakeStackFast();
+ if (!fs) return 0;
+ uptr local_stack;
+ uptr real_stack = reinterpret_cast<uptr>(&local_stack);
+ FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
+ if (!ff) return 0; // Out of fake stack.
+ uptr ptr = reinterpret_cast<uptr>(ff);
+ SetShadow(ptr, size, class_id, 0);
+ return ptr;
+}
+
+ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
+ FakeStack::Deallocate(ptr, class_id);
+ SetShadow(ptr, size, class_id, kMagic8);
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan;
+#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \
+ __asan_stack_malloc_##class_id(uptr size) { \
+ return OnMalloc(class_id, size); \
+ } \
+ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \
+ uptr ptr, uptr size) { \
+ OnFree(ptr, class_id, size); \
+ }
+
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)
+DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__asan_get_current_fake_stack() { return GetFakeStackFast(); }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
+ void **end) {
+ FakeStack *fs = reinterpret_cast<FakeStack*>(fake_stack);
+ if (!fs) return nullptr;
+ uptr frame_beg, frame_end;
+ FakeFrame *frame = reinterpret_cast<FakeFrame *>(fs->AddrIsInFakeStack(
+ reinterpret_cast<uptr>(addr), &frame_beg, &frame_end));
+ if (!frame) return nullptr;
+ if (frame->magic != kCurrentStackFrameMagic)
+ return nullptr;
+ if (beg) *beg = reinterpret_cast<void*>(frame_beg);
+ if (end) *end = reinterpret_cast<void*>(frame_end);
+ return reinterpret_cast<void*>(frame->real_stack);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_alloca_poison(uptr addr, uptr size) {
+ uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize;
+ uptr PartialRzAddr = addr + size;
+ uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask;
+ uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1);
+ FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic);
+ FastPoisonShadowPartialRightRedzone(
+ PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY,
+ RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic);
+ FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_allocas_unpoison(uptr top, uptr bottom) {
+ if ((!top) || (top > bottom)) return;
+ REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
+ (bottom - top) / SHADOW_GRANULARITY);
+}
+} // extern "C"
//===-- asan_fake_stack.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_fake_stack.cc, implements FakeStack.
+// ASan-private header for asan_fake_stack.cpp, implements FakeStack.
//===----------------------------------------------------------------------===//
#ifndef ASAN_FAKE_STACK_H
+++ /dev/null
-//===-- asan_flags.cc -------------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// ASan flag parsing logic.
-//===----------------------------------------------------------------------===//
-
-#include "asan_activation.h"
-#include "asan_flags.h"
-#include "asan_interface_internal.h"
-#include "asan_stack.h"
-#include "lsan/lsan_common.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_flag_parser.h"
-#include "ubsan/ubsan_flags.h"
-#include "ubsan/ubsan_platform.h"
-
-namespace __asan {
-
-Flags asan_flags_dont_use_directly; // use via flags().
-
-static const char *MaybeCallAsanDefaultOptions() {
- return (&__asan_default_options) ? __asan_default_options() : "";
-}
-
-static const char *MaybeUseAsanDefaultOptionsCompileDefinition() {
-#ifdef ASAN_DEFAULT_OPTIONS
- return SANITIZER_STRINGIFY(ASAN_DEFAULT_OPTIONS);
-#else
- return "";
-#endif
-}
-
-void Flags::SetDefaults() {
-#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
-#include "asan_flags.inc"
-#undef ASAN_FLAG
-}
-
-static void RegisterAsanFlags(FlagParser *parser, Flags *f) {
-#define ASAN_FLAG(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &f->Name);
-#include "asan_flags.inc"
-#undef ASAN_FLAG
-}
-
-void InitializeFlags() {
- // Set the default values and prepare for parsing ASan and common flags.
- SetCommonFlagsDefaults();
- {
- CommonFlags cf;
- cf.CopyFrom(*common_flags());
- cf.detect_leaks = cf.detect_leaks && CAN_SANITIZE_LEAKS;
- cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH");
- cf.malloc_context_size = kDefaultMallocContextSize;
- cf.intercept_tls_get_addr = true;
- cf.exitcode = 1;
- OverrideCommonFlags(cf);
- }
- Flags *f = flags();
- f->SetDefaults();
-
- FlagParser asan_parser;
- RegisterAsanFlags(&asan_parser, f);
- RegisterCommonFlags(&asan_parser);
-
- // Set the default values and prepare for parsing LSan and UBSan flags
- // (which can also overwrite common flags).
-#if CAN_SANITIZE_LEAKS
- __lsan::Flags *lf = __lsan::flags();
- lf->SetDefaults();
-
- FlagParser lsan_parser;
- __lsan::RegisterLsanFlags(&lsan_parser, lf);
- RegisterCommonFlags(&lsan_parser);
-#endif
-
-#if CAN_SANITIZE_UB
- __ubsan::Flags *uf = __ubsan::flags();
- uf->SetDefaults();
-
- FlagParser ubsan_parser;
- __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
- RegisterCommonFlags(&ubsan_parser);
-#endif
-
- if (SANITIZER_MAC) {
- // Support macOS MallocScribble and MallocPreScribble:
- // <https://developer.apple.com/library/content/documentation/Performance/
- // Conceptual/ManagingMemory/Articles/MallocDebug.html>
- if (GetEnv("MallocScribble")) {
- f->max_free_fill_size = 0x1000;
- }
- if (GetEnv("MallocPreScribble")) {
- f->malloc_fill_byte = 0xaa;
- }
- }
-
- // Override from ASan compile definition.
- const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
- asan_parser.ParseString(asan_compile_def);
-
- // Override from user-specified string.
- const char *asan_default_options = MaybeCallAsanDefaultOptions();
- asan_parser.ParseString(asan_default_options);
-#if CAN_SANITIZE_UB
- const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
- ubsan_parser.ParseString(ubsan_default_options);
-#endif
-#if CAN_SANITIZE_LEAKS
- const char *lsan_default_options = __lsan::MaybeCallLsanDefaultOptions();
- lsan_parser.ParseString(lsan_default_options);
-#endif
-
- // Override from command line.
- asan_parser.ParseString(GetEnv("ASAN_OPTIONS"));
-#if CAN_SANITIZE_LEAKS
- lsan_parser.ParseString(GetEnv("LSAN_OPTIONS"));
-#endif
-#if CAN_SANITIZE_UB
- ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
-#endif
-
- InitializeCommonFlags();
-
- // TODO(eugenis): dump all flags at verbosity>=2?
- if (Verbosity()) ReportUnrecognizedFlags();
-
- if (common_flags()->help) {
- // TODO(samsonov): print all of the flags (ASan, LSan, common).
- asan_parser.PrintFlagDescriptions();
- }
-
- // Flag validation:
- if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) {
- Report("%s: detect_leaks is not supported on this platform.\n",
- SanitizerToolName);
- Die();
- }
- // Ensure that redzone is at least SHADOW_GRANULARITY.
- if (f->redzone < (int)SHADOW_GRANULARITY)
- f->redzone = SHADOW_GRANULARITY;
- // Make "strict_init_order" imply "check_initialization_order".
- // TODO(samsonov): Use a single runtime flag for an init-order checker.
- if (f->strict_init_order) {
- f->check_initialization_order = true;
- }
- CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax);
- CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log);
- CHECK_GE(f->redzone, 16);
- CHECK_GE(f->max_redzone, f->redzone);
- CHECK_LE(f->max_redzone, 2048);
- CHECK(IsPowerOfTwo(f->redzone));
- CHECK(IsPowerOfTwo(f->max_redzone));
- if (SANITIZER_RTEMS) {
- CHECK(!f->unmap_shadow_on_exit);
- CHECK(!f->protect_shadow_gap);
- }
-
- // quarantine_size is deprecated but we still honor it.
- // quarantine_size can not be used together with quarantine_size_mb.
- if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) {
- Report("%s: please use either 'quarantine_size' (deprecated) or "
- "quarantine_size_mb, but not both\n", SanitizerToolName);
- Die();
- }
- if (f->quarantine_size >= 0)
- f->quarantine_size_mb = f->quarantine_size >> 20;
- if (f->quarantine_size_mb < 0) {
- const int kDefaultQuarantineSizeMb =
- (ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8;
- f->quarantine_size_mb = kDefaultQuarantineSizeMb;
- }
- if (f->thread_local_quarantine_size_kb < 0) {
- const u32 kDefaultThreadLocalQuarantineSizeKb =
- // It is not advised to go lower than 64Kb, otherwise quarantine batches
- // pushed from thread local quarantine to global one will create too
- // much overhead. One quarantine batch size is 8Kb and it holds up to
- // 1021 chunk, which amounts to 1/8 memory overhead per batch when
- // thread local quarantine is set to 64Kb.
- (ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10);
- f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;
- }
- if (f->thread_local_quarantine_size_kb == 0 && f->quarantine_size_mb > 0) {
- Report("%s: thread_local_quarantine_size_kb can be set to 0 only when "
- "quarantine_size_mb is set to 0\n", SanitizerToolName);
- Die();
- }
- if (!f->replace_str && common_flags()->intercept_strlen) {
- Report("WARNING: strlen interceptor is enabled even though replace_str=0. "
- "Use intercept_strlen=0 to disable it.");
- }
- if (!f->replace_str && common_flags()->intercept_strchr) {
- Report("WARNING: strchr* interceptors are enabled even though "
- "replace_str=0. Use intercept_strchr=0 to disable them.");
- }
- if (!f->replace_str && common_flags()->intercept_strndup) {
- Report("WARNING: strndup* interceptors are enabled even though "
- "replace_str=0. Use intercept_strndup=0 to disable them.");
- }
-}
-
-} // namespace __asan
-
-SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) {
- return "";
-}
--- /dev/null
+//===-- asan_flags.cpp ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan flag parsing logic.
+//===----------------------------------------------------------------------===//
+
+#include "asan_activation.h"
+#include "asan_flags.h"
+#include "asan_interface_internal.h"
+#include "asan_stack.h"
+#include "lsan/lsan_common.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "ubsan/ubsan_flags.h"
+#include "ubsan/ubsan_platform.h"
+
+namespace __asan {
+
+Flags asan_flags_dont_use_directly; // use via flags().
+
+static const char *MaybeCallAsanDefaultOptions() {
+ return (&__asan_default_options) ? __asan_default_options() : "";
+}
+
+static const char *MaybeUseAsanDefaultOptionsCompileDefinition() {
+#ifdef ASAN_DEFAULT_OPTIONS
+ return SANITIZER_STRINGIFY(ASAN_DEFAULT_OPTIONS);
+#else
+ return "";
+#endif
+}
+
+void Flags::SetDefaults() {
+#define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "asan_flags.inc"
+#undef ASAN_FLAG
+}
+
+static void RegisterAsanFlags(FlagParser *parser, Flags *f) {
+#define ASAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "asan_flags.inc"
+#undef ASAN_FLAG
+}
+
+void InitializeFlags() {
+ // Set the default values and prepare for parsing ASan and common flags.
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.detect_leaks = cf.detect_leaks && CAN_SANITIZE_LEAKS;
+ cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH");
+ cf.malloc_context_size = kDefaultMallocContextSize;
+ cf.intercept_tls_get_addr = true;
+ cf.exitcode = 1;
+ OverrideCommonFlags(cf);
+ }
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser asan_parser;
+ RegisterAsanFlags(&asan_parser, f);
+ RegisterCommonFlags(&asan_parser);
+
+ // Set the default values and prepare for parsing LSan and UBSan flags
+ // (which can also overwrite common flags).
+#if CAN_SANITIZE_LEAKS
+ __lsan::Flags *lf = __lsan::flags();
+ lf->SetDefaults();
+
+ FlagParser lsan_parser;
+ __lsan::RegisterLsanFlags(&lsan_parser, lf);
+ RegisterCommonFlags(&lsan_parser);
+#endif
+
+#if CAN_SANITIZE_UB
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
+
+ if (SANITIZER_MAC) {
+ // Support macOS MallocScribble and MallocPreScribble:
+ // <https://developer.apple.com/library/content/documentation/Performance/
+ // Conceptual/ManagingMemory/Articles/MallocDebug.html>
+ if (GetEnv("MallocScribble")) {
+ f->max_free_fill_size = 0x1000;
+ }
+ if (GetEnv("MallocPreScribble")) {
+ f->malloc_fill_byte = 0xaa;
+ }
+ }
+
+ // Override from ASan compile definition.
+ const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition();
+ asan_parser.ParseString(asan_compile_def);
+
+ // Override from user-specified string.
+ const char *asan_default_options = MaybeCallAsanDefaultOptions();
+ asan_parser.ParseString(asan_default_options);
+#if CAN_SANITIZE_UB
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
+#if CAN_SANITIZE_LEAKS
+ const char *lsan_default_options = __lsan::MaybeCallLsanDefaultOptions();
+ lsan_parser.ParseString(lsan_default_options);
+#endif
+
+ // Override from command line.
+ asan_parser.ParseStringFromEnv("ASAN_OPTIONS");
+#if CAN_SANITIZE_LEAKS
+ lsan_parser.ParseStringFromEnv("LSAN_OPTIONS");
+#endif
+#if CAN_SANITIZE_UB
+ ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS");
+#endif
+
+ InitializeCommonFlags();
+
+ // TODO(eugenis): dump all flags at verbosity>=2?
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) {
+ // TODO(samsonov): print all of the flags (ASan, LSan, common).
+ asan_parser.PrintFlagDescriptions();
+ }
+
+ // Flag validation:
+ if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) {
+ Report("%s: detect_leaks is not supported on this platform.\n",
+ SanitizerToolName);
+ Die();
+ }
+ // Ensure that redzone is at least SHADOW_GRANULARITY.
+ if (f->redzone < (int)SHADOW_GRANULARITY)
+ f->redzone = SHADOW_GRANULARITY;
+ // Make "strict_init_order" imply "check_initialization_order".
+ // TODO(samsonov): Use a single runtime flag for an init-order checker.
+ if (f->strict_init_order) {
+ f->check_initialization_order = true;
+ }
+ CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax);
+ CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log);
+ CHECK_GE(f->redzone, 16);
+ CHECK_GE(f->max_redzone, f->redzone);
+ CHECK_LE(f->max_redzone, 2048);
+ CHECK(IsPowerOfTwo(f->redzone));
+ CHECK(IsPowerOfTwo(f->max_redzone));
+ if (SANITIZER_RTEMS) {
+ CHECK(!f->unmap_shadow_on_exit);
+ CHECK(!f->protect_shadow_gap);
+ }
+
+ // quarantine_size is deprecated but we still honor it.
+ // quarantine_size can not be used together with quarantine_size_mb.
+ if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) {
+ Report("%s: please use either 'quarantine_size' (deprecated) or "
+ "quarantine_size_mb, but not both\n", SanitizerToolName);
+ Die();
+ }
+ if (f->quarantine_size >= 0)
+ f->quarantine_size_mb = f->quarantine_size >> 20;
+ if (f->quarantine_size_mb < 0) {
+ const int kDefaultQuarantineSizeMb =
+ (ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8;
+ f->quarantine_size_mb = kDefaultQuarantineSizeMb;
+ }
+ if (f->thread_local_quarantine_size_kb < 0) {
+ const u32 kDefaultThreadLocalQuarantineSizeKb =
+ // It is not advised to go lower than 64Kb, otherwise quarantine batches
+ // pushed from thread local quarantine to global one will create too
+ // much overhead. One quarantine batch size is 8Kb and it holds up to
+ // 1021 chunk, which amounts to 1/8 memory overhead per batch when
+ // thread local quarantine is set to 64Kb.
+ (ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10);
+ f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb;
+ }
+ if (f->thread_local_quarantine_size_kb == 0 && f->quarantine_size_mb > 0) {
+ Report("%s: thread_local_quarantine_size_kb can be set to 0 only when "
+ "quarantine_size_mb is set to 0\n", SanitizerToolName);
+ Die();
+ }
+ if (!f->replace_str && common_flags()->intercept_strlen) {
+ Report("WARNING: strlen interceptor is enabled even though replace_str=0. "
+ "Use intercept_strlen=0 to disable it.");
+ }
+ if (!f->replace_str && common_flags()->intercept_strchr) {
+ Report("WARNING: strchr* interceptors are enabled even though "
+ "replace_str=0. Use intercept_strchr=0 to disable them.");
+ }
+ if (!f->replace_str && common_flags()->intercept_strndup) {
+ Report("WARNING: strndup* interceptors are enabled even though "
+ "replace_str=0. Use intercept_strndup=0 to disable them.");
+ }
+}
+
+} // namespace __asan
+
+SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) {
+ return "";
+}
//===-- asan_flags.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- asan_flags.inc ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
ASAN_FLAG(bool, halt_on_error, true,
"Crash the program after printing the first error report "
"(WARNING: USE AT YOUR OWN RISK!)")
-ASAN_FLAG(bool, use_odr_indicator, false,
- "Use special ODR indicator symbol for ODR violation detection")
ASAN_FLAG(bool, allocator_frees_and_returns_null_on_realloc_zero, true,
"realloc(p, 0) is equivalent to free(p) by default (Same as the "
"POSIX standard). If set to false, realloc(p, 0) will return a "
ASAN_FLAG(bool, verify_asan_link_order, true,
"Check position of ASan runtime in library list (needs to be disabled"
" when other library has to be preloaded system-wide)")
+ASAN_FLAG(bool, windows_hook_rtl_allocators, false,
+ "(Windows only) enable hooking of Rtl(Allocate|Free|Size|ReAllocate)Heap.")
+++ /dev/null
-//===-- asan_fuchsia.cc --------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===---------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Fuchsia-specific details.
-//===---------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_fuchsia.h"
-#if SANITIZER_FUCHSIA
-
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_stack.h"
-#include "asan_thread.h"
-
-#include <limits.h>
-#include <zircon/sanitizer.h>
-#include <zircon/syscalls.h>
-#include <zircon/threads.h>
-
-namespace __asan {
-
-// The system already set up the shadow memory for us.
-// __sanitizer::GetMaxUserVirtualAddress has already been called by
-// AsanInitInternal->InitializeHighMemEnd (asan_rtl.cc).
-// Just do some additional sanity checks here.
-void InitializeShadowMemory() {
- if (Verbosity()) PrintAddressSpaceLayout();
-
- // Make sure SHADOW_OFFSET doesn't use __asan_shadow_memory_dynamic_address.
- __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
- DCHECK(kLowShadowBeg != kDefaultShadowSentinel);
- __asan_shadow_memory_dynamic_address = kLowShadowBeg;
-
- CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
- CHECK_EQ(kHighMemEnd, __sanitizer::ShadowBounds.memory_limit - 1);
- CHECK_EQ(kHighMemBeg, __sanitizer::ShadowBounds.shadow_limit);
- CHECK_EQ(kHighShadowBeg, __sanitizer::ShadowBounds.shadow_base);
- CHECK_EQ(kShadowGapEnd, __sanitizer::ShadowBounds.shadow_base - 1);
- CHECK_EQ(kLowShadowEnd, 0);
- CHECK_EQ(kLowShadowBeg, 0);
-}
-
-void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
- UNIMPLEMENTED();
-}
-
-void AsanCheckDynamicRTPrereqs() {}
-void AsanCheckIncompatibleRT() {}
-void InitializeAsanInterceptors() {}
-
-void *AsanDoesNotSupportStaticLinkage() { return nullptr; }
-
-void InitializePlatformExceptionHandlers() {}
-void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
- UNIMPLEMENTED();
-}
-
-// We can use a plain thread_local variable for TSD.
-static thread_local void *per_thread;
-
-void *AsanTSDGet() { return per_thread; }
-
-void AsanTSDSet(void *tsd) { per_thread = tsd; }
-
-// There's no initialization needed, and the passed-in destructor
-// will never be called. Instead, our own thread destruction hook
-// (below) will call AsanThread::TSDDtor directly.
-void AsanTSDInit(void (*destructor)(void *tsd)) {
- DCHECK(destructor == &PlatformTSDDtor);
-}
-
-void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); }
-
-static inline size_t AsanThreadMmapSize() {
- return RoundUpTo(sizeof(AsanThread), PAGE_SIZE);
-}
-
-struct AsanThread::InitOptions {
- uptr stack_bottom, stack_size;
-};
-
-// Shared setup between thread creation and startup for the initial thread.
-static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid,
- uptr user_id, bool detached,
- const char *name, uptr stack_bottom,
- uptr stack_size) {
- // In lieu of AsanThread::Create.
- AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__);
-
- AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
- u32 tid =
- asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args);
- asanThreadRegistry().SetThreadName(tid, name);
-
- // On other systems, AsanThread::Init() is called from the new
- // thread itself. But on Fuchsia we already know the stack address
- // range beforehand, so we can do most of the setup right now.
- const AsanThread::InitOptions options = {stack_bottom, stack_size};
- thread->Init(&options);
-
- return thread;
-}
-
-// This gets the same arguments passed to Init by CreateAsanThread, above.
-// We're in the creator thread before the new thread is actually started,
-// but its stack address range is already known. We don't bother tracking
-// the static TLS address range because the system itself already uses an
-// ASan-aware allocator for that.
-void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) {
- DCHECK_NE(GetCurrentThread(), this);
- DCHECK_NE(GetCurrentThread(), nullptr);
- CHECK_NE(options->stack_bottom, 0);
- CHECK_NE(options->stack_size, 0);
- stack_bottom_ = options->stack_bottom;
- stack_top_ = options->stack_bottom + options->stack_size;
-}
-
-// Called by __asan::AsanInitInternal (asan_rtl.c).
-AsanThread *CreateMainThread() {
- thrd_t self = thrd_current();
- char name[ZX_MAX_NAME_LEN];
- CHECK_NE(__sanitizer::MainThreadStackBase, 0);
- CHECK_GT(__sanitizer::MainThreadStackSize, 0);
- AsanThread *t = CreateAsanThread(
- nullptr, 0, reinterpret_cast<uptr>(self), true,
- _zx_object_get_property(thrd_get_zx_handle(self), ZX_PROP_NAME, name,
- sizeof(name)) == ZX_OK
- ? name
- : nullptr,
- __sanitizer::MainThreadStackBase, __sanitizer::MainThreadStackSize);
- SetCurrentThread(t);
- return t;
-}
-
-// This is called before each thread creation is attempted. So, in
-// its first call, the calling thread is the initial and sole thread.
-static void *BeforeThreadCreateHook(uptr user_id, bool detached,
- const char *name, uptr stack_bottom,
- uptr stack_size) {
- EnsureMainThreadIDIsCorrect();
- // Strict init-order checking is thread-hostile.
- if (flags()->strict_init_order) StopInitOrderChecking();
-
- GET_STACK_TRACE_THREAD;
- u32 parent_tid = GetCurrentTidOrInvalid();
-
- return CreateAsanThread(&stack, parent_tid, user_id, detached, name,
- stack_bottom, stack_size);
-}
-
-// This is called after creating a new thread (in the creating thread),
-// with the pointer returned by BeforeThreadCreateHook (above).
-static void ThreadCreateHook(void *hook, bool aborted) {
- AsanThread *thread = static_cast<AsanThread *>(hook);
- if (!aborted) {
- // The thread was created successfully.
- // ThreadStartHook is already running in the new thread.
- } else {
- // The thread wasn't created after all.
- // Clean up everything we set up in BeforeThreadCreateHook.
- asanThreadRegistry().FinishThread(thread->tid());
- UnmapOrDie(thread, AsanThreadMmapSize());
- }
-}
-
-// This is called in the newly-created thread before it runs anything else,
-// with the pointer returned by BeforeThreadCreateHook (above).
-// cf. asan_interceptors.cc:asan_thread_start
-static void ThreadStartHook(void *hook, uptr os_id) {
- AsanThread *thread = static_cast<AsanThread *>(hook);
- SetCurrentThread(thread);
-
- // In lieu of AsanThread::ThreadStart.
- asanThreadRegistry().StartThread(thread->tid(), os_id, /*workerthread*/ false,
- nullptr);
-}
-
-// Each thread runs this just before it exits,
-// with the pointer returned by BeforeThreadCreateHook (above).
-// All per-thread destructors have already been called.
-static void ThreadExitHook(void *hook, uptr os_id) {
- AsanThread::TSDDtor(per_thread);
-}
-
-} // namespace __asan
-
-// These are declared (in extern "C") by <zircon/sanitizer.h>.
-// The system runtime will call our definitions directly.
-
-void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached,
- const char *name, void *stack_base,
- size_t stack_size) {
- return __asan::BeforeThreadCreateHook(
- reinterpret_cast<uptr>(thread), detached, name,
- reinterpret_cast<uptr>(stack_base), stack_size);
-}
-
-void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) {
- __asan::ThreadCreateHook(hook, error != thrd_success);
-}
-
-void __sanitizer_thread_start_hook(void *hook, thrd_t self) {
- __asan::ThreadStartHook(hook, reinterpret_cast<uptr>(self));
-}
-
-void __sanitizer_thread_exit_hook(void *hook, thrd_t self) {
- __asan::ThreadExitHook(hook, reinterpret_cast<uptr>(self));
-}
-
-#endif // SANITIZER_FUCHSIA
--- /dev/null
+//===-- asan_fuchsia.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Fuchsia-specific details.
+//===---------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_fuchsia.h"
+#if SANITIZER_FUCHSIA
+
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+
+#include <limits.h>
+#include <zircon/sanitizer.h>
+#include <zircon/syscalls.h>
+#include <zircon/threads.h>
+
+namespace __asan {
+
+// The system already set up the shadow memory for us.
+// __sanitizer::GetMaxUserVirtualAddress has already been called by
+// AsanInitInternal->InitializeHighMemEnd (asan_rtl.cpp).
+// Just do some additional sanity checks here.
+void InitializeShadowMemory() {
+ if (Verbosity()) PrintAddressSpaceLayout();
+
+ // Make sure SHADOW_OFFSET doesn't use __asan_shadow_memory_dynamic_address.
+ __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
+ DCHECK(kLowShadowBeg != kDefaultShadowSentinel);
+ __asan_shadow_memory_dynamic_address = kLowShadowBeg;
+
+ CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
+ CHECK_EQ(kHighMemEnd, __sanitizer::ShadowBounds.memory_limit - 1);
+ CHECK_EQ(kHighMemBeg, __sanitizer::ShadowBounds.shadow_limit);
+ CHECK_EQ(kHighShadowBeg, __sanitizer::ShadowBounds.shadow_base);
+ CHECK_EQ(kShadowGapEnd, __sanitizer::ShadowBounds.shadow_base - 1);
+ CHECK_EQ(kLowShadowEnd, 0);
+ CHECK_EQ(kLowShadowBeg, 0);
+}
+
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ UNIMPLEMENTED();
+}
+
+void AsanCheckDynamicRTPrereqs() {}
+void AsanCheckIncompatibleRT() {}
+void InitializeAsanInterceptors() {}
+
+void *AsanDoesNotSupportStaticLinkage() { return nullptr; }
+
+void InitializePlatformExceptionHandlers() {}
+void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ UNIMPLEMENTED();
+}
+
+// We can use a plain thread_local variable for TSD.
+static thread_local void *per_thread;
+
+void *AsanTSDGet() { return per_thread; }
+
+void AsanTSDSet(void *tsd) { per_thread = tsd; }
+
+// There's no initialization needed, and the passed-in destructor
+// will never be called. Instead, our own thread destruction hook
+// (below) will call AsanThread::TSDDtor directly.
+void AsanTSDInit(void (*destructor)(void *tsd)) {
+ DCHECK(destructor == &PlatformTSDDtor);
+}
+
+void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); }
+
+static inline size_t AsanThreadMmapSize() {
+ return RoundUpTo(sizeof(AsanThread), PAGE_SIZE);
+}
+
+struct AsanThread::InitOptions {
+ uptr stack_bottom, stack_size;
+};
+
+// Shared setup between thread creation and startup for the initial thread.
+static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid,
+ uptr user_id, bool detached,
+ const char *name, uptr stack_bottom,
+ uptr stack_size) {
+ // In lieu of AsanThread::Create.
+ AsanThread *thread = (AsanThread *)MmapOrDie(AsanThreadMmapSize(), __func__);
+
+ AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
+ u32 tid =
+ asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args);
+ asanThreadRegistry().SetThreadName(tid, name);
+
+ // On other systems, AsanThread::Init() is called from the new
+ // thread itself. But on Fuchsia we already know the stack address
+ // range beforehand, so we can do most of the setup right now.
+ const AsanThread::InitOptions options = {stack_bottom, stack_size};
+ thread->Init(&options);
+
+ return thread;
+}
+
+// This gets the same arguments passed to Init by CreateAsanThread, above.
+// We're in the creator thread before the new thread is actually started,
+// but its stack address range is already known. We don't bother tracking
+// the static TLS address range because the system itself already uses an
+// ASan-aware allocator for that.
+void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) {
+ DCHECK_NE(GetCurrentThread(), this);
+ DCHECK_NE(GetCurrentThread(), nullptr);
+ CHECK_NE(options->stack_bottom, 0);
+ CHECK_NE(options->stack_size, 0);
+ stack_bottom_ = options->stack_bottom;
+ stack_top_ = options->stack_bottom + options->stack_size;
+}
+
+// Called by __asan::AsanInitInternal (asan_rtl.c).
+AsanThread *CreateMainThread() {
+ thrd_t self = thrd_current();
+ char name[ZX_MAX_NAME_LEN];
+ CHECK_NE(__sanitizer::MainThreadStackBase, 0);
+ CHECK_GT(__sanitizer::MainThreadStackSize, 0);
+ AsanThread *t = CreateAsanThread(
+ nullptr, 0, reinterpret_cast<uptr>(self), true,
+ _zx_object_get_property(thrd_get_zx_handle(self), ZX_PROP_NAME, name,
+ sizeof(name)) == ZX_OK
+ ? name
+ : nullptr,
+ __sanitizer::MainThreadStackBase, __sanitizer::MainThreadStackSize);
+ SetCurrentThread(t);
+ return t;
+}
+
+// This is called before each thread creation is attempted. So, in
+// its first call, the calling thread is the initial and sole thread.
+static void *BeforeThreadCreateHook(uptr user_id, bool detached,
+ const char *name, uptr stack_bottom,
+ uptr stack_size) {
+ EnsureMainThreadIDIsCorrect();
+ // Strict init-order checking is thread-hostile.
+ if (flags()->strict_init_order) StopInitOrderChecking();
+
+ GET_STACK_TRACE_THREAD;
+ u32 parent_tid = GetCurrentTidOrInvalid();
+
+ return CreateAsanThread(&stack, parent_tid, user_id, detached, name,
+ stack_bottom, stack_size);
+}
+
+// This is called after creating a new thread (in the creating thread),
+// with the pointer returned by BeforeThreadCreateHook (above).
+static void ThreadCreateHook(void *hook, bool aborted) {
+ AsanThread *thread = static_cast<AsanThread *>(hook);
+ if (!aborted) {
+ // The thread was created successfully.
+ // ThreadStartHook is already running in the new thread.
+ } else {
+ // The thread wasn't created after all.
+ // Clean up everything we set up in BeforeThreadCreateHook.
+ asanThreadRegistry().FinishThread(thread->tid());
+ UnmapOrDie(thread, AsanThreadMmapSize());
+ }
+}
+
+// This is called in the newly-created thread before it runs anything else,
+// with the pointer returned by BeforeThreadCreateHook (above).
+// cf. asan_interceptors.cpp:asan_thread_start
+static void ThreadStartHook(void *hook, uptr os_id) {
+ AsanThread *thread = static_cast<AsanThread *>(hook);
+ SetCurrentThread(thread);
+
+ // In lieu of AsanThread::ThreadStart.
+ asanThreadRegistry().StartThread(thread->tid(), os_id, ThreadType::Regular,
+ nullptr);
+}
+
+// Each thread runs this just before it exits,
+// with the pointer returned by BeforeThreadCreateHook (above).
+// All per-thread destructors have already been called.
+static void ThreadExitHook(void *hook, uptr os_id) {
+ AsanThread::TSDDtor(per_thread);
+}
+
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+
+} // namespace __asan
+
+// These are declared (in extern "C") by <zircon/sanitizer.h>.
+// The system runtime will call our definitions directly.
+
+void *__sanitizer_before_thread_create_hook(thrd_t thread, bool detached,
+ const char *name, void *stack_base,
+ size_t stack_size) {
+ return __asan::BeforeThreadCreateHook(
+ reinterpret_cast<uptr>(thread), detached, name,
+ reinterpret_cast<uptr>(stack_base), stack_size);
+}
+
+void __sanitizer_thread_create_hook(void *hook, thrd_t thread, int error) {
+ __asan::ThreadCreateHook(hook, error != thrd_success);
+}
+
+void __sanitizer_thread_start_hook(void *hook, thrd_t self) {
+ __asan::ThreadStartHook(hook, reinterpret_cast<uptr>(self));
+}
+
+void __sanitizer_thread_exit_hook(void *hook, thrd_t self) {
+ __asan::ThreadExitHook(hook, reinterpret_cast<uptr>(self));
+}
+
+#endif // SANITIZER_FUCHSIA
+++ /dev/null
-//===-- asan_globals.cc ---------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Handle globals.
-//===----------------------------------------------------------------------===//
-
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_stats.h"
-#include "asan_suppressions.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-
-namespace __asan {
-
-typedef __asan_global Global;
-
-struct ListOfGlobals {
- const Global *g;
- ListOfGlobals *next;
-};
-
-static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
-static LowLevelAllocator allocator_for_globals;
-static ListOfGlobals *list_of_all_globals;
-
-static const int kDynamicInitGlobalsInitialCapacity = 512;
-struct DynInitGlobal {
- Global g;
- bool initialized;
-};
-typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
-// Lazy-initialized and never deleted.
-static VectorOfGlobals *dynamic_init_globals;
-
-// We want to remember where a certain range of globals was registered.
-struct GlobalRegistrationSite {
- u32 stack_id;
- Global *g_first, *g_last;
-};
-typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
-static GlobalRegistrationSiteVector *global_registration_site_vector;
-
-ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
- FastPoisonShadow(g->beg, g->size_with_redzone, value);
-}
-
-ALWAYS_INLINE void PoisonRedZones(const Global &g) {
- uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY);
- FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size,
- kAsanGlobalRedzoneMagic);
- if (g.size != aligned_size) {
- FastPoisonShadowPartialRightRedzone(
- g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY),
- g.size % SHADOW_GRANULARITY,
- SHADOW_GRANULARITY,
- kAsanGlobalRedzoneMagic);
- }
-}
-
-const uptr kMinimalDistanceFromAnotherGlobal = 64;
-
-static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
- if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
- if (addr >= g.beg + g.size_with_redzone) return false;
- return true;
-}
-
-static void ReportGlobal(const Global &g, const char *prefix) {
- Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
- prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
- g.module_name, g.has_dynamic_init);
- if (g.location) {
- Report(" location (%p): name=%s[%p], %d %d\n", g.location,
- g.location->filename, g.location->filename, g.location->line_no,
- g.location->column_no);
- }
-}
-
-static u32 FindRegistrationSite(const Global *g) {
- mu_for_globals.CheckLocked();
- CHECK(global_registration_site_vector);
- for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
- GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
- if (g >= grs.g_first && g <= grs.g_last)
- return grs.stack_id;
- }
- return 0;
-}
-
-int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
- int max_globals) {
- if (!flags()->report_globals) return 0;
- BlockingMutexLock lock(&mu_for_globals);
- int res = 0;
- for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
- const Global &g = *l->g;
- if (flags()->report_globals >= 2)
- ReportGlobal(g, "Search");
- if (IsAddressNearGlobal(addr, g)) {
-#if defined(__GNUC__) && defined(__sparc__)
- internal_memcpy(&globals[res], &g, sizeof(g));
-#else
- globals[res] = g;
-#endif
- if (reg_sites)
- reg_sites[res] = FindRegistrationSite(&g);
- res++;
- if (res == max_globals) break;
- }
- }
- return res;
-}
-
-enum GlobalSymbolState {
- UNREGISTERED = 0,
- REGISTERED = 1
-};
-
-// Check ODR violation for given global G via special ODR indicator. We use
-// this method in case compiler instruments global variables through their
-// local aliases.
-static void CheckODRViolationViaIndicator(const Global *g) {
- u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
- if (*odr_indicator == UNREGISTERED) {
- *odr_indicator = REGISTERED;
- return;
- }
- // If *odr_indicator is DEFINED, some module have already registered
- // externally visible symbol with the same name. This is an ODR violation.
- for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
- if (g->odr_indicator == l->g->odr_indicator &&
- (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
- !IsODRViolationSuppressed(g->name))
- ReportODRViolation(g, FindRegistrationSite(g),
- l->g, FindRegistrationSite(l->g));
- }
-}
-
-// Clang provides two different ways for global variables protection:
-// it can poison the global itself or its private alias. In former
-// case we may poison same symbol multiple times, that can help us to
-// cheaply detect ODR violation: if we try to poison an already poisoned
-// global, we have ODR violation error.
-// In latter case, we poison each symbol exactly once, so we use special
-// indicator symbol to perform similar check.
-// In either case, compiler provides a special odr_indicator field to Global
-// structure, that can contain two kinds of values:
-// 1) Non-zero value. In this case, odr_indicator is an address of
-// corresponding indicator variable for given global.
-// 2) Zero. This means that we don't use private aliases for global variables
-// and can freely check ODR violation with the first method.
-//
-// This routine chooses between two different methods of ODR violation
-// detection.
-static inline bool UseODRIndicator(const Global *g) {
- // Use ODR indicator method iff use_odr_indicator flag is set and
- // indicator symbol address is not 0.
- return flags()->use_odr_indicator && g->odr_indicator > 0;
-}
-
-// Register a global variable.
-// This function may be called more than once for every global
-// so we store the globals in a map.
-static void RegisterGlobal(const Global *g) {
- CHECK(asan_inited);
- if (flags()->report_globals >= 2)
- ReportGlobal(*g, "Added");
- CHECK(flags()->report_globals);
- CHECK(AddrIsInMem(g->beg));
- if (!AddrIsAlignedByGranularity(g->beg)) {
- Report("The following global variable is not properly aligned.\n");
- Report("This may happen if another global with the same name\n");
- Report("resides in another non-instrumented module.\n");
- Report("Or the global comes from a C file built w/o -fno-common.\n");
- Report("In either case this is likely an ODR violation bug,\n");
- Report("but AddressSanitizer can not provide more details.\n");
- ReportODRViolation(g, FindRegistrationSite(g), g, FindRegistrationSite(g));
- CHECK(AddrIsAlignedByGranularity(g->beg));
- }
- CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- if (flags()->detect_odr_violation) {
- // Try detecting ODR (One Definition Rule) violation, i.e. the situation
- // where two globals with the same name are defined in different modules.
- if (UseODRIndicator(g))
- CheckODRViolationViaIndicator(g);
- }
- if (CanPoisonMemory())
- PoisonRedZones(*g);
- ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals;
- l->g = g;
- l->next = list_of_all_globals;
- list_of_all_globals = l;
- if (g->has_dynamic_init) {
- if (!dynamic_init_globals) {
- dynamic_init_globals =
- new (allocator_for_globals) VectorOfGlobals; // NOLINT
- dynamic_init_globals->reserve(kDynamicInitGlobalsInitialCapacity);
- }
- DynInitGlobal dyn_global = { *g, false };
- dynamic_init_globals->push_back(dyn_global);
- }
-}
-
-static void UnregisterGlobal(const Global *g) {
- CHECK(asan_inited);
- if (flags()->report_globals >= 2)
- ReportGlobal(*g, "Removed");
- CHECK(flags()->report_globals);
- CHECK(AddrIsInMem(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->beg));
- CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
- if (CanPoisonMemory())
- PoisonShadowForGlobal(g, 0);
- // We unpoison the shadow memory for the global but we do not remove it from
- // the list because that would require O(n^2) time with the current list
- // implementation. It might not be worth doing anyway.
-
- // Release ODR indicator.
- if (UseODRIndicator(g)) {
- u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
- *odr_indicator = UNREGISTERED;
- }
-}
-
-void StopInitOrderChecking() {
- BlockingMutexLock lock(&mu_for_globals);
- if (!flags()->check_initialization_order || !dynamic_init_globals)
- return;
- flags()->check_initialization_order = false;
- for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
- DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
- const Global *g = &dyn_g.g;
- // Unpoison the whole global.
- PoisonShadowForGlobal(g, 0);
- // Poison redzones back.
- PoisonRedZones(*g);
- }
-}
-
-static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
-
-const char *MaybeDemangleGlobalName(const char *name) {
- // We can spoil names of globals with C linkage, so use an heuristic
- // approach to check if the name should be demangled.
- bool should_demangle = false;
- if (name[0] == '_' && name[1] == 'Z')
- should_demangle = true;
- else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
- should_demangle = true;
-
- return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
-}
-
-// Check if the global is a zero-terminated ASCII string. If so, print it.
-void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) {
- for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
- unsigned char c = *(unsigned char *)p;
- if (c == '\0' || !IsASCII(c)) return;
- }
- if (*(char *)(g.beg + g.size - 1) != '\0') return;
- str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
- (char *)g.beg);
-}
-
-static const char *GlobalFilename(const __asan_global &g) {
- const char *res = g.module_name;
- // Prefer the filename from source location, if is available.
- if (g.location) res = g.location->filename;
- CHECK(res);
- return res;
-}
-
-void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) {
- str->append("%s", GlobalFilename(g));
- if (!g.location) return;
- if (g.location->line_no) str->append(":%d", g.location->line_no);
- if (g.location->column_no) str->append(":%d", g.location->column_no);
-}
-
-} // namespace __asan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-
-// Apply __asan_register_globals to all globals found in the same loaded
-// executable or shared library as `flag'. The flag tracks whether globals have
-// already been registered or not for this image.
-void __asan_register_image_globals(uptr *flag) {
- if (*flag)
- return;
- AsanApplyToGlobals(__asan_register_globals, flag);
- *flag = 1;
-}
-
-// This mirrors __asan_register_image_globals.
-void __asan_unregister_image_globals(uptr *flag) {
- if (!*flag)
- return;
- AsanApplyToGlobals(__asan_unregister_globals, flag);
- *flag = 0;
-}
-
-void __asan_register_elf_globals(uptr *flag, void *start, void *stop) {
- if (*flag) return;
- if (!start) return;
- CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global));
- __asan_global *globals_start = (__asan_global*)start;
- __asan_global *globals_stop = (__asan_global*)stop;
- __asan_register_globals(globals_start, globals_stop - globals_start);
- *flag = 1;
-}
-
-void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) {
- if (!*flag) return;
- if (!start) return;
- CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global));
- __asan_global *globals_start = (__asan_global*)start;
- __asan_global *globals_stop = (__asan_global*)stop;
- __asan_unregister_globals(globals_start, globals_stop - globals_start);
- *flag = 0;
-}
-
-// Register an array of globals.
-void __asan_register_globals(__asan_global *globals, uptr n) {
- if (!flags()->report_globals) return;
- GET_STACK_TRACE_MALLOC;
- u32 stack_id = StackDepotPut(stack);
- BlockingMutexLock lock(&mu_for_globals);
- if (!global_registration_site_vector) {
- global_registration_site_vector =
- new (allocator_for_globals) GlobalRegistrationSiteVector; // NOLINT
- global_registration_site_vector->reserve(128);
- }
- GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
- global_registration_site_vector->push_back(site);
- if (flags()->report_globals >= 2) {
- PRINT_CURRENT_STACK();
- Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
- }
- for (uptr i = 0; i < n; i++) {
- if (SANITIZER_WINDOWS && globals[i].beg == 0) {
- // The MSVC incremental linker may pad globals out to 256 bytes. As long
- // as __asan_global is less than 256 bytes large and its size is a power
- // of two, we can skip over the padding.
- static_assert(
- sizeof(__asan_global) < 256 &&
- (sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0,
- "sizeof(__asan_global) incompatible with incremental linker padding");
- // If these are padding bytes, the rest of the global should be zero.
- CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 &&
- globals[i].name == nullptr && globals[i].module_name == nullptr &&
- globals[i].odr_indicator == 0);
- continue;
- }
- RegisterGlobal(&globals[i]);
- }
-
- // Poison the metadata. It should not be accessible to user code.
- PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global),
- kAsanGlobalRedzoneMagic);
-}
-
-// Unregister an array of globals.
-// We must do this when a shared objects gets dlclosed.
-void __asan_unregister_globals(__asan_global *globals, uptr n) {
- if (!flags()->report_globals) return;
- BlockingMutexLock lock(&mu_for_globals);
- for (uptr i = 0; i < n; i++) {
- if (SANITIZER_WINDOWS && globals[i].beg == 0) {
- // Skip globals that look like padding from the MSVC incremental linker.
- // See comment in __asan_register_globals.
- continue;
- }
- UnregisterGlobal(&globals[i]);
- }
-
- // Unpoison the metadata.
- PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global), 0);
-}
-
-// This method runs immediately prior to dynamic initialization in each TU,
-// when all dynamically initialized globals are unpoisoned. This method
-// poisons all global variables not defined in this TU, so that a dynamic
-// initializer can only touch global variables in the same TU.
-void __asan_before_dynamic_init(const char *module_name) {
- if (!flags()->check_initialization_order ||
- !CanPoisonMemory() ||
- !dynamic_init_globals)
- return;
- bool strict_init_order = flags()->strict_init_order;
- CHECK(module_name);
- CHECK(asan_inited);
- BlockingMutexLock lock(&mu_for_globals);
- if (flags()->report_globals >= 3)
- Printf("DynInitPoison module: %s\n", module_name);
- for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
- DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
- const Global *g = &dyn_g.g;
- if (dyn_g.initialized)
- continue;
- if (g->module_name != module_name)
- PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
- else if (!strict_init_order)
- dyn_g.initialized = true;
- }
-}
-
-// This method runs immediately after dynamic initialization in each TU, when
-// all dynamically initialized globals except for those defined in the current
-// TU are poisoned. It simply unpoisons all dynamically initialized globals.
-void __asan_after_dynamic_init() {
- if (!flags()->check_initialization_order ||
- !CanPoisonMemory() ||
- !dynamic_init_globals)
- return;
- CHECK(asan_inited);
- BlockingMutexLock lock(&mu_for_globals);
- // FIXME: Optionally report that we're unpoisoning globals from a module.
- for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
- DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
- const Global *g = &dyn_g.g;
- if (!dyn_g.initialized) {
- // Unpoison the whole global.
- PoisonShadowForGlobal(g, 0);
- // Poison redzones back.
- PoisonRedZones(*g);
- }
- }
-}
--- /dev/null
+//===-- asan_globals.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Handle globals.
+//===----------------------------------------------------------------------===//
+
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_suppressions.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __asan {
+
+typedef __asan_global Global;
+
+struct ListOfGlobals {
+ const Global *g;
+ ListOfGlobals *next;
+};
+
+static BlockingMutex mu_for_globals(LINKER_INITIALIZED);
+static LowLevelAllocator allocator_for_globals;
+static ListOfGlobals *list_of_all_globals;
+
+static const int kDynamicInitGlobalsInitialCapacity = 512;
+struct DynInitGlobal {
+ Global g;
+ bool initialized;
+};
+typedef InternalMmapVector<DynInitGlobal> VectorOfGlobals;
+// Lazy-initialized and never deleted.
+static VectorOfGlobals *dynamic_init_globals;
+
+// We want to remember where a certain range of globals was registered.
+struct GlobalRegistrationSite {
+ u32 stack_id;
+ Global *g_first, *g_last;
+};
+typedef InternalMmapVector<GlobalRegistrationSite> GlobalRegistrationSiteVector;
+static GlobalRegistrationSiteVector *global_registration_site_vector;
+
+ALWAYS_INLINE void PoisonShadowForGlobal(const Global *g, u8 value) {
+ FastPoisonShadow(g->beg, g->size_with_redzone, value);
+}
+
+ALWAYS_INLINE void PoisonRedZones(const Global &g) {
+ uptr aligned_size = RoundUpTo(g.size, SHADOW_GRANULARITY);
+ FastPoisonShadow(g.beg + aligned_size, g.size_with_redzone - aligned_size,
+ kAsanGlobalRedzoneMagic);
+ if (g.size != aligned_size) {
+ FastPoisonShadowPartialRightRedzone(
+ g.beg + RoundDownTo(g.size, SHADOW_GRANULARITY),
+ g.size % SHADOW_GRANULARITY,
+ SHADOW_GRANULARITY,
+ kAsanGlobalRedzoneMagic);
+ }
+}
+
+const uptr kMinimalDistanceFromAnotherGlobal = 64;
+
+static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
+ if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
+ if (addr >= g.beg + g.size_with_redzone) return false;
+ return true;
+}
+
+static void ReportGlobal(const Global &g, const char *prefix) {
+ Report(
+ "%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu "
+ "odr_indicator=%p\n",
+ prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
+ g.module_name, g.has_dynamic_init, (void *)g.odr_indicator);
+ if (g.location) {
+ Report(" location (%p): name=%s[%p], %d %d\n", g.location,
+ g.location->filename, g.location->filename, g.location->line_no,
+ g.location->column_no);
+ }
+}
+
+static u32 FindRegistrationSite(const Global *g) {
+ mu_for_globals.CheckLocked();
+ CHECK(global_registration_site_vector);
+ for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
+ GlobalRegistrationSite &grs = (*global_registration_site_vector)[i];
+ if (g >= grs.g_first && g <= grs.g_last)
+ return grs.stack_id;
+ }
+ return 0;
+}
+
+int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites,
+ int max_globals) {
+ if (!flags()->report_globals) return 0;
+ BlockingMutexLock lock(&mu_for_globals);
+ int res = 0;
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
+ const Global &g = *l->g;
+ if (flags()->report_globals >= 2)
+ ReportGlobal(g, "Search");
+ if (IsAddressNearGlobal(addr, g)) {
+ internal_memcpy(&globals[res], &g, sizeof(g));
+ if (reg_sites)
+ reg_sites[res] = FindRegistrationSite(&g);
+ res++;
+ if (res == max_globals)
+ break;
+ }
+ }
+ return res;
+}
+
+enum GlobalSymbolState {
+ UNREGISTERED = 0,
+ REGISTERED = 1
+};
+
+// Check ODR violation for given global G via special ODR indicator. We use
+// this method in case compiler instruments global variables through their
+// local aliases.
+static void CheckODRViolationViaIndicator(const Global *g) {
+ // Instrumentation requests to skip ODR check.
+ if (g->odr_indicator == UINTPTR_MAX)
+ return;
+ u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
+ if (*odr_indicator == UNREGISTERED) {
+ *odr_indicator = REGISTERED;
+ return;
+ }
+ // If *odr_indicator is DEFINED, some module have already registered
+ // externally visible symbol with the same name. This is an ODR violation.
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
+ if (g->odr_indicator == l->g->odr_indicator &&
+ (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
+ !IsODRViolationSuppressed(g->name))
+ ReportODRViolation(g, FindRegistrationSite(g),
+ l->g, FindRegistrationSite(l->g));
+ }
+}
+
+// Check ODR violation for given global G by checking if it's already poisoned.
+// We use this method in case compiler doesn't use private aliases for global
+// variables.
+static void CheckODRViolationViaPoisoning(const Global *g) {
+ if (__asan_region_is_poisoned(g->beg, g->size_with_redzone)) {
+ // This check may not be enough: if the first global is much larger
+ // the entire redzone of the second global may be within the first global.
+ for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
+ if (g->beg == l->g->beg &&
+ (flags()->detect_odr_violation >= 2 || g->size != l->g->size) &&
+ !IsODRViolationSuppressed(g->name))
+ ReportODRViolation(g, FindRegistrationSite(g),
+ l->g, FindRegistrationSite(l->g));
+ }
+ }
+}
+
+// Clang provides two different ways for global variables protection:
+// it can poison the global itself or its private alias. In former
+// case we may poison same symbol multiple times, that can help us to
+// cheaply detect ODR violation: if we try to poison an already poisoned
+// global, we have ODR violation error.
+// In latter case, we poison each symbol exactly once, so we use special
+// indicator symbol to perform similar check.
+// In either case, compiler provides a special odr_indicator field to Global
+// structure, that can contain two kinds of values:
+// 1) Non-zero value. In this case, odr_indicator is an address of
+// corresponding indicator variable for given global.
+// 2) Zero. This means that we don't use private aliases for global variables
+// and can freely check ODR violation with the first method.
+//
+// This routine chooses between two different methods of ODR violation
+// detection.
+static inline bool UseODRIndicator(const Global *g) {
+ return g->odr_indicator > 0;
+}
+
+// Register a global variable.
+// This function may be called more than once for every global
+// so we store the globals in a map.
+static void RegisterGlobal(const Global *g) {
+ CHECK(asan_inited);
+ if (flags()->report_globals >= 2)
+ ReportGlobal(*g, "Added");
+ CHECK(flags()->report_globals);
+ CHECK(AddrIsInMem(g->beg));
+ if (!AddrIsAlignedByGranularity(g->beg)) {
+ Report("The following global variable is not properly aligned.\n");
+ Report("This may happen if another global with the same name\n");
+ Report("resides in another non-instrumented module.\n");
+ Report("Or the global comes from a C file built w/o -fno-common.\n");
+ Report("In either case this is likely an ODR violation bug,\n");
+ Report("but AddressSanitizer can not provide more details.\n");
+ ReportODRViolation(g, FindRegistrationSite(g), g, FindRegistrationSite(g));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ }
+ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+ if (flags()->detect_odr_violation) {
+ // Try detecting ODR (One Definition Rule) violation, i.e. the situation
+ // where two globals with the same name are defined in different modules.
+ if (UseODRIndicator(g))
+ CheckODRViolationViaIndicator(g);
+ else
+ CheckODRViolationViaPoisoning(g);
+ }
+ if (CanPoisonMemory())
+ PoisonRedZones(*g);
+ ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals;
+ l->g = g;
+ l->next = list_of_all_globals;
+ list_of_all_globals = l;
+ if (g->has_dynamic_init) {
+ if (!dynamic_init_globals) {
+ dynamic_init_globals =
+ new (allocator_for_globals) VectorOfGlobals; // NOLINT
+ dynamic_init_globals->reserve(kDynamicInitGlobalsInitialCapacity);
+ }
+ DynInitGlobal dyn_global = { *g, false };
+ dynamic_init_globals->push_back(dyn_global);
+ }
+}
+
+static void UnregisterGlobal(const Global *g) {
+ CHECK(asan_inited);
+ if (flags()->report_globals >= 2)
+ ReportGlobal(*g, "Removed");
+ CHECK(flags()->report_globals);
+ CHECK(AddrIsInMem(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->beg));
+ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone));
+ if (CanPoisonMemory())
+ PoisonShadowForGlobal(g, 0);
+ // We unpoison the shadow memory for the global but we do not remove it from
+ // the list because that would require O(n^2) time with the current list
+ // implementation. It might not be worth doing anyway.
+
+ // Release ODR indicator.
+ if (UseODRIndicator(g) && g->odr_indicator != UINTPTR_MAX) {
+ u8 *odr_indicator = reinterpret_cast<u8 *>(g->odr_indicator);
+ *odr_indicator = UNREGISTERED;
+ }
+}
+
+void StopInitOrderChecking() {
+ BlockingMutexLock lock(&mu_for_globals);
+ if (!flags()->check_initialization_order || !dynamic_init_globals)
+ return;
+ flags()->check_initialization_order = false;
+ for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
+ DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+ const Global *g = &dyn_g.g;
+ // Unpoison the whole global.
+ PoisonShadowForGlobal(g, 0);
+ // Poison redzones back.
+ PoisonRedZones(*g);
+ }
+}
+
+static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
+
+const char *MaybeDemangleGlobalName(const char *name) {
+ // We can spoil names of globals with C linkage, so use an heuristic
+ // approach to check if the name should be demangled.
+ bool should_demangle = false;
+ if (name[0] == '_' && name[1] == 'Z')
+ should_demangle = true;
+ else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
+ should_demangle = true;
+
+ return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
+}
+
+// Check if the global is a zero-terminated ASCII string. If so, print it.
+void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) {
+ for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
+ unsigned char c = *(unsigned char *)p;
+ if (c == '\0' || !IsASCII(c)) return;
+ }
+ if (*(char *)(g.beg + g.size - 1) != '\0') return;
+ str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
+ (char *)g.beg);
+}
+
+static const char *GlobalFilename(const __asan_global &g) {
+ const char *res = g.module_name;
+ // Prefer the filename from source location, if is available.
+ if (g.location) res = g.location->filename;
+ CHECK(res);
+ return res;
+}
+
+void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) {
+ str->append("%s", GlobalFilename(g));
+ if (!g.location) return;
+ if (g.location->line_no) str->append(":%d", g.location->line_no);
+ if (g.location->column_no) str->append(":%d", g.location->column_no);
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+
+// Apply __asan_register_globals to all globals found in the same loaded
+// executable or shared library as `flag'. The flag tracks whether globals have
+// already been registered or not for this image.
+void __asan_register_image_globals(uptr *flag) {
+ if (*flag)
+ return;
+ AsanApplyToGlobals(__asan_register_globals, flag);
+ *flag = 1;
+}
+
+// This mirrors __asan_register_image_globals.
+void __asan_unregister_image_globals(uptr *flag) {
+ if (!*flag)
+ return;
+ AsanApplyToGlobals(__asan_unregister_globals, flag);
+ *flag = 0;
+}
+
+void __asan_register_elf_globals(uptr *flag, void *start, void *stop) {
+ if (*flag) return;
+ if (!start) return;
+ CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global));
+ __asan_global *globals_start = (__asan_global*)start;
+ __asan_global *globals_stop = (__asan_global*)stop;
+ __asan_register_globals(globals_start, globals_stop - globals_start);
+ *flag = 1;
+}
+
+void __asan_unregister_elf_globals(uptr *flag, void *start, void *stop) {
+ if (!*flag) return;
+ if (!start) return;
+ CHECK_EQ(0, ((uptr)stop - (uptr)start) % sizeof(__asan_global));
+ __asan_global *globals_start = (__asan_global*)start;
+ __asan_global *globals_stop = (__asan_global*)stop;
+ __asan_unregister_globals(globals_start, globals_stop - globals_start);
+ *flag = 0;
+}
+
+// Register an array of globals.
+void __asan_register_globals(__asan_global *globals, uptr n) {
+ if (!flags()->report_globals) return;
+ GET_STACK_TRACE_MALLOC;
+ u32 stack_id = StackDepotPut(stack);
+ BlockingMutexLock lock(&mu_for_globals);
+ if (!global_registration_site_vector) {
+ global_registration_site_vector =
+ new (allocator_for_globals) GlobalRegistrationSiteVector; // NOLINT
+ global_registration_site_vector->reserve(128);
+ }
+ GlobalRegistrationSite site = {stack_id, &globals[0], &globals[n - 1]};
+ global_registration_site_vector->push_back(site);
+ if (flags()->report_globals >= 2) {
+ PRINT_CURRENT_STACK();
+ Printf("=== ID %d; %p %p\n", stack_id, &globals[0], &globals[n - 1]);
+ }
+ for (uptr i = 0; i < n; i++) {
+ if (SANITIZER_WINDOWS && globals[i].beg == 0) {
+ // The MSVC incremental linker may pad globals out to 256 bytes. As long
+ // as __asan_global is less than 256 bytes large and its size is a power
+ // of two, we can skip over the padding.
+ static_assert(
+ sizeof(__asan_global) < 256 &&
+ (sizeof(__asan_global) & (sizeof(__asan_global) - 1)) == 0,
+ "sizeof(__asan_global) incompatible with incremental linker padding");
+ // If these are padding bytes, the rest of the global should be zero.
+ CHECK(globals[i].size == 0 && globals[i].size_with_redzone == 0 &&
+ globals[i].name == nullptr && globals[i].module_name == nullptr &&
+ globals[i].odr_indicator == 0);
+ continue;
+ }
+ RegisterGlobal(&globals[i]);
+ }
+
+ // Poison the metadata. It should not be accessible to user code.
+ PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global),
+ kAsanGlobalRedzoneMagic);
+}
+
+// Unregister an array of globals.
+// We must do this when a shared objects gets dlclosed.
+void __asan_unregister_globals(__asan_global *globals, uptr n) {
+ if (!flags()->report_globals) return;
+ BlockingMutexLock lock(&mu_for_globals);
+ for (uptr i = 0; i < n; i++) {
+ if (SANITIZER_WINDOWS && globals[i].beg == 0) {
+ // Skip globals that look like padding from the MSVC incremental linker.
+ // See comment in __asan_register_globals.
+ continue;
+ }
+ UnregisterGlobal(&globals[i]);
+ }
+
+ // Unpoison the metadata.
+ PoisonShadow(reinterpret_cast<uptr>(globals), n * sizeof(__asan_global), 0);
+}
+
+// This method runs immediately prior to dynamic initialization in each TU,
+// when all dynamically initialized globals are unpoisoned. This method
+// poisons all global variables not defined in this TU, so that a dynamic
+// initializer can only touch global variables in the same TU.
+void __asan_before_dynamic_init(const char *module_name) {
+ if (!flags()->check_initialization_order ||
+ !CanPoisonMemory() ||
+ !dynamic_init_globals)
+ return;
+ bool strict_init_order = flags()->strict_init_order;
+ CHECK(module_name);
+ CHECK(asan_inited);
+ BlockingMutexLock lock(&mu_for_globals);
+ if (flags()->report_globals >= 3)
+ Printf("DynInitPoison module: %s\n", module_name);
+ for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
+ DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+ const Global *g = &dyn_g.g;
+ if (dyn_g.initialized)
+ continue;
+ if (g->module_name != module_name)
+ PoisonShadowForGlobal(g, kAsanInitializationOrderMagic);
+ else if (!strict_init_order)
+ dyn_g.initialized = true;
+ }
+}
+
+// This method runs immediately after dynamic initialization in each TU, when
+// all dynamically initialized globals except for those defined in the current
+// TU are poisoned. It simply unpoisons all dynamically initialized globals.
+void __asan_after_dynamic_init() {
+ if (!flags()->check_initialization_order ||
+ !CanPoisonMemory() ||
+ !dynamic_init_globals)
+ return;
+ CHECK(asan_inited);
+ BlockingMutexLock lock(&mu_for_globals);
+ // FIXME: Optionally report that we're unpoisoning globals from a module.
+ for (uptr i = 0, n = dynamic_init_globals->size(); i < n; ++i) {
+ DynInitGlobal &dyn_g = (*dynamic_init_globals)[i];
+ const Global *g = &dyn_g.g;
+ if (!dyn_g.initialized) {
+ // Unpoison the whole global.
+ PoisonShadowForGlobal(g, 0);
+ // Poison redzones back.
+ PoisonRedZones(*g);
+ }
+ }
+}
+++ /dev/null
-//===-- asan_globals_win.cc -----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Global registration code that is linked into every Windows DLL and EXE.
-//
-//===----------------------------------------------------------------------===//
-
-#include "asan_interface_internal.h"
-#if SANITIZER_WINDOWS
-
-namespace __asan {
-
-#pragma section(".ASAN$GA", read, write) // NOLINT
-#pragma section(".ASAN$GZ", read, write) // NOLINT
-extern "C" __declspec(allocate(".ASAN$GA"))
- ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_start = {};
-extern "C" __declspec(allocate(".ASAN$GZ"))
- ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_end = {};
-#pragma comment(linker, "/merge:.ASAN=.data")
-
-static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
- __asan_global *start = &__asan_globals_start + 1;
- __asan_global *end = &__asan_globals_end;
- uptr bytediff = (uptr)end - (uptr)start;
- if (bytediff % sizeof(__asan_global) != 0) {
-#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
- __debugbreak();
-#else
- CHECK("corrupt asan global array");
-#endif
- }
- // We know end >= start because the linker sorts the portion after the dollar
- // sign alphabetically.
- uptr n = end - start;
- hook(start, n);
-}
-
-static void register_dso_globals() {
- call_on_globals(&__asan_register_globals);
-}
-
-static void unregister_dso_globals() {
- call_on_globals(&__asan_unregister_globals);
-}
-
-// Register globals
-#pragma section(".CRT$XCU", long, read) // NOLINT
-#pragma section(".CRT$XTX", long, read) // NOLINT
-extern "C" __declspec(allocate(".CRT$XCU"))
-void (*const __asan_dso_reg_hook)() = ®ister_dso_globals;
-extern "C" __declspec(allocate(".CRT$XTX"))
-void (*const __asan_dso_unreg_hook)() = &unregister_dso_globals;
-
-} // namespace __asan
-
-#endif // SANITIZER_WINDOWS
--- /dev/null
+//===-- asan_globals_win.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Global registration code that is linked into every Windows DLL and EXE.
+//
+//===----------------------------------------------------------------------===//
+
+#include "asan_interface_internal.h"
+#if SANITIZER_WINDOWS
+
+namespace __asan {
+
+#pragma section(".ASAN$GA", read, write) // NOLINT
+#pragma section(".ASAN$GZ", read, write) // NOLINT
+extern "C" __declspec(allocate(".ASAN$GA"))
+ ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_start = {};
+extern "C" __declspec(allocate(".ASAN$GZ"))
+ ALIGNED(sizeof(__asan_global)) __asan_global __asan_globals_end = {};
+#pragma comment(linker, "/merge:.ASAN=.data")
+
+static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
+ __asan_global *start = &__asan_globals_start + 1;
+ __asan_global *end = &__asan_globals_end;
+ uptr bytediff = (uptr)end - (uptr)start;
+ if (bytediff % sizeof(__asan_global) != 0) {
+#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
+ __debugbreak();
+#else
+ CHECK("corrupt asan global array");
+#endif
+ }
+ // We know end >= start because the linker sorts the portion after the dollar
+ // sign alphabetically.
+ uptr n = end - start;
+ hook(start, n);
+}
+
+static void register_dso_globals() {
+ call_on_globals(&__asan_register_globals);
+}
+
+static void unregister_dso_globals() {
+ call_on_globals(&__asan_unregister_globals);
+}
+
+// Register globals
+#pragma section(".CRT$XCU", long, read) // NOLINT
+#pragma section(".CRT$XTX", long, read) // NOLINT
+extern "C" __declspec(allocate(".CRT$XCU"))
+void (*const __asan_dso_reg_hook)() = ®ister_dso_globals;
+extern "C" __declspec(allocate(".CRT$XTX"))
+void (*const __asan_dso_unreg_hook)() = &unregister_dso_globals;
+
+} // namespace __asan
+
+#endif // SANITIZER_WINDOWS
//===-- asan_init_version.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- asan_interceptors.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Intercept various libc functions.
-//===----------------------------------------------------------------------===//
-
-#include "asan_interceptors.h"
-#include "asan_allocator.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_stats.h"
-#include "asan_suppressions.h"
-#include "lsan/lsan_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-
-// There is no general interception at all on Fuchsia and RTEMS.
-// Only the functions in asan_interceptors_memintrinsics.cc are
-// really defined to replace libc functions.
-#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
-
-#if SANITIZER_POSIX
-#include "sanitizer_common/sanitizer_posix.h"
-#endif
-
-#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION || \
- ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION
-#include <unwind.h>
-#endif
-
-#if defined(__i386) && SANITIZER_LINUX
-#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1"
-#elif defined(__mips__) && SANITIZER_LINUX
-#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
-#endif
-
-namespace __asan {
-
-#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
- ASAN_READ_RANGE((ctx), (s), \
- common_flags()->strict_string_checks ? (len) + 1 : (n))
-
-#define ASAN_READ_STRING(ctx, s, n) \
- ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
-
-static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
-#if SANITIZER_INTERCEPT_STRNLEN
- if (REAL(strnlen)) {
- return REAL(strnlen)(s, maxlen);
- }
-#endif
- return internal_strnlen(s, maxlen);
-}
-
-void SetThreadName(const char *name) {
- AsanThread *t = GetCurrentThread();
- if (t)
- asanThreadRegistry().SetThreadName(t->tid(), name);
-}
-
-int OnExit() {
- if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks &&
- __lsan::HasReportedLeaks()) {
- return common_flags()->exitcode;
- }
- // FIXME: ask frontend whether we need to return failure.
- return 0;
-}
-
-} // namespace __asan
-
-// ---------------------- Wrappers ---------------- {{{1
-using namespace __asan; // NOLINT
-
-DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
-
-#define ASAN_INTERCEPTOR_ENTER(ctx, func) \
- AsanInterceptorContext _ctx = {#func}; \
- ctx = (void *)&_ctx; \
- (void) ctx; \
-
-#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name)
-#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
- ASAN_INTERCEPT_FUNC_VER(name, ver)
-#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
- ASAN_WRITE_RANGE(ctx, ptr, size)
-#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
- ASAN_READ_RANGE(ctx, ptr, size)
-#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
- ASAN_INTERCEPTOR_ENTER(ctx, func); \
- do { \
- if (asan_init_is_running) \
- return REAL(func)(__VA_ARGS__); \
- if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \
- return REAL(func)(__VA_ARGS__); \
- ENSURE_ASAN_INITED(); \
- } while (false)
-#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
-// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name)
-// But asan does not remember UserId's for threads (pthread_t);
-// and remembers all ever existed threads, so the linear search by UserId
-// can be slow.
-#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
- do { \
- } while (false)
-#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
-// Strict init-order checking is dlopen-hostile:
-// https://github.com/google/sanitizers/issues/178
-#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
- do { \
- if (flags()->strict_init_order) \
- StopInitOrderChecking(); \
- CheckNoDeepBind(filename, flag); \
- } while (false)
-#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
-#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle)
-#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED()
-#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
-#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
- if (AsanThread *t = GetCurrentThread()) { \
- *begin = t->tls_begin(); \
- *end = t->tls_end(); \
- } else { \
- *begin = *end = 0; \
- }
-
-#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \
- do { \
- ASAN_INTERCEPTOR_ENTER(ctx, memmove); \
- ASAN_MEMMOVE_IMPL(ctx, to, from, size); \
- } while (false)
-
-#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \
- do { \
- ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \
- ASAN_MEMCPY_IMPL(ctx, to, from, size); \
- } while (false)
-
-#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \
- do { \
- ASAN_INTERCEPTOR_ENTER(ctx, memset); \
- ASAN_MEMSET_IMPL(ctx, block, c, size); \
- } while (false)
-
-#include "sanitizer_common/sanitizer_common_interceptors.inc"
-#include "sanitizer_common/sanitizer_signal_interceptors.inc"
-
-// Syscall interceptors don't have contexts, we don't support suppressions
-// for them.
-#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s)
-#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s)
-#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-#include "sanitizer_common/sanitizer_common_syscalls.inc"
-#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
-
-struct ThreadStartParam {
- atomic_uintptr_t t;
- atomic_uintptr_t is_registered;
-};
-
-#if ASAN_INTERCEPT_PTHREAD_CREATE
-static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
- ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg);
- AsanThread *t = nullptr;
- while ((t = reinterpret_cast<AsanThread *>(
- atomic_load(¶m->t, memory_order_acquire))) == nullptr)
- internal_sched_yield();
- SetCurrentThread(t);
- return t->ThreadStart(GetTid(), ¶m->is_registered);
-}
-
-INTERCEPTOR(int, pthread_create, void *thread,
- void *attr, void *(*start_routine)(void*), void *arg) {
- EnsureMainThreadIDIsCorrect();
- // Strict init-order checking is thread-hostile.
- if (flags()->strict_init_order)
- StopInitOrderChecking();
- GET_STACK_TRACE_THREAD;
- int detached = 0;
- if (attr)
- REAL(pthread_attr_getdetachstate)(attr, &detached);
- ThreadStartParam param;
- atomic_store(¶m.t, 0, memory_order_relaxed);
- atomic_store(¶m.is_registered, 0, memory_order_relaxed);
- int result;
- {
- // Ignore all allocations made by pthread_create: thread stack/TLS may be
- // stored by pthread for future reuse even after thread destruction, and
- // the linked list it's stored in doesn't even hold valid pointers to the
- // objects, the latter are calculated by obscure pointer arithmetic.
-#if CAN_SANITIZE_LEAKS
- __lsan::ScopedInterceptorDisabler disabler;
-#endif
- result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m);
- }
- if (result == 0) {
- u32 current_tid = GetCurrentTidOrInvalid();
- AsanThread *t =
- AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
- atomic_store(¶m.t, reinterpret_cast<uptr>(t), memory_order_release);
- // Wait until the AsanThread object is initialized and the ThreadRegistry
- // entry is in "started" state. One reason for this is that after this
- // interceptor exits, the child thread's stack may be the only thing holding
- // the |arg| pointer. This may cause LSan to report a leak if leak checking
- // happens at a point when the interceptor has already exited, but the stack
- // range for the child thread is not yet known.
- while (atomic_load(¶m.is_registered, memory_order_acquire) == 0)
- internal_sched_yield();
- }
- return result;
-}
-
-INTERCEPTOR(int, pthread_join, void *t, void **arg) {
- return real_pthread_join(t, arg);
-}
-
-DEFINE_REAL_PTHREAD_FUNCTIONS
-#endif // ASAN_INTERCEPT_PTHREAD_CREATE
-
-#if ASAN_INTERCEPT_SWAPCONTEXT
-static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
- // Align to page size.
- uptr PageSize = GetPageSizeCached();
- uptr bottom = stack & ~(PageSize - 1);
- ssize += stack - bottom;
- ssize = RoundUpTo(ssize, PageSize);
- static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
- if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) {
- PoisonShadow(bottom, ssize, 0);
- }
-}
-
-INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp,
- struct ucontext_t *ucp) {
- static bool reported_warning = false;
- if (!reported_warning) {
- Report("WARNING: ASan doesn't fully support makecontext/swapcontext "
- "functions and may produce false positives in some cases!\n");
- reported_warning = true;
- }
- // Clear shadow memory for new context (it may share stack
- // with current context).
- uptr stack, ssize;
- ReadContextStack(ucp, &stack, &ssize);
- ClearShadowMemoryForContextStack(stack, ssize);
-#if __has_attribute(__indirect_return__) && \
- (defined(__x86_64__) || defined(__i386__))
- int (*real_swapcontext)(struct ucontext_t *, struct ucontext_t *)
- __attribute__((__indirect_return__))
- = REAL(swapcontext);
- int res = real_swapcontext(oucp, ucp);
-#else
- int res = REAL(swapcontext)(oucp, ucp);
-#endif
- // swapcontext technically does not return, but program may swap context to
- // "oucp" later, that would look as if swapcontext() returned 0.
- // We need to clear shadow for ucp once again, as it may be in arbitrary
- // state.
- ClearShadowMemoryForContextStack(stack, ssize);
- return res;
-}
-#endif // ASAN_INTERCEPT_SWAPCONTEXT
-
-#if SANITIZER_NETBSD
-#define longjmp __longjmp14
-#define siglongjmp __siglongjmp14
-#endif
-
-INTERCEPTOR(void, longjmp, void *env, int val) {
- __asan_handle_no_return();
- REAL(longjmp)(env, val);
-}
-
-#if ASAN_INTERCEPT__LONGJMP
-INTERCEPTOR(void, _longjmp, void *env, int val) {
- __asan_handle_no_return();
- REAL(_longjmp)(env, val);
-}
-#endif
-
-#if ASAN_INTERCEPT___LONGJMP_CHK
-INTERCEPTOR(void, __longjmp_chk, void *env, int val) {
- __asan_handle_no_return();
- REAL(__longjmp_chk)(env, val);
-}
-#endif
-
-#if ASAN_INTERCEPT_SIGLONGJMP
-INTERCEPTOR(void, siglongjmp, void *env, int val) {
- __asan_handle_no_return();
- REAL(siglongjmp)(env, val);
-}
-#endif
-
-#if ASAN_INTERCEPT___CXA_THROW
-INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
- CHECK(REAL(__cxa_throw));
- __asan_handle_no_return();
- REAL(__cxa_throw)(a, b, c);
-}
-#endif
-
-#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION
-INTERCEPTOR(void, __cxa_rethrow_primary_exception, void *a) {
- CHECK(REAL(__cxa_rethrow_primary_exception));
- __asan_handle_no_return();
- REAL(__cxa_rethrow_primary_exception)(a);
-}
-#endif
-
-#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION
-INTERCEPTOR(_Unwind_Reason_Code, _Unwind_RaiseException,
- _Unwind_Exception *object) {
- CHECK(REAL(_Unwind_RaiseException));
- __asan_handle_no_return();
- return REAL(_Unwind_RaiseException)(object);
-}
-#endif
-
-#if ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION
-INTERCEPTOR(_Unwind_Reason_Code, _Unwind_SjLj_RaiseException,
- _Unwind_Exception *object) {
- CHECK(REAL(_Unwind_SjLj_RaiseException));
- __asan_handle_no_return();
- return REAL(_Unwind_SjLj_RaiseException)(object);
-}
-#endif
-
-#if ASAN_INTERCEPT_INDEX
-# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
-INTERCEPTOR(char*, index, const char *string, int c)
- ALIAS(WRAPPER_NAME(strchr));
-# else
-# if SANITIZER_MAC
-DECLARE_REAL(char*, index, const char *string, int c)
-OVERRIDE_FUNCTION(index, strchr);
-# else
-DEFINE_REAL(char*, index, const char *string, int c)
-# endif
-# endif
-#endif // ASAN_INTERCEPT_INDEX
-
-// For both strcat() and strncat() we need to check the validity of |to|
-// argument irrespective of the |from| length.
-INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT
- ENSURE_ASAN_INITED();
- if (flags()->replace_str) {
- uptr from_length = REAL(strlen)(from);
- ASAN_READ_RANGE(ctx, from, from_length + 1);
- uptr to_length = REAL(strlen)(to);
- ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
- ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
- // If the copying actually happens, the |from| string should not overlap
- // with the resulting string starting at |to|, which has a length of
- // to_length + from_length + 1.
- if (from_length > 0) {
- CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1,
- from, from_length + 1);
- }
- }
- return REAL(strcat)(to, from); // NOLINT
-}
-
-INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strncat);
- ENSURE_ASAN_INITED();
- if (flags()->replace_str) {
- uptr from_length = MaybeRealStrnlen(from, size);
- uptr copy_length = Min(size, from_length + 1);
- ASAN_READ_RANGE(ctx, from, copy_length);
- uptr to_length = REAL(strlen)(to);
- ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
- ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
- if (from_length > 0) {
- CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
- from, copy_length);
- }
- }
- return REAL(strncat)(to, from, size);
-}
-
-INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT
-#if SANITIZER_MAC
- if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT
-#endif
- // strcpy is called from malloc_default_purgeable_zone()
- // in __asan::ReplaceSystemAlloc() on Mac.
- if (asan_init_is_running) {
- return REAL(strcpy)(to, from); // NOLINT
- }
- ENSURE_ASAN_INITED();
- if (flags()->replace_str) {
- uptr from_size = REAL(strlen)(from) + 1;
- CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size);
- ASAN_READ_RANGE(ctx, from, from_size);
- ASAN_WRITE_RANGE(ctx, to, from_size);
- }
- return REAL(strcpy)(to, from); // NOLINT
-}
-
-INTERCEPTOR(char*, strdup, const char *s) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strdup);
- if (UNLIKELY(!asan_inited)) return internal_strdup(s);
- ENSURE_ASAN_INITED();
- uptr length = REAL(strlen)(s);
- if (flags()->replace_str) {
- ASAN_READ_RANGE(ctx, s, length + 1);
- }
- GET_STACK_TRACE_MALLOC;
- void *new_mem = asan_malloc(length + 1, &stack);
- REAL(memcpy)(new_mem, s, length + 1);
- return reinterpret_cast<char*>(new_mem);
-}
-
-#if ASAN_INTERCEPT___STRDUP
-INTERCEPTOR(char*, __strdup, const char *s) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strdup);
- if (UNLIKELY(!asan_inited)) return internal_strdup(s);
- ENSURE_ASAN_INITED();
- uptr length = REAL(strlen)(s);
- if (flags()->replace_str) {
- ASAN_READ_RANGE(ctx, s, length + 1);
- }
- GET_STACK_TRACE_MALLOC;
- void *new_mem = asan_malloc(length + 1, &stack);
- REAL(memcpy)(new_mem, s, length + 1);
- return reinterpret_cast<char*>(new_mem);
-}
-#endif // ASAN_INTERCEPT___STRDUP
-
-INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strncpy);
- ENSURE_ASAN_INITED();
- if (flags()->replace_str) {
- uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1);
- CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size);
- ASAN_READ_RANGE(ctx, from, from_size);
- ASAN_WRITE_RANGE(ctx, to, size);
- }
- return REAL(strncpy)(to, from, size);
-}
-
-INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
- char **endptr, int base) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strtol);
- ENSURE_ASAN_INITED();
- if (!flags()->replace_str) {
- return REAL(strtol)(nptr, endptr, base);
- }
- char *real_endptr;
- long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
- StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
- return result;
-}
-
-INTERCEPTOR(int, atoi, const char *nptr) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, atoi);
-#if SANITIZER_MAC
- if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr);
-#endif
- ENSURE_ASAN_INITED();
- if (!flags()->replace_str) {
- return REAL(atoi)(nptr);
- }
- char *real_endptr;
- // "man atoi" tells that behavior of atoi(nptr) is the same as
- // strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the
- // parsed integer can't be stored in *long* type (even if it's
- // different from int). So, we just imitate this behavior.
- int result = REAL(strtol)(nptr, &real_endptr, 10);
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
- return result;
-}
-
-INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, atol);
-#if SANITIZER_MAC
- if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr);
-#endif
- ENSURE_ASAN_INITED();
- if (!flags()->replace_str) {
- return REAL(atol)(nptr);
- }
- char *real_endptr;
- long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
- return result;
-}
-
-#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
-INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
- char **endptr, int base) {
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, strtoll);
- ENSURE_ASAN_INITED();
- if (!flags()->replace_str) {
- return REAL(strtoll)(nptr, endptr, base);
- }
- char *real_endptr;
- long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
- StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
- return result;
-}
-
-INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
- void *ctx;
- ASAN_INTERCEPTOR_ENTER(ctx, atoll);
- ENSURE_ASAN_INITED();
- if (!flags()->replace_str) {
- return REAL(atoll)(nptr);
- }
- char *real_endptr;
- long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
- FixRealStrtolEndptr(nptr, &real_endptr);
- ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
- return result;
-}
-#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
-
-#if ASAN_INTERCEPT___CXA_ATEXIT
-static void AtCxaAtexit(void *unused) {
- (void)unused;
- StopInitOrderChecking();
-}
-
-INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
- void *dso_handle) {
-#if SANITIZER_MAC
- if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle);
-#endif
- ENSURE_ASAN_INITED();
- int res = REAL(__cxa_atexit)(func, arg, dso_handle);
- REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr);
- return res;
-}
-#endif // ASAN_INTERCEPT___CXA_ATEXIT
-
-// ---------------------- InitializeAsanInterceptors ---------------- {{{1
-namespace __asan {
-void InitializeAsanInterceptors() {
- static bool was_called_once;
- CHECK(!was_called_once);
- was_called_once = true;
- InitializeCommonInterceptors();
- InitializeSignalInterceptors();
-
- // Intercept str* functions.
- ASAN_INTERCEPT_FUNC(strcat); // NOLINT
- ASAN_INTERCEPT_FUNC(strcpy); // NOLINT
- ASAN_INTERCEPT_FUNC(strncat);
- ASAN_INTERCEPT_FUNC(strncpy);
- ASAN_INTERCEPT_FUNC(strdup);
-#if ASAN_INTERCEPT___STRDUP
- ASAN_INTERCEPT_FUNC(__strdup);
-#endif
-#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
- ASAN_INTERCEPT_FUNC(index);
-#endif
-
- ASAN_INTERCEPT_FUNC(atoi);
- ASAN_INTERCEPT_FUNC(atol);
- ASAN_INTERCEPT_FUNC(strtol);
-#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
- ASAN_INTERCEPT_FUNC(atoll);
- ASAN_INTERCEPT_FUNC(strtoll);
-#endif
-
- // Intecept jump-related functions.
- ASAN_INTERCEPT_FUNC(longjmp);
-
-#if ASAN_INTERCEPT_SWAPCONTEXT
- ASAN_INTERCEPT_FUNC(swapcontext);
-#endif
-#if ASAN_INTERCEPT__LONGJMP
- ASAN_INTERCEPT_FUNC(_longjmp);
-#endif
-#if ASAN_INTERCEPT___LONGJMP_CHK
- ASAN_INTERCEPT_FUNC(__longjmp_chk);
-#endif
-#if ASAN_INTERCEPT_SIGLONGJMP
- ASAN_INTERCEPT_FUNC(siglongjmp);
-#endif
-
- // Intercept exception handling functions.
-#if ASAN_INTERCEPT___CXA_THROW
- ASAN_INTERCEPT_FUNC(__cxa_throw);
-#endif
-#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION
- ASAN_INTERCEPT_FUNC(__cxa_rethrow_primary_exception);
-#endif
- // Indirectly intercept std::rethrow_exception.
-#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION
- INTERCEPT_FUNCTION(_Unwind_RaiseException);
-#endif
- // Indirectly intercept std::rethrow_exception.
-#if ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION
- INTERCEPT_FUNCTION(_Unwind_SjLj_RaiseException);
-#endif
-
- // Intercept threading-related functions
-#if ASAN_INTERCEPT_PTHREAD_CREATE
-#if defined(ASAN_PTHREAD_CREATE_VERSION)
- ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION);
-#else
- ASAN_INTERCEPT_FUNC(pthread_create);
-#endif
- ASAN_INTERCEPT_FUNC(pthread_join);
-#endif
-
- // Intercept atexit function.
-#if ASAN_INTERCEPT___CXA_ATEXIT
- ASAN_INTERCEPT_FUNC(__cxa_atexit);
-#endif
-
- InitializePlatformInterceptors();
-
- VReport(1, "AddressSanitizer: libc interceptors initialized\n");
-}
-
-} // namespace __asan
-
-#endif // !SANITIZER_FUCHSIA
--- /dev/null
+//===-- asan_interceptors.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Intercept various libc functions.
+//===----------------------------------------------------------------------===//
+
+#include "asan_interceptors.h"
+#include "asan_allocator.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_suppressions.h"
+#include "lsan/lsan_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+// There is no general interception at all on Fuchsia and RTEMS.
+// Only the functions in asan_interceptors_memintrinsics.cpp are
+// really defined to replace libc functions.
+#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
+
+#if SANITIZER_POSIX
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
+
+#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION || \
+ ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION
+#include <unwind.h>
+#endif
+
+#if defined(__i386) && SANITIZER_LINUX
+#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1"
+#elif defined(__mips__) && SANITIZER_LINUX
+#define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2"
+#endif
+
+namespace __asan {
+
+#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \
+ ASAN_READ_RANGE((ctx), (s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n))
+
+#define ASAN_READ_STRING(ctx, s, n) \
+ ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n))
+
+static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
+#if SANITIZER_INTERCEPT_STRNLEN
+ if (REAL(strnlen)) {
+ return REAL(strnlen)(s, maxlen);
+ }
+#endif
+ return internal_strnlen(s, maxlen);
+}
+
+void SetThreadName(const char *name) {
+ AsanThread *t = GetCurrentThread();
+ if (t)
+ asanThreadRegistry().SetThreadName(t->tid(), name);
+}
+
+int OnExit() {
+ if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks &&
+ __lsan::HasReportedLeaks()) {
+ return common_flags()->exitcode;
+ }
+ // FIXME: ask frontend whether we need to return failure.
+ return 0;
+}
+
+} // namespace __asan
+
+// ---------------------- Wrappers ---------------- {{{1
+using namespace __asan; // NOLINT
+
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
+
+#define ASAN_INTERCEPTOR_ENTER(ctx, func) \
+ AsanInterceptorContext _ctx = {#func}; \
+ ctx = (void *)&_ctx; \
+ (void) ctx; \
+
+#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name)
+#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
+ ASAN_INTERCEPT_FUNC_VER(name, ver)
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ ASAN_WRITE_RANGE(ctx, ptr, size)
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
+ ASAN_READ_RANGE(ctx, ptr, size)
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ ASAN_INTERCEPTOR_ENTER(ctx, func); \
+ do { \
+ if (asan_init_is_running) \
+ return REAL(func)(__VA_ARGS__); \
+ if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \
+ return REAL(func)(__VA_ARGS__); \
+ ENSURE_ASAN_INITED(); \
+ } while (false)
+#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
+ do { \
+ } while (false)
+#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
+ do { \
+ } while (false)
+#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
+ do { \
+ } while (false)
+#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
+ do { \
+ } while (false)
+#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name)
+// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name)
+// But asan does not remember UserId's for threads (pthread_t);
+// and remembers all ever existed threads, so the linear search by UserId
+// can be slow.
+#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
+ do { \
+ } while (false)
+#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
+// Strict init-order checking is dlopen-hostile:
+// https://github.com/google/sanitizers/issues/178
+#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
+ do { \
+ if (flags()->strict_init_order) \
+ StopInitOrderChecking(); \
+ CheckNoDeepBind(filename, flag); \
+ } while (false)
+#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit()
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle)
+#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED()
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited)
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (AsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
+
+#define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \
+ do { \
+ ASAN_INTERCEPTOR_ENTER(ctx, memmove); \
+ ASAN_MEMMOVE_IMPL(ctx, to, from, size); \
+ } while (false)
+
+#define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \
+ do { \
+ ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \
+ ASAN_MEMCPY_IMPL(ctx, to, from, size); \
+ } while (false)
+
+#define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \
+ do { \
+ ASAN_INTERCEPTOR_ENTER(ctx, memset); \
+ ASAN_MEMSET_IMPL(ctx, block, c, size); \
+ } while (false)
+
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
+#include "sanitizer_common/sanitizer_signal_interceptors.inc"
+
+// Syscall interceptors don't have contexts, we don't support suppressions
+// for them.
+#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s)
+#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s)
+#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
+ do { \
+ (void)(p); \
+ (void)(s); \
+ } while (false)
+#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
+ do { \
+ (void)(p); \
+ (void)(s); \
+ } while (false)
+#include "sanitizer_common/sanitizer_common_syscalls.inc"
+#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
+
+struct ThreadStartParam {
+ atomic_uintptr_t t;
+ atomic_uintptr_t is_registered;
+};
+
+#if ASAN_INTERCEPT_PTHREAD_CREATE
+static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
+ ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg);
+ AsanThread *t = nullptr;
+ while ((t = reinterpret_cast<AsanThread *>(
+ atomic_load(¶m->t, memory_order_acquire))) == nullptr)
+ internal_sched_yield();
+ SetCurrentThread(t);
+ return t->ThreadStart(GetTid(), ¶m->is_registered);
+}
+
+INTERCEPTOR(int, pthread_create, void *thread,
+ void *attr, void *(*start_routine)(void*), void *arg) {
+ EnsureMainThreadIDIsCorrect();
+ // Strict init-order checking is thread-hostile.
+ if (flags()->strict_init_order)
+ StopInitOrderChecking();
+ GET_STACK_TRACE_THREAD;
+ int detached = 0;
+ if (attr)
+ REAL(pthread_attr_getdetachstate)(attr, &detached);
+ ThreadStartParam param;
+ atomic_store(¶m.t, 0, memory_order_relaxed);
+ atomic_store(¶m.is_registered, 0, memory_order_relaxed);
+ int result;
+ {
+ // Ignore all allocations made by pthread_create: thread stack/TLS may be
+ // stored by pthread for future reuse even after thread destruction, and
+ // the linked list it's stored in doesn't even hold valid pointers to the
+ // objects, the latter are calculated by obscure pointer arithmetic.
+#if CAN_SANITIZE_LEAKS
+ __lsan::ScopedInterceptorDisabler disabler;
+#endif
+ result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m);
+ }
+ if (result == 0) {
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t =
+ AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
+ atomic_store(¶m.t, reinterpret_cast<uptr>(t), memory_order_release);
+ // Wait until the AsanThread object is initialized and the ThreadRegistry
+ // entry is in "started" state. One reason for this is that after this
+ // interceptor exits, the child thread's stack may be the only thing holding
+ // the |arg| pointer. This may cause LSan to report a leak if leak checking
+ // happens at a point when the interceptor has already exited, but the stack
+ // range for the child thread is not yet known.
+ while (atomic_load(¶m.is_registered, memory_order_acquire) == 0)
+ internal_sched_yield();
+ }
+ return result;
+}
+
+INTERCEPTOR(int, pthread_join, void *t, void **arg) {
+ return real_pthread_join(t, arg);
+}
+
+DEFINE_REAL_PTHREAD_FUNCTIONS
+#endif // ASAN_INTERCEPT_PTHREAD_CREATE
+
+#if ASAN_INTERCEPT_SWAPCONTEXT
+static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) {
+ // Align to page size.
+ uptr PageSize = GetPageSizeCached();
+ uptr bottom = stack & ~(PageSize - 1);
+ ssize += stack - bottom;
+ ssize = RoundUpTo(ssize, PageSize);
+ static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb
+ if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) {
+ PoisonShadow(bottom, ssize, 0);
+ }
+}
+
+INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp,
+ struct ucontext_t *ucp) {
+ static bool reported_warning = false;
+ if (!reported_warning) {
+ Report("WARNING: ASan doesn't fully support makecontext/swapcontext "
+ "functions and may produce false positives in some cases!\n");
+ reported_warning = true;
+ }
+ // Clear shadow memory for new context (it may share stack
+ // with current context).
+ uptr stack, ssize;
+ ReadContextStack(ucp, &stack, &ssize);
+ ClearShadowMemoryForContextStack(stack, ssize);
+#if __has_attribute(__indirect_return__) && \
+ (defined(__x86_64__) || defined(__i386__))
+ int (*real_swapcontext)(struct ucontext_t *, struct ucontext_t *)
+ __attribute__((__indirect_return__))
+ = REAL(swapcontext);
+ int res = real_swapcontext(oucp, ucp);
+#else
+ int res = REAL(swapcontext)(oucp, ucp);
+#endif
+ // swapcontext technically does not return, but program may swap context to
+ // "oucp" later, that would look as if swapcontext() returned 0.
+ // We need to clear shadow for ucp once again, as it may be in arbitrary
+ // state.
+ ClearShadowMemoryForContextStack(stack, ssize);
+ return res;
+}
+#endif // ASAN_INTERCEPT_SWAPCONTEXT
+
+#if SANITIZER_NETBSD
+#define longjmp __longjmp14
+#define siglongjmp __siglongjmp14
+#endif
+
+INTERCEPTOR(void, longjmp, void *env, int val) {
+ __asan_handle_no_return();
+ REAL(longjmp)(env, val);
+}
+
+#if ASAN_INTERCEPT__LONGJMP
+INTERCEPTOR(void, _longjmp, void *env, int val) {
+ __asan_handle_no_return();
+ REAL(_longjmp)(env, val);
+}
+#endif
+
+#if ASAN_INTERCEPT___LONGJMP_CHK
+INTERCEPTOR(void, __longjmp_chk, void *env, int val) {
+ __asan_handle_no_return();
+ REAL(__longjmp_chk)(env, val);
+}
+#endif
+
+#if ASAN_INTERCEPT_SIGLONGJMP
+INTERCEPTOR(void, siglongjmp, void *env, int val) {
+ __asan_handle_no_return();
+ REAL(siglongjmp)(env, val);
+}
+#endif
+
+#if ASAN_INTERCEPT___CXA_THROW
+INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
+ CHECK(REAL(__cxa_throw));
+ __asan_handle_no_return();
+ REAL(__cxa_throw)(a, b, c);
+}
+#endif
+
+#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION
+INTERCEPTOR(void, __cxa_rethrow_primary_exception, void *a) {
+ CHECK(REAL(__cxa_rethrow_primary_exception));
+ __asan_handle_no_return();
+ REAL(__cxa_rethrow_primary_exception)(a);
+}
+#endif
+
+#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION
+INTERCEPTOR(_Unwind_Reason_Code, _Unwind_RaiseException,
+ _Unwind_Exception *object) {
+ CHECK(REAL(_Unwind_RaiseException));
+ __asan_handle_no_return();
+ return REAL(_Unwind_RaiseException)(object);
+}
+#endif
+
+#if ASAN_INTERCEPT__SJLJ_UNWIND_RAISEEXCEPTION
+INTERCEPTOR(_Unwind_Reason_Code, _Unwind_SjLj_RaiseException,
+ _Unwind_Exception *object) {
+ CHECK(REAL(_Unwind_SjLj_RaiseException));
+ __asan_handle_no_return();
+ return REAL(_Unwind_SjLj_RaiseException)(object);
+}
+#endif
+
+#if ASAN_INTERCEPT_INDEX
+# if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
+INTERCEPTOR(char*, index, const char *string, int c)
+ ALIAS(WRAPPER_NAME(strchr));
+# else
+# if SANITIZER_MAC
+DECLARE_REAL(char*, index, const char *string, int c)
+OVERRIDE_FUNCTION(index, strchr);
+# else
+DEFINE_REAL(char*, index, const char *string, int c)
+# endif
+# endif
+#endif // ASAN_INTERCEPT_INDEX
+
+// For both strcat() and strncat() we need to check the validity of |to|
+// argument irrespective of the |from| length.
+INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT
+ ENSURE_ASAN_INITED();
+ if (flags()->replace_str) {
+ uptr from_length = REAL(strlen)(from);
+ ASAN_READ_RANGE(ctx, from, from_length + 1);
+ uptr to_length = REAL(strlen)(to);
+ ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
+ ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
+ // If the copying actually happens, the |from| string should not overlap
+ // with the resulting string starting at |to|, which has a length of
+ // to_length + from_length + 1.
+ if (from_length > 0) {
+ CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1,
+ from, from_length + 1);
+ }
+ }
+ return REAL(strcat)(to, from); // NOLINT
+}
+
+INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strncat);
+ ENSURE_ASAN_INITED();
+ if (flags()->replace_str) {
+ uptr from_length = MaybeRealStrnlen(from, size);
+ uptr copy_length = Min(size, from_length + 1);
+ ASAN_READ_RANGE(ctx, from, copy_length);
+ uptr to_length = REAL(strlen)(to);
+ ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length);
+ ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1);
+ if (from_length > 0) {
+ CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
+ from, copy_length);
+ }
+ }
+ return REAL(strncat)(to, from, size);
+}
+
+INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT
+#if SANITIZER_MAC
+ if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT
+#endif
+ // strcpy is called from malloc_default_purgeable_zone()
+ // in __asan::ReplaceSystemAlloc() on Mac.
+ if (asan_init_is_running) {
+ return REAL(strcpy)(to, from); // NOLINT
+ }
+ ENSURE_ASAN_INITED();
+ if (flags()->replace_str) {
+ uptr from_size = REAL(strlen)(from) + 1;
+ CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size);
+ ASAN_READ_RANGE(ctx, from, from_size);
+ ASAN_WRITE_RANGE(ctx, to, from_size);
+ }
+ return REAL(strcpy)(to, from); // NOLINT
+}
+
+INTERCEPTOR(char*, strdup, const char *s) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strdup);
+ if (UNLIKELY(!asan_inited)) return internal_strdup(s);
+ ENSURE_ASAN_INITED();
+ uptr length = REAL(strlen)(s);
+ if (flags()->replace_str) {
+ ASAN_READ_RANGE(ctx, s, length + 1);
+ }
+ GET_STACK_TRACE_MALLOC;
+ void *new_mem = asan_malloc(length + 1, &stack);
+ REAL(memcpy)(new_mem, s, length + 1);
+ return reinterpret_cast<char*>(new_mem);
+}
+
+#if ASAN_INTERCEPT___STRDUP
+INTERCEPTOR(char*, __strdup, const char *s) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strdup);
+ if (UNLIKELY(!asan_inited)) return internal_strdup(s);
+ ENSURE_ASAN_INITED();
+ uptr length = REAL(strlen)(s);
+ if (flags()->replace_str) {
+ ASAN_READ_RANGE(ctx, s, length + 1);
+ }
+ GET_STACK_TRACE_MALLOC;
+ void *new_mem = asan_malloc(length + 1, &stack);
+ REAL(memcpy)(new_mem, s, length + 1);
+ return reinterpret_cast<char*>(new_mem);
+}
+#endif // ASAN_INTERCEPT___STRDUP
+
+INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strncpy);
+ ENSURE_ASAN_INITED();
+ if (flags()->replace_str) {
+ uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1);
+ CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size);
+ ASAN_READ_RANGE(ctx, from, from_size);
+ ASAN_WRITE_RANGE(ctx, to, size);
+ }
+ return REAL(strncpy)(to, from, size);
+}
+
+INTERCEPTOR(long, strtol, const char *nptr, // NOLINT
+ char **endptr, int base) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strtol);
+ ENSURE_ASAN_INITED();
+ if (!flags()->replace_str) {
+ return REAL(strtol)(nptr, endptr, base);
+ }
+ char *real_endptr;
+ long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+ return result;
+}
+
+INTERCEPTOR(int, atoi, const char *nptr) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, atoi);
+#if SANITIZER_MAC
+ if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr);
+#endif
+ ENSURE_ASAN_INITED();
+ if (!flags()->replace_str) {
+ return REAL(atoi)(nptr);
+ }
+ char *real_endptr;
+ // "man atoi" tells that behavior of atoi(nptr) is the same as
+ // strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the
+ // parsed integer can't be stored in *long* type (even if it's
+ // different from int). So, we just imitate this behavior.
+ int result = REAL(strtol)(nptr, &real_endptr, 10);
+ FixRealStrtolEndptr(nptr, &real_endptr);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
+ return result;
+}
+
+INTERCEPTOR(long, atol, const char *nptr) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, atol);
+#if SANITIZER_MAC
+ if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr);
+#endif
+ ENSURE_ASAN_INITED();
+ if (!flags()->replace_str) {
+ return REAL(atol)(nptr);
+ }
+ char *real_endptr;
+ long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT
+ FixRealStrtolEndptr(nptr, &real_endptr);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
+ return result;
+}
+
+#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT
+ char **endptr, int base) {
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, strtoll);
+ ENSURE_ASAN_INITED();
+ if (!flags()->replace_str) {
+ return REAL(strtoll)(nptr, endptr, base);
+ }
+ char *real_endptr;
+ long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+ return result;
+}
+
+INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT
+ void *ctx;
+ ASAN_INTERCEPTOR_ENTER(ctx, atoll);
+ ENSURE_ASAN_INITED();
+ if (!flags()->replace_str) {
+ return REAL(atoll)(nptr);
+ }
+ char *real_endptr;
+ long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT
+ FixRealStrtolEndptr(nptr, &real_endptr);
+ ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1);
+ return result;
+}
+#endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+
+#if ASAN_INTERCEPT___CXA_ATEXIT
+static void AtCxaAtexit(void *unused) {
+ (void)unused;
+ StopInitOrderChecking();
+}
+
+INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
+ void *dso_handle) {
+#if SANITIZER_MAC
+ if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle);
+#endif
+ ENSURE_ASAN_INITED();
+ int res = REAL(__cxa_atexit)(func, arg, dso_handle);
+ REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr);
+ return res;
+}
+#endif // ASAN_INTERCEPT___CXA_ATEXIT
+
+#if ASAN_INTERCEPT_VFORK
+DEFINE_REAL(int, vfork)
+DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
+#endif
+
+// ---------------------- InitializeAsanInterceptors ---------------- {{{1
+namespace __asan {
+void InitializeAsanInterceptors() {
+ static bool was_called_once;
+ CHECK(!was_called_once);
+ was_called_once = true;
+ InitializeCommonInterceptors();
+ InitializeSignalInterceptors();
+
+ // Intercept str* functions.
+ ASAN_INTERCEPT_FUNC(strcat); // NOLINT
+ ASAN_INTERCEPT_FUNC(strcpy); // NOLINT
+ ASAN_INTERCEPT_FUNC(strncat);
+ ASAN_INTERCEPT_FUNC(strncpy);
+ ASAN_INTERCEPT_FUNC(strdup);
+#if ASAN_INTERCEPT___STRDUP
+ ASAN_INTERCEPT_FUNC(__strdup);
+#endif
+#if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX
+ ASAN_INTERCEPT_FUNC(index);
+#endif
+
+ ASAN_INTERCEPT_FUNC(atoi);
+ ASAN_INTERCEPT_FUNC(atol);
+ ASAN_INTERCEPT_FUNC(strtol);
+#if ASAN_INTERCEPT_ATOLL_AND_STRTOLL
+ ASAN_INTERCEPT_FUNC(atoll);
+ ASAN_INTERCEPT_FUNC(strtoll);
+#endif
+
+ // Intecept jump-related functions.
+ ASAN_INTERCEPT_FUNC(longjmp);
+
+#if ASAN_INTERCEPT_SWAPCONTEXT
+ ASAN_INTERCEPT_FUNC(swapcontext);
+#endif
+#if ASAN_INTERCEPT__LONGJMP
+ ASAN_INTERCEPT_FUNC(_longjmp);
+#endif
+#if ASAN_INTERCEPT___LONGJMP_CHK
+ ASAN_INTERCEPT_FUNC(__longjmp_chk);
+#endif
+#if ASAN_INTERCEPT_SIGLONGJMP
+ ASAN_INTERCEPT_FUNC(siglongjmp);
+#endif
+
+ // Intercept exception handling functions.
+#if ASAN_INTERCEPT___CXA_THROW
+ ASAN_INTERCEPT_FUNC(__cxa_throw);
+#endif
+#if ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION
+ ASAN_INTERCEPT_FUNC(__cxa_rethrow_primary_exception);
+#endif
+ // Indirectly intercept std::rethrow_exception.
+#if ASAN_INTERCEPT__UNWIND_RAISEEXCEPTION
+ INTERCEPT_FUNCTION(_Unwind_RaiseException);
+#endif
+ // Indirectly intercept std::rethrow_exception.
+#if ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION
+ INTERCEPT_FUNCTION(_Unwind_SjLj_RaiseException);
+#endif
+
+ // Intercept threading-related functions
+#if ASAN_INTERCEPT_PTHREAD_CREATE
+#if defined(ASAN_PTHREAD_CREATE_VERSION)
+ ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION);
+#else
+ ASAN_INTERCEPT_FUNC(pthread_create);
+#endif
+ ASAN_INTERCEPT_FUNC(pthread_join);
+#endif
+
+ // Intercept atexit function.
+#if ASAN_INTERCEPT___CXA_ATEXIT
+ ASAN_INTERCEPT_FUNC(__cxa_atexit);
+#endif
+
+#if ASAN_INTERCEPT_VFORK
+ ASAN_INTERCEPT_FUNC(vfork);
+#endif
+
+ InitializePlatformInterceptors();
+
+ VReport(1, "AddressSanitizer: libc interceptors initialized\n");
+}
+
+} // namespace __asan
+
+#endif // !SANITIZER_FUCHSIA
//===-- asan_interceptors.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_interceptors.cc
+// ASan-private header for asan_interceptors.cpp
//===----------------------------------------------------------------------===//
#ifndef ASAN_INTERCEPTORS_H
#define ASAN_INTERCEPTORS_H
#if ASAN_HAS_EXCEPTIONS && !SANITIZER_WINDOWS && !SANITIZER_SOLARIS && \
!SANITIZER_NETBSD
# define ASAN_INTERCEPT___CXA_THROW 1
-# if ! defined(ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION) \
- || ASAN_HAS_CXA_RETHROW_PRIMARY_EXCEPTION
-# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1
-# else
-# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 0
-# endif
+# define ASAN_INTERCEPT___CXA_RETHROW_PRIMARY_EXCEPTION 1
# if defined(_GLIBCXX_SJLJ_EXCEPTIONS) || (SANITIZER_IOS && defined(__arm__))
# define ASAN_INTERCEPT__UNWIND_SJLJ_RAISEEXCEPTION 1
# else
# define ASAN_INTERCEPT___STRDUP 0
#endif
+#if SANITIZER_LINUX && (defined(__arm__) || defined(__aarch64__) || \
+ defined(__i386__) || defined(__x86_64__))
+# define ASAN_INTERCEPT_VFORK 1
+#else
+# define ASAN_INTERCEPT_VFORK 0
+#endif
+
DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size)
DECLARE_REAL(char*, strchr, const char *str, int c)
DECLARE_REAL(SIZE_T, strlen, const char *s)
DECLARE_REAL(char*, strstr, const char *s1, const char *s2)
#if !SANITIZER_MAC
-#define ASAN_INTERCEPT_FUNC(name) \
- do { \
- if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
- VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
+#define ASAN_INTERCEPT_FUNC(name) \
+ do { \
+ if (!INTERCEPT_FUNCTION(name)) \
+ VReport(1, "AddressSanitizer: failed to intercept '%s'\n'", #name); \
} while (0)
-#define ASAN_INTERCEPT_FUNC_VER(name, ver) \
- do { \
- if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \
- VReport( \
- 1, "AddressSanitizer: failed to intercept '" #name "@@" #ver "'\n"); \
+#define ASAN_INTERCEPT_FUNC_VER(name, ver) \
+ do { \
+ if (!INTERCEPT_FUNCTION_VER(name, ver)) \
+ VReport(1, "AddressSanitizer: failed to intercept '%s@@%s'\n", #name, \
+ #ver); \
} while (0)
#else
// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
+++ /dev/null
-//===-- asan_interceptors_memintrinsics.cc --------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===---------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// ASan versions of memcpy, memmove, and memset.
-//===---------------------------------------------------------------------===//
-
-#include "asan_interceptors_memintrinsics.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_suppressions.h"
-
-using namespace __asan; // NOLINT
-
-void *__asan_memcpy(void *to, const void *from, uptr size) {
- ASAN_MEMCPY_IMPL(nullptr, to, from, size);
-}
-
-void *__asan_memset(void *block, int c, uptr size) {
- ASAN_MEMSET_IMPL(nullptr, block, c, size);
-}
-
-void *__asan_memmove(void *to, const void *from, uptr size) {
- ASAN_MEMMOVE_IMPL(nullptr, to, from, size);
-}
-
-#if SANITIZER_FUCHSIA || SANITIZER_RTEMS
-
-// Fuchsia and RTEMS don't use sanitizer_common_interceptors.inc, but
-// the only things there it wants are these three. Just define them
-// as aliases here rather than repeating the contents.
-
-extern "C" decltype(__asan_memcpy) memcpy[[gnu::alias("__asan_memcpy")]];
-extern "C" decltype(__asan_memmove) memmove[[gnu::alias("__asan_memmove")]];
-extern "C" decltype(__asan_memset) memset[[gnu::alias("__asan_memset")]];
-
-#endif // SANITIZER_FUCHSIA || SANITIZER_RTEMS
--- /dev/null
+//===-- asan_interceptors_memintrinsics.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// ASan versions of memcpy, memmove, and memset.
+//===---------------------------------------------------------------------===//
+
+#include "asan_interceptors_memintrinsics.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_suppressions.h"
+
+using namespace __asan; // NOLINT
+
+void *__asan_memcpy(void *to, const void *from, uptr size) {
+ ASAN_MEMCPY_IMPL(nullptr, to, from, size);
+}
+
+void *__asan_memset(void *block, int c, uptr size) {
+ ASAN_MEMSET_IMPL(nullptr, block, c, size);
+}
+
+void *__asan_memmove(void *to, const void *from, uptr size) {
+ ASAN_MEMMOVE_IMPL(nullptr, to, from, size);
+}
+
+#if SANITIZER_FUCHSIA || SANITIZER_RTEMS
+
+// Fuchsia and RTEMS don't use sanitizer_common_interceptors.inc, but
+// the only things there it wants are these three. Just define them
+// as aliases here rather than repeating the contents.
+
+extern "C" decltype(__asan_memcpy) memcpy[[gnu::alias("__asan_memcpy")]];
+extern "C" decltype(__asan_memmove) memmove[[gnu::alias("__asan_memmove")]];
+extern "C" decltype(__asan_memset) memset[[gnu::alias("__asan_memset")]];
+
+#endif // SANITIZER_FUCHSIA || SANITIZER_RTEMS
//===-- asan_interceptors_memintrinsics.h -----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_memintrin.cc
+// ASan-private header for asan_interceptors_memintrinsics.cpp
//===---------------------------------------------------------------------===//
#ifndef ASAN_MEMINTRIN_H
#define ASAN_MEMINTRIN_H
--- /dev/null
+#include "sanitizer_common/sanitizer_asm.h"
+
+#if defined(__linux__)
+#define COMMON_INTERCEPTOR_SPILL_AREA __asan_extra_spill_area
+#define COMMON_INTERCEPTOR_HANDLE_VFORK __asan_handle_vfork
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_arm.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_x86_64.inc.S"
+#include "sanitizer_common/sanitizer_common_interceptors_vfork_i386.inc.S"
+#endif
+
+NO_EXEC_STACK_DIRECTIVE
//===-- asan_interface.inc ------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Asan interface list.
INTERFACE_FUNCTION(__asan_get_report_sp)
INTERFACE_FUNCTION(__asan_get_shadow_mapping)
INTERFACE_FUNCTION(__asan_handle_no_return)
+INTERFACE_FUNCTION(__asan_handle_vfork)
INTERFACE_FUNCTION(__asan_init)
INTERFACE_FUNCTION(__asan_load_cxx_array_cookie)
INTERFACE_FUNCTION(__asan_load1)
//===-- asan_interface_internal.h -------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
const char* __asan_default_suppressions();
+
+ SANITIZER_INTERFACE_ATTRIBUTE void __asan_handle_vfork(void *sp);
} // extern "C"
#endif // ASAN_INTERFACE_INTERNAL_H
//===-- asan_internal.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void AsanInitFromRtl();
-// asan_win.cc
+// asan_win.cpp
void InitializePlatformExceptionHandlers();
// Returns whether an address is a valid allocated system heap block.
// 'addr' must point to the beginning of the block.
bool IsSystemHeapAddress(uptr addr);
-// asan_rtl.cc
+// asan_rtl.cpp
void PrintAddressSpaceLayout();
void NORETURN ShowStatsAndAbort();
-// asan_shadow_setup.cc
+// asan_shadow_setup.cpp
void InitializeShadowMemory();
-// asan_malloc_linux.cc / asan_malloc_mac.cc
+// asan_malloc_linux.cpp / asan_malloc_mac.cpp
void ReplaceSystemMalloc();
-// asan_linux.cc / asan_mac.cc / asan_rtems.cc / asan_win.cc
+// asan_linux.cpp / asan_mac.cpp / asan_rtems.cpp / asan_win.cpp
uptr FindDynamicShadowStart();
void *AsanDoesNotSupportStaticLinkage();
void AsanCheckDynamicRTPrereqs();
void AsanCheckIncompatibleRT();
-// asan_thread.cc
+// asan_thread.cpp
AsanThread *CreateMainThread();
// Support function for __asan_(un)register_image_globals. Searches for the
void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name);
+// Returns `true` iff most of ASan init process should be skipped due to the
+// ASan library being loaded via `dlopen()`. Platforms may perform any
+// `dlopen()` specific initialization inside this function.
+bool HandleDlopenInit();
+
// Add convenient macro for interface functions that may be represented as
// weak hooks.
#define ASAN_MALLOC_HOOK(ptr, size) \
+++ /dev/null
-//===-- asan_linux.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Linux-specific details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
- SANITIZER_SOLARIS
-
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_premap_shadow.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_freebsd.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <dlfcn.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <unwind.h>
-
-#if SANITIZER_FREEBSD
-#include <sys/link_elf.h>
-#endif
-
-#if SANITIZER_SOLARIS
-#include <link.h>
-#endif
-
-#if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS
-#include <ucontext.h>
-extern "C" void* _DYNAMIC;
-#elif SANITIZER_NETBSD
-#include <link_elf.h>
-#include <ucontext.h>
-extern Elf_Dyn _DYNAMIC;
-#else
-#include <sys/ucontext.h>
-#include <link.h>
-#endif
-
-// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
-// 32-bit mode.
-#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
- __FreeBSD_version <= 902001 // v9.2
-#define ucontext_t xucontext_t
-#endif
-
-typedef enum {
- ASAN_RT_VERSION_UNDEFINED = 0,
- ASAN_RT_VERSION_DYNAMIC,
- ASAN_RT_VERSION_STATIC,
-} asan_rt_version_t;
-
-// FIXME: perhaps also store abi version here?
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-asan_rt_version_t __asan_rt_version;
-}
-
-namespace __asan {
-
-void InitializePlatformInterceptors() {}
-void InitializePlatformExceptionHandlers() {}
-bool IsSystemHeapAddress (uptr addr) { return false; }
-
-void *AsanDoesNotSupportStaticLinkage() {
- // This will fail to link with -static.
- return &_DYNAMIC; // defined in link.h
-}
-
-static void UnmapFromTo(uptr from, uptr to) {
- CHECK(to >= from);
- if (to == from) return;
- uptr res = internal_munmap(reinterpret_cast<void *>(from), to - from);
- if (UNLIKELY(internal_iserror(res))) {
- Report(
- "ERROR: AddresSanitizer failed to unmap 0x%zx (%zd) bytes at address "
- "%p\n",
- to - from, to - from, from);
- CHECK("unable to unmap" && 0);
- }
-}
-
-#if ASAN_PREMAP_SHADOW
-uptr FindPremappedShadowStart() {
- uptr granularity = GetMmapGranularity();
- uptr shadow_start = reinterpret_cast<uptr>(&__asan_shadow);
- uptr premap_shadow_size = PremapShadowSize();
- uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity);
- // We may have mapped too much. Release extra memory.
- UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size);
- return shadow_start;
-}
-#endif
-
-uptr FindDynamicShadowStart() {
-#if ASAN_PREMAP_SHADOW
- if (!PremapShadowFailed())
- return FindPremappedShadowStart();
-#endif
-
- uptr granularity = GetMmapGranularity();
- uptr alignment = granularity * 8;
- uptr left_padding = granularity;
- uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity);
- uptr map_size = shadow_size + left_padding + alignment;
-
- uptr map_start = (uptr)MmapNoAccess(map_size);
- CHECK_NE(map_start, ~(uptr)0);
-
- uptr shadow_start = RoundUpTo(map_start + left_padding, alignment);
- UnmapFromTo(map_start, shadow_start - left_padding);
- UnmapFromTo(shadow_start + shadow_size, map_start + map_size);
-
- return shadow_start;
-}
-
-void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
- UNIMPLEMENTED();
-}
-
-#if SANITIZER_ANDROID
-// FIXME: should we do anything for Android?
-void AsanCheckDynamicRTPrereqs() {}
-void AsanCheckIncompatibleRT() {}
-#else
-static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size,
- void *data) {
- VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n",
- info->dlpi_name, info->dlpi_addr);
-
- // Continue until the first dynamic library is found
- if (!info->dlpi_name || info->dlpi_name[0] == 0)
- return 0;
-
- // Ignore vDSO
- if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0)
- return 0;
-
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD
- // Ignore first entry (the main program)
- char **p = (char **)data;
- if (!(*p)) {
- *p = (char *)-1;
- return 0;
- }
-#endif
-
-#if SANITIZER_SOLARIS
- // Ignore executable on Solaris
- if (info->dlpi_addr == 0)
- return 0;
-#endif
-
- *(const char **)data = info->dlpi_name;
- return 1;
-}
-
-static bool IsDynamicRTName(const char *libname) {
- return internal_strstr(libname, "libclang_rt.asan") ||
- internal_strstr(libname, "libasan.so");
-}
-
-static void ReportIncompatibleRT() {
- Report("Your application is linked against incompatible ASan runtimes.\n");
- Die();
-}
-
-void AsanCheckDynamicRTPrereqs() {
- if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order)
- return;
-
- // Ensure that dynamic RT is the first DSO in the list
- const char *first_dso_name = nullptr;
- dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name);
- if (first_dso_name && !IsDynamicRTName(first_dso_name)) {
- Report("ASan runtime does not come first in initial library list; "
- "you should either link runtime to your application or "
- "manually preload it with LD_PRELOAD.\n");
- Die();
- }
-}
-
-void AsanCheckIncompatibleRT() {
- if (ASAN_DYNAMIC) {
- if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {
- __asan_rt_version = ASAN_RT_VERSION_DYNAMIC;
- } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) {
- ReportIncompatibleRT();
- }
- } else {
- if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {
- // Ensure that dynamic runtime is not present. We should detect it
- // as early as possible, otherwise ASan interceptors could bind to
- // the functions in dynamic ASan runtime instead of the functions in
- // system libraries, causing crashes later in ASan initialization.
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- char filename[PATH_MAX];
- MemoryMappedSegment segment(filename, sizeof(filename));
- while (proc_maps.Next(&segment)) {
- if (IsDynamicRTName(segment.filename)) {
- Report("Your application is linked against "
- "incompatible ASan runtimes.\n");
- Die();
- }
- }
- __asan_rt_version = ASAN_RT_VERSION_STATIC;
- } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) {
- ReportIncompatibleRT();
- }
- }
-}
-#endif // SANITIZER_ANDROID
-
-#if !SANITIZER_ANDROID
-void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
- ucontext_t *ucp = (ucontext_t*)context;
- *stack = (uptr)ucp->uc_stack.ss_sp;
- *ssize = ucp->uc_stack.ss_size;
-}
-#else
-void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
- UNIMPLEMENTED();
-}
-#endif
-
-void *AsanDlSymNext(const char *sym) {
- return dlsym(RTLD_NEXT, sym);
-}
-
-} // namespace __asan
-
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
- // SANITIZER_SOLARIS
--- /dev/null
+//===-- asan_linux.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Linux-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+ SANITIZER_SOLARIS
+
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_premap_shadow.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_freebsd.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <unwind.h>
+
+#if SANITIZER_FREEBSD
+#include <sys/link_elf.h>
+#endif
+
+#if SANITIZER_SOLARIS
+#include <link.h>
+#endif
+
+#if SANITIZER_ANDROID || SANITIZER_FREEBSD || SANITIZER_SOLARIS
+#include <ucontext.h>
+extern "C" void* _DYNAMIC;
+#elif SANITIZER_NETBSD
+#include <link_elf.h>
+#include <ucontext.h>
+extern Elf_Dyn _DYNAMIC;
+#else
+#include <sys/ucontext.h>
+#include <link.h>
+#endif
+
+// x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in
+// 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) && \
+ __FreeBSD_version <= 902001 // v9.2
+#define ucontext_t xucontext_t
+#endif
+
+typedef enum {
+ ASAN_RT_VERSION_UNDEFINED = 0,
+ ASAN_RT_VERSION_DYNAMIC,
+ ASAN_RT_VERSION_STATIC,
+} asan_rt_version_t;
+
+// FIXME: perhaps also store abi version here?
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+asan_rt_version_t __asan_rt_version;
+}
+
+namespace __asan {
+
+void InitializePlatformInterceptors() {}
+void InitializePlatformExceptionHandlers() {}
+bool IsSystemHeapAddress (uptr addr) { return false; }
+
+void *AsanDoesNotSupportStaticLinkage() {
+ // This will fail to link with -static.
+ return &_DYNAMIC; // defined in link.h
+}
+
+static void UnmapFromTo(uptr from, uptr to) {
+ CHECK(to >= from);
+ if (to == from) return;
+ uptr res = internal_munmap(reinterpret_cast<void *>(from), to - from);
+ if (UNLIKELY(internal_iserror(res))) {
+ Report(
+ "ERROR: AddresSanitizer failed to unmap 0x%zx (%zd) bytes at address "
+ "%p\n",
+ to - from, to - from, from);
+ CHECK("unable to unmap" && 0);
+ }
+}
+
+#if ASAN_PREMAP_SHADOW
+uptr FindPremappedShadowStart() {
+ uptr granularity = GetMmapGranularity();
+ uptr shadow_start = reinterpret_cast<uptr>(&__asan_shadow);
+ uptr premap_shadow_size = PremapShadowSize();
+ uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity);
+ // We may have mapped too much. Release extra memory.
+ UnmapFromTo(shadow_start + shadow_size, shadow_start + premap_shadow_size);
+ return shadow_start;
+}
+#endif
+
+uptr FindDynamicShadowStart() {
+#if ASAN_PREMAP_SHADOW
+ if (!PremapShadowFailed())
+ return FindPremappedShadowStart();
+#endif
+
+ uptr granularity = GetMmapGranularity();
+ uptr alignment = granularity * 8;
+ uptr left_padding = granularity;
+ uptr shadow_size = RoundUpTo(kHighShadowEnd, granularity);
+ uptr map_size = shadow_size + left_padding + alignment;
+
+ uptr map_start = (uptr)MmapNoAccess(map_size);
+ CHECK_NE(map_start, ~(uptr)0);
+
+ uptr shadow_start = RoundUpTo(map_start + left_padding, alignment);
+ UnmapFromTo(map_start, shadow_start - left_padding);
+ UnmapFromTo(shadow_start + shadow_size, map_start + map_size);
+
+ return shadow_start;
+}
+
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ UNIMPLEMENTED();
+}
+
+#if SANITIZER_ANDROID
+// FIXME: should we do anything for Android?
+void AsanCheckDynamicRTPrereqs() {}
+void AsanCheckIncompatibleRT() {}
+#else
+static int FindFirstDSOCallback(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ VReport(2, "info->dlpi_name = %s\tinfo->dlpi_addr = %p\n",
+ info->dlpi_name, info->dlpi_addr);
+
+ // Continue until the first dynamic library is found
+ if (!info->dlpi_name || info->dlpi_name[0] == 0)
+ return 0;
+
+ // Ignore vDSO
+ if (internal_strncmp(info->dlpi_name, "linux-", sizeof("linux-") - 1) == 0)
+ return 0;
+
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD
+ // Ignore first entry (the main program)
+ char **p = (char **)data;
+ if (!(*p)) {
+ *p = (char *)-1;
+ return 0;
+ }
+#endif
+
+#if SANITIZER_SOLARIS
+ // Ignore executable on Solaris
+ if (info->dlpi_addr == 0)
+ return 0;
+#endif
+
+ *(const char **)data = info->dlpi_name;
+ return 1;
+}
+
+static bool IsDynamicRTName(const char *libname) {
+ return internal_strstr(libname, "libclang_rt.asan") ||
+ internal_strstr(libname, "libasan.so");
+}
+
+static void ReportIncompatibleRT() {
+ Report("Your application is linked against incompatible ASan runtimes.\n");
+ Die();
+}
+
+void AsanCheckDynamicRTPrereqs() {
+ if (!ASAN_DYNAMIC || !flags()->verify_asan_link_order)
+ return;
+
+ // Ensure that dynamic RT is the first DSO in the list
+ const char *first_dso_name = nullptr;
+ dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name);
+ if (first_dso_name && !IsDynamicRTName(first_dso_name)) {
+ Report("ASan runtime does not come first in initial library list; "
+ "you should either link runtime to your application or "
+ "manually preload it with LD_PRELOAD.\n");
+ Die();
+ }
+}
+
+void AsanCheckIncompatibleRT() {
+ if (ASAN_DYNAMIC) {
+ if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {
+ __asan_rt_version = ASAN_RT_VERSION_DYNAMIC;
+ } else if (__asan_rt_version != ASAN_RT_VERSION_DYNAMIC) {
+ ReportIncompatibleRT();
+ }
+ } else {
+ if (__asan_rt_version == ASAN_RT_VERSION_UNDEFINED) {
+ // Ensure that dynamic runtime is not present. We should detect it
+ // as early as possible, otherwise ASan interceptors could bind to
+ // the functions in dynamic ASan runtime instead of the functions in
+ // system libraries, causing crashes later in ASan initialization.
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ char filename[PATH_MAX];
+ MemoryMappedSegment segment(filename, sizeof(filename));
+ while (proc_maps.Next(&segment)) {
+ if (IsDynamicRTName(segment.filename)) {
+ Report("Your application is linked against "
+ "incompatible ASan runtimes.\n");
+ Die();
+ }
+ }
+ __asan_rt_version = ASAN_RT_VERSION_STATIC;
+ } else if (__asan_rt_version != ASAN_RT_VERSION_STATIC) {
+ ReportIncompatibleRT();
+ }
+ }
+}
+#endif // SANITIZER_ANDROID
+
+#if !SANITIZER_ANDROID
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ ucontext_t *ucp = (ucontext_t*)context;
+ *stack = (uptr)ucp->uc_stack.ss_sp;
+ *ssize = ucp->uc_stack.ss_size;
+}
+#else
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
+}
+#endif
+
+void *AsanDlSymNext(const char *sym) {
+ return dlsym(RTLD_NEXT, sym);
+}
+
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+
+} // namespace __asan
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
+ // SANITIZER_SOLARIS
+++ /dev/null
-//===-- asan_mac.cc -------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Mac-specific details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_stack.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_mac.h"
-
-#include <dlfcn.h>
-#include <fcntl.h>
-#include <libkern/OSAtomic.h>
-#include <mach-o/dyld.h>
-#include <mach-o/getsect.h>
-#include <mach-o/loader.h>
-#include <pthread.h>
-#include <stdlib.h> // for free()
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/sysctl.h>
-#include <sys/ucontext.h>
-#include <unistd.h>
-
-// from <crt_externs.h>, but we don't have that file on iOS
-extern "C" {
- extern char ***_NSGetArgv(void);
- extern char ***_NSGetEnviron(void);
-}
-
-namespace __asan {
-
-void InitializePlatformInterceptors() {}
-void InitializePlatformExceptionHandlers() {}
-bool IsSystemHeapAddress (uptr addr) { return false; }
-
-// No-op. Mac does not support static linkage anyway.
-void *AsanDoesNotSupportStaticLinkage() {
- return 0;
-}
-
-uptr FindDynamicShadowStart() {
- uptr granularity = GetMmapGranularity();
- uptr alignment = 8 * granularity;
- uptr left_padding = granularity;
- uptr space_size = kHighShadowEnd + left_padding;
-
- uptr largest_gap_found = 0;
- uptr max_occupied_addr = 0;
- VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size);
- uptr shadow_start =
- FindAvailableMemoryRange(space_size, alignment, granularity,
- &largest_gap_found, &max_occupied_addr);
- // If the shadow doesn't fit, restrict the address space to make it fit.
- if (shadow_start == 0) {
- VReport(
- 2,
- "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n",
- largest_gap_found, max_occupied_addr);
- uptr new_max_vm = RoundDownTo(largest_gap_found << SHADOW_SCALE, alignment);
- if (new_max_vm < max_occupied_addr) {
- Report("Unable to find a memory range for dynamic shadow.\n");
- Report(
- "space_size = %p, largest_gap_found = %p, max_occupied_addr = %p, "
- "new_max_vm = %p\n",
- space_size, largest_gap_found, max_occupied_addr, new_max_vm);
- CHECK(0 && "cannot place shadow");
- }
- RestrictMemoryToMaxAddress(new_max_vm);
- kHighMemEnd = new_max_vm - 1;
- space_size = kHighShadowEnd + left_padding;
- VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size);
- shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity,
- nullptr, nullptr);
- if (shadow_start == 0) {
- Report("Unable to find a memory range after restricting VM.\n");
- CHECK(0 && "cannot place shadow after restricting vm");
- }
- }
- CHECK_NE((uptr)0, shadow_start);
- CHECK(IsAligned(shadow_start, alignment));
- return shadow_start;
-}
-
-// No-op. Mac does not support static linkage anyway.
-void AsanCheckDynamicRTPrereqs() {}
-
-// No-op. Mac does not support static linkage anyway.
-void AsanCheckIncompatibleRT() {}
-
-void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
- // Find the Mach-O header for the image containing the needle
- Dl_info info;
- int err = dladdr(needle, &info);
- if (err == 0) return;
-
-#if __LP64__
- const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase;
-#else
- const struct mach_header *mh = (struct mach_header *)info.dli_fbase;
-#endif
-
- // Look up the __asan_globals section in that image and register its globals
- unsigned long size = 0;
- __asan_global *globals = (__asan_global *)getsectiondata(
- mh,
- "__DATA", "__asan_globals",
- &size);
-
- if (!globals) return;
- if (size % sizeof(__asan_global) != 0) return;
- op(globals, size / sizeof(__asan_global));
-}
-
-void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
- UNIMPLEMENTED();
-}
-
-// Support for the following functions from libdispatch on Mac OS:
-// dispatch_async_f()
-// dispatch_async()
-// dispatch_sync_f()
-// dispatch_sync()
-// dispatch_after_f()
-// dispatch_after()
-// dispatch_group_async_f()
-// dispatch_group_async()
-// TODO(glider): libdispatch API contains other functions that we don't support
-// yet.
-//
-// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
-// they can cause jobs to run on a thread different from the current one.
-// TODO(glider): if so, we need a test for this (otherwise we should remove
-// them).
-//
-// The following functions use dispatch_barrier_async_f() (which isn't a library
-// function but is exported) and are thus supported:
-// dispatch_source_set_cancel_handler_f()
-// dispatch_source_set_cancel_handler()
-// dispatch_source_set_event_handler_f()
-// dispatch_source_set_event_handler()
-//
-// The reference manual for Grand Central Dispatch is available at
-// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
-// The implementation details are at
-// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
-
-typedef void* dispatch_group_t;
-typedef void* dispatch_queue_t;
-typedef void* dispatch_source_t;
-typedef u64 dispatch_time_t;
-typedef void (*dispatch_function_t)(void *block);
-typedef void* (*worker_t)(void *block);
-
-// A wrapper for the ObjC blocks used to support libdispatch.
-typedef struct {
- void *block;
- dispatch_function_t func;
- u32 parent_tid;
-} asan_block_context_t;
-
-ALWAYS_INLINE
-void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
- AsanThread *t = GetCurrentThread();
- if (!t) {
- t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
- parent_tid, stack, /* detached */ true);
- t->Init();
- asanThreadRegistry().StartThread(t->tid(), GetTid(),
- /* workerthread */ true, 0);
- SetCurrentThread(t);
- }
-}
-
-// For use by only those functions that allocated the context via
-// alloc_asan_context().
-extern "C"
-void asan_dispatch_call_block_and_release(void *block) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *context = (asan_block_context_t*)block;
- VReport(2,
- "asan_dispatch_call_block_and_release(): "
- "context: %p, pthread_self: %p\n",
- block, pthread_self());
- asan_register_worker_thread(context->parent_tid, &stack);
- // Call the original dispatcher for the block.
- context->func(context->block);
- asan_free(context, &stack, FROM_MALLOC);
-}
-
-} // namespace __asan
-
-using namespace __asan; // NOLINT
-
-// Wrap |ctxt| and |func| into an asan_block_context_t.
-// The caller retains control of the allocated context.
-extern "C"
-asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
- BufferedStackTrace *stack) {
- asan_block_context_t *asan_ctxt =
- (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
- asan_ctxt->block = ctxt;
- asan_ctxt->func = func;
- asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
- return asan_ctxt;
-}
-
-// Define interceptor for dispatch_*_f function with the three most common
-// parameters: dispatch_queue_t, context, dispatch_function_t.
-#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
- INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
- dispatch_function_t func) { \
- GET_STACK_TRACE_THREAD; \
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
- if (Verbosity() >= 2) { \
- Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
- asan_ctxt, pthread_self()); \
- PRINT_CURRENT_STACK(); \
- } \
- return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
- asan_dispatch_call_block_and_release); \
- }
-
-INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
-INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
-INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
-
-INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
- dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (Verbosity() >= 2) {
- Report("dispatch_after_f: %p\n", asan_ctxt);
- PRINT_CURRENT_STACK();
- }
- return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
-
-INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
- dispatch_queue_t dq, void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_THREAD;
- asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
- if (Verbosity() >= 2) {
- Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
- asan_ctxt, pthread_self());
- PRINT_CURRENT_STACK();
- }
- REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
- asan_dispatch_call_block_and_release);
-}
-
-#if !defined(MISSING_BLOCKS_SUPPORT)
-extern "C" {
-void dispatch_async(dispatch_queue_t dq, void(^work)(void));
-void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
- void(^work)(void));
-void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
- void(^work)(void));
-void dispatch_source_set_cancel_handler(dispatch_source_t ds,
- void(^work)(void));
-void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
-}
-
-#define GET_ASAN_BLOCK(work) \
- void (^asan_block)(void); \
- int parent_tid = GetCurrentTidOrInvalid(); \
- asan_block = ^(void) { \
- GET_STACK_TRACE_THREAD; \
- asan_register_worker_thread(parent_tid, &stack); \
- work(); \
- }
-
-INTERCEPTOR(void, dispatch_async,
- dispatch_queue_t dq, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_async)(dq, asan_block);
-}
-
-INTERCEPTOR(void, dispatch_group_async,
- dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_group_async)(dg, dq, asan_block);
-}
-
-INTERCEPTOR(void, dispatch_after,
- dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_after)(when, queue, asan_block);
-}
-
-INTERCEPTOR(void, dispatch_source_set_cancel_handler,
- dispatch_source_t ds, void(^work)(void)) {
- if (!work) {
- REAL(dispatch_source_set_cancel_handler)(ds, work);
- return;
- }
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
-}
-
-INTERCEPTOR(void, dispatch_source_set_event_handler,
- dispatch_source_t ds, void(^work)(void)) {
- ENABLE_FRAME_POINTER;
- GET_ASAN_BLOCK(work);
- REAL(dispatch_source_set_event_handler)(ds, asan_block);
-}
-#endif
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- asan_mac.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Mac-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mac.h"
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <libkern/OSAtomic.h>
+#include <mach-o/dyld.h>
+#include <mach-o/getsect.h>
+#include <mach-o/loader.h>
+#include <pthread.h>
+#include <stdlib.h> // for free()
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+#include <sys/ucontext.h>
+#include <unistd.h>
+
+// from <crt_externs.h>, but we don't have that file on iOS
+extern "C" {
+ extern char ***_NSGetArgv(void);
+ extern char ***_NSGetEnviron(void);
+}
+
+namespace __asan {
+
+void InitializePlatformInterceptors() {}
+void InitializePlatformExceptionHandlers() {}
+bool IsSystemHeapAddress (uptr addr) { return false; }
+
+// No-op. Mac does not support static linkage anyway.
+void *AsanDoesNotSupportStaticLinkage() {
+ return 0;
+}
+
+uptr FindDynamicShadowStart() {
+ uptr granularity = GetMmapGranularity();
+ uptr alignment = 8 * granularity;
+ uptr left_padding = granularity;
+ uptr space_size = kHighShadowEnd + left_padding;
+
+ uptr largest_gap_found = 0;
+ uptr max_occupied_addr = 0;
+ VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size);
+ uptr shadow_start =
+ FindAvailableMemoryRange(space_size, alignment, granularity,
+ &largest_gap_found, &max_occupied_addr);
+ // If the shadow doesn't fit, restrict the address space to make it fit.
+ if (shadow_start == 0) {
+ VReport(
+ 2,
+ "Shadow doesn't fit, largest_gap_found = %p, max_occupied_addr = %p\n",
+ largest_gap_found, max_occupied_addr);
+ uptr new_max_vm = RoundDownTo(largest_gap_found << SHADOW_SCALE, alignment);
+ if (new_max_vm < max_occupied_addr) {
+ Report("Unable to find a memory range for dynamic shadow.\n");
+ Report(
+ "space_size = %p, largest_gap_found = %p, max_occupied_addr = %p, "
+ "new_max_vm = %p\n",
+ space_size, largest_gap_found, max_occupied_addr, new_max_vm);
+ CHECK(0 && "cannot place shadow");
+ }
+ RestrictMemoryToMaxAddress(new_max_vm);
+ kHighMemEnd = new_max_vm - 1;
+ space_size = kHighShadowEnd + left_padding;
+ VReport(2, "FindDynamicShadowStart, space_size = %p\n", space_size);
+ shadow_start = FindAvailableMemoryRange(space_size, alignment, granularity,
+ nullptr, nullptr);
+ if (shadow_start == 0) {
+ Report("Unable to find a memory range after restricting VM.\n");
+ CHECK(0 && "cannot place shadow after restricting vm");
+ }
+ }
+ CHECK_NE((uptr)0, shadow_start);
+ CHECK(IsAligned(shadow_start, alignment));
+ return shadow_start;
+}
+
+// No-op. Mac does not support static linkage anyway.
+void AsanCheckDynamicRTPrereqs() {}
+
+// No-op. Mac does not support static linkage anyway.
+void AsanCheckIncompatibleRT() {}
+
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ // Find the Mach-O header for the image containing the needle
+ Dl_info info;
+ int err = dladdr(needle, &info);
+ if (err == 0) return;
+
+#if __LP64__
+ const struct mach_header_64 *mh = (struct mach_header_64 *)info.dli_fbase;
+#else
+ const struct mach_header *mh = (struct mach_header *)info.dli_fbase;
+#endif
+
+ // Look up the __asan_globals section in that image and register its globals
+ unsigned long size = 0;
+ __asan_global *globals = (__asan_global *)getsectiondata(
+ mh,
+ "__DATA", "__asan_globals",
+ &size);
+
+ if (!globals) return;
+ if (size % sizeof(__asan_global) != 0) return;
+ op(globals, size / sizeof(__asan_global));
+}
+
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
+}
+
+// Support for the following functions from libdispatch on Mac OS:
+// dispatch_async_f()
+// dispatch_async()
+// dispatch_sync_f()
+// dispatch_sync()
+// dispatch_after_f()
+// dispatch_after()
+// dispatch_group_async_f()
+// dispatch_group_async()
+// TODO(glider): libdispatch API contains other functions that we don't support
+// yet.
+//
+// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
+// they can cause jobs to run on a thread different from the current one.
+// TODO(glider): if so, we need a test for this (otherwise we should remove
+// them).
+//
+// The following functions use dispatch_barrier_async_f() (which isn't a library
+// function but is exported) and are thus supported:
+// dispatch_source_set_cancel_handler_f()
+// dispatch_source_set_cancel_handler()
+// dispatch_source_set_event_handler_f()
+// dispatch_source_set_event_handler()
+//
+// The reference manual for Grand Central Dispatch is available at
+// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
+// The implementation details are at
+// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
+
+typedef void* dispatch_group_t;
+typedef void* dispatch_queue_t;
+typedef void* dispatch_source_t;
+typedef u64 dispatch_time_t;
+typedef void (*dispatch_function_t)(void *block);
+typedef void* (*worker_t)(void *block);
+
+// A wrapper for the ObjC blocks used to support libdispatch.
+typedef struct {
+ void *block;
+ dispatch_function_t func;
+ u32 parent_tid;
+} asan_block_context_t;
+
+ALWAYS_INLINE
+void asan_register_worker_thread(int parent_tid, StackTrace *stack) {
+ AsanThread *t = GetCurrentThread();
+ if (!t) {
+ t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr,
+ parent_tid, stack, /* detached */ true);
+ t->Init();
+ asanThreadRegistry().StartThread(t->tid(), GetTid(), ThreadType::Worker,
+ nullptr);
+ SetCurrentThread(t);
+ }
+}
+
+// For use by only those functions that allocated the context via
+// alloc_asan_context().
+extern "C"
+void asan_dispatch_call_block_and_release(void *block) {
+ GET_STACK_TRACE_THREAD;
+ asan_block_context_t *context = (asan_block_context_t*)block;
+ VReport(2,
+ "asan_dispatch_call_block_and_release(): "
+ "context: %p, pthread_self: %p\n",
+ block, pthread_self());
+ asan_register_worker_thread(context->parent_tid, &stack);
+ // Call the original dispatcher for the block.
+ context->func(context->block);
+ asan_free(context, &stack, FROM_MALLOC);
+}
+
+} // namespace __asan
+
+using namespace __asan; // NOLINT
+
+// Wrap |ctxt| and |func| into an asan_block_context_t.
+// The caller retains control of the allocated context.
+extern "C"
+asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
+ BufferedStackTrace *stack) {
+ asan_block_context_t *asan_ctxt =
+ (asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
+ asan_ctxt->block = ctxt;
+ asan_ctxt->func = func;
+ asan_ctxt->parent_tid = GetCurrentTidOrInvalid();
+ return asan_ctxt;
+}
+
+// Define interceptor for dispatch_*_f function with the three most common
+// parameters: dispatch_queue_t, context, dispatch_function_t.
+#define INTERCEPT_DISPATCH_X_F_3(dispatch_x_f) \
+ INTERCEPTOR(void, dispatch_x_f, dispatch_queue_t dq, void *ctxt, \
+ dispatch_function_t func) { \
+ GET_STACK_TRACE_THREAD; \
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \
+ if (Verbosity() >= 2) { \
+ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \
+ asan_ctxt, pthread_self()); \
+ PRINT_CURRENT_STACK(); \
+ } \
+ return REAL(dispatch_x_f)(dq, (void*)asan_ctxt, \
+ asan_dispatch_call_block_and_release); \
+ }
+
+INTERCEPT_DISPATCH_X_F_3(dispatch_async_f)
+INTERCEPT_DISPATCH_X_F_3(dispatch_sync_f)
+INTERCEPT_DISPATCH_X_F_3(dispatch_barrier_async_f)
+
+INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
+ dispatch_queue_t dq, void *ctxt,
+ dispatch_function_t func) {
+ GET_STACK_TRACE_THREAD;
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (Verbosity() >= 2) {
+ Report("dispatch_after_f: %p\n", asan_ctxt);
+ PRINT_CURRENT_STACK();
+ }
+ return REAL(dispatch_after_f)(when, dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
+ dispatch_queue_t dq, void *ctxt,
+ dispatch_function_t func) {
+ GET_STACK_TRACE_THREAD;
+ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack);
+ if (Verbosity() >= 2) {
+ Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n",
+ asan_ctxt, pthread_self());
+ PRINT_CURRENT_STACK();
+ }
+ REAL(dispatch_group_async_f)(group, dq, (void*)asan_ctxt,
+ asan_dispatch_call_block_and_release);
+}
+
+#if !defined(MISSING_BLOCKS_SUPPORT)
+extern "C" {
+void dispatch_async(dispatch_queue_t dq, void(^work)(void));
+void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
+ void(^work)(void));
+void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
+ void(^work)(void));
+void dispatch_source_set_cancel_handler(dispatch_source_t ds,
+ void(^work)(void));
+void dispatch_source_set_event_handler(dispatch_source_t ds, void(^work)(void));
+}
+
+#define GET_ASAN_BLOCK(work) \
+ void (^asan_block)(void); \
+ int parent_tid = GetCurrentTidOrInvalid(); \
+ asan_block = ^(void) { \
+ GET_STACK_TRACE_THREAD; \
+ asan_register_worker_thread(parent_tid, &stack); \
+ work(); \
+ }
+
+INTERCEPTOR(void, dispatch_async,
+ dispatch_queue_t dq, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_async)(dq, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_group_async,
+ dispatch_group_t dg, dispatch_queue_t dq, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_group_async)(dg, dq, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_after,
+ dispatch_time_t when, dispatch_queue_t queue, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_after)(when, queue, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_source_set_cancel_handler,
+ dispatch_source_t ds, void(^work)(void)) {
+ if (!work) {
+ REAL(dispatch_source_set_cancel_handler)(ds, work);
+ return;
+ }
+ ENABLE_FRAME_POINTER;
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_source_set_cancel_handler)(ds, asan_block);
+}
+
+INTERCEPTOR(void, dispatch_source_set_event_handler,
+ dispatch_source_t ds, void(^work)(void)) {
+ ENABLE_FRAME_POINTER;
+ GET_ASAN_BLOCK(work);
+ REAL(dispatch_source_set_event_handler)(ds, asan_block);
+}
+#endif
+
+#endif // SANITIZER_MAC
+++ /dev/null
-//===-- asan_malloc_linux.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Linux-specific malloc interception.
-// We simply define functions like malloc, free, realloc, etc.
-// They will replace the corresponding libc functions automagically.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \
- SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS
-
-#include "sanitizer_common/sanitizer_allocator_checks.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
-#include "asan_allocator.h"
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_malloc_local.h"
-#include "asan_stack.h"
-
-// ---------------------- Replacement functions ---------------- {{{1
-using namespace __asan; // NOLINT
-
-static uptr allocated_for_dlsym;
-static uptr last_dlsym_alloc_size_in_words;
-static const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024;
-static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
-
-static INLINE bool IsInDlsymAllocPool(const void *ptr) {
- uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
- return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]);
-}
-
-static void *AllocateFromLocalPool(uptr size_in_bytes) {
- uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
- void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym];
- last_dlsym_alloc_size_in_words = size_in_words;
- allocated_for_dlsym += size_in_words;
- CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
- return mem;
-}
-
-static void DeallocateFromLocalPool(const void *ptr) {
- // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store
- // error messages and instead uses malloc followed by free. To avoid pool
- // exhaustion due to long object filenames, handle that special case here.
- uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words;
- void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset];
- if (prev_mem == ptr) {
- REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize);
- allocated_for_dlsym = prev_offset;
- last_dlsym_alloc_size_in_words = 0;
- }
-}
-
-static int PosixMemalignFromLocalPool(void **memptr, uptr alignment,
- uptr size_in_bytes) {
- if (UNLIKELY(!CheckPosixMemalignAlignment(alignment)))
- return errno_EINVAL;
-
- CHECK(alignment >= kWordSize);
-
- uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym];
- uptr aligned_addr = RoundUpTo(addr, alignment);
- uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize);
-
- uptr *end_mem = (uptr*)(aligned_addr + aligned_size);
- uptr allocated = end_mem - alloc_memory_for_dlsym;
- if (allocated >= kDlsymAllocPoolSize)
- return errno_ENOMEM;
-
- allocated_for_dlsym = allocated;
- *memptr = (void*)aligned_addr;
- return 0;
-}
-
-#if SANITIZER_RTEMS
-void* MemalignFromLocalPool(uptr alignment, uptr size) {
- void *ptr = nullptr;
- alignment = Max(alignment, kWordSize);
- PosixMemalignFromLocalPool(&ptr, alignment, size);
- return ptr;
-}
-
-bool IsFromLocalPool(const void *ptr) {
- return IsInDlsymAllocPool(ptr);
-}
-#endif
-
-static INLINE bool MaybeInDlsym() {
- // Fuchsia doesn't use dlsym-based interceptors.
- return !SANITIZER_FUCHSIA && asan_init_is_running;
-}
-
-static INLINE bool UseLocalPool() {
- return EarlyMalloc() || MaybeInDlsym();
-}
-
-static void *ReallocFromLocalPool(void *ptr, uptr size) {
- const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
- const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
- void *new_ptr;
- if (UNLIKELY(UseLocalPool())) {
- new_ptr = AllocateFromLocalPool(size);
- } else {
- ENSURE_ASAN_INITED();
- GET_STACK_TRACE_MALLOC;
- new_ptr = asan_malloc(size, &stack);
- }
- internal_memcpy(new_ptr, ptr, copy_size);
- return new_ptr;
-}
-
-INTERCEPTOR(void, free, void *ptr) {
- GET_STACK_TRACE_FREE;
- if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
- DeallocateFromLocalPool(ptr);
- return;
- }
- asan_free(ptr, &stack, FROM_MALLOC);
-}
-
-#if SANITIZER_INTERCEPT_CFREE
-INTERCEPTOR(void, cfree, void *ptr) {
- GET_STACK_TRACE_FREE;
- if (UNLIKELY(IsInDlsymAllocPool(ptr)))
- return;
- asan_free(ptr, &stack, FROM_MALLOC);
-}
-#endif // SANITIZER_INTERCEPT_CFREE
-
-INTERCEPTOR(void*, malloc, uptr size) {
- if (UNLIKELY(UseLocalPool()))
- // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
- return AllocateFromLocalPool(size);
- ENSURE_ASAN_INITED();
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
-}
-
-INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
- if (UNLIKELY(UseLocalPool()))
- // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
- return AllocateFromLocalPool(nmemb * size);
- ENSURE_ASAN_INITED();
- GET_STACK_TRACE_MALLOC;
- return asan_calloc(nmemb, size, &stack);
-}
-
-INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
- if (UNLIKELY(IsInDlsymAllocPool(ptr)))
- return ReallocFromLocalPool(ptr, size);
- if (UNLIKELY(UseLocalPool()))
- return AllocateFromLocalPool(size);
- ENSURE_ASAN_INITED();
- GET_STACK_TRACE_MALLOC;
- return asan_realloc(ptr, size, &stack);
-}
-
-#if SANITIZER_INTERCEPT_MEMALIGN
-INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
- GET_STACK_TRACE_MALLOC;
- return asan_memalign(boundary, size, &stack, FROM_MALLOC);
-}
-
-INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
- GET_STACK_TRACE_MALLOC;
- void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
- DTLS_on_libc_memalign(res, size);
- return res;
-}
-#endif // SANITIZER_INTERCEPT_MEMALIGN
-
-#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
-INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
- GET_STACK_TRACE_MALLOC;
- return asan_aligned_alloc(boundary, size, &stack);
-}
-#endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC
-
-INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
- GET_CURRENT_PC_BP_SP;
- (void)sp;
- return asan_malloc_usable_size(ptr, pc, bp);
-}
-
-#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
-// We avoid including malloc.h for portability reasons.
-// man mallinfo says the fields are "long", but the implementation uses int.
-// It doesn't matter much -- we just need to make sure that the libc's mallinfo
-// is not called.
-struct fake_mallinfo {
- int x[10];
-};
-
-INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
- struct fake_mallinfo res;
- REAL(memset)(&res, 0, sizeof(res));
- return res;
-}
-
-INTERCEPTOR(int, mallopt, int cmd, int value) {
- return -1;
-}
-#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
-
-INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
- if (UNLIKELY(UseLocalPool()))
- return PosixMemalignFromLocalPool(memptr, alignment, size);
- GET_STACK_TRACE_MALLOC;
- return asan_posix_memalign(memptr, alignment, size, &stack);
-}
-
-INTERCEPTOR(void*, valloc, uptr size) {
- GET_STACK_TRACE_MALLOC;
- return asan_valloc(size, &stack);
-}
-
-#if SANITIZER_INTERCEPT_PVALLOC
-INTERCEPTOR(void*, pvalloc, uptr size) {
- GET_STACK_TRACE_MALLOC;
- return asan_pvalloc(size, &stack);
-}
-#endif // SANITIZER_INTERCEPT_PVALLOC
-
-INTERCEPTOR(void, malloc_stats, void) {
- __asan_print_accumulated_stats();
-}
-
-#if SANITIZER_ANDROID
-// Format of __libc_malloc_dispatch has changed in Android L.
-// While we are moving towards a solution that does not depend on bionic
-// internals, here is something to support both K* and L releases.
-struct MallocDebugK {
- void *(*malloc)(uptr bytes);
- void (*free)(void *mem);
- void *(*calloc)(uptr n_elements, uptr elem_size);
- void *(*realloc)(void *oldMem, uptr bytes);
- void *(*memalign)(uptr alignment, uptr bytes);
- uptr (*malloc_usable_size)(void *mem);
-};
-
-struct MallocDebugL {
- void *(*calloc)(uptr n_elements, uptr elem_size);
- void (*free)(void *mem);
- fake_mallinfo (*mallinfo)(void);
- void *(*malloc)(uptr bytes);
- uptr (*malloc_usable_size)(void *mem);
- void *(*memalign)(uptr alignment, uptr bytes);
- int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
- void* (*pvalloc)(uptr size);
- void *(*realloc)(void *oldMem, uptr bytes);
- void* (*valloc)(uptr size);
-};
-
-ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
- WRAP(malloc), WRAP(free), WRAP(calloc),
- WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
-
-ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
- WRAP(calloc), WRAP(free), WRAP(mallinfo),
- WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
- WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
- WRAP(valloc)};
-
-namespace __asan {
-void ReplaceSystemMalloc() {
- void **__libc_malloc_dispatch_p =
- (void **)AsanDlSymNext("__libc_malloc_dispatch");
- if (__libc_malloc_dispatch_p) {
- // Decide on K vs L dispatch format by the presence of
- // __libc_malloc_default_dispatch export in libc.
- void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
- if (default_dispatch_p)
- *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
- else
- *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
- }
-}
-} // namespace __asan
-
-#else // SANITIZER_ANDROID
-
-namespace __asan {
-void ReplaceSystemMalloc() {
-}
-} // namespace __asan
-#endif // SANITIZER_ANDROID
-
-#endif // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX ||
- // SANITIZER_NETBSD || SANITIZER_SOLARIS
--- /dev/null
+//===-- asan_malloc_linux.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Linux-specific malloc interception.
+// We simply define functions like malloc, free, realloc, etc.
+// They will replace the corresponding libc functions automagically.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \
+ SANITIZER_NETBSD || SANITIZER_RTEMS || SANITIZER_SOLARIS
+
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_malloc_local.h"
+#include "asan_stack.h"
+
+// ---------------------- Replacement functions ---------------- {{{1
+using namespace __asan; // NOLINT
+
+static uptr allocated_for_dlsym;
+static uptr last_dlsym_alloc_size_in_words;
+static const uptr kDlsymAllocPoolSize = SANITIZER_RTEMS ? 4096 : 1024;
+static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
+
+static INLINE bool IsInDlsymAllocPool(const void *ptr) {
+ uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+ return off < allocated_for_dlsym * sizeof(alloc_memory_for_dlsym[0]);
+}
+
+static void *AllocateFromLocalPool(uptr size_in_bytes) {
+ uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
+ void *mem = (void*)&alloc_memory_for_dlsym[allocated_for_dlsym];
+ last_dlsym_alloc_size_in_words = size_in_words;
+ allocated_for_dlsym += size_in_words;
+ CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
+ return mem;
+}
+
+static void DeallocateFromLocalPool(const void *ptr) {
+ // Hack: since glibc 2.27 dlsym no longer uses stack-allocated memory to store
+ // error messages and instead uses malloc followed by free. To avoid pool
+ // exhaustion due to long object filenames, handle that special case here.
+ uptr prev_offset = allocated_for_dlsym - last_dlsym_alloc_size_in_words;
+ void *prev_mem = (void*)&alloc_memory_for_dlsym[prev_offset];
+ if (prev_mem == ptr) {
+ REAL(memset)(prev_mem, 0, last_dlsym_alloc_size_in_words * kWordSize);
+ allocated_for_dlsym = prev_offset;
+ last_dlsym_alloc_size_in_words = 0;
+ }
+}
+
+static int PosixMemalignFromLocalPool(void **memptr, uptr alignment,
+ uptr size_in_bytes) {
+ if (UNLIKELY(!CheckPosixMemalignAlignment(alignment)))
+ return errno_EINVAL;
+
+ CHECK(alignment >= kWordSize);
+
+ uptr addr = (uptr)&alloc_memory_for_dlsym[allocated_for_dlsym];
+ uptr aligned_addr = RoundUpTo(addr, alignment);
+ uptr aligned_size = RoundUpTo(size_in_bytes, kWordSize);
+
+ uptr *end_mem = (uptr*)(aligned_addr + aligned_size);
+ uptr allocated = end_mem - alloc_memory_for_dlsym;
+ if (allocated >= kDlsymAllocPoolSize)
+ return errno_ENOMEM;
+
+ allocated_for_dlsym = allocated;
+ *memptr = (void*)aligned_addr;
+ return 0;
+}
+
+#if SANITIZER_RTEMS
+void* MemalignFromLocalPool(uptr alignment, uptr size) {
+ void *ptr = nullptr;
+ alignment = Max(alignment, kWordSize);
+ PosixMemalignFromLocalPool(&ptr, alignment, size);
+ return ptr;
+}
+
+bool IsFromLocalPool(const void *ptr) {
+ return IsInDlsymAllocPool(ptr);
+}
+#endif
+
+static INLINE bool MaybeInDlsym() {
+ // Fuchsia doesn't use dlsym-based interceptors.
+ return !SANITIZER_FUCHSIA && asan_init_is_running;
+}
+
+static INLINE bool UseLocalPool() {
+ return EarlyMalloc() || MaybeInDlsym();
+}
+
+static void *ReallocFromLocalPool(void *ptr, uptr size) {
+ const uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
+ const uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
+ void *new_ptr;
+ if (UNLIKELY(UseLocalPool())) {
+ new_ptr = AllocateFromLocalPool(size);
+ } else {
+ ENSURE_ASAN_INITED();
+ GET_STACK_TRACE_MALLOC;
+ new_ptr = asan_malloc(size, &stack);
+ }
+ internal_memcpy(new_ptr, ptr, copy_size);
+ return new_ptr;
+}
+
+INTERCEPTOR(void, free, void *ptr) {
+ GET_STACK_TRACE_FREE;
+ if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
+ DeallocateFromLocalPool(ptr);
+ return;
+ }
+ asan_free(ptr, &stack, FROM_MALLOC);
+}
+
+#if SANITIZER_INTERCEPT_CFREE
+INTERCEPTOR(void, cfree, void *ptr) {
+ GET_STACK_TRACE_FREE;
+ if (UNLIKELY(IsInDlsymAllocPool(ptr)))
+ return;
+ asan_free(ptr, &stack, FROM_MALLOC);
+}
+#endif // SANITIZER_INTERCEPT_CFREE
+
+INTERCEPTOR(void*, malloc, uptr size) {
+ if (UNLIKELY(UseLocalPool()))
+ // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
+ return AllocateFromLocalPool(size);
+ ENSURE_ASAN_INITED();
+ GET_STACK_TRACE_MALLOC;
+ return asan_malloc(size, &stack);
+}
+
+INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
+ if (UNLIKELY(UseLocalPool()))
+ // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
+ return AllocateFromLocalPool(nmemb * size);
+ ENSURE_ASAN_INITED();
+ GET_STACK_TRACE_MALLOC;
+ return asan_calloc(nmemb, size, &stack);
+}
+
+INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
+ if (UNLIKELY(IsInDlsymAllocPool(ptr)))
+ return ReallocFromLocalPool(ptr, size);
+ if (UNLIKELY(UseLocalPool()))
+ return AllocateFromLocalPool(size);
+ ENSURE_ASAN_INITED();
+ GET_STACK_TRACE_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+}
+
+#if SANITIZER_INTERCEPT_REALLOCARRAY
+INTERCEPTOR(void*, reallocarray, void *ptr, uptr nmemb, uptr size) {
+ ENSURE_ASAN_INITED();
+ GET_STACK_TRACE_MALLOC;
+ return asan_reallocarray(ptr, nmemb, size, &stack);
+}
+#endif // SANITIZER_INTERCEPT_REALLOCARRAY
+
+#if SANITIZER_INTERCEPT_MEMALIGN
+INTERCEPTOR(void*, memalign, uptr boundary, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_memalign(boundary, size, &stack, FROM_MALLOC);
+}
+
+INTERCEPTOR(void*, __libc_memalign, uptr boundary, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ void *res = asan_memalign(boundary, size, &stack, FROM_MALLOC);
+ DTLS_on_libc_memalign(res, size);
+ return res;
+}
+#endif // SANITIZER_INTERCEPT_MEMALIGN
+
+#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
+INTERCEPTOR(void*, aligned_alloc, uptr boundary, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_aligned_alloc(boundary, size, &stack);
+}
+#endif // SANITIZER_INTERCEPT_ALIGNED_ALLOC
+
+INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ return asan_malloc_usable_size(ptr, pc, bp);
+}
+
+#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
+// We avoid including malloc.h for portability reasons.
+// man mallinfo says the fields are "long", but the implementation uses int.
+// It doesn't matter much -- we just need to make sure that the libc's mallinfo
+// is not called.
+struct fake_mallinfo {
+ int x[10];
+};
+
+INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
+ struct fake_mallinfo res;
+ REAL(memset)(&res, 0, sizeof(res));
+ return res;
+}
+
+INTERCEPTOR(int, mallopt, int cmd, int value) {
+ return 0;
+}
+#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
+
+INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
+ if (UNLIKELY(UseLocalPool()))
+ return PosixMemalignFromLocalPool(memptr, alignment, size);
+ GET_STACK_TRACE_MALLOC;
+ return asan_posix_memalign(memptr, alignment, size, &stack);
+}
+
+INTERCEPTOR(void*, valloc, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_valloc(size, &stack);
+}
+
+#if SANITIZER_INTERCEPT_PVALLOC
+INTERCEPTOR(void*, pvalloc, uptr size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_pvalloc(size, &stack);
+}
+#endif // SANITIZER_INTERCEPT_PVALLOC
+
+INTERCEPTOR(void, malloc_stats, void) {
+ __asan_print_accumulated_stats();
+}
+
+#if SANITIZER_ANDROID
+// Format of __libc_malloc_dispatch has changed in Android L.
+// While we are moving towards a solution that does not depend on bionic
+// internals, here is something to support both K* and L releases.
+struct MallocDebugK {
+ void *(*malloc)(uptr bytes);
+ void (*free)(void *mem);
+ void *(*calloc)(uptr n_elements, uptr elem_size);
+ void *(*realloc)(void *oldMem, uptr bytes);
+ void *(*memalign)(uptr alignment, uptr bytes);
+ uptr (*malloc_usable_size)(void *mem);
+};
+
+struct MallocDebugL {
+ void *(*calloc)(uptr n_elements, uptr elem_size);
+ void (*free)(void *mem);
+ fake_mallinfo (*mallinfo)(void);
+ void *(*malloc)(uptr bytes);
+ uptr (*malloc_usable_size)(void *mem);
+ void *(*memalign)(uptr alignment, uptr bytes);
+ int (*posix_memalign)(void **memptr, uptr alignment, uptr size);
+ void* (*pvalloc)(uptr size);
+ void *(*realloc)(void *oldMem, uptr bytes);
+ void* (*valloc)(uptr size);
+};
+
+ALIGNED(32) const MallocDebugK asan_malloc_dispatch_k = {
+ WRAP(malloc), WRAP(free), WRAP(calloc),
+ WRAP(realloc), WRAP(memalign), WRAP(malloc_usable_size)};
+
+ALIGNED(32) const MallocDebugL asan_malloc_dispatch_l = {
+ WRAP(calloc), WRAP(free), WRAP(mallinfo),
+ WRAP(malloc), WRAP(malloc_usable_size), WRAP(memalign),
+ WRAP(posix_memalign), WRAP(pvalloc), WRAP(realloc),
+ WRAP(valloc)};
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+ void **__libc_malloc_dispatch_p =
+ (void **)AsanDlSymNext("__libc_malloc_dispatch");
+ if (__libc_malloc_dispatch_p) {
+ // Decide on K vs L dispatch format by the presence of
+ // __libc_malloc_default_dispatch export in libc.
+ void *default_dispatch_p = AsanDlSymNext("__libc_malloc_default_dispatch");
+ if (default_dispatch_p)
+ *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_k;
+ else
+ *__libc_malloc_dispatch_p = (void *)&asan_malloc_dispatch_l;
+ }
+}
+} // namespace __asan
+
+#else // SANITIZER_ANDROID
+
+namespace __asan {
+void ReplaceSystemMalloc() {
+}
+} // namespace __asan
+#endif // SANITIZER_ANDROID
+
+#endif // SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX ||
+ // SANITIZER_NETBSD || SANITIZER_SOLARIS
//===-- asan_malloc_local.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#include "sanitizer_common/sanitizer_platform.h"
#include "asan_internal.h"
-// On RTEMS, we use the local pool to handle memory allocation when the ASan
-// run-time is not up.
static INLINE bool EarlyMalloc() {
- return SANITIZER_RTEMS && (!__asan::asan_inited ||
- __asan::asan_init_is_running);
+ return SANITIZER_RTEMS &&
+ (!__asan::asan_inited || __asan::asan_init_is_running);
}
-void* MemalignFromLocalPool(uptr alignment, uptr size);
-
#if SANITIZER_RTEMS
bool IsFromLocalPool(const void *ptr);
+void *MemalignFromLocalPool(uptr alignment, uptr size);
+
+// On RTEMS, we use the local pool to handle memory allocation when the ASan
+// run-time is not up. This macro is expanded in the context of the operator new
+// implementation.
+#define MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow) \
+ do { \
+ if (UNLIKELY(EarlyMalloc())) { \
+ void *res = MemalignFromLocalPool(SHADOW_GRANULARITY, size); \
+ if (!nothrow) \
+ CHECK(res); \
+ return res; \
+ } \
+ } while (0)
-#define ALLOCATE_FROM_LOCAL_POOL UNLIKELY(EarlyMalloc())
#define IS_FROM_LOCAL_POOL(ptr) UNLIKELY(IsFromLocalPool(ptr))
#else // SANITIZER_RTEMS
-#define ALLOCATE_FROM_LOCAL_POOL 0
+#define MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow)
#define IS_FROM_LOCAL_POOL(ptr) 0
#endif // SANITIZER_RTEMS
+++ /dev/null
-//===-- asan_malloc_mac.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Mac-specific malloc interception.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "asan_interceptors.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_stats.h"
-
-using namespace __asan;
-#define COMMON_MALLOC_ZONE_NAME "asan"
-#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED()
-#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited
-#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock()
-#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock()
-#define COMMON_MALLOC_MEMALIGN(alignment, size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC)
-#define COMMON_MALLOC_MALLOC(size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = asan_malloc(size, &stack)
-#define COMMON_MALLOC_REALLOC(ptr, size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = asan_realloc(ptr, size, &stack);
-#define COMMON_MALLOC_CALLOC(count, size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = asan_calloc(count, size, &stack);
-#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \
- GET_STACK_TRACE_MALLOC; \
- int res = asan_posix_memalign(memptr, alignment, size, &stack);
-#define COMMON_MALLOC_VALLOC(size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
-#define COMMON_MALLOC_FREE(ptr) \
- GET_STACK_TRACE_FREE; \
- asan_free(ptr, &stack, FROM_MALLOC);
-#define COMMON_MALLOC_SIZE(ptr) \
- uptr size = asan_mz_size(ptr);
-#define COMMON_MALLOC_FILL_STATS(zone, stats) \
- AsanMallocStats malloc_stats; \
- FillMallocStatistics(&malloc_stats); \
- CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \
- internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
-#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
- GET_STACK_TRACE_FREE; \
- ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
-#define COMMON_MALLOC_NAMESPACE __asan
-
-#include "sanitizer_common/sanitizer_malloc_mac.inc"
-
-#endif
--- /dev/null
+//===-- asan_malloc_mac.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "asan_interceptors.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "lsan/lsan_common.h"
+
+using namespace __asan;
+#define COMMON_MALLOC_ZONE_NAME "asan"
+#define COMMON_MALLOC_ENTER() ENSURE_ASAN_INITED()
+#define COMMON_MALLOC_SANITIZER_INITIALIZED asan_inited
+#define COMMON_MALLOC_FORCE_LOCK() asan_mz_force_lock()
+#define COMMON_MALLOC_FORCE_UNLOCK() asan_mz_force_unlock()
+#define COMMON_MALLOC_MEMALIGN(alignment, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_memalign(alignment, size, &stack, FROM_MALLOC)
+#define COMMON_MALLOC_MALLOC(size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_malloc(size, &stack)
+#define COMMON_MALLOC_REALLOC(ptr, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_realloc(ptr, size, &stack);
+#define COMMON_MALLOC_CALLOC(count, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_calloc(count, size, &stack);
+#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \
+ GET_STACK_TRACE_MALLOC; \
+ int res = asan_posix_memalign(memptr, alignment, size, &stack);
+#define COMMON_MALLOC_VALLOC(size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
+#define COMMON_MALLOC_FREE(ptr) \
+ GET_STACK_TRACE_FREE; \
+ asan_free(ptr, &stack, FROM_MALLOC);
+#define COMMON_MALLOC_SIZE(ptr) \
+ uptr size = asan_mz_size(ptr);
+#define COMMON_MALLOC_FILL_STATS(zone, stats) \
+ AsanMallocStats malloc_stats; \
+ FillMallocStatistics(&malloc_stats); \
+ CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats)); \
+ internal_memcpy(stats, &malloc_stats, sizeof(malloc_statistics_t));
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
+ GET_STACK_TRACE_FREE; \
+ ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
+#define COMMON_MALLOC_NAMESPACE __asan
+#define COMMON_MALLOC_HAS_ZONE_ENUMERATOR 0
+#define COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT 1
+
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
+
+namespace COMMON_MALLOC_NAMESPACE {
+
+bool HandleDlopenInit() {
+ static_assert(SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be true");
+ // We have no reliable way of knowing how we are being loaded
+ // so make it a requirement on Apple platforms to set this environment
+ // variable to indicate that we want to perform initialization via
+ // dlopen().
+ auto init_str = GetEnv("APPLE_ASAN_INIT_FOR_DLOPEN");
+ if (!init_str)
+ return false;
+ if (internal_strncmp(init_str, "1", 1) != 0)
+ return false;
+ // When we are loaded via `dlopen()` path we still initialize the malloc zone
+ // so Symbolication clients (e.g. `leaks`) that load the ASan allocator can
+ // find an initialized malloc zone.
+ InitMallocZoneFields();
+ return true;
+}
+} // namespace COMMON_MALLOC_NAMESPACE
+
+namespace {
+
+void mi_extra_init(sanitizer_malloc_introspection_t *mi) {
+ uptr last_byte_plus_one = 0;
+ mi->allocator_ptr = 0;
+ // Range is [begin_ptr, end_ptr)
+ __lsan::GetAllocatorGlobalRange(&(mi->allocator_ptr), &last_byte_plus_one);
+ CHECK_NE(mi->allocator_ptr, 0);
+ CHECK_GT(last_byte_plus_one, mi->allocator_ptr);
+ mi->allocator_size = last_byte_plus_one - (mi->allocator_ptr);
+ CHECK_GT(mi->allocator_size, 0);
+}
+} // namespace
+
+#endif
+++ /dev/null
-//===-- asan_malloc_win.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Windows-specific malloc interception.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-// Intentionally not including windows.h here, to avoid the risk of
-// pulling in conflicting declarations of these functions. (With mingw-w64,
-// there's a risk of windows.h pulling in stdint.h.)
-typedef int BOOL;
-typedef void *HANDLE;
-typedef const void *LPCVOID;
-typedef void *LPVOID;
-
-#define HEAP_ZERO_MEMORY 0x00000008
-#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010
-
-
-#include "asan_allocator.h"
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_stack.h"
-#include "interception/interception.h"
-
-#include <stddef.h>
-
-using namespace __asan; // NOLINT
-
-// MT: Simply defining functions with the same signature in *.obj
-// files overrides the standard functions in the CRT.
-// MD: Memory allocation functions are defined in the CRT .dll,
-// so we have to intercept them before they are called for the first time.
-
-#if ASAN_DYNAMIC
-# define ALLOCATION_FUNCTION_ATTRIBUTE
-#else
-# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
-#endif
-
-extern "C" {
-ALLOCATION_FUNCTION_ATTRIBUTE
-void free(void *ptr) {
- GET_STACK_TRACE_FREE;
- return asan_free(ptr, &stack, FROM_MALLOC);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void _free_dbg(void *ptr, int) {
- free(ptr);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void _free_base(void *ptr) {
- free(ptr);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *malloc(size_t size) {
- GET_STACK_TRACE_MALLOC;
- return asan_malloc(size, &stack);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_malloc_base(size_t size) {
- return malloc(size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_malloc_dbg(size_t size, int, const char *, int) {
- return malloc(size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *calloc(size_t nmemb, size_t size) {
- GET_STACK_TRACE_MALLOC;
- return asan_calloc(nmemb, size, &stack);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_calloc_base(size_t nmemb, size_t size) {
- return calloc(nmemb, size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
- return calloc(nmemb, size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
- return calloc(nmemb, size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *realloc(void *ptr, size_t size) {
- GET_STACK_TRACE_MALLOC;
- return asan_realloc(ptr, size, &stack);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_realloc_dbg(void *ptr, size_t size, int) {
- UNREACHABLE("_realloc_dbg should not exist!");
- return 0;
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_realloc_base(void *ptr, size_t size) {
- return realloc(ptr, size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_recalloc(void *p, size_t n, size_t elem_size) {
- if (!p)
- return calloc(n, elem_size);
- const size_t size = n * elem_size;
- if (elem_size != 0 && size / elem_size != n)
- return 0;
- return realloc(p, size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_recalloc_base(void *p, size_t n, size_t elem_size) {
- return _recalloc(p, n, elem_size);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize(void *ptr) {
- GET_CURRENT_PC_BP_SP;
- (void)sp;
- return asan_malloc_usable_size(ptr, pc, bp);
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_expand(void *memblock, size_t size) {
- // _expand is used in realloc-like functions to resize the buffer if possible.
- // We don't want memory to stand still while resizing buffers, so return 0.
- return 0;
-}
-
-ALLOCATION_FUNCTION_ATTRIBUTE
-void *_expand_dbg(void *memblock, size_t size) {
- return _expand(memblock, size);
-}
-
-// TODO(timurrrr): Might want to add support for _aligned_* allocation
-// functions to detect a bit more bugs. Those functions seem to wrap malloc().
-
-int _CrtDbgReport(int, const char*, int,
- const char*, const char*, ...) {
- ShowStatsAndAbort();
-}
-
-int _CrtDbgReportW(int reportType, const wchar_t*, int,
- const wchar_t*, const wchar_t*, ...) {
- ShowStatsAndAbort();
-}
-
-int _CrtSetReportMode(int, int) {
- return 0;
-}
-} // extern "C"
-
-INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
- SIZE_T dwBytes) {
- GET_STACK_TRACE_MALLOC;
- void *p = asan_malloc(dwBytes, &stack);
- // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
- // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
- // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
- if (dwFlags == HEAP_ZERO_MEMORY)
- internal_memset(p, 0, asan_mz_size(p));
- else
- CHECK(dwFlags == 0 && "unsupported heap flags");
- return p;
-}
-
-INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
- CHECK(dwFlags == 0 && "unsupported heap flags");
- GET_STACK_TRACE_FREE;
- asan_free(lpMem, &stack, FROM_MALLOC);
- return true;
-}
-
-INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
- LPVOID lpMem, SIZE_T dwBytes) {
- GET_STACK_TRACE_MALLOC;
- // Realloc should never reallocate in place.
- if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
- return nullptr;
- CHECK(dwFlags == 0 && "unsupported heap flags");
- return asan_realloc(lpMem, dwBytes, &stack);
-}
-
-INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
- LPCVOID lpMem) {
- CHECK(dwFlags == 0 && "unsupported heap flags");
- GET_CURRENT_PC_BP_SP;
- (void)sp;
- return asan_malloc_usable_size(lpMem, pc, bp);
-}
-
-namespace __asan {
-
-static void TryToOverrideFunction(const char *fname, uptr new_func) {
- // Failure here is not fatal. The CRT may not be present, and different CRT
- // versions use different symbols.
- if (!__interception::OverrideFunction(fname, new_func))
- VPrintf(2, "Failed to override function %s\n", fname);
-}
-
-void ReplaceSystemMalloc() {
-#if defined(ASAN_DYNAMIC)
- TryToOverrideFunction("free", (uptr)free);
- TryToOverrideFunction("_free_base", (uptr)free);
- TryToOverrideFunction("malloc", (uptr)malloc);
- TryToOverrideFunction("_malloc_base", (uptr)malloc);
- TryToOverrideFunction("_malloc_crt", (uptr)malloc);
- TryToOverrideFunction("calloc", (uptr)calloc);
- TryToOverrideFunction("_calloc_base", (uptr)calloc);
- TryToOverrideFunction("_calloc_crt", (uptr)calloc);
- TryToOverrideFunction("realloc", (uptr)realloc);
- TryToOverrideFunction("_realloc_base", (uptr)realloc);
- TryToOverrideFunction("_realloc_crt", (uptr)realloc);
- TryToOverrideFunction("_recalloc", (uptr)_recalloc);
- TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
- TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
- TryToOverrideFunction("_msize", (uptr)_msize);
- TryToOverrideFunction("_expand", (uptr)_expand);
- TryToOverrideFunction("_expand_base", (uptr)_expand);
-
- // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
- // enable cross-module inlining. This means our _malloc_base hook won't catch
- // all CRT allocations. This code here patches the import table of
- // ucrtbase.dll so that all attempts to use the lower-level win32 heap
- // allocation API will be directed to ASan's heap. We don't currently
- // intercept all calls to HeapAlloc. If we did, we would have to check on
- // HeapFree whether the pointer came from ASan of from the system.
-#define INTERCEPT_UCRT_FUNCTION(func) \
- if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \
- "api-ms-win-core-heap-l1-1-0.dll", func)) \
- VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
- INTERCEPT_UCRT_FUNCTION(HeapAlloc);
- INTERCEPT_UCRT_FUNCTION(HeapFree);
- INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
- INTERCEPT_UCRT_FUNCTION(HeapSize);
-#undef INTERCEPT_UCRT_FUNCTION
-#endif
-}
-} // namespace __asan
-
-#endif // _WIN32
--- /dev/null
+//===-- asan_malloc_win.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_stack.h"
+#include "interception/interception.h"
+#include <stddef.h>
+
+// Intentionally not including windows.h here, to avoid the risk of
+// pulling in conflicting declarations of these functions. (With mingw-w64,
+// there's a risk of windows.h pulling in stdint.h.)
+typedef int BOOL;
+typedef void *HANDLE;
+typedef const void *LPCVOID;
+typedef void *LPVOID;
+
+typedef unsigned long DWORD;
+constexpr unsigned long HEAP_ZERO_MEMORY = 0x00000008;
+constexpr unsigned long HEAP_REALLOC_IN_PLACE_ONLY = 0x00000010;
+constexpr unsigned long HEAP_ALLOCATE_SUPPORTED_FLAGS = (HEAP_ZERO_MEMORY);
+constexpr unsigned long HEAP_ALLOCATE_UNSUPPORTED_FLAGS =
+ (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
+constexpr unsigned long HEAP_FREE_SUPPORTED_FLAGS = (0);
+constexpr unsigned long HEAP_FREE_UNSUPPORTED_FLAGS =
+ (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
+constexpr unsigned long HEAP_REALLOC_SUPPORTED_FLAGS =
+ (HEAP_REALLOC_IN_PLACE_ONLY | HEAP_ZERO_MEMORY);
+constexpr unsigned long HEAP_REALLOC_UNSUPPORTED_FLAGS =
+ (~HEAP_ALLOCATE_SUPPORTED_FLAGS);
+
+
+extern "C" {
+LPVOID WINAPI HeapAlloc(HANDLE hHeap, DWORD dwFlags, size_t dwBytes);
+LPVOID WINAPI HeapReAlloc(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem,
+ size_t dwBytes);
+BOOL WINAPI HeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
+size_t WINAPI HeapSize(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+
+BOOL WINAPI HeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
+}
+
+using namespace __asan; // NOLINT
+
+// MT: Simply defining functions with the same signature in *.obj
+// files overrides the standard functions in the CRT.
+// MD: Memory allocation functions are defined in the CRT .dll,
+// so we have to intercept them before they are called for the first time.
+
+#if ASAN_DYNAMIC
+# define ALLOCATION_FUNCTION_ATTRIBUTE
+#else
+# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
+#endif
+
+extern "C" {
+ALLOCATION_FUNCTION_ATTRIBUTE
+size_t _msize(void *ptr) {
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ return asan_malloc_usable_size(ptr, pc, bp);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+size_t _msize_base(void *ptr) {
+ return _msize(ptr);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void free(void *ptr) {
+ GET_STACK_TRACE_FREE;
+ return asan_free(ptr, &stack, FROM_MALLOC);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void _free_dbg(void *ptr, int) {
+ free(ptr);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void _free_base(void *ptr) {
+ free(ptr);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *malloc(size_t size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_malloc(size, &stack);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_malloc_base(size_t size) {
+ return malloc(size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_malloc_dbg(size_t size, int, const char *, int) {
+ return malloc(size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *calloc(size_t nmemb, size_t size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_calloc(nmemb, size, &stack);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_calloc_base(size_t nmemb, size_t size) {
+ return calloc(nmemb, size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
+ return calloc(nmemb, size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
+ return calloc(nmemb, size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *realloc(void *ptr, size_t size) {
+ GET_STACK_TRACE_MALLOC;
+ return asan_realloc(ptr, size, &stack);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_realloc_dbg(void *ptr, size_t size, int) {
+ UNREACHABLE("_realloc_dbg should not exist!");
+ return 0;
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_realloc_base(void *ptr, size_t size) {
+ return realloc(ptr, size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_recalloc(void *p, size_t n, size_t elem_size) {
+ if (!p)
+ return calloc(n, elem_size);
+ const size_t size = n * elem_size;
+ if (elem_size != 0 && size / elem_size != n)
+ return 0;
+
+ size_t old_size = _msize(p);
+ void *new_alloc = malloc(size);
+ if (new_alloc) {
+ REAL(memcpy)(new_alloc, p, Min<size_t>(size, old_size));
+ if (old_size < size)
+ REAL(memset)(((u8 *)new_alloc) + old_size, 0, size - old_size);
+ free(p);
+ }
+ return new_alloc;
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_recalloc_base(void *p, size_t n, size_t elem_size) {
+ return _recalloc(p, n, elem_size);
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_expand(void *memblock, size_t size) {
+ // _expand is used in realloc-like functions to resize the buffer if possible.
+ // We don't want memory to stand still while resizing buffers, so return 0.
+ return 0;
+}
+
+ALLOCATION_FUNCTION_ATTRIBUTE
+void *_expand_dbg(void *memblock, size_t size) {
+ return _expand(memblock, size);
+}
+
+// TODO(timurrrr): Might want to add support for _aligned_* allocation
+// functions to detect a bit more bugs. Those functions seem to wrap malloc().
+
+int _CrtDbgReport(int, const char*, int,
+ const char*, const char*, ...) {
+ ShowStatsAndAbort();
+}
+
+int _CrtDbgReportW(int reportType, const wchar_t*, int,
+ const wchar_t*, const wchar_t*, ...) {
+ ShowStatsAndAbort();
+}
+
+int _CrtSetReportMode(int, int) {
+ return 0;
+}
+} // extern "C"
+
+#define OWNED_BY_RTL(heap, memory) \
+ (!__sanitizer_get_ownership(memory) && HeapValidate(heap, 0, memory))
+
+INTERCEPTOR_WINAPI(size_t, HeapSize, HANDLE hHeap, DWORD dwFlags,
+ LPCVOID lpMem) {
+ // If the RTL allocators are hooked we need to check whether the ASAN
+ // allocator owns the pointer we're about to use. Allocations occur before
+ // interception takes place, so if it is not owned by the RTL heap we can
+ // pass it to the ASAN heap for inspection.
+ if (flags()->windows_hook_rtl_allocators) {
+ if (!asan_inited || OWNED_BY_RTL(hHeap, lpMem))
+ return REAL(HeapSize)(hHeap, dwFlags, lpMem);
+ } else {
+ CHECK(dwFlags == 0 && "unsupported heap flags");
+ }
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ return asan_malloc_usable_size(lpMem, pc, bp);
+}
+
+INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
+ size_t dwBytes) {
+ // If the ASAN runtime is not initialized, or we encounter an unsupported
+ // flag, fall back to the original allocator.
+ if (flags()->windows_hook_rtl_allocators) {
+ if (UNLIKELY(!asan_inited ||
+ (dwFlags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
+ return REAL(HeapAlloc)(hHeap, dwFlags, dwBytes);
+ }
+ } else {
+ // In the case that we don't hook the rtl allocators,
+ // this becomes an assert since there is no failover to the original
+ // allocator.
+ CHECK((HEAP_ALLOCATE_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
+ "unsupported flags");
+ }
+ GET_STACK_TRACE_MALLOC;
+ void *p = asan_malloc(dwBytes, &stack);
+ // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
+ // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
+ // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
+ if (p && (dwFlags & HEAP_ZERO_MEMORY)) {
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ auto usable_size = asan_malloc_usable_size(p, pc, bp);
+ internal_memset(p, 0, usable_size);
+ }
+ return p;
+}
+
+INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
+ // Heap allocations happen before this function is hooked, so we must fall
+ // back to the original function if the pointer is not from the ASAN heap,
+ // or unsupported flags are provided.
+ if (flags()->windows_hook_rtl_allocators) {
+ if (OWNED_BY_RTL(hHeap, lpMem))
+ return REAL(HeapFree)(hHeap, dwFlags, lpMem);
+ } else {
+ CHECK((HEAP_FREE_UNSUPPORTED_FLAGS & dwFlags) != 0 && "unsupported flags");
+ }
+ GET_STACK_TRACE_FREE;
+ asan_free(lpMem, &stack, FROM_MALLOC);
+ return true;
+}
+
+namespace __asan {
+using AllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, size_t);
+using ReAllocFunction = LPVOID(WINAPI *)(HANDLE, DWORD, LPVOID, size_t);
+using SizeFunction = size_t(WINAPI *)(HANDLE, DWORD, LPVOID);
+using FreeFunction = BOOL(WINAPI *)(HANDLE, DWORD, LPVOID);
+
+void *SharedReAlloc(ReAllocFunction reallocFunc, SizeFunction heapSizeFunc,
+ FreeFunction freeFunc, AllocFunction allocFunc,
+ HANDLE hHeap, DWORD dwFlags, LPVOID lpMem, size_t dwBytes) {
+ CHECK(reallocFunc && heapSizeFunc && freeFunc && allocFunc);
+ GET_STACK_TRACE_MALLOC;
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ if (flags()->windows_hook_rtl_allocators) {
+ enum AllocationOwnership { NEITHER = 0, ASAN = 1, RTL = 2 };
+ AllocationOwnership ownershipState;
+ bool owned_rtlalloc = false;
+ bool owned_asan = __sanitizer_get_ownership(lpMem);
+
+ if (!owned_asan)
+ owned_rtlalloc = HeapValidate(hHeap, 0, lpMem);
+
+ if (owned_asan && !owned_rtlalloc)
+ ownershipState = ASAN;
+ else if (!owned_asan && owned_rtlalloc)
+ ownershipState = RTL;
+ else if (!owned_asan && !owned_rtlalloc)
+ ownershipState = NEITHER;
+
+ // If this heap block which was allocated before the ASAN
+ // runtime came up, use the real HeapFree function.
+ if (UNLIKELY(!asan_inited)) {
+ return reallocFunc(hHeap, dwFlags, lpMem, dwBytes);
+ }
+ bool only_asan_supported_flags =
+ (HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) == 0;
+
+ if (ownershipState == RTL ||
+ (ownershipState == NEITHER && !only_asan_supported_flags)) {
+ if (only_asan_supported_flags) {
+ // if this is a conversion to ASAN upported flags, transfer this
+ // allocation to the ASAN allocator
+ void *replacement_alloc;
+ if (dwFlags & HEAP_ZERO_MEMORY)
+ replacement_alloc = asan_calloc(1, dwBytes, &stack);
+ else
+ replacement_alloc = asan_malloc(dwBytes, &stack);
+ if (replacement_alloc) {
+ size_t old_size = heapSizeFunc(hHeap, dwFlags, lpMem);
+ if (old_size == ((size_t)0) - 1) {
+ asan_free(replacement_alloc, &stack, FROM_MALLOC);
+ return nullptr;
+ }
+ REAL(memcpy)(replacement_alloc, lpMem, old_size);
+ freeFunc(hHeap, dwFlags, lpMem);
+ }
+ return replacement_alloc;
+ } else {
+ // owned by rtl or neither with unsupported ASAN flags,
+ // just pass back to original allocator
+ CHECK(ownershipState == RTL || ownershipState == NEITHER);
+ CHECK(!only_asan_supported_flags);
+ return reallocFunc(hHeap, dwFlags, lpMem, dwBytes);
+ }
+ }
+
+ if (ownershipState == ASAN && !only_asan_supported_flags) {
+ // Conversion to unsupported flags allocation,
+ // transfer this allocation back to the original allocator.
+ void *replacement_alloc = allocFunc(hHeap, dwFlags, dwBytes);
+ size_t old_usable_size = 0;
+ if (replacement_alloc) {
+ old_usable_size = asan_malloc_usable_size(lpMem, pc, bp);
+ REAL(memcpy)(replacement_alloc, lpMem,
+ Min<size_t>(dwBytes, old_usable_size));
+ asan_free(lpMem, &stack, FROM_MALLOC);
+ }
+ return replacement_alloc;
+ }
+
+ CHECK((ownershipState == ASAN || ownershipState == NEITHER) &&
+ only_asan_supported_flags);
+ // At this point we should either be ASAN owned with ASAN supported flags
+ // or we owned by neither and have supported flags.
+ // Pass through even when it's neither since this could be a null realloc or
+ // UAF that ASAN needs to catch.
+ } else {
+ CHECK((HEAP_REALLOC_UNSUPPORTED_FLAGS & dwFlags) != 0 &&
+ "unsupported flags");
+ }
+ // asan_realloc will never reallocate in place, so for now this flag is
+ // unsupported until we figure out a way to fake this.
+ if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
+ return nullptr;
+
+ // HeapReAlloc and HeapAlloc both happily accept 0 sized allocations.
+ // passing a 0 size into asan_realloc will free the allocation.
+ // To avoid this and keep behavior consistent, fudge the size if 0.
+ // (asan_malloc already does this)
+ if (dwBytes == 0)
+ dwBytes = 1;
+
+ size_t old_size;
+ if (dwFlags & HEAP_ZERO_MEMORY)
+ old_size = asan_malloc_usable_size(lpMem, pc, bp);
+
+ void *ptr = asan_realloc(lpMem, dwBytes, &stack);
+ if (ptr == nullptr)
+ return nullptr;
+
+ if (dwFlags & HEAP_ZERO_MEMORY) {
+ size_t new_size = asan_malloc_usable_size(ptr, pc, bp);
+ if (old_size < new_size)
+ REAL(memset)(((u8 *)ptr) + old_size, 0, new_size - old_size);
+ }
+
+ return ptr;
+}
+} // namespace __asan
+
+INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
+ LPVOID lpMem, size_t dwBytes) {
+ return SharedReAlloc(REAL(HeapReAlloc), (SizeFunction)REAL(HeapSize),
+ REAL(HeapFree), REAL(HeapAlloc), hHeap, dwFlags, lpMem,
+ dwBytes);
+}
+
+// The following functions are undocumented and subject to change.
+// However, hooking them is necessary to hook Windows heap
+// allocations with detours and their definitions are unlikely to change.
+// Comments in /minkernel/ntos/rtl/heappublic.c indicate that these functions
+// are part of the heap's public interface.
+typedef unsigned long LOGICAL;
+
+// This function is documented as part of the Driver Development Kit but *not*
+// the Windows Development Kit.
+LOGICAL RtlFreeHeap(void* HeapHandle, DWORD Flags,
+ void* BaseAddress);
+
+// This function is documented as part of the Driver Development Kit but *not*
+// the Windows Development Kit.
+void* RtlAllocateHeap(void* HeapHandle, DWORD Flags, size_t Size);
+
+// This function is completely undocumented.
+void*
+RtlReAllocateHeap(void* HeapHandle, DWORD Flags, void* BaseAddress,
+ size_t Size);
+
+// This function is completely undocumented.
+size_t RtlSizeHeap(void* HeapHandle, DWORD Flags, void* BaseAddress);
+
+INTERCEPTOR_WINAPI(size_t, RtlSizeHeap, HANDLE HeapHandle, DWORD Flags,
+ void* BaseAddress) {
+ if (!flags()->windows_hook_rtl_allocators ||
+ UNLIKELY(!asan_inited || OWNED_BY_RTL(HeapHandle, BaseAddress))) {
+ return REAL(RtlSizeHeap)(HeapHandle, Flags, BaseAddress);
+ }
+ GET_CURRENT_PC_BP_SP;
+ (void)sp;
+ return asan_malloc_usable_size(BaseAddress, pc, bp);
+}
+
+INTERCEPTOR_WINAPI(BOOL, RtlFreeHeap, HANDLE HeapHandle, DWORD Flags,
+ void* BaseAddress) {
+ // Heap allocations happen before this function is hooked, so we must fall
+ // back to the original function if the pointer is not from the ASAN heap, or
+ // unsupported flags are provided.
+ if (!flags()->windows_hook_rtl_allocators ||
+ UNLIKELY((HEAP_FREE_UNSUPPORTED_FLAGS & Flags) != 0 ||
+ OWNED_BY_RTL(HeapHandle, BaseAddress))) {
+ return REAL(RtlFreeHeap)(HeapHandle, Flags, BaseAddress);
+ }
+ GET_STACK_TRACE_FREE;
+ asan_free(BaseAddress, &stack, FROM_MALLOC);
+ return true;
+}
+
+INTERCEPTOR_WINAPI(void*, RtlAllocateHeap, HANDLE HeapHandle, DWORD Flags,
+ size_t Size) {
+ // If the ASAN runtime is not initialized, or we encounter an unsupported
+ // flag, fall back to the original allocator.
+ if (!flags()->windows_hook_rtl_allocators ||
+ UNLIKELY(!asan_inited ||
+ (Flags & HEAP_ALLOCATE_UNSUPPORTED_FLAGS) != 0)) {
+ return REAL(RtlAllocateHeap)(HeapHandle, Flags, Size);
+ }
+ GET_STACK_TRACE_MALLOC;
+ void *p;
+ // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
+ // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
+ // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
+ if (Flags & HEAP_ZERO_MEMORY) {
+ p = asan_calloc(Size, 1, &stack);
+ } else {
+ p = asan_malloc(Size, &stack);
+ }
+ return p;
+}
+
+INTERCEPTOR_WINAPI(void*, RtlReAllocateHeap, HANDLE HeapHandle, DWORD Flags,
+ void* BaseAddress, size_t Size) {
+ // If it's actually a heap block which was allocated before the ASAN runtime
+ // came up, use the real RtlFreeHeap function.
+ if (!flags()->windows_hook_rtl_allocators)
+ return REAL(RtlReAllocateHeap)(HeapHandle, Flags, BaseAddress, Size);
+
+ return SharedReAlloc(REAL(RtlReAllocateHeap), REAL(RtlSizeHeap),
+ REAL(RtlFreeHeap), REAL(RtlAllocateHeap), HeapHandle,
+ Flags, BaseAddress, Size);
+}
+
+namespace __asan {
+
+static void TryToOverrideFunction(const char *fname, uptr new_func) {
+ // Failure here is not fatal. The CRT may not be present, and different CRT
+ // versions use different symbols.
+ if (!__interception::OverrideFunction(fname, new_func))
+ VPrintf(2, "Failed to override function %s\n", fname);
+}
+
+void ReplaceSystemMalloc() {
+#if defined(ASAN_DYNAMIC)
+ TryToOverrideFunction("free", (uptr)free);
+ TryToOverrideFunction("_free_base", (uptr)free);
+ TryToOverrideFunction("malloc", (uptr)malloc);
+ TryToOverrideFunction("_malloc_base", (uptr)malloc);
+ TryToOverrideFunction("_malloc_crt", (uptr)malloc);
+ TryToOverrideFunction("calloc", (uptr)calloc);
+ TryToOverrideFunction("_calloc_base", (uptr)calloc);
+ TryToOverrideFunction("_calloc_crt", (uptr)calloc);
+ TryToOverrideFunction("realloc", (uptr)realloc);
+ TryToOverrideFunction("_realloc_base", (uptr)realloc);
+ TryToOverrideFunction("_realloc_crt", (uptr)realloc);
+ TryToOverrideFunction("_recalloc", (uptr)_recalloc);
+ TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
+ TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
+ TryToOverrideFunction("_msize", (uptr)_msize);
+ TryToOverrideFunction("_msize_base", (uptr)_msize);
+ TryToOverrideFunction("_expand", (uptr)_expand);
+ TryToOverrideFunction("_expand_base", (uptr)_expand);
+
+ if (flags()->windows_hook_rtl_allocators) {
+ INTERCEPT_FUNCTION(HeapSize);
+ INTERCEPT_FUNCTION(HeapFree);
+ INTERCEPT_FUNCTION(HeapReAlloc);
+ INTERCEPT_FUNCTION(HeapAlloc);
+
+ // Undocumented functions must be intercepted by name, not by symbol.
+ __interception::OverrideFunction("RtlSizeHeap", (uptr)WRAP(RtlSizeHeap),
+ (uptr *)&REAL(RtlSizeHeap));
+ __interception::OverrideFunction("RtlFreeHeap", (uptr)WRAP(RtlFreeHeap),
+ (uptr *)&REAL(RtlFreeHeap));
+ __interception::OverrideFunction("RtlReAllocateHeap",
+ (uptr)WRAP(RtlReAllocateHeap),
+ (uptr *)&REAL(RtlReAllocateHeap));
+ __interception::OverrideFunction("RtlAllocateHeap",
+ (uptr)WRAP(RtlAllocateHeap),
+ (uptr *)&REAL(RtlAllocateHeap));
+ } else {
+#define INTERCEPT_UCRT_FUNCTION(func) \
+ if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll", \
+ "api-ms-win-core-heap-l1-1-0.dll", func)) \
+ VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
+ INTERCEPT_UCRT_FUNCTION(HeapAlloc);
+ INTERCEPT_UCRT_FUNCTION(HeapFree);
+ INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
+ INTERCEPT_UCRT_FUNCTION(HeapSize);
+#undef INTERCEPT_UCRT_FUNCTION
+ }
+ // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
+ // enable cross-module inlining. This means our _malloc_base hook won't catch
+ // all CRT allocations. This code here patches the import table of
+ // ucrtbase.dll so that all attempts to use the lower-level win32 heap
+ // allocation API will be directed to ASan's heap. We don't currently
+ // intercept all calls to HeapAlloc. If we did, we would have to check on
+ // HeapFree whether the pointer came from ASan of from the system.
+
+#endif // defined(ASAN_DYNAMIC)
+}
+} // namespace __asan
+
+#endif // _WIN32
//===-- asan_mapping.h ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset =
0x7FFFFFFF & (~0xFFFULL << kDefaultShadowScale); // < 2G.
-static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
-static const u64 kIosShadowOffset64 = 0x120200000;
-static const u64 kIosSimShadowOffset32 = 1ULL << 30;
-static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64;
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37;
-static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
+static const u64 kPPC64_ShadowOffset64 = 1ULL << 44;
static const u64 kSystemZ_ShadowOffset64 = 1ULL << 52;
static const u64 kSPARC64_ShadowOffset64 = 1ULL << 43; // 0x80000000000
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
# elif SANITIZER_WINDOWS
# define SHADOW_OFFSET kWindowsShadowOffset32
# elif SANITIZER_IOS
-# if SANITIZER_IOSSIM
-# define SHADOW_OFFSET kIosSimShadowOffset32
-# else
-# define SHADOW_OFFSET kIosShadowOffset32
-# endif
+# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
# elif SANITIZER_MYRIAD2
# define SHADOW_OFFSET kMyriadShadowOffset32
# else
# endif
#else
# if SANITIZER_IOS
-# if SANITIZER_IOSSIM
-# define SHADOW_OFFSET kIosSimShadowOffset64
-# else
-# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
-# endif
+# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
# elif defined(__aarch64__)
# define SHADOW_OFFSET kAArch64_ShadowOffset64
# elif defined(__powerpc64__)
# define SHADOW_OFFSET kDefaultShadowOffset64
# elif defined(__mips64)
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
-# elif defined(__sparc__)
-# define SHADOW_OFFSET kSPARC64_ShadowOffset64
+#elif defined(__sparc__)
+#define SHADOW_OFFSET kSPARC64_ShadowOffset64
# elif SANITIZER_WINDOWS64
# define SHADOW_OFFSET __asan_shadow_memory_dynamic_address
# else
//===-- asan_mapping_myriad.h -----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- asan_mapping_sparc64.h ----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- asan_memory_profile.cc.cc -----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// This file implements __sanitizer_print_memory_profile.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_stoptheworld.h"
-#include "lsan/lsan_common.h"
-#include "asan/asan_allocator.h"
-
-#if CAN_SANITIZE_LEAKS
-
-namespace __asan {
-
-struct AllocationSite {
- u32 id;
- uptr total_size;
- uptr count;
-};
-
-class HeapProfile {
- public:
- HeapProfile() { allocations_.reserve(1024); }
-
- void ProcessChunk(const AsanChunkView &cv) {
- if (cv.IsAllocated()) {
- total_allocated_user_size_ += cv.UsedSize();
- total_allocated_count_++;
- u32 id = cv.GetAllocStackId();
- if (id)
- Insert(id, cv.UsedSize());
- } else if (cv.IsQuarantined()) {
- total_quarantined_user_size_ += cv.UsedSize();
- total_quarantined_count_++;
- } else {
- total_other_count_++;
- }
- }
-
- void Print(uptr top_percent, uptr max_number_of_contexts) {
- Sort(allocations_.data(), allocations_.size(),
- [](const AllocationSite &a, const AllocationSite &b) {
- return a.total_size > b.total_size;
- });
- CHECK(total_allocated_user_size_);
- uptr total_shown = 0;
- Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
- "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
- "showing top %zd%% (at most %zd unique contexts)\n",
- total_allocated_user_size_, total_allocated_count_,
- total_quarantined_user_size_, total_quarantined_count_,
- total_other_count_, total_allocated_count_ +
- total_quarantined_count_ + total_other_count_, top_percent,
- max_number_of_contexts);
- for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts);
- i++) {
- auto &a = allocations_[i];
- Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
- a.total_size * 100 / total_allocated_user_size_, a.count);
- StackDepotGet(a.id).Print();
- total_shown += a.total_size;
- if (total_shown * 100 / total_allocated_user_size_ > top_percent)
- break;
- }
- }
-
- private:
- uptr total_allocated_user_size_ = 0;
- uptr total_allocated_count_ = 0;
- uptr total_quarantined_user_size_ = 0;
- uptr total_quarantined_count_ = 0;
- uptr total_other_count_ = 0;
- InternalMmapVector<AllocationSite> allocations_;
-
- void Insert(u32 id, uptr size) {
- // Linear lookup will be good enough for most cases (although not all).
- for (uptr i = 0; i < allocations_.size(); i++) {
- if (allocations_[i].id == id) {
- allocations_[i].total_size += size;
- allocations_[i].count++;
- return;
- }
- }
- allocations_.push_back({id, size, 1});
- }
-};
-
-static void ChunkCallback(uptr chunk, void *arg) {
- reinterpret_cast<HeapProfile*>(arg)->ProcessChunk(
- FindHeapChunkByAllocBeg(chunk));
-}
-
-static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list,
- void *argument) {
- HeapProfile hp;
- __lsan::ForEachChunk(ChunkCallback, &hp);
- uptr *Arg = reinterpret_cast<uptr*>(argument);
- hp.Print(Arg[0], Arg[1]);
-
- if (Verbosity())
- __asan_print_accumulated_stats();
-}
-
-} // namespace __asan
-
-#endif // CAN_SANITIZE_LEAKS
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_print_memory_profile(uptr top_percent,
- uptr max_number_of_contexts) {
-#if CAN_SANITIZE_LEAKS
- uptr Arg[2];
- Arg[0] = top_percent;
- Arg[1] = max_number_of_contexts;
- __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg);
-#endif // CAN_SANITIZE_LEAKS
-}
-} // extern "C"
--- /dev/null
+//===-- asan_memory_profile.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file implements __sanitizer_print_memory_profile.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "lsan/lsan_common.h"
+#include "asan/asan_allocator.h"
+
+#if CAN_SANITIZE_LEAKS
+
+namespace __asan {
+
+struct AllocationSite {
+ u32 id;
+ uptr total_size;
+ uptr count;
+};
+
+class HeapProfile {
+ public:
+ HeapProfile() { allocations_.reserve(1024); }
+
+ void ProcessChunk(const AsanChunkView &cv) {
+ if (cv.IsAllocated()) {
+ total_allocated_user_size_ += cv.UsedSize();
+ total_allocated_count_++;
+ u32 id = cv.GetAllocStackId();
+ if (id)
+ Insert(id, cv.UsedSize());
+ } else if (cv.IsQuarantined()) {
+ total_quarantined_user_size_ += cv.UsedSize();
+ total_quarantined_count_++;
+ } else {
+ total_other_count_++;
+ }
+ }
+
+ void Print(uptr top_percent, uptr max_number_of_contexts) {
+ Sort(allocations_.data(), allocations_.size(),
+ [](const AllocationSite &a, const AllocationSite &b) {
+ return a.total_size > b.total_size;
+ });
+ CHECK(total_allocated_user_size_);
+ uptr total_shown = 0;
+ Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
+ "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
+ "showing top %zd%% (at most %zd unique contexts)\n",
+ total_allocated_user_size_, total_allocated_count_,
+ total_quarantined_user_size_, total_quarantined_count_,
+ total_other_count_, total_allocated_count_ +
+ total_quarantined_count_ + total_other_count_, top_percent,
+ max_number_of_contexts);
+ for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts);
+ i++) {
+ auto &a = allocations_[i];
+ Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
+ a.total_size * 100 / total_allocated_user_size_, a.count);
+ StackDepotGet(a.id).Print();
+ total_shown += a.total_size;
+ if (total_shown * 100 / total_allocated_user_size_ > top_percent)
+ break;
+ }
+ }
+
+ private:
+ uptr total_allocated_user_size_ = 0;
+ uptr total_allocated_count_ = 0;
+ uptr total_quarantined_user_size_ = 0;
+ uptr total_quarantined_count_ = 0;
+ uptr total_other_count_ = 0;
+ InternalMmapVector<AllocationSite> allocations_;
+
+ void Insert(u32 id, uptr size) {
+ // Linear lookup will be good enough for most cases (although not all).
+ for (uptr i = 0; i < allocations_.size(); i++) {
+ if (allocations_[i].id == id) {
+ allocations_[i].total_size += size;
+ allocations_[i].count++;
+ return;
+ }
+ }
+ allocations_.push_back({id, size, 1});
+ }
+};
+
+static void ChunkCallback(uptr chunk, void *arg) {
+ reinterpret_cast<HeapProfile*>(arg)->ProcessChunk(
+ FindHeapChunkByAllocBeg(chunk));
+}
+
+static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ HeapProfile hp;
+ __lsan::ForEachChunk(ChunkCallback, &hp);
+ uptr *Arg = reinterpret_cast<uptr*>(argument);
+ hp.Print(Arg[0], Arg[1]);
+
+ if (Verbosity())
+ __asan_print_accumulated_stats();
+}
+
+} // namespace __asan
+
+#endif // CAN_SANITIZE_LEAKS
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_memory_profile(uptr top_percent,
+ uptr max_number_of_contexts) {
+#if CAN_SANITIZE_LEAKS
+ uptr Arg[2];
+ Arg[0] = top_percent;
+ Arg[1] = max_number_of_contexts;
+ __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg);
+#endif // CAN_SANITIZE_LEAKS
+}
+} // extern "C"
+++ /dev/null
-//===-- asan_interceptors.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Interceptors for operators new and delete.
-//===----------------------------------------------------------------------===//
-
-#include "asan_allocator.h"
-#include "asan_internal.h"
-#include "asan_malloc_local.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-
-#include "interception/interception.h"
-
-#include <stddef.h>
-
-// C++ operators can't have dllexport attributes on Windows. We export them
-// anyway by passing extra -export flags to the linker, which is exactly that
-// dllexport would normally do. We need to export them in order to make the
-// VS2015 dynamic CRT (MD) work.
-#if SANITIZER_WINDOWS && defined(_MSC_VER)
-#define CXX_OPERATOR_ATTRIBUTE
-#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym))
-#ifdef _WIN64
-COMMENT_EXPORT("??2@YAPEAX_K@Z") // operator new
-COMMENT_EXPORT("??2@YAPEAX_KAEBUnothrow_t@std@@@Z") // operator new nothrow
-COMMENT_EXPORT("??3@YAXPEAX@Z") // operator delete
-COMMENT_EXPORT("??3@YAXPEAX_K@Z") // sized operator delete
-COMMENT_EXPORT("??_U@YAPEAX_K@Z") // operator new[]
-COMMENT_EXPORT("??_V@YAXPEAX@Z") // operator delete[]
-#else
-COMMENT_EXPORT("??2@YAPAXI@Z") // operator new
-COMMENT_EXPORT("??2@YAPAXIABUnothrow_t@std@@@Z") // operator new nothrow
-COMMENT_EXPORT("??3@YAXPAX@Z") // operator delete
-COMMENT_EXPORT("??3@YAXPAXI@Z") // sized operator delete
-COMMENT_EXPORT("??_U@YAPAXI@Z") // operator new[]
-COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[]
-#endif
-#undef COMMENT_EXPORT
-#else
-#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
-#endif
-
-using namespace __asan; // NOLINT
-
-// FreeBSD prior v9.2 have wrong definition of 'size_t'.
-// http://svnweb.freebsd.org/base?view=revision&revision=232261
-#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
-#include <sys/param.h>
-#if __FreeBSD_version <= 902001 // v9.2
-#define size_t unsigned
-#endif // __FreeBSD_version
-#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
-
-// This code has issues on OSX.
-// See https://github.com/google/sanitizers/issues/131.
-
-// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
-namespace std {
-struct nothrow_t {};
-enum class align_val_t: size_t {};
-} // namespace std
-
-// TODO(alekseyshl): throw std::bad_alloc instead of dying on OOM.
-// For local pool allocation, align to SHADOW_GRANULARITY to match asan
-// allocator behavior.
-#define OPERATOR_NEW_BODY(type, nothrow) \
- if (ALLOCATE_FROM_LOCAL_POOL) {\
- void *res = MemalignFromLocalPool(SHADOW_GRANULARITY, size);\
- if (!nothrow) CHECK(res);\
- return res;\
- }\
- GET_STACK_TRACE_MALLOC;\
- void *res = asan_memalign(0, size, &stack, type);\
- if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
- return res;
-#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \
- if (ALLOCATE_FROM_LOCAL_POOL) {\
- void *res = MemalignFromLocalPool((uptr)align, size);\
- if (!nothrow) CHECK(res);\
- return res;\
- }\
- GET_STACK_TRACE_MALLOC;\
- void *res = asan_memalign((uptr)align, size, &stack, type);\
- if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
- return res;
-
-// On OS X it's not enough to just provide our own 'operator new' and
-// 'operator delete' implementations, because they're going to be in the
-// runtime dylib, and the main executable will depend on both the runtime
-// dylib and libstdc++, each of those'll have its implementation of new and
-// delete.
-// To make sure that C++ allocation/deallocation operators are overridden on
-// OS X we need to intercept them using their mangled names.
-#if !SANITIZER_MAC
-CXX_OPERATOR_ATTRIBUTE
-void *operator new(size_t size)
-{ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new[](size_t size)
-{ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new(size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new[](size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new(size_t size, std::align_val_t align)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new[](size_t size, std::align_val_t align)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/); }
-CXX_OPERATOR_ATTRIBUTE
-void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/); }
-
-#else // SANITIZER_MAC
-INTERCEPTOR(void *, _Znwm, size_t size) {
- OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/);
-}
-INTERCEPTOR(void *, _Znam, size_t size) {
- OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/);
-}
-INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/);
-}
-INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/);
-}
-#endif // !SANITIZER_MAC
-
-#define OPERATOR_DELETE_BODY(type) \
- if (IS_FROM_LOCAL_POOL(ptr)) return;\
- GET_STACK_TRACE_FREE;\
- asan_delete(ptr, 0, 0, &stack, type);
-
-#define OPERATOR_DELETE_BODY_SIZE(type) \
- if (IS_FROM_LOCAL_POOL(ptr)) return;\
- GET_STACK_TRACE_FREE;\
- asan_delete(ptr, size, 0, &stack, type);
-
-#define OPERATOR_DELETE_BODY_ALIGN(type) \
- if (IS_FROM_LOCAL_POOL(ptr)) return;\
- GET_STACK_TRACE_FREE;\
- asan_delete(ptr, 0, static_cast<uptr>(align), &stack, type);
-
-#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \
- if (IS_FROM_LOCAL_POOL(ptr)) return;\
- GET_STACK_TRACE_FREE;\
- asan_delete(ptr, size, static_cast<uptr>(align), &stack, type);
-
-#if !SANITIZER_MAC
-CXX_OPERATOR_ATTRIBUTE
-void operator delete(void *ptr) NOEXCEPT
-{ OPERATOR_DELETE_BODY(FROM_NEW); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete[](void *ptr) NOEXCEPT
-{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete(void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY(FROM_NEW); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete[](void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete(void *ptr, size_t size) NOEXCEPT
-{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete[](void *ptr, size_t size) NOEXCEPT
-{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete(void *ptr, std::align_val_t align) NOEXCEPT
-{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT
-{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT
-{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW); }
-CXX_OPERATOR_ATTRIBUTE
-void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT
-{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR); }
-
-#else // SANITIZER_MAC
-INTERCEPTOR(void, _ZdlPv, void *ptr)
-{ OPERATOR_DELETE_BODY(FROM_NEW); }
-INTERCEPTOR(void, _ZdaPv, void *ptr)
-{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
-INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY(FROM_NEW); }
-INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
-#endif // !SANITIZER_MAC
--- /dev/null
+//===-- asan_interceptors.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_internal.h"
+#include "asan_malloc_local.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+
+#include "interception/interception.h"
+
+#include <stddef.h>
+
+// C++ operators can't have dllexport attributes on Windows. We export them
+// anyway by passing extra -export flags to the linker, which is exactly that
+// dllexport would normally do. We need to export them in order to make the
+// VS2015 dynamic CRT (MD) work.
+#if SANITIZER_WINDOWS && defined(_MSC_VER)
+#define CXX_OPERATOR_ATTRIBUTE
+#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:" sym))
+#ifdef _WIN64
+COMMENT_EXPORT("??2@YAPEAX_K@Z") // operator new
+COMMENT_EXPORT("??2@YAPEAX_KAEBUnothrow_t@std@@@Z") // operator new nothrow
+COMMENT_EXPORT("??3@YAXPEAX@Z") // operator delete
+COMMENT_EXPORT("??3@YAXPEAX_K@Z") // sized operator delete
+COMMENT_EXPORT("??_U@YAPEAX_K@Z") // operator new[]
+COMMENT_EXPORT("??_V@YAXPEAX@Z") // operator delete[]
+#else
+COMMENT_EXPORT("??2@YAPAXI@Z") // operator new
+COMMENT_EXPORT("??2@YAPAXIABUnothrow_t@std@@@Z") // operator new nothrow
+COMMENT_EXPORT("??3@YAXPAX@Z") // operator delete
+COMMENT_EXPORT("??3@YAXPAXI@Z") // sized operator delete
+COMMENT_EXPORT("??_U@YAPAXI@Z") // operator new[]
+COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[]
+#endif
+#undef COMMENT_EXPORT
+#else
+#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE
+#endif
+
+using namespace __asan; // NOLINT
+
+// FreeBSD prior v9.2 have wrong definition of 'size_t'.
+// http://svnweb.freebsd.org/base?view=revision&revision=232261
+#if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
+#include <sys/param.h>
+#if __FreeBSD_version <= 902001 // v9.2
+#define size_t unsigned
+#endif // __FreeBSD_version
+#endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32
+
+// This code has issues on OSX.
+// See https://github.com/google/sanitizers/issues/131.
+
+// Fake std::nothrow_t and std::align_val_t to avoid including <new>.
+namespace std {
+struct nothrow_t {};
+enum class align_val_t: size_t {};
+} // namespace std
+
+// TODO(alekseyshl): throw std::bad_alloc instead of dying on OOM.
+// For local pool allocation, align to SHADOW_GRANULARITY to match asan
+// allocator behavior.
+#define OPERATOR_NEW_BODY(type, nothrow) \
+ MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow); \
+ GET_STACK_TRACE_MALLOC; \
+ void *res = asan_memalign(0, size, &stack, type); \
+ if (!nothrow && UNLIKELY(!res)) \
+ ReportOutOfMemory(size, &stack); \
+ return res;
+#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \
+ MAYBE_ALLOCATE_FROM_LOCAL_POOL(nothrow); \
+ GET_STACK_TRACE_MALLOC; \
+ void *res = asan_memalign((uptr)align, size, &stack, type); \
+ if (!nothrow && UNLIKELY(!res)) \
+ ReportOutOfMemory(size, &stack); \
+ return res;
+
+// On OS X it's not enough to just provide our own 'operator new' and
+// 'operator delete' implementations, because they're going to be in the
+// runtime dylib, and the main executable will depend on both the runtime
+// dylib and libstdc++, each of those'll have its implementation of new and
+// delete.
+// To make sure that C++ allocation/deallocation operators are overridden on
+// OS X we need to intercept them using their mangled names.
+#if !SANITIZER_MAC
+CXX_OPERATOR_ATTRIBUTE
+void *operator new(size_t size)
+{ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new[](size_t size)
+{ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align)
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align)
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/); }
+CXX_OPERATOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/); }
+
+#else // SANITIZER_MAC
+INTERCEPTOR(void *, _Znwm, size_t size) {
+ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/);
+}
+INTERCEPTOR(void *, _Znam, size_t size) {
+ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/);
+}
+INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/);
+}
+INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/);
+}
+#endif // !SANITIZER_MAC
+
+#define OPERATOR_DELETE_BODY(type) \
+ if (IS_FROM_LOCAL_POOL(ptr)) return;\
+ GET_STACK_TRACE_FREE;\
+ asan_delete(ptr, 0, 0, &stack, type);
+
+#define OPERATOR_DELETE_BODY_SIZE(type) \
+ if (IS_FROM_LOCAL_POOL(ptr)) return;\
+ GET_STACK_TRACE_FREE;\
+ asan_delete(ptr, size, 0, &stack, type);
+
+#define OPERATOR_DELETE_BODY_ALIGN(type) \
+ if (IS_FROM_LOCAL_POOL(ptr)) return;\
+ GET_STACK_TRACE_FREE;\
+ asan_delete(ptr, 0, static_cast<uptr>(align), &stack, type);
+
+#define OPERATOR_DELETE_BODY_SIZE_ALIGN(type) \
+ if (IS_FROM_LOCAL_POOL(ptr)) return;\
+ GET_STACK_TRACE_FREE;\
+ asan_delete(ptr, size, static_cast<uptr>(align), &stack, type);
+
+#if !SANITIZER_MAC
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size) NOEXCEPT
+{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) NOEXCEPT
+{ OPERATOR_DELETE_BODY_SIZE(FROM_NEW_BR); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align) NOEXCEPT
+{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT
+{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY_ALIGN(FROM_NEW_BR); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT
+{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW); }
+CXX_OPERATOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size, std::align_val_t align) NOEXCEPT
+{ OPERATOR_DELETE_BODY_SIZE_ALIGN(FROM_NEW_BR); }
+
+#else // SANITIZER_MAC
+INTERCEPTOR(void, _ZdlPv, void *ptr)
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
+INTERCEPTOR(void, _ZdaPv, void *ptr)
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW); }
+INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY(FROM_NEW_BR); }
+#endif // !SANITIZER_MAC
+++ /dev/null
-//===-- asan_poisoning.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Shadow memory poisoning by ASan RTL and by user application.
-//===----------------------------------------------------------------------===//
-
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_flags.h"
-
-namespace __asan {
-
-static atomic_uint8_t can_poison_memory;
-
-void SetCanPoisonMemory(bool value) {
- atomic_store(&can_poison_memory, value, memory_order_release);
-}
-
-bool CanPoisonMemory() {
- return atomic_load(&can_poison_memory, memory_order_acquire);
-}
-
-void PoisonShadow(uptr addr, uptr size, u8 value) {
- if (value && !CanPoisonMemory()) return;
- CHECK(AddrIsAlignedByGranularity(addr));
- CHECK(AddrIsInMem(addr));
- CHECK(AddrIsAlignedByGranularity(addr + size));
- CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
- CHECK(REAL(memset));
- FastPoisonShadow(addr, size, value);
-}
-
-void PoisonShadowPartialRightRedzone(uptr addr,
- uptr size,
- uptr redzone_size,
- u8 value) {
- if (!CanPoisonMemory()) return;
- CHECK(AddrIsAlignedByGranularity(addr));
- CHECK(AddrIsInMem(addr));
- FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
-}
-
-struct ShadowSegmentEndpoint {
- u8 *chunk;
- s8 offset; // in [0, SHADOW_GRANULARITY)
- s8 value; // = *chunk;
-
- explicit ShadowSegmentEndpoint(uptr address) {
- chunk = (u8*)MemToShadow(address);
- offset = address & (SHADOW_GRANULARITY - 1);
- value = *chunk;
- }
-};
-
-void FlushUnneededASanShadowMemory(uptr p, uptr size) {
- // Since asan's mapping is compacting, the shadow chunk may be
- // not page-aligned, so we only flush the page-aligned portion.
- ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
-}
-
-void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
- uptr end = ptr + size;
- if (Verbosity()) {
- Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
- poison ? "" : "un", ptr, end, size);
- if (Verbosity() >= 2)
- PRINT_CURRENT_STACK();
- }
- CHECK(size);
- CHECK_LE(size, 4096);
- CHECK(IsAligned(end, SHADOW_GRANULARITY));
- if (!IsAligned(ptr, SHADOW_GRANULARITY)) {
- *(u8 *)MemToShadow(ptr) =
- poison ? static_cast<u8>(ptr % SHADOW_GRANULARITY) : 0;
- ptr |= SHADOW_GRANULARITY - 1;
- ptr++;
- }
- for (; ptr < end; ptr += SHADOW_GRANULARITY)
- *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0;
-}
-
-} // namespace __asan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-// Current implementation of __asan_(un)poison_memory_region doesn't check
-// that user program (un)poisons the memory it owns. It poisons memory
-// conservatively, and unpoisons progressively to make sure asan shadow
-// mapping invariant is preserved (see detailed mapping description here:
-// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
-//
-// * if user asks to poison region [left, right), the program poisons
-// at least [left, AlignDown(right)).
-// * if user asks to unpoison region [left, right), the program unpoisons
-// at most [AlignDown(left), right).
-void __asan_poison_memory_region(void const volatile *addr, uptr size) {
- if (!flags()->allow_user_poisoning || size == 0) return;
- uptr beg_addr = (uptr)addr;
- uptr end_addr = beg_addr + size;
- VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
- (void *)end_addr);
- ShadowSegmentEndpoint beg(beg_addr);
- ShadowSegmentEndpoint end(end_addr);
- if (beg.chunk == end.chunk) {
- CHECK_LT(beg.offset, end.offset);
- s8 value = beg.value;
- CHECK_EQ(value, end.value);
- // We can only poison memory if the byte in end.offset is unaddressable.
- // No need to re-poison memory if it is poisoned already.
- if (value > 0 && value <= end.offset) {
- if (beg.offset > 0) {
- *beg.chunk = Min(value, beg.offset);
- } else {
- *beg.chunk = kAsanUserPoisonedMemoryMagic;
- }
- }
- return;
- }
- CHECK_LT(beg.chunk, end.chunk);
- if (beg.offset > 0) {
- // Mark bytes from beg.offset as unaddressable.
- if (beg.value == 0) {
- *beg.chunk = beg.offset;
- } else {
- *beg.chunk = Min(beg.value, beg.offset);
- }
- beg.chunk++;
- }
- REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
- // Poison if byte in end.offset is unaddressable.
- if (end.value > 0 && end.value <= end.offset) {
- *end.chunk = kAsanUserPoisonedMemoryMagic;
- }
-}
-
-void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
- if (!flags()->allow_user_poisoning || size == 0) return;
- uptr beg_addr = (uptr)addr;
- uptr end_addr = beg_addr + size;
- VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
- (void *)end_addr);
- ShadowSegmentEndpoint beg(beg_addr);
- ShadowSegmentEndpoint end(end_addr);
- if (beg.chunk == end.chunk) {
- CHECK_LT(beg.offset, end.offset);
- s8 value = beg.value;
- CHECK_EQ(value, end.value);
- // We unpoison memory bytes up to enbytes up to end.offset if it is not
- // unpoisoned already.
- if (value != 0) {
- *beg.chunk = Max(value, end.offset);
- }
- return;
- }
- CHECK_LT(beg.chunk, end.chunk);
- if (beg.offset > 0) {
- *beg.chunk = 0;
- beg.chunk++;
- }
- REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
- if (end.offset > 0 && end.value != 0) {
- *end.chunk = Max(end.value, end.offset);
- }
-}
-
-int __asan_address_is_poisoned(void const volatile *addr) {
- return __asan::AddressIsPoisoned((uptr)addr);
-}
-
-uptr __asan_region_is_poisoned(uptr beg, uptr size) {
- if (!size) return 0;
- uptr end = beg + size;
- if (SANITIZER_MYRIAD2) {
- // On Myriad, address not in DRAM range need to be treated as
- // unpoisoned.
- if (!AddrIsInMem(beg) && !AddrIsInShadow(beg)) return 0;
- if (!AddrIsInMem(end) && !AddrIsInShadow(end)) return 0;
- } else {
- if (!AddrIsInMem(beg)) return beg;
- if (!AddrIsInMem(end)) return end;
- }
- CHECK_LT(beg, end);
- uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
- uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
- uptr shadow_beg = MemToShadow(aligned_b);
- uptr shadow_end = MemToShadow(aligned_e);
- // First check the first and the last application bytes,
- // then check the SHADOW_GRANULARITY-aligned region by calling
- // mem_is_zero on the corresponding shadow.
- if (!__asan::AddressIsPoisoned(beg) &&
- !__asan::AddressIsPoisoned(end - 1) &&
- (shadow_end <= shadow_beg ||
- __sanitizer::mem_is_zero((const char *)shadow_beg,
- shadow_end - shadow_beg)))
- return 0;
- // The fast check failed, so we have a poisoned byte somewhere.
- // Find it slowly.
- for (; beg < end; beg++)
- if (__asan::AddressIsPoisoned(beg))
- return beg;
- UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
- return 0;
-}
-
-#define CHECK_SMALL_REGION(p, size, isWrite) \
- do { \
- uptr __p = reinterpret_cast<uptr>(p); \
- uptr __size = size; \
- if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \
- __asan::AddressIsPoisoned(__p + __size - 1))) { \
- GET_CURRENT_PC_BP_SP; \
- uptr __bad = __asan_region_is_poisoned(__p, __size); \
- __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
- } \
- } while (false)
-
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-u16 __sanitizer_unaligned_load16(const uu16 *p) {
- CHECK_SMALL_REGION(p, sizeof(*p), false);
- return *p;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-u32 __sanitizer_unaligned_load32(const uu32 *p) {
- CHECK_SMALL_REGION(p, sizeof(*p), false);
- return *p;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-u64 __sanitizer_unaligned_load64(const uu64 *p) {
- CHECK_SMALL_REGION(p, sizeof(*p), false);
- return *p;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
- CHECK_SMALL_REGION(p, sizeof(*p), true);
- *p = x;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
- CHECK_SMALL_REGION(p, sizeof(*p), true);
- *p = x;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
- CHECK_SMALL_REGION(p, sizeof(*p), true);
- *p = x;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_poison_cxx_array_cookie(uptr p) {
- if (SANITIZER_WORDSIZE != 64) return;
- if (!flags()->poison_array_cookie) return;
- uptr s = MEM_TO_SHADOW(p);
- *reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-uptr __asan_load_cxx_array_cookie(uptr *p) {
- if (SANITIZER_WORDSIZE != 64) return *p;
- if (!flags()->poison_array_cookie) return *p;
- uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
- u8 sval = *reinterpret_cast<u8*>(s);
- if (sval == kAsanArrayCookieMagic) return *p;
- // If sval is not kAsanArrayCookieMagic it can only be freed memory,
- // which means that we are going to get double-free. So, return 0 to avoid
- // infinite loop of destructors. We don't want to report a double-free here
- // though, so print a warning just in case.
- // CHECK_EQ(sval, kAsanHeapFreeMagic);
- if (sval == kAsanHeapFreeMagic) {
- Report("AddressSanitizer: loaded array cookie from free-d memory; "
- "expect a double-free report\n");
- return 0;
- }
- // The cookie may remain unpoisoned if e.g. it comes from a custom
- // operator new defined inside a class.
- return *p;
-}
-
-// This is a simplified version of __asan_(un)poison_memory_region, which
-// assumes that left border of region to be poisoned is properly aligned.
-static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
- if (size == 0) return;
- uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1);
- PoisonShadow(addr, aligned_size,
- do_poison ? kAsanStackUseAfterScopeMagic : 0);
- if (size == aligned_size)
- return;
- s8 end_offset = (s8)(size - aligned_size);
- s8* shadow_end = (s8*)MemToShadow(addr + aligned_size);
- s8 end_value = *shadow_end;
- if (do_poison) {
- // If possible, mark all the bytes mapping to last shadow byte as
- // unaddressable.
- if (end_value > 0 && end_value <= end_offset)
- *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
- } else {
- // If necessary, mark few first bytes mapping to last shadow byte
- // as addressable
- if (end_value != 0)
- *shadow_end = Max(end_value, end_offset);
- }
-}
-
-void __asan_set_shadow_00(uptr addr, uptr size) {
- REAL(memset)((void *)addr, 0, size);
-}
-
-void __asan_set_shadow_f1(uptr addr, uptr size) {
- REAL(memset)((void *)addr, 0xf1, size);
-}
-
-void __asan_set_shadow_f2(uptr addr, uptr size) {
- REAL(memset)((void *)addr, 0xf2, size);
-}
-
-void __asan_set_shadow_f3(uptr addr, uptr size) {
- REAL(memset)((void *)addr, 0xf3, size);
-}
-
-void __asan_set_shadow_f5(uptr addr, uptr size) {
- REAL(memset)((void *)addr, 0xf5, size);
-}
-
-void __asan_set_shadow_f8(uptr addr, uptr size) {
- REAL(memset)((void *)addr, 0xf8, size);
-}
-
-void __asan_poison_stack_memory(uptr addr, uptr size) {
- VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
- PoisonAlignedStackMemory(addr, size, true);
-}
-
-void __asan_unpoison_stack_memory(uptr addr, uptr size) {
- VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size);
- PoisonAlignedStackMemory(addr, size, false);
-}
-
-void __sanitizer_annotate_contiguous_container(const void *beg_p,
- const void *end_p,
- const void *old_mid_p,
- const void *new_mid_p) {
- if (!flags()->detect_container_overflow) return;
- VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
- new_mid_p);
- uptr beg = reinterpret_cast<uptr>(beg_p);
- uptr end = reinterpret_cast<uptr>(end_p);
- uptr old_mid = reinterpret_cast<uptr>(old_mid_p);
- uptr new_mid = reinterpret_cast<uptr>(new_mid_p);
- uptr granularity = SHADOW_GRANULARITY;
- if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end &&
- IsAligned(beg, granularity))) {
- GET_STACK_TRACE_FATAL_HERE;
- ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid,
- &stack);
- }
- CHECK_LE(end - beg,
- FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check.
-
- uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
- uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
- uptr d1 = RoundDownTo(old_mid, granularity);
- // uptr d2 = RoundUpTo(old_mid, granularity);
- // Currently we should be in this state:
- // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good.
- // Make a quick sanity check that we are indeed in this state.
- //
- // FIXME: Two of these three checks are disabled until we fix
- // https://github.com/google/sanitizers/issues/258.
- // if (d1 != d2)
- // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
- if (a + granularity <= d1)
- CHECK_EQ(*(u8*)MemToShadow(a), 0);
- // if (d2 + granularity <= c && c <= end)
- // CHECK_EQ(*(u8 *)MemToShadow(c - granularity),
- // kAsanContiguousContainerOOBMagic);
-
- uptr b1 = RoundDownTo(new_mid, granularity);
- uptr b2 = RoundUpTo(new_mid, granularity);
- // New state:
- // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good.
- PoisonShadow(a, b1 - a, 0);
- PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic);
- if (b1 != b2) {
- CHECK_EQ(b2 - b1, granularity);
- *(u8*)MemToShadow(b1) = static_cast<u8>(new_mid - b1);
- }
-}
-
-const void *__sanitizer_contiguous_container_find_bad_address(
- const void *beg_p, const void *mid_p, const void *end_p) {
- if (!flags()->detect_container_overflow)
- return nullptr;
- uptr beg = reinterpret_cast<uptr>(beg_p);
- uptr end = reinterpret_cast<uptr>(end_p);
- uptr mid = reinterpret_cast<uptr>(mid_p);
- CHECK_LE(beg, mid);
- CHECK_LE(mid, end);
- // Check some bytes starting from beg, some bytes around mid, and some bytes
- // ending with end.
- uptr kMaxRangeToCheck = 32;
- uptr r1_beg = beg;
- uptr r1_end = Min(beg + kMaxRangeToCheck, mid);
- uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
- uptr r2_end = Min(end, mid + kMaxRangeToCheck);
- uptr r3_beg = Max(end - kMaxRangeToCheck, mid);
- uptr r3_end = end;
- for (uptr i = r1_beg; i < r1_end; i++)
- if (AddressIsPoisoned(i))
- return reinterpret_cast<const void *>(i);
- for (uptr i = r2_beg; i < mid; i++)
- if (AddressIsPoisoned(i))
- return reinterpret_cast<const void *>(i);
- for (uptr i = mid; i < r2_end; i++)
- if (!AddressIsPoisoned(i))
- return reinterpret_cast<const void *>(i);
- for (uptr i = r3_beg; i < r3_end; i++)
- if (!AddressIsPoisoned(i))
- return reinterpret_cast<const void *>(i);
- return nullptr;
-}
-
-int __sanitizer_verify_contiguous_container(const void *beg_p,
- const void *mid_p,
- const void *end_p) {
- return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p,
- end_p) == nullptr;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
- AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
- AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false);
-}
-
-// --- Implementation of LSan-specific functions --- {{{1
-namespace __lsan {
-bool WordIsPoisoned(uptr addr) {
- return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0);
-}
-}
--- /dev/null
+//===-- asan_poisoning.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Shadow memory poisoning by ASan RTL and by user application.
+//===----------------------------------------------------------------------===//
+
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_flags.h"
+
+namespace __asan {
+
+static atomic_uint8_t can_poison_memory;
+
+void SetCanPoisonMemory(bool value) {
+ atomic_store(&can_poison_memory, value, memory_order_release);
+}
+
+bool CanPoisonMemory() {
+ return atomic_load(&can_poison_memory, memory_order_acquire);
+}
+
+void PoisonShadow(uptr addr, uptr size, u8 value) {
+ if (value && !CanPoisonMemory()) return;
+ CHECK(AddrIsAlignedByGranularity(addr));
+ CHECK(AddrIsInMem(addr));
+ CHECK(AddrIsAlignedByGranularity(addr + size));
+ CHECK(AddrIsInMem(addr + size - SHADOW_GRANULARITY));
+ CHECK(REAL(memset));
+ FastPoisonShadow(addr, size, value);
+}
+
+void PoisonShadowPartialRightRedzone(uptr addr,
+ uptr size,
+ uptr redzone_size,
+ u8 value) {
+ if (!CanPoisonMemory()) return;
+ CHECK(AddrIsAlignedByGranularity(addr));
+ CHECK(AddrIsInMem(addr));
+ FastPoisonShadowPartialRightRedzone(addr, size, redzone_size, value);
+}
+
+struct ShadowSegmentEndpoint {
+ u8 *chunk;
+ s8 offset; // in [0, SHADOW_GRANULARITY)
+ s8 value; // = *chunk;
+
+ explicit ShadowSegmentEndpoint(uptr address) {
+ chunk = (u8*)MemToShadow(address);
+ offset = address & (SHADOW_GRANULARITY - 1);
+ value = *chunk;
+ }
+};
+
+void FlushUnneededASanShadowMemory(uptr p, uptr size) {
+ // Since asan's mapping is compacting, the shadow chunk may be
+ // not page-aligned, so we only flush the page-aligned portion.
+ ReleaseMemoryPagesToOS(MemToShadow(p), MemToShadow(p + size));
+}
+
+void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
+ uptr end = ptr + size;
+ if (Verbosity()) {
+ Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
+ poison ? "" : "un", ptr, end, size);
+ if (Verbosity() >= 2)
+ PRINT_CURRENT_STACK();
+ }
+ CHECK(size);
+ CHECK_LE(size, 4096);
+ CHECK(IsAligned(end, SHADOW_GRANULARITY));
+ if (!IsAligned(ptr, SHADOW_GRANULARITY)) {
+ *(u8 *)MemToShadow(ptr) =
+ poison ? static_cast<u8>(ptr % SHADOW_GRANULARITY) : 0;
+ ptr |= SHADOW_GRANULARITY - 1;
+ ptr++;
+ }
+ for (; ptr < end; ptr += SHADOW_GRANULARITY)
+ *(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0;
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+// Current implementation of __asan_(un)poison_memory_region doesn't check
+// that user program (un)poisons the memory it owns. It poisons memory
+// conservatively, and unpoisons progressively to make sure asan shadow
+// mapping invariant is preserved (see detailed mapping description here:
+// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
+//
+// * if user asks to poison region [left, right), the program poisons
+// at least [left, AlignDown(right)).
+// * if user asks to unpoison region [left, right), the program unpoisons
+// at most [AlignDown(left), right).
+void __asan_poison_memory_region(void const volatile *addr, uptr size) {
+ if (!flags()->allow_user_poisoning || size == 0) return;
+ uptr beg_addr = (uptr)addr;
+ uptr end_addr = beg_addr + size;
+ VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr,
+ (void *)end_addr);
+ ShadowSegmentEndpoint beg(beg_addr);
+ ShadowSegmentEndpoint end(end_addr);
+ if (beg.chunk == end.chunk) {
+ CHECK_LT(beg.offset, end.offset);
+ s8 value = beg.value;
+ CHECK_EQ(value, end.value);
+ // We can only poison memory if the byte in end.offset is unaddressable.
+ // No need to re-poison memory if it is poisoned already.
+ if (value > 0 && value <= end.offset) {
+ if (beg.offset > 0) {
+ *beg.chunk = Min(value, beg.offset);
+ } else {
+ *beg.chunk = kAsanUserPoisonedMemoryMagic;
+ }
+ }
+ return;
+ }
+ CHECK_LT(beg.chunk, end.chunk);
+ if (beg.offset > 0) {
+ // Mark bytes from beg.offset as unaddressable.
+ if (beg.value == 0) {
+ *beg.chunk = beg.offset;
+ } else {
+ *beg.chunk = Min(beg.value, beg.offset);
+ }
+ beg.chunk++;
+ }
+ REAL(memset)(beg.chunk, kAsanUserPoisonedMemoryMagic, end.chunk - beg.chunk);
+ // Poison if byte in end.offset is unaddressable.
+ if (end.value > 0 && end.value <= end.offset) {
+ *end.chunk = kAsanUserPoisonedMemoryMagic;
+ }
+}
+
+void __asan_unpoison_memory_region(void const volatile *addr, uptr size) {
+ if (!flags()->allow_user_poisoning || size == 0) return;
+ uptr beg_addr = (uptr)addr;
+ uptr end_addr = beg_addr + size;
+ VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr,
+ (void *)end_addr);
+ ShadowSegmentEndpoint beg(beg_addr);
+ ShadowSegmentEndpoint end(end_addr);
+ if (beg.chunk == end.chunk) {
+ CHECK_LT(beg.offset, end.offset);
+ s8 value = beg.value;
+ CHECK_EQ(value, end.value);
+ // We unpoison memory bytes up to enbytes up to end.offset if it is not
+ // unpoisoned already.
+ if (value != 0) {
+ *beg.chunk = Max(value, end.offset);
+ }
+ return;
+ }
+ CHECK_LT(beg.chunk, end.chunk);
+ if (beg.offset > 0) {
+ *beg.chunk = 0;
+ beg.chunk++;
+ }
+ REAL(memset)(beg.chunk, 0, end.chunk - beg.chunk);
+ if (end.offset > 0 && end.value != 0) {
+ *end.chunk = Max(end.value, end.offset);
+ }
+}
+
+int __asan_address_is_poisoned(void const volatile *addr) {
+ return __asan::AddressIsPoisoned((uptr)addr);
+}
+
+uptr __asan_region_is_poisoned(uptr beg, uptr size) {
+ if (!size) return 0;
+ uptr end = beg + size;
+ if (SANITIZER_MYRIAD2) {
+ // On Myriad, address not in DRAM range need to be treated as
+ // unpoisoned.
+ if (!AddrIsInMem(beg) && !AddrIsInShadow(beg)) return 0;
+ if (!AddrIsInMem(end) && !AddrIsInShadow(end)) return 0;
+ } else {
+ if (!AddrIsInMem(beg)) return beg;
+ if (!AddrIsInMem(end)) return end;
+ }
+ CHECK_LT(beg, end);
+ uptr aligned_b = RoundUpTo(beg, SHADOW_GRANULARITY);
+ uptr aligned_e = RoundDownTo(end, SHADOW_GRANULARITY);
+ uptr shadow_beg = MemToShadow(aligned_b);
+ uptr shadow_end = MemToShadow(aligned_e);
+ // First check the first and the last application bytes,
+ // then check the SHADOW_GRANULARITY-aligned region by calling
+ // mem_is_zero on the corresponding shadow.
+ if (!__asan::AddressIsPoisoned(beg) &&
+ !__asan::AddressIsPoisoned(end - 1) &&
+ (shadow_end <= shadow_beg ||
+ __sanitizer::mem_is_zero((const char *)shadow_beg,
+ shadow_end - shadow_beg)))
+ return 0;
+ // The fast check failed, so we have a poisoned byte somewhere.
+ // Find it slowly.
+ for (; beg < end; beg++)
+ if (__asan::AddressIsPoisoned(beg))
+ return beg;
+ UNREACHABLE("mem_is_zero returned false, but poisoned byte was not found");
+ return 0;
+}
+
+#define CHECK_SMALL_REGION(p, size, isWrite) \
+ do { \
+ uptr __p = reinterpret_cast<uptr>(p); \
+ uptr __size = size; \
+ if (UNLIKELY(__asan::AddressIsPoisoned(__p) || \
+ __asan::AddressIsPoisoned(__p + __size - 1))) { \
+ GET_CURRENT_PC_BP_SP; \
+ uptr __bad = __asan_region_is_poisoned(__p, __size); \
+ __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\
+ } \
+ } while (false)
+
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+u16 __sanitizer_unaligned_load16(const uu16 *p) {
+ CHECK_SMALL_REGION(p, sizeof(*p), false);
+ return *p;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+u32 __sanitizer_unaligned_load32(const uu32 *p) {
+ CHECK_SMALL_REGION(p, sizeof(*p), false);
+ return *p;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+u64 __sanitizer_unaligned_load64(const uu64 *p) {
+ CHECK_SMALL_REGION(p, sizeof(*p), false);
+ return *p;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store16(uu16 *p, u16 x) {
+ CHECK_SMALL_REGION(p, sizeof(*p), true);
+ *p = x;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store32(uu32 *p, u32 x) {
+ CHECK_SMALL_REGION(p, sizeof(*p), true);
+ *p = x;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store64(uu64 *p, u64 x) {
+ CHECK_SMALL_REGION(p, sizeof(*p), true);
+ *p = x;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_poison_cxx_array_cookie(uptr p) {
+ if (SANITIZER_WORDSIZE != 64) return;
+ if (!flags()->poison_array_cookie) return;
+ uptr s = MEM_TO_SHADOW(p);
+ *reinterpret_cast<u8*>(s) = kAsanArrayCookieMagic;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_load_cxx_array_cookie(uptr *p) {
+ if (SANITIZER_WORDSIZE != 64) return *p;
+ if (!flags()->poison_array_cookie) return *p;
+ uptr s = MEM_TO_SHADOW(reinterpret_cast<uptr>(p));
+ u8 sval = *reinterpret_cast<u8*>(s);
+ if (sval == kAsanArrayCookieMagic) return *p;
+ // If sval is not kAsanArrayCookieMagic it can only be freed memory,
+ // which means that we are going to get double-free. So, return 0 to avoid
+ // infinite loop of destructors. We don't want to report a double-free here
+ // though, so print a warning just in case.
+ // CHECK_EQ(sval, kAsanHeapFreeMagic);
+ if (sval == kAsanHeapFreeMagic) {
+ Report("AddressSanitizer: loaded array cookie from free-d memory; "
+ "expect a double-free report\n");
+ return 0;
+ }
+ // The cookie may remain unpoisoned if e.g. it comes from a custom
+ // operator new defined inside a class.
+ return *p;
+}
+
+// This is a simplified version of __asan_(un)poison_memory_region, which
+// assumes that left border of region to be poisoned is properly aligned.
+static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) {
+ if (size == 0) return;
+ uptr aligned_size = size & ~(SHADOW_GRANULARITY - 1);
+ PoisonShadow(addr, aligned_size,
+ do_poison ? kAsanStackUseAfterScopeMagic : 0);
+ if (size == aligned_size)
+ return;
+ s8 end_offset = (s8)(size - aligned_size);
+ s8* shadow_end = (s8*)MemToShadow(addr + aligned_size);
+ s8 end_value = *shadow_end;
+ if (do_poison) {
+ // If possible, mark all the bytes mapping to last shadow byte as
+ // unaddressable.
+ if (end_value > 0 && end_value <= end_offset)
+ *shadow_end = (s8)kAsanStackUseAfterScopeMagic;
+ } else {
+ // If necessary, mark few first bytes mapping to last shadow byte
+ // as addressable
+ if (end_value != 0)
+ *shadow_end = Max(end_value, end_offset);
+ }
+}
+
+void __asan_set_shadow_00(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0, size);
+}
+
+void __asan_set_shadow_f1(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf1, size);
+}
+
+void __asan_set_shadow_f2(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf2, size);
+}
+
+void __asan_set_shadow_f3(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf3, size);
+}
+
+void __asan_set_shadow_f5(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf5, size);
+}
+
+void __asan_set_shadow_f8(uptr addr, uptr size) {
+ REAL(memset)((void *)addr, 0xf8, size);
+}
+
+void __asan_poison_stack_memory(uptr addr, uptr size) {
+ VReport(1, "poisoning: %p %zx\n", (void *)addr, size);
+ PoisonAlignedStackMemory(addr, size, true);
+}
+
+void __asan_unpoison_stack_memory(uptr addr, uptr size) {
+ VReport(1, "unpoisoning: %p %zx\n", (void *)addr, size);
+ PoisonAlignedStackMemory(addr, size, false);
+}
+
+void __sanitizer_annotate_contiguous_container(const void *beg_p,
+ const void *end_p,
+ const void *old_mid_p,
+ const void *new_mid_p) {
+ if (!flags()->detect_container_overflow) return;
+ VPrintf(2, "contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p,
+ new_mid_p);
+ uptr beg = reinterpret_cast<uptr>(beg_p);
+ uptr end = reinterpret_cast<uptr>(end_p);
+ uptr old_mid = reinterpret_cast<uptr>(old_mid_p);
+ uptr new_mid = reinterpret_cast<uptr>(new_mid_p);
+ uptr granularity = SHADOW_GRANULARITY;
+ if (!(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end &&
+ IsAligned(beg, granularity))) {
+ GET_STACK_TRACE_FATAL_HERE;
+ ReportBadParamsToAnnotateContiguousContainer(beg, end, old_mid, new_mid,
+ &stack);
+ }
+ CHECK_LE(end - beg,
+ FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check.
+
+ uptr a = RoundDownTo(Min(old_mid, new_mid), granularity);
+ uptr c = RoundUpTo(Max(old_mid, new_mid), granularity);
+ uptr d1 = RoundDownTo(old_mid, granularity);
+ // uptr d2 = RoundUpTo(old_mid, granularity);
+ // Currently we should be in this state:
+ // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good.
+ // Make a quick sanity check that we are indeed in this state.
+ //
+ // FIXME: Two of these three checks are disabled until we fix
+ // https://github.com/google/sanitizers/issues/258.
+ // if (d1 != d2)
+ // CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
+ if (a + granularity <= d1)
+ CHECK_EQ(*(u8*)MemToShadow(a), 0);
+ // if (d2 + granularity <= c && c <= end)
+ // CHECK_EQ(*(u8 *)MemToShadow(c - granularity),
+ // kAsanContiguousContainerOOBMagic);
+
+ uptr b1 = RoundDownTo(new_mid, granularity);
+ uptr b2 = RoundUpTo(new_mid, granularity);
+ // New state:
+ // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good.
+ PoisonShadow(a, b1 - a, 0);
+ PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic);
+ if (b1 != b2) {
+ CHECK_EQ(b2 - b1, granularity);
+ *(u8*)MemToShadow(b1) = static_cast<u8>(new_mid - b1);
+ }
+}
+
+const void *__sanitizer_contiguous_container_find_bad_address(
+ const void *beg_p, const void *mid_p, const void *end_p) {
+ if (!flags()->detect_container_overflow)
+ return nullptr;
+ uptr beg = reinterpret_cast<uptr>(beg_p);
+ uptr end = reinterpret_cast<uptr>(end_p);
+ uptr mid = reinterpret_cast<uptr>(mid_p);
+ CHECK_LE(beg, mid);
+ CHECK_LE(mid, end);
+ // Check some bytes starting from beg, some bytes around mid, and some bytes
+ // ending with end.
+ uptr kMaxRangeToCheck = 32;
+ uptr r1_beg = beg;
+ uptr r1_end = Min(beg + kMaxRangeToCheck, mid);
+ uptr r2_beg = Max(beg, mid - kMaxRangeToCheck);
+ uptr r2_end = Min(end, mid + kMaxRangeToCheck);
+ uptr r3_beg = Max(end - kMaxRangeToCheck, mid);
+ uptr r3_end = end;
+ for (uptr i = r1_beg; i < r1_end; i++)
+ if (AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ for (uptr i = r2_beg; i < mid; i++)
+ if (AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ for (uptr i = mid; i < r2_end; i++)
+ if (!AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ for (uptr i = r3_beg; i < r3_end; i++)
+ if (!AddressIsPoisoned(i))
+ return reinterpret_cast<const void *>(i);
+ return nullptr;
+}
+
+int __sanitizer_verify_contiguous_container(const void *beg_p,
+ const void *mid_p,
+ const void *end_p) {
+ return __sanitizer_contiguous_container_find_bad_address(beg_p, mid_p,
+ end_p) == nullptr;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
+ AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
+ AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false);
+}
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+bool WordIsPoisoned(uptr addr) {
+ return (__asan_region_is_poisoned(addr, sizeof(uptr)) != 0);
+}
+}
//===-- asan_poisoning.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#include "asan_internal.h"
#include "asan_mapping.h"
#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_platform.h"
namespace __asan {
ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size,
u8 value) {
DCHECK(!value || CanPoisonMemory());
+#if SANITIZER_FUCHSIA
+ __sanitizer_fill_shadow(aligned_beg, aligned_size, value,
+ common_flags()->clear_shadow_mmap_threshold);
+#else
uptr shadow_beg = MEM_TO_SHADOW(aligned_beg);
uptr shadow_end = MEM_TO_SHADOW(
aligned_beg + aligned_size - SHADOW_GRANULARITY) + 1;
// probably provide higher-level interface for these operations.
// For now, just memset on Windows.
if (value || SANITIZER_WINDOWS == 1 ||
- // TODO(mcgrathr): Fuchsia doesn't allow the shadow mapping to be
- // changed at all. It doesn't currently have an efficient means
- // to zero a bunch of pages, but maybe we should add one.
- SANITIZER_FUCHSIA == 1 ||
// RTEMS doesn't have have pages, let alone a fast way to zero
// them, so default to memset.
SANITIZER_RTEMS == 1 ||
ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr);
}
}
+#endif // SANITIZER_FUCHSIA
}
ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone(
+++ /dev/null
-//===-- asan_posix.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Posix-specific details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_POSIX
-
-#include "asan_internal.h"
-#include "asan_interceptors.h"
-#include "asan_mapping.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_posix.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-
-#include <pthread.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <unistd.h>
-
-namespace __asan {
-
-void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
- StartReportDeadlySignal();
- SignalContext sig(siginfo, context);
- ReportDeadlySignal(sig);
-}
-
-// ---------------------- TSD ---------------- {{{1
-
-static pthread_key_t tsd_key;
-static bool tsd_key_inited = false;
-void AsanTSDInit(void (*destructor)(void *tsd)) {
- CHECK(!tsd_key_inited);
- tsd_key_inited = true;
- CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
-}
-
-void *AsanTSDGet() {
- CHECK(tsd_key_inited);
- return pthread_getspecific(tsd_key);
-}
-
-void AsanTSDSet(void *tsd) {
- CHECK(tsd_key_inited);
- pthread_setspecific(tsd_key, tsd);
-}
-
-void PlatformTSDDtor(void *tsd) {
- AsanThreadContext *context = (AsanThreadContext*)tsd;
- if (context->destructor_iterations > 1) {
- context->destructor_iterations--;
- CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
- return;
- }
- AsanThread::TSDDtor(tsd);
-}
-} // namespace __asan
-
-#endif // SANITIZER_POSIX
--- /dev/null
+//===-- asan_posix.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Posix-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_POSIX
+
+#include "asan_internal.h"
+#include "asan_interceptors.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+namespace __asan {
+
+void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ StartReportDeadlySignal();
+ SignalContext sig(siginfo, context);
+ ReportDeadlySignal(sig);
+}
+
+// ---------------------- TSD ---------------- {{{1
+
+#if SANITIZER_NETBSD && !ASAN_DYNAMIC
+// Thread Static Data cannot be used in early static ASan init on NetBSD.
+// Reuse the Asan TSD API for compatibility with existing code
+// with an alternative implementation.
+
+static void (*tsd_destructor)(void *tsd) = nullptr;
+
+struct tsd_key {
+ tsd_key() : key(nullptr) {}
+ ~tsd_key() {
+ CHECK(tsd_destructor);
+ if (key)
+ (*tsd_destructor)(key);
+ }
+ void *key;
+};
+
+static thread_local struct tsd_key key;
+
+void AsanTSDInit(void (*destructor)(void *tsd)) {
+ CHECK(!tsd_destructor);
+ tsd_destructor = destructor;
+}
+
+void *AsanTSDGet() {
+ CHECK(tsd_destructor);
+ return key.key;
+}
+
+void AsanTSDSet(void *tsd) {
+ CHECK(tsd_destructor);
+ CHECK(tsd);
+ CHECK(!key.key);
+ key.key = tsd;
+}
+
+void PlatformTSDDtor(void *tsd) {
+ CHECK(tsd_destructor);
+ CHECK_EQ(key.key, tsd);
+ key.key = nullptr;
+ // Make sure that signal handler can not see a stale current thread pointer.
+ atomic_signal_fence(memory_order_seq_cst);
+ AsanThread::TSDDtor(tsd);
+}
+#else
+static pthread_key_t tsd_key;
+static bool tsd_key_inited = false;
+void AsanTSDInit(void (*destructor)(void *tsd)) {
+ CHECK(!tsd_key_inited);
+ tsd_key_inited = true;
+ CHECK_EQ(0, pthread_key_create(&tsd_key, destructor));
+}
+
+void *AsanTSDGet() {
+ CHECK(tsd_key_inited);
+ return pthread_getspecific(tsd_key);
+}
+
+void AsanTSDSet(void *tsd) {
+ CHECK(tsd_key_inited);
+ pthread_setspecific(tsd_key, tsd);
+}
+
+void PlatformTSDDtor(void *tsd) {
+ AsanThreadContext *context = (AsanThreadContext*)tsd;
+ if (context->destructor_iterations > 1) {
+ context->destructor_iterations--;
+ CHECK_EQ(0, pthread_setspecific(tsd_key, tsd));
+ return;
+ }
+ AsanThread::TSDDtor(tsd);
+}
+#endif
+} // namespace __asan
+
+#endif // SANITIZER_POSIX
+++ /dev/null
-//===-- asan_preinit.cc ---------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Call __asan_init at the very early stage of process startup.
-//===----------------------------------------------------------------------===//
-#include "asan_internal.h"
-
-using namespace __asan;
-
-#if SANITIZER_CAN_USE_PREINIT_ARRAY
- // The symbol is called __local_asan_preinit, because it's not intended to be
- // exported.
- // This code linked into the main executable when -fsanitize=address is in
- // the link flags. It can only use exported interface functions.
- __attribute__((section(".preinit_array"), used))
- void (*__local_asan_preinit)(void) = __asan_init;
-#endif
--- /dev/null
+//===-- asan_preinit.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Call __asan_init at the very early stage of process startup.
+//===----------------------------------------------------------------------===//
+#include "asan_internal.h"
+
+using namespace __asan;
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+ // The symbol is called __local_asan_preinit, because it's not intended to be
+ // exported.
+ // This code linked into the main executable when -fsanitize=address is in
+ // the link flags. It can only use exported interface functions.
+ __attribute__((section(".preinit_array"), used))
+ void (*__local_asan_preinit)(void) = __asan_init;
+#endif
+++ /dev/null
-//===-- asan_premap_shadow.cc ---------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Reserve shadow memory with an ifunc resolver.
-//===----------------------------------------------------------------------===//
-
-#include "asan_mapping.h"
-
-#if ASAN_PREMAP_SHADOW
-
-#include "asan_premap_shadow.h"
-#include "sanitizer_common/sanitizer_posix.h"
-
-namespace __asan {
-
-// The code in this file needs to run in an unrelocated binary. It may not
-// access any external symbol, including its own non-hidden globals.
-
-// Conservative upper limit.
-uptr PremapShadowSize() {
- uptr granularity = GetMmapGranularity();
- return RoundUpTo(GetMaxVirtualAddress() >> SHADOW_SCALE, granularity);
-}
-
-// Returns an address aligned to 8 pages, such that one page on the left and
-// PremapShadowSize() bytes on the right of it are mapped r/o.
-uptr PremapShadow() {
- uptr granularity = GetMmapGranularity();
- uptr alignment = granularity * 8;
- uptr left_padding = granularity;
- uptr shadow_size = PremapShadowSize();
- uptr map_size = shadow_size + left_padding + alignment;
-
- uptr map_start = (uptr)MmapNoAccess(map_size);
- CHECK_NE(map_start, ~(uptr)0);
-
- uptr shadow_start = RoundUpTo(map_start + left_padding, alignment);
- uptr shadow_end = shadow_start + shadow_size;
- internal_munmap(reinterpret_cast<void *>(map_start),
- shadow_start - left_padding - map_start);
- internal_munmap(reinterpret_cast<void *>(shadow_end),
- map_start + map_size - shadow_end);
- return shadow_start;
-}
-
-bool PremapShadowFailed() {
- uptr shadow = reinterpret_cast<uptr>(&__asan_shadow);
- uptr resolver = reinterpret_cast<uptr>(&__asan_premap_shadow);
- // shadow == resolver is how Android KitKat and older handles ifunc.
- // shadow == 0 just in case.
- if (shadow == 0 || shadow == resolver)
- return true;
- return false;
-}
-} // namespace __asan
-
-extern "C" {
-decltype(__asan_shadow)* __asan_premap_shadow() {
- // The resolver may be called multiple times. Map the shadow just once.
- static uptr premapped_shadow = 0;
- if (!premapped_shadow) premapped_shadow = __asan::PremapShadow();
- return reinterpret_cast<decltype(__asan_shadow)*>(premapped_shadow);
-}
-
-// __asan_shadow is a "function" that has the same address as the first byte of
-// the shadow mapping.
-INTERFACE_ATTRIBUTE __attribute__((ifunc("__asan_premap_shadow"))) void
-__asan_shadow();
-}
-
-#endif // ASAN_PREMAP_SHADOW
--- /dev/null
+//===-- asan_premap_shadow.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Reserve shadow memory with an ifunc resolver.
+//===----------------------------------------------------------------------===//
+
+#include "asan_mapping.h"
+
+#if ASAN_PREMAP_SHADOW
+
+#include "asan_premap_shadow.h"
+#include "sanitizer_common/sanitizer_posix.h"
+
+namespace __asan {
+
+// The code in this file needs to run in an unrelocated binary. It may not
+// access any external symbol, including its own non-hidden globals.
+
+// Conservative upper limit.
+uptr PremapShadowSize() {
+ uptr granularity = GetMmapGranularity();
+ return RoundUpTo(GetMaxVirtualAddress() >> SHADOW_SCALE, granularity);
+}
+
+// Returns an address aligned to 8 pages, such that one page on the left and
+// PremapShadowSize() bytes on the right of it are mapped r/o.
+uptr PremapShadow() {
+ uptr granularity = GetMmapGranularity();
+ uptr alignment = granularity * 8;
+ uptr left_padding = granularity;
+ uptr shadow_size = PremapShadowSize();
+ uptr map_size = shadow_size + left_padding + alignment;
+
+ uptr map_start = (uptr)MmapNoAccess(map_size);
+ CHECK_NE(map_start, ~(uptr)0);
+
+ uptr shadow_start = RoundUpTo(map_start + left_padding, alignment);
+ uptr shadow_end = shadow_start + shadow_size;
+ internal_munmap(reinterpret_cast<void *>(map_start),
+ shadow_start - left_padding - map_start);
+ internal_munmap(reinterpret_cast<void *>(shadow_end),
+ map_start + map_size - shadow_end);
+ return shadow_start;
+}
+
+bool PremapShadowFailed() {
+ uptr shadow = reinterpret_cast<uptr>(&__asan_shadow);
+ uptr resolver = reinterpret_cast<uptr>(&__asan_premap_shadow);
+ // shadow == resolver is how Android KitKat and older handles ifunc.
+ // shadow == 0 just in case.
+ if (shadow == 0 || shadow == resolver)
+ return true;
+ return false;
+}
+} // namespace __asan
+
+extern "C" {
+decltype(__asan_shadow)* __asan_premap_shadow() {
+ // The resolver may be called multiple times. Map the shadow just once.
+ static uptr premapped_shadow = 0;
+ if (!premapped_shadow) premapped_shadow = __asan::PremapShadow();
+ return reinterpret_cast<decltype(__asan_shadow)*>(premapped_shadow);
+}
+
+// __asan_shadow is a "function" that has the same address as the first byte of
+// the shadow mapping.
+INTERFACE_ATTRIBUTE __attribute__((ifunc("__asan_premap_shadow"))) void
+__asan_shadow();
+}
+
+#endif // ASAN_PREMAP_SHADOW
//===-- asan_mapping.h ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- asan_report.cc ----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// This file contains error reporting code.
-//===----------------------------------------------------------------------===//
-
-#include "asan_errors.h"
-#include "asan_flags.h"
-#include "asan_descriptions.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_report.h"
-#include "asan_scariness_score.h"
-#include "asan_stack.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_report_decorator.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-
-namespace __asan {
-
-// -------------------- User-specified callbacks ----------------- {{{1
-static void (*error_report_callback)(const char*);
-static char *error_message_buffer = nullptr;
-static uptr error_message_buffer_pos = 0;
-static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED);
-static const unsigned kAsanBuggyPcPoolSize = 25;
-static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
-
-void AppendToErrorMessageBuffer(const char *buffer) {
- BlockingMutexLock l(&error_message_buf_mutex);
- if (!error_message_buffer) {
- error_message_buffer =
- (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
- error_message_buffer_pos = 0;
- }
- uptr length = internal_strlen(buffer);
- RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
- uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
- internal_strncpy(error_message_buffer + error_message_buffer_pos,
- buffer, remaining);
- error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
- // FIXME: reallocate the buffer instead of truncating the message.
- error_message_buffer_pos += Min(remaining, length);
-}
-
-// ---------------------- Helper functions ----------------------- {{{1
-
-void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
- bool in_shadow, const char *after) {
- Decorator d;
- str->append("%s%s%x%x%s%s", before,
- in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4,
- byte & 15, d.Default(), after);
-}
-
-static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
- const char *zone_name) {
- if (zone_ptr) {
- if (zone_name) {
- Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
- ptr, zone_ptr, zone_name);
- } else {
- Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
- ptr, zone_ptr);
- }
- } else {
- Printf("malloc_zone_from_ptr(%p) = 0\n", ptr);
- }
-}
-
-// ---------------------- Address Descriptions ------------------- {{{1
-
-bool ParseFrameDescription(const char *frame_descr,
- InternalMmapVector<StackVarDescr> *vars) {
- CHECK(frame_descr);
- const char *p;
- // This string is created by the compiler and has the following form:
- // "n alloc_1 alloc_2 ... alloc_n"
- // where alloc_i looks like "offset size len ObjectName"
- // or "offset size len ObjectName:line".
- uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
- if (n_objects == 0)
- return false;
-
- for (uptr i = 0; i < n_objects; i++) {
- uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
- uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
- uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
- if (beg == 0 || size == 0 || *p != ' ') {
- return false;
- }
- p++;
- char *colon_pos = internal_strchr(p, ':');
- uptr line = 0;
- uptr name_len = len;
- if (colon_pos != nullptr && colon_pos < p + len) {
- name_len = colon_pos - p;
- line = (uptr)internal_simple_strtoll(colon_pos + 1, nullptr, 10);
- }
- StackVarDescr var = {beg, size, p, name_len, line};
- vars->push_back(var);
- p += len;
- }
-
- return true;
-}
-
-// -------------------- Different kinds of reports ----------------- {{{1
-
-// Use ScopedInErrorReport to run common actions just before and
-// immediately after printing error report.
-class ScopedInErrorReport {
- public:
- explicit ScopedInErrorReport(bool fatal = false)
- : halt_on_error_(fatal || flags()->halt_on_error) {
- // Make sure the registry and sanitizer report mutexes are locked while
- // we're printing an error report.
- // We can lock them only here to avoid self-deadlock in case of
- // recursive reports.
- asanThreadRegistry().Lock();
- Printf(
- "=================================================================\n");
- }
-
- ~ScopedInErrorReport() {
- if (halt_on_error_ && !__sanitizer_acquire_crash_state()) {
- asanThreadRegistry().Unlock();
- return;
- }
- ASAN_ON_ERROR();
- if (current_error_.IsValid()) current_error_.Print();
-
- // Make sure the current thread is announced.
- DescribeThread(GetCurrentThread());
- // We may want to grab this lock again when printing stats.
- asanThreadRegistry().Unlock();
- // Print memory stats.
- if (flags()->print_stats)
- __asan_print_accumulated_stats();
-
- if (common_flags()->print_cmdline)
- PrintCmdline();
-
- if (common_flags()->print_module_map == 2) PrintModuleMap();
-
- // Copy the message buffer so that we could start logging without holding a
- // lock that gets aquired during printing.
- InternalMmapVector<char> buffer_copy(kErrorMessageBufferSize);
- {
- BlockingMutexLock l(&error_message_buf_mutex);
- internal_memcpy(buffer_copy.data(),
- error_message_buffer, kErrorMessageBufferSize);
- }
-
- LogFullErrorReport(buffer_copy.data());
-
- if (error_report_callback) {
- error_report_callback(buffer_copy.data());
- }
-
- if (halt_on_error_ && common_flags()->abort_on_error) {
- // On Android the message is truncated to 512 characters.
- // FIXME: implement "compact" error format, possibly without, or with
- // highly compressed stack traces?
- // FIXME: or just use the summary line as abort message?
- SetAbortMessage(buffer_copy.data());
- }
-
- // In halt_on_error = false mode, reset the current error object (before
- // unlocking).
- if (!halt_on_error_)
- internal_memset(¤t_error_, 0, sizeof(current_error_));
-
- if (halt_on_error_) {
- Report("ABORTING\n");
- Die();
- }
- }
-
- void ReportError(const ErrorDescription &description) {
- // Can only report one error per ScopedInErrorReport.
- CHECK_EQ(current_error_.kind, kErrorKindInvalid);
- current_error_ = description;
- }
-
- static ErrorDescription &CurrentError() {
- return current_error_;
- }
-
- private:
- ScopedErrorReportLock error_report_lock_;
- // Error currently being reported. This enables the destructor to interact
- // with the debugger and point it to an error description.
- static ErrorDescription current_error_;
- bool halt_on_error_;
-};
-
-ErrorDescription ScopedInErrorReport::current_error_(LINKER_INITIALIZED);
-
-void ReportDeadlySignal(const SignalContext &sig) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig);
- in_report.ReportError(error);
-}
-
-void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
- ScopedInErrorReport in_report;
- ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
- in_report.ReportError(error);
-}
-
-void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size,
- uptr delete_alignment,
- BufferedStackTrace *free_stack) {
- ScopedInErrorReport in_report;
- ErrorNewDeleteTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
- delete_size, delete_alignment);
- in_report.ReportError(error);
-}
-
-void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
- ScopedInErrorReport in_report;
- ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr);
- in_report.ReportError(error);
-}
-
-void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
- AllocType alloc_type,
- AllocType dealloc_type) {
- ScopedInErrorReport in_report;
- ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
- alloc_type, dealloc_type);
- in_report.ReportError(error);
-}
-
-void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr);
- in_report.ReportError(error);
-}
-
-void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack,
- addr);
- in_report.ReportError(error);
-}
-
-void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorCallocOverflow error(GetCurrentTidOrInvalid(), stack, count, size);
- in_report.ReportError(error);
-}
-
-void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorPvallocOverflow error(GetCurrentTidOrInvalid(), stack, size);
- in_report.ReportError(error);
-}
-
-void ReportInvalidAllocationAlignment(uptr alignment,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorInvalidAllocationAlignment error(GetCurrentTidOrInvalid(), stack,
- alignment);
- in_report.ReportError(error);
-}
-
-void ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorInvalidAlignedAllocAlignment error(GetCurrentTidOrInvalid(), stack,
- size, alignment);
- in_report.ReportError(error);
-}
-
-void ReportInvalidPosixMemalignAlignment(uptr alignment,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorInvalidPosixMemalignAlignment error(GetCurrentTidOrInvalid(), stack,
- alignment);
- in_report.ReportError(error);
-}
-
-void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorAllocationSizeTooBig error(GetCurrentTidOrInvalid(), stack, user_size,
- total_size, max_size);
- in_report.ReportError(error);
-}
-
-void ReportRssLimitExceeded(BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorRssLimitExceeded error(GetCurrentTidOrInvalid(), stack);
- in_report.ReportError(error);
-}
-
-void ReportOutOfMemory(uptr requested_size, BufferedStackTrace *stack) {
- ScopedInErrorReport in_report(/*fatal*/ true);
- ErrorOutOfMemory error(GetCurrentTidOrInvalid(), stack, requested_size);
- in_report.ReportError(error);
-}
-
-void ReportStringFunctionMemoryRangesOverlap(const char *function,
- const char *offset1, uptr length1,
- const char *offset2, uptr length2,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- ErrorStringFunctionMemoryRangesOverlap error(
- GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2,
- length2, function);
- in_report.ReportError(error);
-}
-
-void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset,
- size);
- in_report.ReportError(error);
-}
-
-void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
- uptr old_mid, uptr new_mid,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- ErrorBadParamsToAnnotateContiguousContainer error(
- GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid);
- in_report.ReportError(error);
-}
-
-void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
- const __asan_global *g2, u32 stack_id2) {
- ScopedInErrorReport in_report;
- ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2,
- stack_id2);
- in_report.ReportError(error);
-}
-
-// ----------------------- CheckForInvalidPointerPair ----------- {{{1
-static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp,
- uptr a1, uptr a2) {
- ScopedInErrorReport in_report;
- ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2);
- in_report.ReportError(error);
-}
-
-static bool IsInvalidPointerPair(uptr a1, uptr a2) {
- if (a1 == a2)
- return false;
-
- // 256B in shadow memory can be iterated quite fast
- static const uptr kMaxOffset = 2048;
-
- uptr left = a1 < a2 ? a1 : a2;
- uptr right = a1 < a2 ? a2 : a1;
- uptr offset = right - left;
- if (offset <= kMaxOffset)
- return __asan_region_is_poisoned(left, offset);
-
- AsanThread *t = GetCurrentThread();
-
- // check whether left is a stack memory pointer
- if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) {
- uptr shadow_offset2 = t->GetStackVariableShadowStart(right);
- return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2;
- }
-
- // check whether left is a heap memory address
- HeapAddressDescription hdesc1, hdesc2;
- if (GetHeapAddressInformation(left, 0, &hdesc1) &&
- hdesc1.chunk_access.access_type == kAccessTypeInside)
- return !GetHeapAddressInformation(right, 0, &hdesc2) ||
- hdesc2.chunk_access.access_type != kAccessTypeInside ||
- hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin;
-
- // check whether left is an address of a global variable
- GlobalAddressDescription gdesc1, gdesc2;
- if (GetGlobalAddressInformation(left, 0, &gdesc1))
- return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) ||
- !gdesc1.PointsInsideTheSameVariable(gdesc2);
-
- if (t->GetStackVariableShadowStart(right) ||
- GetHeapAddressInformation(right, 0, &hdesc2) ||
- GetGlobalAddressInformation(right - 1, 0, &gdesc2))
- return true;
-
- // At this point we know nothing about both a1 and a2 addresses.
- return false;
-}
-
-static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
- switch (flags()->detect_invalid_pointer_pairs) {
- case 0 : return;
- case 1 : if (p1 == nullptr || p2 == nullptr) return; break;
- }
-
- uptr a1 = reinterpret_cast<uptr>(p1);
- uptr a2 = reinterpret_cast<uptr>(p2);
-
- if (IsInvalidPointerPair(a1, a2)) {
- GET_CALLER_PC_BP_SP;
- ReportInvalidPointerPair(pc, bp, sp, a1, a2);
- }
-}
-// ----------------------- Mac-specific reports ----------------- {{{1
-
-void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
- BufferedStackTrace *stack) {
- ScopedInErrorReport in_report;
- Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
- "This is an unrecoverable problem, exiting now.\n",
- addr);
- PrintZoneForPointer(addr, zone_ptr, zone_name);
- stack->Print();
- DescribeAddressIfHeap(addr);
-}
-
-// -------------- SuppressErrorReport -------------- {{{1
-// Avoid error reports duplicating for ASan recover mode.
-static bool SuppressErrorReport(uptr pc) {
- if (!common_flags()->suppress_equal_pcs) return false;
- for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) {
- uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]);
- if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp,
- pc, memory_order_relaxed))
- return false;
- if (cmp == pc) return true;
- }
- Die();
-}
-
-void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
- uptr access_size, u32 exp, bool fatal) {
- if (!fatal && SuppressErrorReport(pc)) return;
- ENABLE_FRAME_POINTER;
-
- // Optimization experiments.
- // The experiments can be used to evaluate potential optimizations that remove
- // instrumentation (assess false negatives). Instead of completely removing
- // some instrumentation, compiler can emit special calls into runtime
- // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass
- // mask of experiments (exp).
- // The reaction to a non-zero value of exp is to be defined.
- (void)exp;
-
- ScopedInErrorReport in_report(fatal);
- ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write,
- access_size);
- in_report.ReportError(error);
-}
-
-} // namespace __asan
-
-// --------------------------- Interface --------------------- {{{1
-using namespace __asan; // NOLINT
-
-void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
- uptr access_size, u32 exp) {
- ENABLE_FRAME_POINTER;
- bool fatal = flags()->halt_on_error;
- ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal);
-}
-
-void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
- BlockingMutexLock l(&error_message_buf_mutex);
- error_report_callback = callback;
-}
-
-void __asan_describe_address(uptr addr) {
- // Thread registry must be locked while we're describing an address.
- asanThreadRegistry().Lock();
- PrintAddressDescription(addr, 1, "");
- asanThreadRegistry().Unlock();
-}
-
-int __asan_report_present() {
- return ScopedInErrorReport::CurrentError().kind != kErrorKindInvalid;
-}
-
-uptr __asan_get_report_pc() {
- if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
- return ScopedInErrorReport::CurrentError().Generic.pc;
- return 0;
-}
-
-uptr __asan_get_report_bp() {
- if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
- return ScopedInErrorReport::CurrentError().Generic.bp;
- return 0;
-}
-
-uptr __asan_get_report_sp() {
- if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
- return ScopedInErrorReport::CurrentError().Generic.sp;
- return 0;
-}
-
-uptr __asan_get_report_address() {
- ErrorDescription &err = ScopedInErrorReport::CurrentError();
- if (err.kind == kErrorKindGeneric)
- return err.Generic.addr_description.Address();
- else if (err.kind == kErrorKindDoubleFree)
- return err.DoubleFree.addr_description.addr;
- return 0;
-}
-
-int __asan_get_report_access_type() {
- if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
- return ScopedInErrorReport::CurrentError().Generic.is_write;
- return 0;
-}
-
-uptr __asan_get_report_access_size() {
- if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
- return ScopedInErrorReport::CurrentError().Generic.access_size;
- return 0;
-}
-
-const char *__asan_get_report_description() {
- if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
- return ScopedInErrorReport::CurrentError().Generic.bug_descr;
- return ScopedInErrorReport::CurrentError().Base.scariness.GetDescription();
-}
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_ptr_sub(void *a, void *b) {
- CheckForInvalidPointerPair(a, b);
-}
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_ptr_cmp(void *a, void *b) {
- CheckForInvalidPointerPair(a, b);
-}
-} // extern "C"
-
-// Provide default implementation of __asan_on_error that does nothing
-// and may be overriden by user.
-SANITIZER_INTERFACE_WEAK_DEF(void, __asan_on_error, void) {}
--- /dev/null
+//===-- asan_report.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file contains error reporting code.
+//===----------------------------------------------------------------------===//
+
+#include "asan_errors.h"
+#include "asan_flags.h"
+#include "asan_descriptions.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_scariness_score.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __asan {
+
+// -------------------- User-specified callbacks ----------------- {{{1
+static void (*error_report_callback)(const char*);
+static char *error_message_buffer = nullptr;
+static uptr error_message_buffer_pos = 0;
+static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED);
+static const unsigned kAsanBuggyPcPoolSize = 25;
+static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
+
+void AppendToErrorMessageBuffer(const char *buffer) {
+ BlockingMutexLock l(&error_message_buf_mutex);
+ if (!error_message_buffer) {
+ error_message_buffer =
+ (char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
+ error_message_buffer_pos = 0;
+ }
+ uptr length = internal_strlen(buffer);
+ RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
+ uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
+ internal_strncpy(error_message_buffer + error_message_buffer_pos,
+ buffer, remaining);
+ error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
+ // FIXME: reallocate the buffer instead of truncating the message.
+ error_message_buffer_pos += Min(remaining, length);
+}
+
+// ---------------------- Helper functions ----------------------- {{{1
+
+void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
+ bool in_shadow, const char *after) {
+ Decorator d;
+ str->append("%s%s%x%x%s%s", before,
+ in_shadow ? d.ShadowByte(byte) : d.MemoryByte(), byte >> 4,
+ byte & 15, d.Default(), after);
+}
+
+static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
+ const char *zone_name) {
+ if (zone_ptr) {
+ if (zone_name) {
+ Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
+ ptr, zone_ptr, zone_name);
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
+ ptr, zone_ptr);
+ }
+ } else {
+ Printf("malloc_zone_from_ptr(%p) = 0\n", ptr);
+ }
+}
+
+// ---------------------- Address Descriptions ------------------- {{{1
+
+bool ParseFrameDescription(const char *frame_descr,
+ InternalMmapVector<StackVarDescr> *vars) {
+ CHECK(frame_descr);
+ const char *p;
+ // This string is created by the compiler and has the following form:
+ // "n alloc_1 alloc_2 ... alloc_n"
+ // where alloc_i looks like "offset size len ObjectName"
+ // or "offset size len ObjectName:line".
+ uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
+ if (n_objects == 0)
+ return false;
+
+ for (uptr i = 0; i < n_objects; i++) {
+ uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
+ uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
+ uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
+ if (beg == 0 || size == 0 || *p != ' ') {
+ return false;
+ }
+ p++;
+ char *colon_pos = internal_strchr(p, ':');
+ uptr line = 0;
+ uptr name_len = len;
+ if (colon_pos != nullptr && colon_pos < p + len) {
+ name_len = colon_pos - p;
+ line = (uptr)internal_simple_strtoll(colon_pos + 1, nullptr, 10);
+ }
+ StackVarDescr var = {beg, size, p, name_len, line};
+ vars->push_back(var);
+ p += len;
+ }
+
+ return true;
+}
+
+// -------------------- Different kinds of reports ----------------- {{{1
+
+// Use ScopedInErrorReport to run common actions just before and
+// immediately after printing error report.
+class ScopedInErrorReport {
+ public:
+ explicit ScopedInErrorReport(bool fatal = false)
+ : halt_on_error_(fatal || flags()->halt_on_error) {
+ // Make sure the registry and sanitizer report mutexes are locked while
+ // we're printing an error report.
+ // We can lock them only here to avoid self-deadlock in case of
+ // recursive reports.
+ asanThreadRegistry().Lock();
+ Printf(
+ "=================================================================\n");
+ }
+
+ ~ScopedInErrorReport() {
+ if (halt_on_error_ && !__sanitizer_acquire_crash_state()) {
+ asanThreadRegistry().Unlock();
+ return;
+ }
+ ASAN_ON_ERROR();
+ if (current_error_.IsValid()) current_error_.Print();
+
+ // Make sure the current thread is announced.
+ DescribeThread(GetCurrentThread());
+ // We may want to grab this lock again when printing stats.
+ asanThreadRegistry().Unlock();
+ // Print memory stats.
+ if (flags()->print_stats)
+ __asan_print_accumulated_stats();
+
+ if (common_flags()->print_cmdline)
+ PrintCmdline();
+
+ if (common_flags()->print_module_map == 2) PrintModuleMap();
+
+ // Copy the message buffer so that we could start logging without holding a
+ // lock that gets aquired during printing.
+ InternalMmapVector<char> buffer_copy(kErrorMessageBufferSize);
+ {
+ BlockingMutexLock l(&error_message_buf_mutex);
+ internal_memcpy(buffer_copy.data(),
+ error_message_buffer, kErrorMessageBufferSize);
+ }
+
+ LogFullErrorReport(buffer_copy.data());
+
+ if (error_report_callback) {
+ error_report_callback(buffer_copy.data());
+ }
+
+ if (halt_on_error_ && common_flags()->abort_on_error) {
+ // On Android the message is truncated to 512 characters.
+ // FIXME: implement "compact" error format, possibly without, or with
+ // highly compressed stack traces?
+ // FIXME: or just use the summary line as abort message?
+ SetAbortMessage(buffer_copy.data());
+ }
+
+ // In halt_on_error = false mode, reset the current error object (before
+ // unlocking).
+ if (!halt_on_error_)
+ internal_memset(¤t_error_, 0, sizeof(current_error_));
+
+ if (halt_on_error_) {
+ Report("ABORTING\n");
+ Die();
+ }
+ }
+
+ void ReportError(const ErrorDescription &description) {
+ // Can only report one error per ScopedInErrorReport.
+ CHECK_EQ(current_error_.kind, kErrorKindInvalid);
+ internal_memcpy(¤t_error_, &description, sizeof(current_error_));
+ }
+
+ static ErrorDescription &CurrentError() {
+ return current_error_;
+ }
+
+ private:
+ ScopedErrorReportLock error_report_lock_;
+ // Error currently being reported. This enables the destructor to interact
+ // with the debugger and point it to an error description.
+ static ErrorDescription current_error_;
+ bool halt_on_error_;
+};
+
+ErrorDescription ScopedInErrorReport::current_error_(LINKER_INITIALIZED);
+
+void ReportDeadlySignal(const SignalContext &sig) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig);
+ in_report.ReportError(error);
+}
+
+void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
+ ScopedInErrorReport in_report;
+ ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
+ in_report.ReportError(error);
+}
+
+void ReportNewDeleteTypeMismatch(uptr addr, uptr delete_size,
+ uptr delete_alignment,
+ BufferedStackTrace *free_stack) {
+ ScopedInErrorReport in_report;
+ ErrorNewDeleteTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
+ delete_size, delete_alignment);
+ in_report.ReportError(error);
+}
+
+void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
+ ScopedInErrorReport in_report;
+ ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr);
+ in_report.ReportError(error);
+}
+
+void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
+ AllocType alloc_type,
+ AllocType dealloc_type) {
+ ScopedInErrorReport in_report;
+ ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
+ alloc_type, dealloc_type);
+ in_report.ReportError(error);
+}
+
+void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr);
+ in_report.ReportError(error);
+}
+
+void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack,
+ addr);
+ in_report.ReportError(error);
+}
+
+void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorCallocOverflow error(GetCurrentTidOrInvalid(), stack, count, size);
+ in_report.ReportError(error);
+}
+
+void ReportReallocArrayOverflow(uptr count, uptr size,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorReallocArrayOverflow error(GetCurrentTidOrInvalid(), stack, count, size);
+ in_report.ReportError(error);
+}
+
+void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorPvallocOverflow error(GetCurrentTidOrInvalid(), stack, size);
+ in_report.ReportError(error);
+}
+
+void ReportInvalidAllocationAlignment(uptr alignment,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorInvalidAllocationAlignment error(GetCurrentTidOrInvalid(), stack,
+ alignment);
+ in_report.ReportError(error);
+}
+
+void ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorInvalidAlignedAllocAlignment error(GetCurrentTidOrInvalid(), stack,
+ size, alignment);
+ in_report.ReportError(error);
+}
+
+void ReportInvalidPosixMemalignAlignment(uptr alignment,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorInvalidPosixMemalignAlignment error(GetCurrentTidOrInvalid(), stack,
+ alignment);
+ in_report.ReportError(error);
+}
+
+void ReportAllocationSizeTooBig(uptr user_size, uptr total_size, uptr max_size,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorAllocationSizeTooBig error(GetCurrentTidOrInvalid(), stack, user_size,
+ total_size, max_size);
+ in_report.ReportError(error);
+}
+
+void ReportRssLimitExceeded(BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorRssLimitExceeded error(GetCurrentTidOrInvalid(), stack);
+ in_report.ReportError(error);
+}
+
+void ReportOutOfMemory(uptr requested_size, BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report(/*fatal*/ true);
+ ErrorOutOfMemory error(GetCurrentTidOrInvalid(), stack, requested_size);
+ in_report.ReportError(error);
+}
+
+void ReportStringFunctionMemoryRangesOverlap(const char *function,
+ const char *offset1, uptr length1,
+ const char *offset2, uptr length2,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorStringFunctionMemoryRangesOverlap error(
+ GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2,
+ length2, function);
+ in_report.ReportError(error);
+}
+
+void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset,
+ size);
+ in_report.ReportError(error);
+}
+
+void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
+ uptr old_mid, uptr new_mid,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ ErrorBadParamsToAnnotateContiguousContainer error(
+ GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid);
+ in_report.ReportError(error);
+}
+
+void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
+ const __asan_global *g2, u32 stack_id2) {
+ ScopedInErrorReport in_report;
+ ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2,
+ stack_id2);
+ in_report.ReportError(error);
+}
+
+// ----------------------- CheckForInvalidPointerPair ----------- {{{1
+static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp,
+ uptr a1, uptr a2) {
+ ScopedInErrorReport in_report;
+ ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2);
+ in_report.ReportError(error);
+}
+
+static bool IsInvalidPointerPair(uptr a1, uptr a2) {
+ if (a1 == a2)
+ return false;
+
+ // 256B in shadow memory can be iterated quite fast
+ static const uptr kMaxOffset = 2048;
+
+ uptr left = a1 < a2 ? a1 : a2;
+ uptr right = a1 < a2 ? a2 : a1;
+ uptr offset = right - left;
+ if (offset <= kMaxOffset)
+ return __asan_region_is_poisoned(left, offset);
+
+ AsanThread *t = GetCurrentThread();
+
+ // check whether left is a stack memory pointer
+ if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) {
+ uptr shadow_offset2 = t->GetStackVariableShadowStart(right);
+ return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2;
+ }
+
+ // check whether left is a heap memory address
+ HeapAddressDescription hdesc1, hdesc2;
+ if (GetHeapAddressInformation(left, 0, &hdesc1) &&
+ hdesc1.chunk_access.access_type == kAccessTypeInside)
+ return !GetHeapAddressInformation(right, 0, &hdesc2) ||
+ hdesc2.chunk_access.access_type != kAccessTypeInside ||
+ hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin;
+
+ // check whether left is an address of a global variable
+ GlobalAddressDescription gdesc1, gdesc2;
+ if (GetGlobalAddressInformation(left, 0, &gdesc1))
+ return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) ||
+ !gdesc1.PointsInsideTheSameVariable(gdesc2);
+
+ if (t->GetStackVariableShadowStart(right) ||
+ GetHeapAddressInformation(right, 0, &hdesc2) ||
+ GetGlobalAddressInformation(right - 1, 0, &gdesc2))
+ return true;
+
+ // At this point we know nothing about both a1 and a2 addresses.
+ return false;
+}
+
+static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
+ switch (flags()->detect_invalid_pointer_pairs) {
+ case 0 : return;
+ case 1 : if (p1 == nullptr || p2 == nullptr) return; break;
+ }
+
+ uptr a1 = reinterpret_cast<uptr>(p1);
+ uptr a2 = reinterpret_cast<uptr>(p2);
+
+ if (IsInvalidPointerPair(a1, a2)) {
+ GET_CALLER_PC_BP_SP;
+ ReportInvalidPointerPair(pc, bp, sp, a1, a2);
+ }
+}
+// ----------------------- Mac-specific reports ----------------- {{{1
+
+void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
+ BufferedStackTrace *stack) {
+ ScopedInErrorReport in_report;
+ Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
+ "This is an unrecoverable problem, exiting now.\n",
+ addr);
+ PrintZoneForPointer(addr, zone_ptr, zone_name);
+ stack->Print();
+ DescribeAddressIfHeap(addr);
+}
+
+// -------------- SuppressErrorReport -------------- {{{1
+// Avoid error reports duplicating for ASan recover mode.
+static bool SuppressErrorReport(uptr pc) {
+ if (!common_flags()->suppress_equal_pcs) return false;
+ for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) {
+ uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]);
+ if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp,
+ pc, memory_order_relaxed))
+ return false;
+ if (cmp == pc) return true;
+ }
+ Die();
+}
+
+void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
+ uptr access_size, u32 exp, bool fatal) {
+ if (!fatal && SuppressErrorReport(pc)) return;
+ ENABLE_FRAME_POINTER;
+
+ // Optimization experiments.
+ // The experiments can be used to evaluate potential optimizations that remove
+ // instrumentation (assess false negatives). Instead of completely removing
+ // some instrumentation, compiler can emit special calls into runtime
+ // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass
+ // mask of experiments (exp).
+ // The reaction to a non-zero value of exp is to be defined.
+ (void)exp;
+
+ ScopedInErrorReport in_report(fatal);
+ ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write,
+ access_size);
+ in_report.ReportError(error);
+}
+
+} // namespace __asan
+
+// --------------------------- Interface --------------------- {{{1
+using namespace __asan; // NOLINT
+
+void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
+ uptr access_size, u32 exp) {
+ ENABLE_FRAME_POINTER;
+ bool fatal = flags()->halt_on_error;
+ ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal);
+}
+
+void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
+ BlockingMutexLock l(&error_message_buf_mutex);
+ error_report_callback = callback;
+}
+
+void __asan_describe_address(uptr addr) {
+ // Thread registry must be locked while we're describing an address.
+ asanThreadRegistry().Lock();
+ PrintAddressDescription(addr, 1, "");
+ asanThreadRegistry().Unlock();
+}
+
+int __asan_report_present() {
+ return ScopedInErrorReport::CurrentError().kind != kErrorKindInvalid;
+}
+
+uptr __asan_get_report_pc() {
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.pc;
+ return 0;
+}
+
+uptr __asan_get_report_bp() {
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.bp;
+ return 0;
+}
+
+uptr __asan_get_report_sp() {
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.sp;
+ return 0;
+}
+
+uptr __asan_get_report_address() {
+ ErrorDescription &err = ScopedInErrorReport::CurrentError();
+ if (err.kind == kErrorKindGeneric)
+ return err.Generic.addr_description.Address();
+ else if (err.kind == kErrorKindDoubleFree)
+ return err.DoubleFree.addr_description.addr;
+ return 0;
+}
+
+int __asan_get_report_access_type() {
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.is_write;
+ return 0;
+}
+
+uptr __asan_get_report_access_size() {
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.access_size;
+ return 0;
+}
+
+const char *__asan_get_report_description() {
+ if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+ return ScopedInErrorReport::CurrentError().Generic.bug_descr;
+ return ScopedInErrorReport::CurrentError().Base.scariness.GetDescription();
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_ptr_sub(void *a, void *b) {
+ CheckForInvalidPointerPair(a, b);
+}
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_ptr_cmp(void *a, void *b) {
+ CheckForInvalidPointerPair(a, b);
+}
+} // extern "C"
+
+// Provide default implementation of __asan_on_error that does nothing
+// and may be overriden by user.
+SANITIZER_INTERFACE_WEAK_DEF(void, __asan_on_error, void) {}
//===-- asan_report.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
BufferedStackTrace *stack);
void ReportCallocOverflow(uptr count, uptr size, BufferedStackTrace *stack);
+void ReportReallocArrayOverflow(uptr count, uptr size,
+ BufferedStackTrace *stack);
void ReportPvallocOverflow(uptr size, BufferedStackTrace *stack);
void ReportInvalidAllocationAlignment(uptr alignment,
BufferedStackTrace *stack);
+++ /dev/null
-//===-- asan_rtems.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// RTEMS-specific details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_rtems.h"
-#if SANITIZER_RTEMS
-
-#include "asan_internal.h"
-#include "asan_interceptors.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-
-#include <pthread.h>
-#include <stdlib.h>
-
-namespace __asan {
-
-static void ResetShadowMemory() {
- uptr shadow_start = SHADOW_OFFSET;
- uptr shadow_end = MEM_TO_SHADOW(kMyriadMemoryEnd32);
- uptr gap_start = MEM_TO_SHADOW(shadow_start);
- uptr gap_end = MEM_TO_SHADOW(shadow_end);
-
- REAL(memset)((void *)shadow_start, 0, shadow_end - shadow_start);
- REAL(memset)((void *)gap_start, kAsanShadowGap, gap_end - gap_start);
-}
-
-void InitializeShadowMemory() {
- kHighMemEnd = 0;
- kMidMemBeg = 0;
- kMidMemEnd = 0;
-
- ResetShadowMemory();
-}
-
-void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
- UNIMPLEMENTED();
-}
-
-void AsanCheckDynamicRTPrereqs() {}
-void AsanCheckIncompatibleRT() {}
-void InitializeAsanInterceptors() {}
-void InitializePlatformInterceptors() {}
-void InitializePlatformExceptionHandlers() {}
-
-// RTEMS only support static linking; it sufficies to return with no
-// error.
-void *AsanDoesNotSupportStaticLinkage() { return nullptr; }
-
-void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
- UNIMPLEMENTED();
-}
-
-void EarlyInit() {
- // Provide early initialization of shadow memory so that
- // instrumented code running before full initialzation will not
- // report spurious errors.
- ResetShadowMemory();
-}
-
-// We can use a plain thread_local variable for TSD.
-static thread_local void *per_thread;
-
-void *AsanTSDGet() { return per_thread; }
-
-void AsanTSDSet(void *tsd) { per_thread = tsd; }
-
-// There's no initialization needed, and the passed-in destructor
-// will never be called. Instead, our own thread destruction hook
-// (below) will call AsanThread::TSDDtor directly.
-void AsanTSDInit(void (*destructor)(void *tsd)) {
- DCHECK(destructor == &PlatformTSDDtor);
-}
-
-void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); }
-
-//
-// Thread registration. We provide an API similar to the Fushia port.
-//
-
-struct AsanThread::InitOptions {
- uptr stack_bottom, stack_size, tls_bottom, tls_size;
-};
-
-// Shared setup between thread creation and startup for the initial thread.
-static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid,
- uptr user_id, bool detached,
- uptr stack_bottom, uptr stack_size,
- uptr tls_bottom, uptr tls_size) {
- // In lieu of AsanThread::Create.
- AsanThread *thread = (AsanThread *)MmapOrDie(sizeof(AsanThread), __func__);
- AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
- asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args);
-
- // On other systems, AsanThread::Init() is called from the new
- // thread itself. But on RTEMS we already know the stack address
- // range beforehand, so we can do most of the setup right now.
- const AsanThread::InitOptions options = {stack_bottom, stack_size,
- tls_bottom, tls_size};
- thread->Init(&options);
- return thread;
-}
-
-// This gets the same arguments passed to Init by CreateAsanThread, above.
-// We're in the creator thread before the new thread is actually started, but
-// its stack and tls address range are already known.
-void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) {
- DCHECK_NE(GetCurrentThread(), this);
- DCHECK_NE(GetCurrentThread(), nullptr);
- CHECK_NE(options->stack_bottom, 0);
- CHECK_NE(options->stack_size, 0);
- stack_bottom_ = options->stack_bottom;
- stack_top_ = options->stack_bottom + options->stack_size;
- tls_begin_ = options->tls_bottom;
- tls_end_ = options->tls_bottom + options->tls_size;
-}
-
-// Called by __asan::AsanInitInternal (asan_rtl.c). Unlike other ports, the
-// main thread on RTEMS does not require special treatment; its AsanThread is
-// already created by the provided hooks. This function simply looks up and
-// returns the created thread.
-AsanThread *CreateMainThread() {
- return GetThreadContextByTidLocked(0)->thread;
-}
-
-// This is called before each thread creation is attempted. So, in
-// its first call, the calling thread is the initial and sole thread.
-static void *BeforeThreadCreateHook(uptr user_id, bool detached,
- uptr stack_bottom, uptr stack_size,
- uptr tls_bottom, uptr tls_size) {
- EnsureMainThreadIDIsCorrect();
- // Strict init-order checking is thread-hostile.
- if (flags()->strict_init_order) StopInitOrderChecking();
-
- GET_STACK_TRACE_THREAD;
- u32 parent_tid = GetCurrentTidOrInvalid();
-
- return CreateAsanThread(&stack, parent_tid, user_id, detached,
- stack_bottom, stack_size, tls_bottom, tls_size);
-}
-
-// This is called after creating a new thread (in the creating thread),
-// with the pointer returned by BeforeThreadCreateHook (above).
-static void ThreadCreateHook(void *hook, bool aborted) {
- AsanThread *thread = static_cast<AsanThread *>(hook);
- if (!aborted) {
- // The thread was created successfully.
- // ThreadStartHook is already running in the new thread.
- } else {
- // The thread wasn't created after all.
- // Clean up everything we set up in BeforeThreadCreateHook.
- asanThreadRegistry().FinishThread(thread->tid());
- UnmapOrDie(thread, sizeof(AsanThread));
- }
-}
-
-// This is called (1) in the newly-created thread before it runs anything else,
-// with the pointer returned by BeforeThreadCreateHook (above). (2) before a
-// thread restart.
-static void ThreadStartHook(void *hook, uptr os_id) {
- if (!hook)
- return;
-
- AsanThread *thread = static_cast<AsanThread *>(hook);
- SetCurrentThread(thread);
-
- ThreadStatus status =
- asanThreadRegistry().GetThreadLocked(thread->tid())->status;
- DCHECK(status == ThreadStatusCreated || status == ThreadStatusRunning);
- // Determine whether we are starting or restarting the thread.
- if (status == ThreadStatusCreated)
- // In lieu of AsanThread::ThreadStart.
- asanThreadRegistry().StartThread(thread->tid(), os_id,
- /*workerthread*/ false, nullptr);
- else {
- // In a thread restart, a thread may resume execution at an
- // arbitrary function entry point, with its stack and TLS state
- // reset. We unpoison the stack in that case.
- PoisonShadow(thread->stack_bottom(), thread->stack_size(), 0);
- }
-}
-
-// Each thread runs this just before it exits,
-// with the pointer returned by BeforeThreadCreateHook (above).
-// All per-thread destructors have already been called.
-static void ThreadExitHook(void *hook, uptr os_id) {
- AsanThread *thread = static_cast<AsanThread *>(hook);
- if (thread)
- AsanThread::TSDDtor(thread->context());
-}
-
-static void HandleExit() {
- // Disable ASan by setting it to uninitialized. Also reset the
- // shadow memory to avoid reporting errors after the run-time has
- // been desroyed.
- if (asan_inited) {
- asan_inited = false;
- ResetShadowMemory();
- }
-}
-
-} // namespace __asan
-
-// These are declared (in extern "C") by <some_path/sanitizer.h>.
-// The system runtime will call our definitions directly.
-
-extern "C" {
-void __sanitizer_early_init() {
- __asan::EarlyInit();
-}
-
-void *__sanitizer_before_thread_create_hook(uptr thread, bool detached,
- const char *name,
- void *stack_base, size_t stack_size,
- void *tls_base, size_t tls_size) {
- return __asan::BeforeThreadCreateHook(
- thread, detached,
- reinterpret_cast<uptr>(stack_base), stack_size,
- reinterpret_cast<uptr>(tls_base), tls_size);
-}
-
-void __sanitizer_thread_create_hook(void *handle, uptr thread, int status) {
- __asan::ThreadCreateHook(handle, status != 0);
-}
-
-void __sanitizer_thread_start_hook(void *handle, uptr self) {
- __asan::ThreadStartHook(handle, self);
-}
-
-void __sanitizer_thread_exit_hook(void *handle, uptr self) {
- __asan::ThreadExitHook(handle, self);
-}
-
-void __sanitizer_exit() {
- __asan::HandleExit();
-}
-} // "C"
-
-#endif // SANITIZER_RTEMS
--- /dev/null
+//===-- asan_rtems.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// RTEMS-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_rtems.h"
+#if SANITIZER_RTEMS
+
+#include "asan_internal.h"
+#include "asan_interceptors.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+namespace __asan {
+
+static void ResetShadowMemory() {
+ uptr shadow_start = SHADOW_OFFSET;
+ uptr shadow_end = MEM_TO_SHADOW(kMyriadMemoryEnd32);
+ uptr gap_start = MEM_TO_SHADOW(shadow_start);
+ uptr gap_end = MEM_TO_SHADOW(shadow_end);
+
+ REAL(memset)((void *)shadow_start, 0, shadow_end - shadow_start);
+ REAL(memset)((void *)gap_start, kAsanShadowGap, gap_end - gap_start);
+}
+
+void InitializeShadowMemory() {
+ kHighMemEnd = 0;
+ kMidMemBeg = 0;
+ kMidMemEnd = 0;
+
+ ResetShadowMemory();
+}
+
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ UNIMPLEMENTED();
+}
+
+void AsanCheckDynamicRTPrereqs() {}
+void AsanCheckIncompatibleRT() {}
+void InitializeAsanInterceptors() {}
+void InitializePlatformInterceptors() {}
+void InitializePlatformExceptionHandlers() {}
+
+// RTEMS only support static linking; it sufficies to return with no
+// error.
+void *AsanDoesNotSupportStaticLinkage() { return nullptr; }
+
+void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ UNIMPLEMENTED();
+}
+
+void EarlyInit() {
+ // Provide early initialization of shadow memory so that
+ // instrumented code running before full initialzation will not
+ // report spurious errors.
+ ResetShadowMemory();
+}
+
+// We can use a plain thread_local variable for TSD.
+static thread_local void *per_thread;
+
+void *AsanTSDGet() { return per_thread; }
+
+void AsanTSDSet(void *tsd) { per_thread = tsd; }
+
+// There's no initialization needed, and the passed-in destructor
+// will never be called. Instead, our own thread destruction hook
+// (below) will call AsanThread::TSDDtor directly.
+void AsanTSDInit(void (*destructor)(void *tsd)) {
+ DCHECK(destructor == &PlatformTSDDtor);
+}
+
+void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); }
+
+//
+// Thread registration. We provide an API similar to the Fushia port.
+//
+
+struct AsanThread::InitOptions {
+ uptr stack_bottom, stack_size, tls_bottom, tls_size;
+};
+
+// Shared setup between thread creation and startup for the initial thread.
+static AsanThread *CreateAsanThread(StackTrace *stack, u32 parent_tid,
+ uptr user_id, bool detached,
+ uptr stack_bottom, uptr stack_size,
+ uptr tls_bottom, uptr tls_size) {
+ // In lieu of AsanThread::Create.
+ AsanThread *thread = (AsanThread *)MmapOrDie(sizeof(AsanThread), __func__);
+ AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
+ asanThreadRegistry().CreateThread(user_id, detached, parent_tid, &args);
+
+ // On other systems, AsanThread::Init() is called from the new
+ // thread itself. But on RTEMS we already know the stack address
+ // range beforehand, so we can do most of the setup right now.
+ const AsanThread::InitOptions options = {stack_bottom, stack_size,
+ tls_bottom, tls_size};
+ thread->Init(&options);
+ return thread;
+}
+
+// This gets the same arguments passed to Init by CreateAsanThread, above.
+// We're in the creator thread before the new thread is actually started, but
+// its stack and tls address range are already known.
+void AsanThread::SetThreadStackAndTls(const AsanThread::InitOptions *options) {
+ DCHECK_NE(GetCurrentThread(), this);
+ DCHECK_NE(GetCurrentThread(), nullptr);
+ CHECK_NE(options->stack_bottom, 0);
+ CHECK_NE(options->stack_size, 0);
+ stack_bottom_ = options->stack_bottom;
+ stack_top_ = options->stack_bottom + options->stack_size;
+ tls_begin_ = options->tls_bottom;
+ tls_end_ = options->tls_bottom + options->tls_size;
+}
+
+// Called by __asan::AsanInitInternal (asan_rtl.c). Unlike other ports, the
+// main thread on RTEMS does not require special treatment; its AsanThread is
+// already created by the provided hooks. This function simply looks up and
+// returns the created thread.
+AsanThread *CreateMainThread() {
+ return GetThreadContextByTidLocked(0)->thread;
+}
+
+// This is called before each thread creation is attempted. So, in
+// its first call, the calling thread is the initial and sole thread.
+static void *BeforeThreadCreateHook(uptr user_id, bool detached,
+ uptr stack_bottom, uptr stack_size,
+ uptr tls_bottom, uptr tls_size) {
+ EnsureMainThreadIDIsCorrect();
+ // Strict init-order checking is thread-hostile.
+ if (flags()->strict_init_order) StopInitOrderChecking();
+
+ GET_STACK_TRACE_THREAD;
+ u32 parent_tid = GetCurrentTidOrInvalid();
+
+ return CreateAsanThread(&stack, parent_tid, user_id, detached,
+ stack_bottom, stack_size, tls_bottom, tls_size);
+}
+
+// This is called after creating a new thread (in the creating thread),
+// with the pointer returned by BeforeThreadCreateHook (above).
+static void ThreadCreateHook(void *hook, bool aborted) {
+ AsanThread *thread = static_cast<AsanThread *>(hook);
+ if (!aborted) {
+ // The thread was created successfully.
+ // ThreadStartHook is already running in the new thread.
+ } else {
+ // The thread wasn't created after all.
+ // Clean up everything we set up in BeforeThreadCreateHook.
+ asanThreadRegistry().FinishThread(thread->tid());
+ UnmapOrDie(thread, sizeof(AsanThread));
+ }
+}
+
+// This is called (1) in the newly-created thread before it runs anything else,
+// with the pointer returned by BeforeThreadCreateHook (above). (2) before a
+// thread restart.
+static void ThreadStartHook(void *hook, uptr os_id) {
+ if (!hook)
+ return;
+
+ AsanThread *thread = static_cast<AsanThread *>(hook);
+ SetCurrentThread(thread);
+
+ ThreadStatus status =
+ asanThreadRegistry().GetThreadLocked(thread->tid())->status;
+ DCHECK(status == ThreadStatusCreated || status == ThreadStatusRunning);
+ // Determine whether we are starting or restarting the thread.
+ if (status == ThreadStatusCreated)
+ // In lieu of AsanThread::ThreadStart.
+ asanThreadRegistry().StartThread(thread->tid(), os_id, ThreadType::Regular,
+ nullptr);
+ else {
+ // In a thread restart, a thread may resume execution at an
+ // arbitrary function entry point, with its stack and TLS state
+ // reset. We unpoison the stack in that case.
+ PoisonShadow(thread->stack_bottom(), thread->stack_size(), 0);
+ }
+}
+
+// Each thread runs this just before it exits,
+// with the pointer returned by BeforeThreadCreateHook (above).
+// All per-thread destructors have already been called.
+static void ThreadExitHook(void *hook, uptr os_id) {
+ AsanThread *thread = static_cast<AsanThread *>(hook);
+ if (thread)
+ AsanThread::TSDDtor(thread->context());
+}
+
+static void HandleExit() {
+ // Disable ASan by setting it to uninitialized. Also reset the
+ // shadow memory to avoid reporting errors after the run-time has
+ // been desroyed.
+ if (asan_inited) {
+ asan_inited = false;
+ ResetShadowMemory();
+ }
+}
+
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+} // namespace __asan
+
+// These are declared (in extern "C") by <some_path/sanitizer.h>.
+// The system runtime will call our definitions directly.
+
+extern "C" {
+void __sanitizer_early_init() {
+ __asan::EarlyInit();
+}
+
+void *__sanitizer_before_thread_create_hook(uptr thread, bool detached,
+ const char *name,
+ void *stack_base, size_t stack_size,
+ void *tls_base, size_t tls_size) {
+ return __asan::BeforeThreadCreateHook(
+ thread, detached,
+ reinterpret_cast<uptr>(stack_base), stack_size,
+ reinterpret_cast<uptr>(tls_base), tls_size);
+}
+
+void __sanitizer_thread_create_hook(void *handle, uptr thread, int status) {
+ __asan::ThreadCreateHook(handle, status != 0);
+}
+
+void __sanitizer_thread_start_hook(void *handle, uptr self) {
+ __asan::ThreadStartHook(handle, self);
+}
+
+void __sanitizer_thread_exit_hook(void *handle, uptr self) {
+ __asan::ThreadExitHook(handle, self);
+}
+
+void __sanitizer_exit() {
+ __asan::HandleExit();
+}
+} // "C"
+
+#endif // SANITIZER_RTEMS
+++ /dev/null
-//===-- asan_rtl.cc -------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Main file of the ASan run-time library.
-//===----------------------------------------------------------------------===//
-
-#include "asan_activation.h"
-#include "asan_allocator.h"
-#include "asan_interceptors.h"
-#include "asan_interface_internal.h"
-#include "asan_internal.h"
-#include "asan_mapping.h"
-#include "asan_poisoning.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_stats.h"
-#include "asan_suppressions.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-#include "lsan/lsan_common.h"
-#include "ubsan/ubsan_init.h"
-#include "ubsan/ubsan_platform.h"
-
-uptr __asan_shadow_memory_dynamic_address; // Global interface symbol.
-int __asan_option_detect_stack_use_after_return; // Global interface symbol.
-uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
-
-namespace __asan {
-
-uptr AsanMappingProfile[kAsanMappingProfileSize];
-
-static void AsanDie() {
- static atomic_uint32_t num_calls;
- if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
- // Don't die twice - run a busy loop.
- while (1) { }
- }
- if (common_flags()->print_module_map >= 1) PrintModuleMap();
- if (flags()->sleep_before_dying) {
- Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
- SleepForSeconds(flags()->sleep_before_dying);
- }
- if (flags()->unmap_shadow_on_exit) {
- if (kMidMemBeg) {
- UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg);
- UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd);
- } else {
- if (kHighShadowEnd)
- UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg);
- }
- }
-}
-
-static void AsanCheckFailed(const char *file, int line, const char *cond,
- u64 v1, u64 v2) {
- Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
- line, cond, (uptr)v1, (uptr)v2);
-
- // Print a stack trace the first time we come here. Otherwise, we probably
- // failed a CHECK during symbolization.
- static atomic_uint32_t num_calls;
- if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) == 0) {
- PRINT_CURRENT_STACK_CHECK();
- }
-
- Die();
-}
-
-// -------------------------- Globals --------------------- {{{1
-int asan_inited;
-bool asan_init_is_running;
-
-#if !ASAN_FIXED_MAPPING
-uptr kHighMemEnd, kMidMemBeg, kMidMemEnd;
-#endif
-
-// -------------------------- Misc ---------------- {{{1
-void ShowStatsAndAbort() {
- __asan_print_accumulated_stats();
- Die();
-}
-
-// --------------- LowLevelAllocateCallbac ---------- {{{1
-static void OnLowLevelAllocate(uptr ptr, uptr size) {
- PoisonShadow(ptr, size, kAsanInternalHeapMagic);
-}
-
-// -------------------------- Run-time entry ------------------- {{{1
-// exported functions
-#define ASAN_REPORT_ERROR(type, is_write, size) \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## size(uptr addr) { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
-} \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
-} \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## size ## _noabort(uptr addr) { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
-} \
-
-ASAN_REPORT_ERROR(load, false, 1)
-ASAN_REPORT_ERROR(load, false, 2)
-ASAN_REPORT_ERROR(load, false, 4)
-ASAN_REPORT_ERROR(load, false, 8)
-ASAN_REPORT_ERROR(load, false, 16)
-ASAN_REPORT_ERROR(store, true, 1)
-ASAN_REPORT_ERROR(store, true, 2)
-ASAN_REPORT_ERROR(store, true, 4)
-ASAN_REPORT_ERROR(store, true, 8)
-ASAN_REPORT_ERROR(store, true, 16)
-
-#define ASAN_REPORT_ERROR_N(type, is_write) \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
-} \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
-} \
-extern "C" NOINLINE INTERFACE_ATTRIBUTE \
-void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
-} \
-
-ASAN_REPORT_ERROR_N(load, false)
-ASAN_REPORT_ERROR_N(store, true)
-
-#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
- if (SANITIZER_MYRIAD2 && !AddrIsInMem(addr) && !AddrIsInShadow(addr)) \
- return; \
- uptr sp = MEM_TO_SHADOW(addr); \
- uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
- : *reinterpret_cast<u16 *>(sp); \
- if (UNLIKELY(s)) { \
- if (UNLIKELY(size >= SHADOW_GRANULARITY || \
- ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \
- (s8)s)) { \
- if (__asan_test_only_reported_buggy_pointer) { \
- *__asan_test_only_reported_buggy_pointer = addr; \
- } else { \
- GET_CALLER_PC_BP_SP; \
- ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \
- fatal); \
- } \
- } \
- }
-
-#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
- extern "C" NOINLINE INTERFACE_ATTRIBUTE \
- void __asan_##type##size(uptr addr) { \
- ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \
- } \
- extern "C" NOINLINE INTERFACE_ATTRIBUTE \
- void __asan_exp_##type##size(uptr addr, u32 exp) { \
- ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \
- } \
- extern "C" NOINLINE INTERFACE_ATTRIBUTE \
- void __asan_##type##size ## _noabort(uptr addr) { \
- ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \
- } \
-
-ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
-ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2)
-ASAN_MEMORY_ACCESS_CALLBACK(load, false, 4)
-ASAN_MEMORY_ACCESS_CALLBACK(load, false, 8)
-ASAN_MEMORY_ACCESS_CALLBACK(load, false, 16)
-ASAN_MEMORY_ACCESS_CALLBACK(store, true, 1)
-ASAN_MEMORY_ACCESS_CALLBACK(store, true, 2)
-ASAN_MEMORY_ACCESS_CALLBACK(store, true, 4)
-ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8)
-ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16)
-
-extern "C"
-NOINLINE INTERFACE_ATTRIBUTE
-void __asan_loadN(uptr addr, uptr size) {
- if (__asan_region_is_poisoned(addr, size)) {
- GET_CALLER_PC_BP_SP;
- ReportGenericError(pc, bp, sp, addr, false, size, 0, true);
- }
-}
-
-extern "C"
-NOINLINE INTERFACE_ATTRIBUTE
-void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
- if (__asan_region_is_poisoned(addr, size)) {
- GET_CALLER_PC_BP_SP;
- ReportGenericError(pc, bp, sp, addr, false, size, exp, true);
- }
-}
-
-extern "C"
-NOINLINE INTERFACE_ATTRIBUTE
-void __asan_loadN_noabort(uptr addr, uptr size) {
- if (__asan_region_is_poisoned(addr, size)) {
- GET_CALLER_PC_BP_SP;
- ReportGenericError(pc, bp, sp, addr, false, size, 0, false);
- }
-}
-
-extern "C"
-NOINLINE INTERFACE_ATTRIBUTE
-void __asan_storeN(uptr addr, uptr size) {
- if (__asan_region_is_poisoned(addr, size)) {
- GET_CALLER_PC_BP_SP;
- ReportGenericError(pc, bp, sp, addr, true, size, 0, true);
- }
-}
-
-extern "C"
-NOINLINE INTERFACE_ATTRIBUTE
-void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
- if (__asan_region_is_poisoned(addr, size)) {
- GET_CALLER_PC_BP_SP;
- ReportGenericError(pc, bp, sp, addr, true, size, exp, true);
- }
-}
-
-extern "C"
-NOINLINE INTERFACE_ATTRIBUTE
-void __asan_storeN_noabort(uptr addr, uptr size) {
- if (__asan_region_is_poisoned(addr, size)) {
- GET_CALLER_PC_BP_SP;
- ReportGenericError(pc, bp, sp, addr, true, size, 0, false);
- }
-}
-
-// Force the linker to keep the symbols for various ASan interface functions.
-// We want to keep those in the executable in order to let the instrumented
-// dynamic libraries access the symbol even if it is not used by the executable
-// itself. This should help if the build system is removing dead code at link
-// time.
-static NOINLINE void force_interface_symbols() {
- volatile int fake_condition = 0; // prevent dead condition elimination.
- // __asan_report_* functions are noreturn, so we need a switch to prevent
- // the compiler from removing any of them.
- // clang-format off
- switch (fake_condition) {
- case 1: __asan_report_load1(0); break;
- case 2: __asan_report_load2(0); break;
- case 3: __asan_report_load4(0); break;
- case 4: __asan_report_load8(0); break;
- case 5: __asan_report_load16(0); break;
- case 6: __asan_report_load_n(0, 0); break;
- case 7: __asan_report_store1(0); break;
- case 8: __asan_report_store2(0); break;
- case 9: __asan_report_store4(0); break;
- case 10: __asan_report_store8(0); break;
- case 11: __asan_report_store16(0); break;
- case 12: __asan_report_store_n(0, 0); break;
- case 13: __asan_report_exp_load1(0, 0); break;
- case 14: __asan_report_exp_load2(0, 0); break;
- case 15: __asan_report_exp_load4(0, 0); break;
- case 16: __asan_report_exp_load8(0, 0); break;
- case 17: __asan_report_exp_load16(0, 0); break;
- case 18: __asan_report_exp_load_n(0, 0, 0); break;
- case 19: __asan_report_exp_store1(0, 0); break;
- case 20: __asan_report_exp_store2(0, 0); break;
- case 21: __asan_report_exp_store4(0, 0); break;
- case 22: __asan_report_exp_store8(0, 0); break;
- case 23: __asan_report_exp_store16(0, 0); break;
- case 24: __asan_report_exp_store_n(0, 0, 0); break;
- case 25: __asan_register_globals(nullptr, 0); break;
- case 26: __asan_unregister_globals(nullptr, 0); break;
- case 27: __asan_set_death_callback(nullptr); break;
- case 28: __asan_set_error_report_callback(nullptr); break;
- case 29: __asan_handle_no_return(); break;
- case 30: __asan_address_is_poisoned(nullptr); break;
- case 31: __asan_poison_memory_region(nullptr, 0); break;
- case 32: __asan_unpoison_memory_region(nullptr, 0); break;
- case 34: __asan_before_dynamic_init(nullptr); break;
- case 35: __asan_after_dynamic_init(); break;
- case 36: __asan_poison_stack_memory(0, 0); break;
- case 37: __asan_unpoison_stack_memory(0, 0); break;
- case 38: __asan_region_is_poisoned(0, 0); break;
- case 39: __asan_describe_address(0); break;
- case 40: __asan_set_shadow_00(0, 0); break;
- case 41: __asan_set_shadow_f1(0, 0); break;
- case 42: __asan_set_shadow_f2(0, 0); break;
- case 43: __asan_set_shadow_f3(0, 0); break;
- case 44: __asan_set_shadow_f5(0, 0); break;
- case 45: __asan_set_shadow_f8(0, 0); break;
- }
- // clang-format on
-}
-
-static void asan_atexit() {
- Printf("AddressSanitizer exit stats:\n");
- __asan_print_accumulated_stats();
- // Print AsanMappingProfile.
- for (uptr i = 0; i < kAsanMappingProfileSize; i++) {
- if (AsanMappingProfile[i] == 0) continue;
- Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]);
- }
-}
-
-static void InitializeHighMemEnd() {
-#if !SANITIZER_MYRIAD2
-#if !ASAN_FIXED_MAPPING
- kHighMemEnd = GetMaxUserVirtualAddress();
- // Increase kHighMemEnd to make sure it's properly
- // aligned together with kHighMemBeg:
- kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1;
-#endif // !ASAN_FIXED_MAPPING
- CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0);
-#endif // !SANITIZER_MYRIAD2
-}
-
-void PrintAddressSpaceLayout() {
- if (kHighMemBeg) {
- Printf("|| `[%p, %p]` || HighMem ||\n",
- (void*)kHighMemBeg, (void*)kHighMemEnd);
- Printf("|| `[%p, %p]` || HighShadow ||\n",
- (void*)kHighShadowBeg, (void*)kHighShadowEnd);
- }
- if (kMidMemBeg) {
- Printf("|| `[%p, %p]` || ShadowGap3 ||\n",
- (void*)kShadowGap3Beg, (void*)kShadowGap3End);
- Printf("|| `[%p, %p]` || MidMem ||\n",
- (void*)kMidMemBeg, (void*)kMidMemEnd);
- Printf("|| `[%p, %p]` || ShadowGap2 ||\n",
- (void*)kShadowGap2Beg, (void*)kShadowGap2End);
- Printf("|| `[%p, %p]` || MidShadow ||\n",
- (void*)kMidShadowBeg, (void*)kMidShadowEnd);
- }
- Printf("|| `[%p, %p]` || ShadowGap ||\n",
- (void*)kShadowGapBeg, (void*)kShadowGapEnd);
- if (kLowShadowBeg) {
- Printf("|| `[%p, %p]` || LowShadow ||\n",
- (void*)kLowShadowBeg, (void*)kLowShadowEnd);
- Printf("|| `[%p, %p]` || LowMem ||\n",
- (void*)kLowMemBeg, (void*)kLowMemEnd);
- }
- Printf("MemToShadow(shadow): %p %p",
- (void*)MEM_TO_SHADOW(kLowShadowBeg),
- (void*)MEM_TO_SHADOW(kLowShadowEnd));
- if (kHighMemBeg) {
- Printf(" %p %p",
- (void*)MEM_TO_SHADOW(kHighShadowBeg),
- (void*)MEM_TO_SHADOW(kHighShadowEnd));
- }
- if (kMidMemBeg) {
- Printf(" %p %p",
- (void*)MEM_TO_SHADOW(kMidShadowBeg),
- (void*)MEM_TO_SHADOW(kMidShadowEnd));
- }
- Printf("\n");
- Printf("redzone=%zu\n", (uptr)flags()->redzone);
- Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
- Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb);
- Printf("thread_local_quarantine_size_kb=%zuK\n",
- (uptr)flags()->thread_local_quarantine_size_kb);
- Printf("malloc_context_size=%zu\n",
- (uptr)common_flags()->malloc_context_size);
-
- Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE);
- Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY);
- Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET);
- CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
- if (kMidMemBeg)
- CHECK(kMidShadowBeg > kLowShadowEnd &&
- kMidMemBeg > kMidShadowEnd &&
- kHighShadowBeg > kMidMemEnd);
-}
-
-static void AsanInitInternal() {
- if (LIKELY(asan_inited)) return;
- SanitizerToolName = "AddressSanitizer";
- CHECK(!asan_init_is_running && "ASan init calls itself!");
- asan_init_is_running = true;
-
- CacheBinaryName();
- CheckASLR();
-
- // Initialize flags. This must be done early, because most of the
- // initialization steps look at flags().
- InitializeFlags();
-
- AsanCheckIncompatibleRT();
- AsanCheckDynamicRTPrereqs();
- AvoidCVE_2016_2143();
-
- SetCanPoisonMemory(flags()->poison_heap);
- SetMallocContextSize(common_flags()->malloc_context_size);
-
- InitializePlatformExceptionHandlers();
-
- InitializeHighMemEnd();
-
- // Make sure we are not statically linked.
- AsanDoesNotSupportStaticLinkage();
-
- // Install tool-specific callbacks in sanitizer_common.
- AddDieCallback(AsanDie);
- SetCheckFailedCallback(AsanCheckFailed);
- SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
-
- __sanitizer_set_report_path(common_flags()->log_path);
-
- __asan_option_detect_stack_use_after_return =
- flags()->detect_stack_use_after_return;
-
- // Re-exec ourselves if we need to set additional env or command line args.
- MaybeReexec();
-
- // Setup internal allocator callback.
- SetLowLevelAllocateMinAlignment(SHADOW_GRANULARITY);
- SetLowLevelAllocateCallback(OnLowLevelAllocate);
-
- InitializeAsanInterceptors();
-
- // Enable system log ("adb logcat") on Android.
- // Doing this before interceptors are initialized crashes in:
- // AsanInitInternal -> android_log_write -> __interceptor_strcmp
- AndroidLogInit();
-
- ReplaceSystemMalloc();
-
- DisableCoreDumperIfNecessary();
-
- InitializeShadowMemory();
-
- AsanTSDInit(PlatformTSDDtor);
- InstallDeadlySignalHandlers(AsanOnDeadlySignal);
-
- AllocatorOptions allocator_options;
- allocator_options.SetFrom(flags(), common_flags());
- InitializeAllocator(allocator_options);
-
- MaybeStartBackgroudThread();
- SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
-
- // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
- // should be set to 1 prior to initializing the threads.
- asan_inited = 1;
- asan_init_is_running = false;
-
- if (flags()->atexit)
- Atexit(asan_atexit);
-
- InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
-
- // Now that ASan runtime is (mostly) initialized, deactivate it if
- // necessary, so that it can be re-activated when requested.
- if (flags()->start_deactivated)
- AsanDeactivate();
-
- // interceptors
- InitTlsSize();
-
- // Create main thread.
- AsanThread *main_thread = CreateMainThread();
- CHECK_EQ(0, main_thread->tid());
- force_interface_symbols(); // no-op.
- SanitizerInitializeUnwinder();
-
- if (CAN_SANITIZE_LEAKS) {
- __lsan::InitCommonLsan();
- if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
- if (flags()->halt_on_error)
- Atexit(__lsan::DoLeakCheck);
- else
- Atexit(__lsan::DoRecoverableLeakCheckVoid);
- }
- }
-
-#if CAN_SANITIZE_UB
- __ubsan::InitAsPlugin();
-#endif
-
- InitializeSuppressions();
-
- if (CAN_SANITIZE_LEAKS) {
- // LateInitialize() calls dlsym, which can allocate an error string buffer
- // in the TLS. Let's ignore the allocation to avoid reporting a leak.
- __lsan::ScopedInterceptorDisabler disabler;
- Symbolizer::LateInitialize();
- } else {
- Symbolizer::LateInitialize();
- }
-
- VReport(1, "AddressSanitizer Init done\n");
-
- if (flags()->sleep_after_init) {
- Report("Sleeping for %d second(s)\n", flags()->sleep_after_init);
- SleepForSeconds(flags()->sleep_after_init);
- }
-}
-
-// Initialize as requested from some part of ASan runtime library (interceptors,
-// allocator, etc).
-void AsanInitFromRtl() {
- AsanInitInternal();
-}
-
-#if ASAN_DYNAMIC
-// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
-// (and thus normal initializers from .preinit_array or modules haven't run).
-
-class AsanInitializer {
-public: // NOLINT
- AsanInitializer() {
- AsanInitFromRtl();
- }
-};
-
-static AsanInitializer asan_initializer;
-#endif // ASAN_DYNAMIC
-
-} // namespace __asan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-void NOINLINE __asan_handle_no_return() {
- if (asan_init_is_running)
- return;
-
- int local_stack;
- AsanThread *curr_thread = GetCurrentThread();
- uptr PageSize = GetPageSizeCached();
- uptr top, bottom;
- if (curr_thread) {
- top = curr_thread->stack_top();
- bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1);
- } else if (SANITIZER_RTEMS) {
- // Give up On RTEMS.
- return;
- } else {
- CHECK(!SANITIZER_FUCHSIA);
- // If we haven't seen this thread, try asking the OS for stack bounds.
- uptr tls_addr, tls_size, stack_size;
- GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr,
- &tls_size);
- top = bottom + stack_size;
- }
- static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M
- if (top - bottom > kMaxExpectedCleanupSize) {
- static bool reported_warning = false;
- if (reported_warning)
- return;
- reported_warning = true;
- Report("WARNING: ASan is ignoring requested __asan_handle_no_return: "
- "stack top: %p; bottom %p; size: %p (%zd)\n"
- "False positive error reports may follow\n"
- "For details see "
- "https://github.com/google/sanitizers/issues/189\n",
- top, bottom, top - bottom, top - bottom);
- return;
- }
- PoisonShadow(bottom, top - bottom, 0);
- if (curr_thread && curr_thread->has_fake_stack())
- curr_thread->fake_stack()->HandleNoReturn();
-}
-
-void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
- SetUserDieCallback(callback);
-}
-
-// Initialize as requested from instrumented application code.
-// We use this call as a trigger to wake up ASan from deactivated state.
-void __asan_init() {
- AsanActivate();
- AsanInitInternal();
-}
-
-void __asan_version_mismatch_check() {
- // Do nothing.
-}
--- /dev/null
+//===-- asan_rtl.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Main file of the ASan run-time library.
+//===----------------------------------------------------------------------===//
+
+#include "asan_activation.h"
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_interface_internal.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_poisoning.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_stats.h"
+#include "asan_suppressions.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "lsan/lsan_common.h"
+#include "ubsan/ubsan_init.h"
+#include "ubsan/ubsan_platform.h"
+
+uptr __asan_shadow_memory_dynamic_address; // Global interface symbol.
+int __asan_option_detect_stack_use_after_return; // Global interface symbol.
+uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan.
+
+namespace __asan {
+
+uptr AsanMappingProfile[kAsanMappingProfileSize];
+
+static void AsanDie() {
+ static atomic_uint32_t num_calls;
+ if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
+ // Don't die twice - run a busy loop.
+ while (1) { }
+ }
+ if (common_flags()->print_module_map >= 1) PrintModuleMap();
+ if (flags()->sleep_before_dying) {
+ Report("Sleeping for %d second(s)\n", flags()->sleep_before_dying);
+ SleepForSeconds(flags()->sleep_before_dying);
+ }
+ if (flags()->unmap_shadow_on_exit) {
+ if (kMidMemBeg) {
+ UnmapOrDie((void*)kLowShadowBeg, kMidMemBeg - kLowShadowBeg);
+ UnmapOrDie((void*)kMidMemEnd, kHighShadowEnd - kMidMemEnd);
+ } else {
+ if (kHighShadowEnd)
+ UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg);
+ }
+ }
+}
+
+static void AsanCheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
+ line, cond, (uptr)v1, (uptr)v2);
+
+ // Print a stack trace the first time we come here. Otherwise, we probably
+ // failed a CHECK during symbolization.
+ static atomic_uint32_t num_calls;
+ if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) == 0) {
+ PRINT_CURRENT_STACK_CHECK();
+ }
+
+ Die();
+}
+
+// -------------------------- Globals --------------------- {{{1
+int asan_inited;
+bool asan_init_is_running;
+
+#if !ASAN_FIXED_MAPPING
+uptr kHighMemEnd, kMidMemBeg, kMidMemEnd;
+#endif
+
+// -------------------------- Misc ---------------- {{{1
+void ShowStatsAndAbort() {
+ __asan_print_accumulated_stats();
+ Die();
+}
+
+// --------------- LowLevelAllocateCallbac ---------- {{{1
+static void OnLowLevelAllocate(uptr ptr, uptr size) {
+ PoisonShadow(ptr, size, kAsanInternalHeapMagic);
+}
+
+// -------------------------- Run-time entry ------------------- {{{1
+// exported functions
+#define ASAN_REPORT_ERROR(type, is_write, size) \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## size(uptr addr) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_exp_ ## type ## size(uptr addr, u32 exp) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## size ## _noabort(uptr addr) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
+} \
+
+ASAN_REPORT_ERROR(load, false, 1)
+ASAN_REPORT_ERROR(load, false, 2)
+ASAN_REPORT_ERROR(load, false, 4)
+ASAN_REPORT_ERROR(load, false, 8)
+ASAN_REPORT_ERROR(load, false, 16)
+ASAN_REPORT_ERROR(store, true, 1)
+ASAN_REPORT_ERROR(store, true, 2)
+ASAN_REPORT_ERROR(store, true, 4)
+ASAN_REPORT_ERROR(store, true, 8)
+ASAN_REPORT_ERROR(store, true, 16)
+
+#define ASAN_REPORT_ERROR_N(type, is_write) \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## _n(uptr addr, uptr size) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_exp_ ## type ## _n(uptr addr, uptr size, u32 exp) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, exp, true); \
+} \
+extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+void __asan_report_ ## type ## _n_noabort(uptr addr, uptr size) { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, 0, false); \
+} \
+
+ASAN_REPORT_ERROR_N(load, false)
+ASAN_REPORT_ERROR_N(store, true)
+
+#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
+ if (SANITIZER_MYRIAD2 && !AddrIsInMem(addr) && !AddrIsInShadow(addr)) \
+ return; \
+ uptr sp = MEM_TO_SHADOW(addr); \
+ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
+ : *reinterpret_cast<u16 *>(sp); \
+ if (UNLIKELY(s)) { \
+ if (UNLIKELY(size >= SHADOW_GRANULARITY || \
+ ((s8)((addr & (SHADOW_GRANULARITY - 1)) + size - 1)) >= \
+ (s8)s)) { \
+ if (__asan_test_only_reported_buggy_pointer) { \
+ *__asan_test_only_reported_buggy_pointer = addr; \
+ } else { \
+ GET_CALLER_PC_BP_SP; \
+ ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \
+ fatal); \
+ } \
+ } \
+ }
+
+#define ASAN_MEMORY_ACCESS_CALLBACK(type, is_write, size) \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_##type##size(uptr addr) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, true) \
+ } \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_exp_##type##size(uptr addr, u32 exp) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp, true) \
+ } \
+ extern "C" NOINLINE INTERFACE_ATTRIBUTE \
+ void __asan_##type##size ## _noabort(uptr addr) { \
+ ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, 0, false) \
+ } \
+
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 1)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 2)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 4)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 8)
+ASAN_MEMORY_ACCESS_CALLBACK(load, false, 16)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 1)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 2)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 4)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8)
+ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16)
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_loadN(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, false, size, 0, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_exp_loadN(uptr addr, uptr size, u32 exp) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, false, size, exp, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_loadN_noabort(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, false, size, 0, false);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_storeN(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, true, size, 0, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_exp_storeN(uptr addr, uptr size, u32 exp) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, true, size, exp, true);
+ }
+}
+
+extern "C"
+NOINLINE INTERFACE_ATTRIBUTE
+void __asan_storeN_noabort(uptr addr, uptr size) {
+ if (__asan_region_is_poisoned(addr, size)) {
+ GET_CALLER_PC_BP_SP;
+ ReportGenericError(pc, bp, sp, addr, true, size, 0, false);
+ }
+}
+
+// Force the linker to keep the symbols for various ASan interface functions.
+// We want to keep those in the executable in order to let the instrumented
+// dynamic libraries access the symbol even if it is not used by the executable
+// itself. This should help if the build system is removing dead code at link
+// time.
+static NOINLINE void force_interface_symbols() {
+ volatile int fake_condition = 0; // prevent dead condition elimination.
+ // __asan_report_* functions are noreturn, so we need a switch to prevent
+ // the compiler from removing any of them.
+ // clang-format off
+ switch (fake_condition) {
+ case 1: __asan_report_load1(0); break;
+ case 2: __asan_report_load2(0); break;
+ case 3: __asan_report_load4(0); break;
+ case 4: __asan_report_load8(0); break;
+ case 5: __asan_report_load16(0); break;
+ case 6: __asan_report_load_n(0, 0); break;
+ case 7: __asan_report_store1(0); break;
+ case 8: __asan_report_store2(0); break;
+ case 9: __asan_report_store4(0); break;
+ case 10: __asan_report_store8(0); break;
+ case 11: __asan_report_store16(0); break;
+ case 12: __asan_report_store_n(0, 0); break;
+ case 13: __asan_report_exp_load1(0, 0); break;
+ case 14: __asan_report_exp_load2(0, 0); break;
+ case 15: __asan_report_exp_load4(0, 0); break;
+ case 16: __asan_report_exp_load8(0, 0); break;
+ case 17: __asan_report_exp_load16(0, 0); break;
+ case 18: __asan_report_exp_load_n(0, 0, 0); break;
+ case 19: __asan_report_exp_store1(0, 0); break;
+ case 20: __asan_report_exp_store2(0, 0); break;
+ case 21: __asan_report_exp_store4(0, 0); break;
+ case 22: __asan_report_exp_store8(0, 0); break;
+ case 23: __asan_report_exp_store16(0, 0); break;
+ case 24: __asan_report_exp_store_n(0, 0, 0); break;
+ case 25: __asan_register_globals(nullptr, 0); break;
+ case 26: __asan_unregister_globals(nullptr, 0); break;
+ case 27: __asan_set_death_callback(nullptr); break;
+ case 28: __asan_set_error_report_callback(nullptr); break;
+ case 29: __asan_handle_no_return(); break;
+ case 30: __asan_address_is_poisoned(nullptr); break;
+ case 31: __asan_poison_memory_region(nullptr, 0); break;
+ case 32: __asan_unpoison_memory_region(nullptr, 0); break;
+ case 34: __asan_before_dynamic_init(nullptr); break;
+ case 35: __asan_after_dynamic_init(); break;
+ case 36: __asan_poison_stack_memory(0, 0); break;
+ case 37: __asan_unpoison_stack_memory(0, 0); break;
+ case 38: __asan_region_is_poisoned(0, 0); break;
+ case 39: __asan_describe_address(0); break;
+ case 40: __asan_set_shadow_00(0, 0); break;
+ case 41: __asan_set_shadow_f1(0, 0); break;
+ case 42: __asan_set_shadow_f2(0, 0); break;
+ case 43: __asan_set_shadow_f3(0, 0); break;
+ case 44: __asan_set_shadow_f5(0, 0); break;
+ case 45: __asan_set_shadow_f8(0, 0); break;
+ }
+ // clang-format on
+}
+
+static void asan_atexit() {
+ Printf("AddressSanitizer exit stats:\n");
+ __asan_print_accumulated_stats();
+ // Print AsanMappingProfile.
+ for (uptr i = 0; i < kAsanMappingProfileSize; i++) {
+ if (AsanMappingProfile[i] == 0) continue;
+ Printf("asan_mapping.h:%zd -- %zd\n", i, AsanMappingProfile[i]);
+ }
+}
+
+static void InitializeHighMemEnd() {
+#if !SANITIZER_MYRIAD2
+#if !ASAN_FIXED_MAPPING
+ kHighMemEnd = GetMaxUserVirtualAddress();
+ // Increase kHighMemEnd to make sure it's properly
+ // aligned together with kHighMemBeg:
+ kHighMemEnd |= SHADOW_GRANULARITY * GetMmapGranularity() - 1;
+#endif // !ASAN_FIXED_MAPPING
+ CHECK_EQ((kHighMemBeg % GetMmapGranularity()), 0);
+#endif // !SANITIZER_MYRIAD2
+}
+
+void PrintAddressSpaceLayout() {
+ if (kHighMemBeg) {
+ Printf("|| `[%p, %p]` || HighMem ||\n",
+ (void*)kHighMemBeg, (void*)kHighMemEnd);
+ Printf("|| `[%p, %p]` || HighShadow ||\n",
+ (void*)kHighShadowBeg, (void*)kHighShadowEnd);
+ }
+ if (kMidMemBeg) {
+ Printf("|| `[%p, %p]` || ShadowGap3 ||\n",
+ (void*)kShadowGap3Beg, (void*)kShadowGap3End);
+ Printf("|| `[%p, %p]` || MidMem ||\n",
+ (void*)kMidMemBeg, (void*)kMidMemEnd);
+ Printf("|| `[%p, %p]` || ShadowGap2 ||\n",
+ (void*)kShadowGap2Beg, (void*)kShadowGap2End);
+ Printf("|| `[%p, %p]` || MidShadow ||\n",
+ (void*)kMidShadowBeg, (void*)kMidShadowEnd);
+ }
+ Printf("|| `[%p, %p]` || ShadowGap ||\n",
+ (void*)kShadowGapBeg, (void*)kShadowGapEnd);
+ if (kLowShadowBeg) {
+ Printf("|| `[%p, %p]` || LowShadow ||\n",
+ (void*)kLowShadowBeg, (void*)kLowShadowEnd);
+ Printf("|| `[%p, %p]` || LowMem ||\n",
+ (void*)kLowMemBeg, (void*)kLowMemEnd);
+ }
+ Printf("MemToShadow(shadow): %p %p",
+ (void*)MEM_TO_SHADOW(kLowShadowBeg),
+ (void*)MEM_TO_SHADOW(kLowShadowEnd));
+ if (kHighMemBeg) {
+ Printf(" %p %p",
+ (void*)MEM_TO_SHADOW(kHighShadowBeg),
+ (void*)MEM_TO_SHADOW(kHighShadowEnd));
+ }
+ if (kMidMemBeg) {
+ Printf(" %p %p",
+ (void*)MEM_TO_SHADOW(kMidShadowBeg),
+ (void*)MEM_TO_SHADOW(kMidShadowEnd));
+ }
+ Printf("\n");
+ Printf("redzone=%zu\n", (uptr)flags()->redzone);
+ Printf("max_redzone=%zu\n", (uptr)flags()->max_redzone);
+ Printf("quarantine_size_mb=%zuM\n", (uptr)flags()->quarantine_size_mb);
+ Printf("thread_local_quarantine_size_kb=%zuK\n",
+ (uptr)flags()->thread_local_quarantine_size_kb);
+ Printf("malloc_context_size=%zu\n",
+ (uptr)common_flags()->malloc_context_size);
+
+ Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE);
+ Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY);
+ Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET);
+ CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7);
+ if (kMidMemBeg)
+ CHECK(kMidShadowBeg > kLowShadowEnd &&
+ kMidMemBeg > kMidShadowEnd &&
+ kHighShadowBeg > kMidMemEnd);
+}
+
+#if defined(__thumb__) && defined(__linux__)
+#define START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
+#endif
+
+#ifndef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
+static bool UNUSED __local_asan_dyninit = [] {
+ MaybeStartBackgroudThread();
+ SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
+
+ return false;
+}();
+#endif
+
+static void AsanInitInternal() {
+ if (LIKELY(asan_inited)) return;
+ SanitizerToolName = "AddressSanitizer";
+ CHECK(!asan_init_is_running && "ASan init calls itself!");
+ asan_init_is_running = true;
+
+ CacheBinaryName();
+ CheckASLR();
+
+ // Initialize flags. This must be done early, because most of the
+ // initialization steps look at flags().
+ InitializeFlags();
+
+ // Stop performing init at this point if we are being loaded via
+ // dlopen() and the platform supports it.
+ if (SANITIZER_SUPPORTS_INIT_FOR_DLOPEN && UNLIKELY(HandleDlopenInit())) {
+ asan_init_is_running = false;
+ VReport(1, "AddressSanitizer init is being performed for dlopen().\n");
+ return;
+ }
+
+ AsanCheckIncompatibleRT();
+ AsanCheckDynamicRTPrereqs();
+ AvoidCVE_2016_2143();
+
+ SetCanPoisonMemory(flags()->poison_heap);
+ SetMallocContextSize(common_flags()->malloc_context_size);
+
+ InitializePlatformExceptionHandlers();
+
+ InitializeHighMemEnd();
+
+ // Make sure we are not statically linked.
+ AsanDoesNotSupportStaticLinkage();
+
+ // Install tool-specific callbacks in sanitizer_common.
+ AddDieCallback(AsanDie);
+ SetCheckFailedCallback(AsanCheckFailed);
+ SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
+
+ __sanitizer_set_report_path(common_flags()->log_path);
+
+ __asan_option_detect_stack_use_after_return =
+ flags()->detect_stack_use_after_return;
+
+ __sanitizer::InitializePlatformEarly();
+
+ // Re-exec ourselves if we need to set additional env or command line args.
+ MaybeReexec();
+
+ // Setup internal allocator callback.
+ SetLowLevelAllocateMinAlignment(SHADOW_GRANULARITY);
+ SetLowLevelAllocateCallback(OnLowLevelAllocate);
+
+ InitializeAsanInterceptors();
+
+ // Enable system log ("adb logcat") on Android.
+ // Doing this before interceptors are initialized crashes in:
+ // AsanInitInternal -> android_log_write -> __interceptor_strcmp
+ AndroidLogInit();
+
+ ReplaceSystemMalloc();
+
+ DisableCoreDumperIfNecessary();
+
+ InitializeShadowMemory();
+
+ AsanTSDInit(PlatformTSDDtor);
+ InstallDeadlySignalHandlers(AsanOnDeadlySignal);
+
+ AllocatorOptions allocator_options;
+ allocator_options.SetFrom(flags(), common_flags());
+ InitializeAllocator(allocator_options);
+
+#ifdef START_BACKGROUND_THREAD_IN_ASAN_INTERNAL
+ MaybeStartBackgroudThread();
+ SetSoftRssLimitExceededCallback(AsanSoftRssLimitExceededCallback);
+#endif
+
+ // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited
+ // should be set to 1 prior to initializing the threads.
+ asan_inited = 1;
+ asan_init_is_running = false;
+
+ if (flags()->atexit)
+ Atexit(asan_atexit);
+
+ InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
+ // Now that ASan runtime is (mostly) initialized, deactivate it if
+ // necessary, so that it can be re-activated when requested.
+ if (flags()->start_deactivated)
+ AsanDeactivate();
+
+ // interceptors
+ InitTlsSize();
+
+ // Create main thread.
+ AsanThread *main_thread = CreateMainThread();
+ CHECK_EQ(0, main_thread->tid());
+ force_interface_symbols(); // no-op.
+ SanitizerInitializeUnwinder();
+
+ if (CAN_SANITIZE_LEAKS) {
+ __lsan::InitCommonLsan();
+ if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) {
+ if (flags()->halt_on_error)
+ Atexit(__lsan::DoLeakCheck);
+ else
+ Atexit(__lsan::DoRecoverableLeakCheckVoid);
+ }
+ }
+
+#if CAN_SANITIZE_UB
+ __ubsan::InitAsPlugin();
+#endif
+
+ InitializeSuppressions();
+
+ if (CAN_SANITIZE_LEAKS) {
+ // LateInitialize() calls dlsym, which can allocate an error string buffer
+ // in the TLS. Let's ignore the allocation to avoid reporting a leak.
+ __lsan::ScopedInterceptorDisabler disabler;
+ Symbolizer::LateInitialize();
+ } else {
+ Symbolizer::LateInitialize();
+ }
+
+ VReport(1, "AddressSanitizer Init done\n");
+
+ if (flags()->sleep_after_init) {
+ Report("Sleeping for %d second(s)\n", flags()->sleep_after_init);
+ SleepForSeconds(flags()->sleep_after_init);
+ }
+}
+
+// Initialize as requested from some part of ASan runtime library (interceptors,
+// allocator, etc).
+void AsanInitFromRtl() {
+ AsanInitInternal();
+}
+
+#if ASAN_DYNAMIC
+// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable
+// (and thus normal initializers from .preinit_array or modules haven't run).
+
+class AsanInitializer {
+public: // NOLINT
+ AsanInitializer() {
+ AsanInitFromRtl();
+ }
+};
+
+static AsanInitializer asan_initializer;
+#endif // ASAN_DYNAMIC
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+void NOINLINE __asan_handle_no_return() {
+ if (asan_init_is_running)
+ return;
+
+ int local_stack;
+ AsanThread *curr_thread = GetCurrentThread();
+ uptr PageSize = GetPageSizeCached();
+ uptr top, bottom;
+ if (curr_thread) {
+ top = curr_thread->stack_top();
+ bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1);
+ } else if (SANITIZER_RTEMS) {
+ // Give up On RTEMS.
+ return;
+ } else {
+ CHECK(!SANITIZER_FUCHSIA);
+ // If we haven't seen this thread, try asking the OS for stack bounds.
+ uptr tls_addr, tls_size, stack_size;
+ GetThreadStackAndTls(/*main=*/false, &bottom, &stack_size, &tls_addr,
+ &tls_size);
+ top = bottom + stack_size;
+ }
+ static const uptr kMaxExpectedCleanupSize = 64 << 20; // 64M
+ if (top - bottom > kMaxExpectedCleanupSize) {
+ static bool reported_warning = false;
+ if (reported_warning)
+ return;
+ reported_warning = true;
+ Report("WARNING: ASan is ignoring requested __asan_handle_no_return: "
+ "stack top: %p; bottom %p; size: %p (%zd)\n"
+ "False positive error reports may follow\n"
+ "For details see "
+ "https://github.com/google/sanitizers/issues/189\n",
+ top, bottom, top - bottom, top - bottom);
+ return;
+ }
+ PoisonShadow(bottom, top - bottom, 0);
+ if (curr_thread && curr_thread->has_fake_stack())
+ curr_thread->fake_stack()->HandleNoReturn();
+}
+
+extern "C" void *__asan_extra_spill_area() {
+ AsanThread *t = GetCurrentThread();
+ CHECK(t);
+ return t->extra_spill_area();
+}
+
+void __asan_handle_vfork(void *sp) {
+ AsanThread *t = GetCurrentThread();
+ CHECK(t);
+ uptr bottom = t->stack_bottom();
+ PoisonShadow(bottom, (uptr)sp - bottom, 0);
+}
+
+void NOINLINE __asan_set_death_callback(void (*callback)(void)) {
+ SetUserDieCallback(callback);
+}
+
+// Initialize as requested from instrumented application code.
+// We use this call as a trigger to wake up ASan from deactivated state.
+void __asan_init() {
+ AsanActivate();
+ AsanInitInternal();
+}
+
+void __asan_version_mismatch_check() {
+ // Do nothing.
+}
//===-- asan_scariness_score.h ----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- asan_shadow_setup.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Set up the shadow memory.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-
-// asan_fuchsia.cc and asan_rtems.cc have their own
-// InitializeShadowMemory implementation.
-#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
-
-#include "asan_internal.h"
-#include "asan_mapping.h"
-
-namespace __asan {
-
-// ---------------------- mmap -------------------- {{{1
-// Reserve memory range [beg, end].
-// We need to use inclusive range because end+1 may not be representable.
-void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
- CHECK_EQ((beg % GetMmapGranularity()), 0);
- CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
- uptr size = end - beg + 1;
- DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
- if (!MmapFixedNoReserve(beg, size, name)) {
- Report(
- "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
- "Perhaps you're using ulimit -v\n",
- size);
- Abort();
- }
- if (common_flags()->no_huge_pages_for_shadow) NoHugePagesInRegion(beg, size);
- if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size);
-}
-
-static void ProtectGap(uptr addr, uptr size) {
- if (!flags()->protect_shadow_gap) {
- // The shadow gap is unprotected, so there is a chance that someone
- // is actually using this memory. Which means it needs a shadow...
- uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached());
- uptr GapShadowEnd =
- RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1;
- if (Verbosity())
- Printf(
- "protect_shadow_gap=0:"
- " not protecting shadow gap, allocating gap's shadow\n"
- "|| `[%p, %p]` || ShadowGap's shadow ||\n",
- GapShadowBeg, GapShadowEnd);
- ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd,
- "unprotected gap shadow");
- return;
- }
- void *res = MmapFixedNoAccess(addr, size, "shadow gap");
- if (addr == (uptr)res) return;
- // A few pages at the start of the address space can not be protected.
- // But we really want to protect as much as possible, to prevent this memory
- // being returned as a result of a non-FIXED mmap().
- if (addr == kZeroBaseShadowStart) {
- uptr step = GetMmapGranularity();
- while (size > step && addr < kZeroBaseMaxShadowStart) {
- addr += step;
- size -= step;
- void *res = MmapFixedNoAccess(addr, size, "shadow gap");
- if (addr == (uptr)res) return;
- }
- }
-
- Report(
- "ERROR: Failed to protect the shadow gap. "
- "ASan cannot proceed correctly. ABORTING.\n");
- DumpProcessMap();
- Die();
-}
-
-static void MaybeReportLinuxPIEBug() {
-#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__aarch64__))
- Report("This might be related to ELF_ET_DYN_BASE change in Linux 4.12.\n");
- Report(
- "See https://github.com/google/sanitizers/issues/856 for possible "
- "workarounds.\n");
-#endif
-}
-
-void InitializeShadowMemory() {
- // Set the shadow memory address to uninitialized.
- __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
-
- uptr shadow_start = kLowShadowBeg;
- // Detect if a dynamic shadow address must used and find a available location
- // when necessary. When dynamic address is used, the macro |kLowShadowBeg|
- // expands to |__asan_shadow_memory_dynamic_address| which is
- // |kDefaultShadowSentinel|.
- bool full_shadow_is_available = false;
- if (shadow_start == kDefaultShadowSentinel) {
- __asan_shadow_memory_dynamic_address = 0;
- CHECK_EQ(0, kLowShadowBeg);
- shadow_start = FindDynamicShadowStart();
- if (SANITIZER_LINUX) full_shadow_is_available = true;
- }
- // Update the shadow memory address (potentially) used by instrumentation.
- __asan_shadow_memory_dynamic_address = shadow_start;
-
- if (kLowShadowBeg) shadow_start -= GetMmapGranularity();
-
- if (!full_shadow_is_available)
- full_shadow_is_available =
- MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
-
-#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
- !ASAN_FIXED_MAPPING
- if (!full_shadow_is_available) {
- kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
- kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
- }
-#endif
-
- if (Verbosity()) PrintAddressSpaceLayout();
-
- if (full_shadow_is_available) {
- // mmap the low shadow plus at least one page at the left.
- if (kLowShadowBeg)
- ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
- // mmap the high shadow.
- ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
- // protect the gap.
- ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
- CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
- } else if (kMidMemBeg &&
- MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
- MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
- CHECK(kLowShadowBeg != kLowShadowEnd);
- // mmap the low shadow plus at least one page at the left.
- ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
- // mmap the mid shadow.
- ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
- // mmap the high shadow.
- ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
- // protect the gaps.
- ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
- ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
- ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
- } else {
- Report(
- "Shadow memory range interleaves with an existing memory mapping. "
- "ASan cannot proceed correctly. ABORTING.\n");
- Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
- shadow_start, kHighShadowEnd);
- MaybeReportLinuxPIEBug();
- DumpProcessMap();
- Die();
- }
-}
-
-} // namespace __asan
-
-#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
--- /dev/null
+//===-- asan_shadow_setup.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Set up the shadow memory.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+
+// asan_fuchsia.cpp and asan_rtems.cpp have their own
+// InitializeShadowMemory implementation.
+#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
+
+#include "asan_internal.h"
+#include "asan_mapping.h"
+
+namespace __asan {
+
+// ---------------------- mmap -------------------- {{{1
+// Reserve memory range [beg, end].
+// We need to use inclusive range because end+1 may not be representable.
+void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) {
+ CHECK_EQ((beg % GetMmapGranularity()), 0);
+ CHECK_EQ(((end + 1) % GetMmapGranularity()), 0);
+ uptr size = end - beg + 1;
+ DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb.
+ if (!MmapFixedNoReserve(beg, size, name)) {
+ Report(
+ "ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. "
+ "Perhaps you're using ulimit -v\n",
+ size);
+ Abort();
+ }
+ SetShadowRegionHugePageMode(beg, size);
+ if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(beg, size);
+}
+
+static void ProtectGap(uptr addr, uptr size) {
+ if (!flags()->protect_shadow_gap) {
+ // The shadow gap is unprotected, so there is a chance that someone
+ // is actually using this memory. Which means it needs a shadow...
+ uptr GapShadowBeg = RoundDownTo(MEM_TO_SHADOW(addr), GetPageSizeCached());
+ uptr GapShadowEnd =
+ RoundUpTo(MEM_TO_SHADOW(addr + size), GetPageSizeCached()) - 1;
+ if (Verbosity())
+ Printf(
+ "protect_shadow_gap=0:"
+ " not protecting shadow gap, allocating gap's shadow\n"
+ "|| `[%p, %p]` || ShadowGap's shadow ||\n",
+ GapShadowBeg, GapShadowEnd);
+ ReserveShadowMemoryRange(GapShadowBeg, GapShadowEnd,
+ "unprotected gap shadow");
+ return;
+ }
+ void *res = MmapFixedNoAccess(addr, size, "shadow gap");
+ if (addr == (uptr)res) return;
+ // A few pages at the start of the address space can not be protected.
+ // But we really want to protect as much as possible, to prevent this memory
+ // being returned as a result of a non-FIXED mmap().
+ if (addr == kZeroBaseShadowStart) {
+ uptr step = GetMmapGranularity();
+ while (size > step && addr < kZeroBaseMaxShadowStart) {
+ addr += step;
+ size -= step;
+ void *res = MmapFixedNoAccess(addr, size, "shadow gap");
+ if (addr == (uptr)res) return;
+ }
+ }
+
+ Report(
+ "ERROR: Failed to protect the shadow gap. "
+ "ASan cannot proceed correctly. ABORTING.\n");
+ DumpProcessMap();
+ Die();
+}
+
+static void MaybeReportLinuxPIEBug() {
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__aarch64__))
+ Report("This might be related to ELF_ET_DYN_BASE change in Linux 4.12.\n");
+ Report(
+ "See https://github.com/google/sanitizers/issues/856 for possible "
+ "workarounds.\n");
+#endif
+}
+
+void InitializeShadowMemory() {
+ // Set the shadow memory address to uninitialized.
+ __asan_shadow_memory_dynamic_address = kDefaultShadowSentinel;
+
+ uptr shadow_start = kLowShadowBeg;
+ // Detect if a dynamic shadow address must used and find a available location
+ // when necessary. When dynamic address is used, the macro |kLowShadowBeg|
+ // expands to |__asan_shadow_memory_dynamic_address| which is
+ // |kDefaultShadowSentinel|.
+ bool full_shadow_is_available = false;
+ if (shadow_start == kDefaultShadowSentinel) {
+ __asan_shadow_memory_dynamic_address = 0;
+ CHECK_EQ(0, kLowShadowBeg);
+ shadow_start = FindDynamicShadowStart();
+ if (SANITIZER_LINUX) full_shadow_is_available = true;
+ }
+ // Update the shadow memory address (potentially) used by instrumentation.
+ __asan_shadow_memory_dynamic_address = shadow_start;
+
+ if (kLowShadowBeg) shadow_start -= GetMmapGranularity();
+
+ if (!full_shadow_is_available)
+ full_shadow_is_available =
+ MemoryRangeIsAvailable(shadow_start, kHighShadowEnd);
+
+#if SANITIZER_LINUX && defined(__x86_64__) && defined(_LP64) && \
+ !ASAN_FIXED_MAPPING
+ if (!full_shadow_is_available) {
+ kMidMemBeg = kLowMemEnd < 0x3000000000ULL ? 0x3000000000ULL : 0;
+ kMidMemEnd = kLowMemEnd < 0x3000000000ULL ? 0x4fffffffffULL : 0;
+ }
+#endif
+
+ if (Verbosity()) PrintAddressSpaceLayout();
+
+ if (full_shadow_is_available) {
+ // mmap the low shadow plus at least one page at the left.
+ if (kLowShadowBeg)
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
+ // mmap the high shadow.
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
+ // protect the gap.
+ ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
+ CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1);
+ } else if (kMidMemBeg &&
+ MemoryRangeIsAvailable(shadow_start, kMidMemBeg - 1) &&
+ MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) {
+ CHECK(kLowShadowBeg != kLowShadowEnd);
+ // mmap the low shadow plus at least one page at the left.
+ ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow");
+ // mmap the mid shadow.
+ ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow");
+ // mmap the high shadow.
+ ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow");
+ // protect the gaps.
+ ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1);
+ ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1);
+ ProtectGap(kShadowGap3Beg, kShadowGap3End - kShadowGap3Beg + 1);
+ } else {
+ Report(
+ "Shadow memory range interleaves with an existing memory mapping. "
+ "ASan cannot proceed correctly. ABORTING.\n");
+ Report("ASan shadow was supposed to be located in the [%p-%p] range.\n",
+ shadow_start, kHighShadowEnd);
+ MaybeReportLinuxPIEBug();
+ DumpProcessMap();
+ Die();
+ }
+}
+
+} // namespace __asan
+
+#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
+++ /dev/null
-//===-- asan_stack.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Code for ASan stack trace.
-//===----------------------------------------------------------------------===//
-#include "asan_internal.h"
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_atomic.h"
-
-namespace __asan {
-
-static atomic_uint32_t malloc_context_size;
-
-void SetMallocContextSize(u32 size) {
- atomic_store(&malloc_context_size, size, memory_order_release);
-}
-
-u32 GetMallocContextSize() {
- return atomic_load(&malloc_context_size, memory_order_acquire);
-}
-
-} // namespace __asan
-
-// ------------------ Interface -------------- {{{1
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_print_stack_trace() {
- using namespace __asan;
- PRINT_CURRENT_STACK();
-}
-} // extern "C"
--- /dev/null
+//===-- asan_stack.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Code for ASan stack trace.
+//===----------------------------------------------------------------------===//
+#include "asan_internal.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+
+namespace __asan {
+
+static atomic_uint32_t malloc_context_size;
+
+void SetMallocContextSize(u32 size) {
+ atomic_store(&malloc_context_size, size, memory_order_release);
+}
+
+u32 GetMallocContextSize() {
+ return atomic_load(&malloc_context_size, memory_order_acquire);
+}
+
+namespace {
+
+// ScopedUnwinding is a scope for stacktracing member of a context
+class ScopedUnwinding {
+ public:
+ explicit ScopedUnwinding(AsanThread *t) : thread(t) {
+ if (thread) {
+ can_unwind = !thread->isUnwinding();
+ thread->setUnwinding(true);
+ }
+ }
+ ~ScopedUnwinding() {
+ if (thread)
+ thread->setUnwinding(false);
+ }
+
+ bool CanUnwind() const { return can_unwind; }
+
+ private:
+ AsanThread *thread = nullptr;
+ bool can_unwind = true;
+};
+
+} // namespace
+
+} // namespace __asan
+
+void __sanitizer::BufferedStackTrace::UnwindImpl(
+ uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+ using namespace __asan;
+ size = 0;
+ if (UNLIKELY(!asan_inited))
+ return;
+ request_fast = StackTrace::WillUseFastUnwind(request_fast);
+ AsanThread *t = GetCurrentThread();
+ ScopedUnwinding unwind_scope(t);
+ if (!unwind_scope.CanUnwind())
+ return;
+ if (request_fast) {
+ if (t) {
+ Unwind(max_depth, pc, bp, nullptr, t->stack_top(), t->stack_bottom(),
+ true);
+ }
+ return;
+ }
+ if (SANITIZER_MIPS && t &&
+ !IsValidFrame(bp, t->stack_top(), t->stack_bottom()))
+ return;
+ Unwind(max_depth, pc, bp, context, 0, 0, false);
+}
+
+// ------------------ Interface -------------- {{{1
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ using namespace __asan;
+ PRINT_CURRENT_STACK();
+}
+} // extern "C"
//===-- asan_stack.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_stack.cc.
+// ASan-private header for asan_stack.cpp.
//===----------------------------------------------------------------------===//
#ifndef ASAN_STACK_H
void SetMallocContextSize(u32 size);
u32 GetMallocContextSize();
-// Get the stack trace with the given pc and bp.
-// The pc will be in the position 0 of the resulting stack trace.
-// The bp may refer to the current frame or to the caller's frame.
-ALWAYS_INLINE
-void GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc, uptr bp,
- void *context, bool fast) {
-#if SANITIZER_WINDOWS
- stack->Unwind(max_depth, pc, bp, context, 0, 0, fast);
-#else
- AsanThread *t;
- stack->size = 0;
- if (LIKELY(asan_inited)) {
- if ((t = GetCurrentThread()) && !t->isUnwinding()) {
- uptr stack_top = t->stack_top();
- uptr stack_bottom = t->stack_bottom();
- ScopedUnwinding unwind_scope(t);
- if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) {
- stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom,
- fast);
- }
- } else if (!t && !fast) {
- /* If GetCurrentThread() has failed, try to do slow unwind anyways. */
- stack->Unwind(max_depth, pc, bp, context, 0, 0, false);
- }
- }
-#endif // SANITIZER_WINDOWS
-}
-
} // namespace __asan
// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors
if (max_size > 1) stack.trace_buffer[1] = GET_CALLER_PC(); \
} \
} else { \
- GetStackTrace(&stack, max_size, StackTrace::GetCurrentPc(), \
- GET_CURRENT_FRAME(), 0, fast); \
+ stack.Unwind(StackTrace::GetCurrentPc(), \
+ GET_CURRENT_FRAME(), nullptr, fast, max_size); \
}
#define GET_STACK_TRACE_FATAL(pc, bp) \
BufferedStackTrace stack; \
- GetStackTrace(&stack, kStackTraceMax, pc, bp, 0, \
- common_flags()->fast_unwind_on_fatal)
+ stack.Unwind(pc, bp, nullptr, \
+ common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_SIGNAL(sig) \
BufferedStackTrace stack; \
- GetStackTrace(&stack, kStackTraceMax, (sig).pc, (sig).bp, (sig).context, \
- common_flags()->fast_unwind_on_fatal)
+ stack.Unwind((sig).pc, (sig).bp, (sig).context, \
+ common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_FATAL_HERE \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
+++ /dev/null
-//===-- asan_stats.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Code related to statistics collected by AddressSanitizer.
-//===----------------------------------------------------------------------===//
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_stats.h"
-#include "asan_thread.h"
-#include "sanitizer_common/sanitizer_allocator_interface.h"
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-
-namespace __asan {
-
-AsanStats::AsanStats() {
- Clear();
-}
-
-void AsanStats::Clear() {
- CHECK(REAL(memset));
- REAL(memset)(this, 0, sizeof(AsanStats));
-}
-
-static void PrintMallocStatsArray(const char *prefix,
- uptr (&array)[kNumberOfSizeClasses]) {
- Printf("%s", prefix);
- for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
- if (!array[i]) continue;
- Printf("%zu:%zu; ", i, array[i]);
- }
- Printf("\n");
-}
-
-void AsanStats::Print() {
- Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
- malloced>>20, malloced_redzones>>20, mallocs);
- Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
- Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
- Printf("Stats: %zuM really freed by %zu calls\n",
- really_freed>>20, real_frees);
- Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
- (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
- mmaps, munmaps);
-
- PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
- Printf("Stats: malloc large: %zu\n", malloc_large);
-}
-
-void AsanStats::MergeFrom(const AsanStats *stats) {
- uptr *dst_ptr = reinterpret_cast<uptr*>(this);
- const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
- uptr num_fields = sizeof(*this) / sizeof(uptr);
- for (uptr i = 0; i < num_fields; i++)
- dst_ptr[i] += src_ptr[i];
-}
-
-static BlockingMutex print_lock(LINKER_INITIALIZED);
-
-static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
-static AsanStats dead_threads_stats(LINKER_INITIALIZED);
-static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
-// Required for malloc_zone_statistics() on OS X. This can't be stored in
-// per-thread AsanStats.
-static uptr max_malloced_memory;
-
-static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
- AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
- AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
- if (AsanThread *t = tctx->thread)
- accumulated_stats->MergeFrom(&t->stats());
-}
-
-static void GetAccumulatedStats(AsanStats *stats) {
- stats->Clear();
- {
- ThreadRegistryLock l(&asanThreadRegistry());
- asanThreadRegistry()
- .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
- }
- stats->MergeFrom(&unknown_thread_stats);
- {
- BlockingMutexLock lock(&dead_threads_stats_lock);
- stats->MergeFrom(&dead_threads_stats);
- }
- // This is not very accurate: we may miss allocation peaks that happen
- // between two updates of accumulated_stats_. For more accurate bookkeeping
- // the maximum should be updated on every malloc(), which is unacceptable.
- if (max_malloced_memory < stats->malloced) {
- max_malloced_memory = stats->malloced;
- }
-}
-
-void FlushToDeadThreadStats(AsanStats *stats) {
- BlockingMutexLock lock(&dead_threads_stats_lock);
- dead_threads_stats.MergeFrom(stats);
- stats->Clear();
-}
-
-void FillMallocStatistics(AsanMallocStats *malloc_stats) {
- AsanStats stats;
- GetAccumulatedStats(&stats);
- malloc_stats->blocks_in_use = stats.mallocs;
- malloc_stats->size_in_use = stats.malloced;
- malloc_stats->max_size_in_use = max_malloced_memory;
- malloc_stats->size_allocated = stats.mmaped;
-}
-
-AsanStats &GetCurrentThreadStats() {
- AsanThread *t = GetCurrentThread();
- return (t) ? t->stats() : unknown_thread_stats;
-}
-
-static void PrintAccumulatedStats() {
- AsanStats stats;
- GetAccumulatedStats(&stats);
- // Use lock to keep reports from mixing up.
- BlockingMutexLock lock(&print_lock);
- stats.Print();
- StackDepotStats *stack_depot_stats = StackDepotGetStats();
- Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
- stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
- PrintInternalAllocatorStats();
-}
-
-} // namespace __asan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-uptr __sanitizer_get_current_allocated_bytes() {
- AsanStats stats;
- GetAccumulatedStats(&stats);
- uptr malloced = stats.malloced;
- uptr freed = stats.freed;
- // Return sane value if malloced < freed due to racy
- // way we update accumulated stats.
- return (malloced > freed) ? malloced - freed : 1;
-}
-
-uptr __sanitizer_get_heap_size() {
- AsanStats stats;
- GetAccumulatedStats(&stats);
- return stats.mmaped - stats.munmaped;
-}
-
-uptr __sanitizer_get_free_bytes() {
- AsanStats stats;
- GetAccumulatedStats(&stats);
- uptr total_free = stats.mmaped
- - stats.munmaped
- + stats.really_freed;
- uptr total_used = stats.malloced
- + stats.malloced_redzones;
- // Return sane value if total_free < total_used due to racy
- // way we update accumulated stats.
- return (total_free > total_used) ? total_free - total_used : 1;
-}
-
-uptr __sanitizer_get_unmapped_bytes() {
- return 0;
-}
-
-void __asan_print_accumulated_stats() {
- PrintAccumulatedStats();
-}
--- /dev/null
+//===-- asan_stats.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Code related to statistics collected by AddressSanitizer.
+//===----------------------------------------------------------------------===//
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_stats.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+AsanStats::AsanStats() {
+ Clear();
+}
+
+void AsanStats::Clear() {
+ CHECK(REAL(memset));
+ REAL(memset)(this, 0, sizeof(AsanStats));
+}
+
+static void PrintMallocStatsArray(const char *prefix,
+ uptr (&array)[kNumberOfSizeClasses]) {
+ Printf("%s", prefix);
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ if (!array[i]) continue;
+ Printf("%zu:%zu; ", i, array[i]);
+ }
+ Printf("\n");
+}
+
+void AsanStats::Print() {
+ Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
+ malloced>>20, malloced_redzones>>20, mallocs);
+ Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
+ Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
+ Printf("Stats: %zuM really freed by %zu calls\n",
+ really_freed>>20, real_frees);
+ Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
+ (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
+ mmaps, munmaps);
+
+ PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size);
+ Printf("Stats: malloc large: %zu\n", malloc_large);
+}
+
+void AsanStats::MergeFrom(const AsanStats *stats) {
+ uptr *dst_ptr = reinterpret_cast<uptr*>(this);
+ const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
+ uptr num_fields = sizeof(*this) / sizeof(uptr);
+ for (uptr i = 0; i < num_fields; i++)
+ dst_ptr[i] += src_ptr[i];
+}
+
+static BlockingMutex print_lock(LINKER_INITIALIZED);
+
+static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
+static AsanStats dead_threads_stats(LINKER_INITIALIZED);
+static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
+// Required for malloc_zone_statistics() on OS X. This can't be stored in
+// per-thread AsanStats.
+static uptr max_malloced_memory;
+
+static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
+ AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
+ AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
+ if (AsanThread *t = tctx->thread)
+ accumulated_stats->MergeFrom(&t->stats());
+}
+
+static void GetAccumulatedStats(AsanStats *stats) {
+ stats->Clear();
+ {
+ ThreadRegistryLock l(&asanThreadRegistry());
+ asanThreadRegistry()
+ .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
+ }
+ stats->MergeFrom(&unknown_thread_stats);
+ {
+ BlockingMutexLock lock(&dead_threads_stats_lock);
+ stats->MergeFrom(&dead_threads_stats);
+ }
+ // This is not very accurate: we may miss allocation peaks that happen
+ // between two updates of accumulated_stats_. For more accurate bookkeeping
+ // the maximum should be updated on every malloc(), which is unacceptable.
+ if (max_malloced_memory < stats->malloced) {
+ max_malloced_memory = stats->malloced;
+ }
+}
+
+void FlushToDeadThreadStats(AsanStats *stats) {
+ BlockingMutexLock lock(&dead_threads_stats_lock);
+ dead_threads_stats.MergeFrom(stats);
+ stats->Clear();
+}
+
+void FillMallocStatistics(AsanMallocStats *malloc_stats) {
+ AsanStats stats;
+ GetAccumulatedStats(&stats);
+ malloc_stats->blocks_in_use = stats.mallocs;
+ malloc_stats->size_in_use = stats.malloced;
+ malloc_stats->max_size_in_use = max_malloced_memory;
+ malloc_stats->size_allocated = stats.mmaped;
+}
+
+AsanStats &GetCurrentThreadStats() {
+ AsanThread *t = GetCurrentThread();
+ return (t) ? t->stats() : unknown_thread_stats;
+}
+
+static void PrintAccumulatedStats() {
+ AsanStats stats;
+ GetAccumulatedStats(&stats);
+ // Use lock to keep reports from mixing up.
+ BlockingMutexLock lock(&print_lock);
+ stats.Print();
+ StackDepotStats *stack_depot_stats = StackDepotGetStats();
+ Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
+ stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
+ PrintInternalAllocatorStats();
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+uptr __sanitizer_get_current_allocated_bytes() {
+ AsanStats stats;
+ GetAccumulatedStats(&stats);
+ uptr malloced = stats.malloced;
+ uptr freed = stats.freed;
+ // Return sane value if malloced < freed due to racy
+ // way we update accumulated stats.
+ return (malloced > freed) ? malloced - freed : 1;
+}
+
+uptr __sanitizer_get_heap_size() {
+ AsanStats stats;
+ GetAccumulatedStats(&stats);
+ return stats.mmaped - stats.munmaped;
+}
+
+uptr __sanitizer_get_free_bytes() {
+ AsanStats stats;
+ GetAccumulatedStats(&stats);
+ uptr total_free = stats.mmaped
+ - stats.munmaped
+ + stats.really_freed;
+ uptr total_used = stats.malloced
+ + stats.malloced_redzones;
+ // Return sane value if total_free < total_used due to racy
+ // way we update accumulated stats.
+ return (total_free > total_used) ? total_free - total_used : 1;
+}
+
+uptr __sanitizer_get_unmapped_bytes() {
+ return 0;
+}
+
+void __asan_print_accumulated_stats() {
+ PrintAccumulatedStats();
+}
//===-- asan_stats.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- asan_suppressions.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Issue suppression and suppression-related functions.
-//===----------------------------------------------------------------------===//
-
-#include "asan_suppressions.h"
-
-#include "asan_stack.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-
-namespace __asan {
-
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
-static SuppressionContext *suppression_ctx = nullptr;
-static const char kInterceptorName[] = "interceptor_name";
-static const char kInterceptorViaFunction[] = "interceptor_via_fun";
-static const char kInterceptorViaLibrary[] = "interceptor_via_lib";
-static const char kODRViolation[] = "odr_violation";
-static const char *kSuppressionTypes[] = {
- kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary,
- kODRViolation};
-
-SANITIZER_INTERFACE_WEAK_DEF(const char *, __asan_default_suppressions, void) {
- return "";
-}
-
-void InitializeSuppressions() {
- CHECK_EQ(nullptr, suppression_ctx);
- suppression_ctx = new (suppression_placeholder) // NOLINT
- SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
- suppression_ctx->ParseFromFile(flags()->suppressions);
- if (&__asan_default_suppressions)
- suppression_ctx->Parse(__asan_default_suppressions());
-}
-
-bool IsInterceptorSuppressed(const char *interceptor_name) {
- CHECK(suppression_ctx);
- Suppression *s;
- // Match "interceptor_name" suppressions.
- return suppression_ctx->Match(interceptor_name, kInterceptorName, &s);
-}
-
-bool HaveStackTraceBasedSuppressions() {
- CHECK(suppression_ctx);
- return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) ||
- suppression_ctx->HasSuppressionType(kInterceptorViaLibrary);
-}
-
-bool IsODRViolationSuppressed(const char *global_var_name) {
- CHECK(suppression_ctx);
- Suppression *s;
- // Match "odr_violation" suppressions.
- return suppression_ctx->Match(global_var_name, kODRViolation, &s);
-}
-
-bool IsStackTraceSuppressed(const StackTrace *stack) {
- if (!HaveStackTraceBasedSuppressions())
- return false;
-
- CHECK(suppression_ctx);
- Symbolizer *symbolizer = Symbolizer::GetOrInit();
- Suppression *s;
- for (uptr i = 0; i < stack->size && stack->trace[i]; i++) {
- uptr addr = stack->trace[i];
-
- if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) {
- // Match "interceptor_via_lib" suppressions.
- if (const char *module_name = symbolizer->GetModuleNameForPc(addr))
- if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s))
- return true;
- }
-
- if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
- SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
- CHECK(frames);
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
- const char *function_name = cur->info.function;
- if (!function_name) {
- continue;
- }
- // Match "interceptor_via_fun" suppressions.
- if (suppression_ctx->Match(function_name, kInterceptorViaFunction,
- &s)) {
- frames->ClearAll();
- return true;
- }
- }
- frames->ClearAll();
- }
- }
- return false;
-}
-
-} // namespace __asan
--- /dev/null
+//===-- asan_suppressions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Issue suppression and suppression-related functions.
+//===----------------------------------------------------------------------===//
+
+#include "asan_suppressions.h"
+
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+namespace __asan {
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kInterceptorName[] = "interceptor_name";
+static const char kInterceptorViaFunction[] = "interceptor_via_fun";
+static const char kInterceptorViaLibrary[] = "interceptor_via_lib";
+static const char kODRViolation[] = "odr_violation";
+static const char *kSuppressionTypes[] = {
+ kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary,
+ kODRViolation};
+
+SANITIZER_INTERFACE_WEAK_DEF(const char *, __asan_default_suppressions, void) {
+ return "";
+}
+
+void InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+ if (&__asan_default_suppressions)
+ suppression_ctx->Parse(__asan_default_suppressions());
+}
+
+bool IsInterceptorSuppressed(const char *interceptor_name) {
+ CHECK(suppression_ctx);
+ Suppression *s;
+ // Match "interceptor_name" suppressions.
+ return suppression_ctx->Match(interceptor_name, kInterceptorName, &s);
+}
+
+bool HaveStackTraceBasedSuppressions() {
+ CHECK(suppression_ctx);
+ return suppression_ctx->HasSuppressionType(kInterceptorViaFunction) ||
+ suppression_ctx->HasSuppressionType(kInterceptorViaLibrary);
+}
+
+bool IsODRViolationSuppressed(const char *global_var_name) {
+ CHECK(suppression_ctx);
+ Suppression *s;
+ // Match "odr_violation" suppressions.
+ return suppression_ctx->Match(global_var_name, kODRViolation, &s);
+}
+
+bool IsStackTraceSuppressed(const StackTrace *stack) {
+ if (!HaveStackTraceBasedSuppressions())
+ return false;
+
+ CHECK(suppression_ctx);
+ Symbolizer *symbolizer = Symbolizer::GetOrInit();
+ Suppression *s;
+ for (uptr i = 0; i < stack->size && stack->trace[i]; i++) {
+ uptr addr = stack->trace[i];
+
+ if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) {
+ // Match "interceptor_via_lib" suppressions.
+ if (const char *module_name = symbolizer->GetModuleNameForPc(addr))
+ if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s))
+ return true;
+ }
+
+ if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) {
+ SymbolizedStack *frames = symbolizer->SymbolizePC(addr);
+ CHECK(frames);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ const char *function_name = cur->info.function;
+ if (!function_name) {
+ continue;
+ }
+ // Match "interceptor_via_fun" suppressions.
+ if (suppression_ctx->Match(function_name, kInterceptorViaFunction,
+ &s)) {
+ frames->ClearAll();
+ return true;
+ }
+ }
+ frames->ClearAll();
+ }
+ }
+ return false;
+}
+
+} // namespace __asan
//===-- asan_suppressions.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_suppressions.cc.
+// ASan-private header for asan_suppressions.cpp.
//===----------------------------------------------------------------------===//
#ifndef ASAN_SUPPRESSIONS_H
#define ASAN_SUPPRESSIONS_H
+++ /dev/null
-//===-- asan_thread.cc ----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Thread-related code.
-//===----------------------------------------------------------------------===//
-#include "asan_allocator.h"
-#include "asan_interceptors.h"
-#include "asan_poisoning.h"
-#include "asan_stack.h"
-#include "asan_thread.h"
-#include "asan_mapping.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
-#include "lsan/lsan_common.h"
-
-namespace __asan {
-
-// AsanThreadContext implementation.
-
-void AsanThreadContext::OnCreated(void *arg) {
- CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
- if (args->stack)
- stack_id = StackDepotPut(*args->stack);
- thread = args->thread;
- thread->set_context(this);
-}
-
-void AsanThreadContext::OnFinished() {
- // Drop the link to the AsanThread object.
- thread = nullptr;
-}
-
-// MIPS requires aligned address
-static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)];
-static ThreadRegistry *asan_thread_registry;
-
-static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED);
-static LowLevelAllocator allocator_for_thread_context;
-
-static ThreadContextBase *GetAsanThreadContext(u32 tid) {
- BlockingMutexLock lock(&mu_for_thread_context);
- return new(allocator_for_thread_context) AsanThreadContext(tid);
-}
-
-ThreadRegistry &asanThreadRegistry() {
- static bool initialized;
- // Don't worry about thread_safety - this should be called when there is
- // a single thread.
- if (!initialized) {
- // Never reuse ASan threads: we store pointer to AsanThreadContext
- // in TSD and can't reliably tell when no more TSD destructors will
- // be called. It would be wrong to reuse AsanThreadContext for another
- // thread before all TSD destructors will be called for it.
- asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry(
- GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads);
- initialized = true;
- }
- return *asan_thread_registry;
-}
-
-AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
- return static_cast<AsanThreadContext *>(
- asanThreadRegistry().GetThreadLocked(tid));
-}
-
-// AsanThread implementation.
-
-AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg,
- u32 parent_tid, StackTrace *stack,
- bool detached) {
- uptr PageSize = GetPageSizeCached();
- uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
- AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__);
- thread->start_routine_ = start_routine;
- thread->arg_ = arg;
- AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
- asanThreadRegistry().CreateThread(*reinterpret_cast<uptr *>(thread), detached,
- parent_tid, &args);
-
- return thread;
-}
-
-void AsanThread::TSDDtor(void *tsd) {
- AsanThreadContext *context = (AsanThreadContext*)tsd;
- VReport(1, "T%d TSDDtor\n", context->tid);
- if (context->thread)
- context->thread->Destroy();
-}
-
-void AsanThread::Destroy() {
- int tid = this->tid();
- VReport(1, "T%d exited\n", tid);
-
- malloc_storage().CommitBack();
- if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack();
- asanThreadRegistry().FinishThread(tid);
- FlushToDeadThreadStats(&stats_);
- // We also clear the shadow on thread destruction because
- // some code may still be executing in later TSD destructors
- // and we don't want it to have any poisoned stack.
- ClearShadowForThreadStackAndTLS();
- DeleteFakeStack(tid);
- uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
- UnmapOrDie(this, size);
- DTLS_Destroy();
-}
-
-void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
- uptr size) {
- if (atomic_load(&stack_switching_, memory_order_relaxed)) {
- Report("ERROR: starting fiber switch while in fiber switch\n");
- Die();
- }
-
- next_stack_bottom_ = bottom;
- next_stack_top_ = bottom + size;
- atomic_store(&stack_switching_, 1, memory_order_release);
-
- FakeStack *current_fake_stack = fake_stack_;
- if (fake_stack_save)
- *fake_stack_save = fake_stack_;
- fake_stack_ = nullptr;
- SetTLSFakeStack(nullptr);
- // if fake_stack_save is null, the fiber will die, delete the fakestack
- if (!fake_stack_save && current_fake_stack)
- current_fake_stack->Destroy(this->tid());
-}
-
-void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save,
- uptr *bottom_old,
- uptr *size_old) {
- if (!atomic_load(&stack_switching_, memory_order_relaxed)) {
- Report("ERROR: finishing a fiber switch that has not started\n");
- Die();
- }
-
- if (fake_stack_save) {
- SetTLSFakeStack(fake_stack_save);
- fake_stack_ = fake_stack_save;
- }
-
- if (bottom_old)
- *bottom_old = stack_bottom_;
- if (size_old)
- *size_old = stack_top_ - stack_bottom_;
- stack_bottom_ = next_stack_bottom_;
- stack_top_ = next_stack_top_;
- atomic_store(&stack_switching_, 0, memory_order_release);
- next_stack_top_ = 0;
- next_stack_bottom_ = 0;
-}
-
-inline AsanThread::StackBounds AsanThread::GetStackBounds() const {
- if (!atomic_load(&stack_switching_, memory_order_acquire)) {
- // Make sure the stack bounds are fully initialized.
- if (stack_bottom_ >= stack_top_) return {0, 0};
- return {stack_bottom_, stack_top_};
- }
- char local;
- const uptr cur_stack = (uptr)&local;
- // Note: need to check next stack first, because FinishSwitchFiber
- // may be in process of overwriting stack_top_/bottom_. But in such case
- // we are already on the next stack.
- if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_)
- return {next_stack_bottom_, next_stack_top_};
- return {stack_bottom_, stack_top_};
-}
-
-uptr AsanThread::stack_top() {
- return GetStackBounds().top;
-}
-
-uptr AsanThread::stack_bottom() {
- return GetStackBounds().bottom;
-}
-
-uptr AsanThread::stack_size() {
- const auto bounds = GetStackBounds();
- return bounds.top - bounds.bottom;
-}
-
-// We want to create the FakeStack lazyly on the first use, but not eralier
-// than the stack size is known and the procedure has to be async-signal safe.
-FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
- uptr stack_size = this->stack_size();
- if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
- return nullptr;
- uptr old_val = 0;
- // fake_stack_ has 3 states:
- // 0 -- not initialized
- // 1 -- being initialized
- // ptr -- initialized
- // This CAS checks if the state was 0 and if so changes it to state 1,
- // if that was successful, it initializes the pointer.
- if (atomic_compare_exchange_strong(
- reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL,
- memory_order_relaxed)) {
- uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size));
- CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log);
- stack_size_log =
- Min(stack_size_log, static_cast<uptr>(flags()->max_uar_stack_size_log));
- stack_size_log =
- Max(stack_size_log, static_cast<uptr>(flags()->min_uar_stack_size_log));
- fake_stack_ = FakeStack::Create(stack_size_log);
- SetTLSFakeStack(fake_stack_);
- return fake_stack_;
- }
- return nullptr;
-}
-
-void AsanThread::Init(const InitOptions *options) {
- next_stack_top_ = next_stack_bottom_ = 0;
- atomic_store(&stack_switching_, false, memory_order_release);
- CHECK_EQ(this->stack_size(), 0U);
- SetThreadStackAndTls(options);
- CHECK_GT(this->stack_size(), 0U);
- CHECK(AddrIsInMem(stack_bottom_));
- CHECK(AddrIsInMem(stack_top_ - 1));
- ClearShadowForThreadStackAndTLS();
- fake_stack_ = nullptr;
- if (__asan_option_detect_stack_use_after_return)
- AsyncSignalSafeLazyInitFakeStack();
- int local = 0;
- VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
- (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
- &local);
-}
-
-// Fuchsia and RTEMS don't use ThreadStart.
-// asan_fuchsia.c/asan_rtems.c define CreateMainThread and
-// SetThreadStackAndTls.
-#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
-
-thread_return_t AsanThread::ThreadStart(
- tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) {
- Init();
- asanThreadRegistry().StartThread(tid(), os_id, /*workerthread*/ false,
- nullptr);
- if (signal_thread_is_registered)
- atomic_store(signal_thread_is_registered, 1, memory_order_release);
-
- if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
-
- if (!start_routine_) {
- // start_routine_ == 0 if we're on the main thread or on one of the
- // OS X libdispatch worker threads. But nobody is supposed to call
- // ThreadStart() for the worker threads.
- CHECK_EQ(tid(), 0);
- return 0;
- }
-
- thread_return_t res = start_routine_(arg_);
-
- // On POSIX systems we defer this to the TSD destructor. LSan will consider
- // the thread's memory as non-live from the moment we call Destroy(), even
- // though that memory might contain pointers to heap objects which will be
- // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before
- // the TSD destructors have run might cause false positives in LSan.
- if (!SANITIZER_POSIX)
- this->Destroy();
-
- return res;
-}
-
-AsanThread *CreateMainThread() {
- AsanThread *main_thread = AsanThread::Create(
- /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0,
- /* stack */ nullptr, /* detached */ true);
- SetCurrentThread(main_thread);
- main_thread->ThreadStart(internal_getpid(),
- /* signal_thread_is_registered */ nullptr);
- return main_thread;
-}
-
-// This implementation doesn't use the argument, which is just passed down
-// from the caller of Init (which see, above). It's only there to support
-// OS-specific implementations that need more information passed through.
-void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
- DCHECK_EQ(options, nullptr);
- uptr tls_size = 0;
- uptr stack_size = 0;
- GetThreadStackAndTls(tid() == 0, const_cast<uptr *>(&stack_bottom_),
- const_cast<uptr *>(&stack_size), &tls_begin_, &tls_size);
- stack_top_ = stack_bottom_ + stack_size;
- tls_end_ = tls_begin_ + tls_size;
- dtls_ = DTLS_Get();
-
- int local;
- CHECK(AddrIsInStack((uptr)&local));
-}
-
-#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
-
-void AsanThread::ClearShadowForThreadStackAndTLS() {
- PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
- if (tls_begin_ != tls_end_) {
- uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY);
- uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY);
- FastPoisonShadowPartialRightRedzone(tls_begin_aligned,
- tls_end_ - tls_begin_aligned,
- tls_end_aligned - tls_end_, 0);
- }
-}
-
-bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
- StackFrameAccess *access) {
- uptr bottom = 0;
- if (AddrIsInStack(addr)) {
- bottom = stack_bottom();
- } else if (has_fake_stack()) {
- bottom = fake_stack()->AddrIsInFakeStack(addr);
- CHECK(bottom);
- access->offset = addr - bottom;
- access->frame_pc = ((uptr*)bottom)[2];
- access->frame_descr = (const char *)((uptr*)bottom)[1];
- return true;
- }
- uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr.
- uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY);
- u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
- u8 *shadow_bottom = (u8*)MemToShadow(bottom);
-
- while (shadow_ptr >= shadow_bottom &&
- *shadow_ptr != kAsanStackLeftRedzoneMagic) {
- shadow_ptr--;
- mem_ptr -= SHADOW_GRANULARITY;
- }
-
- while (shadow_ptr >= shadow_bottom &&
- *shadow_ptr == kAsanStackLeftRedzoneMagic) {
- shadow_ptr--;
- mem_ptr -= SHADOW_GRANULARITY;
- }
-
- if (shadow_ptr < shadow_bottom) {
- return false;
- }
-
- uptr* ptr = (uptr*)(mem_ptr + SHADOW_GRANULARITY);
- CHECK(ptr[0] == kCurrentStackFrameMagic);
- access->offset = addr - (uptr)ptr;
- access->frame_pc = ptr[2];
- access->frame_descr = (const char*)ptr[1];
- return true;
-}
-
-uptr AsanThread::GetStackVariableShadowStart(uptr addr) {
- uptr bottom = 0;
- if (AddrIsInStack(addr)) {
- bottom = stack_bottom();
- } else if (has_fake_stack()) {
- bottom = fake_stack()->AddrIsInFakeStack(addr);
- CHECK(bottom);
- } else
- return 0;
-
- uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr.
- u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
- u8 *shadow_bottom = (u8*)MemToShadow(bottom);
-
- while (shadow_ptr >= shadow_bottom &&
- (*shadow_ptr != kAsanStackLeftRedzoneMagic &&
- *shadow_ptr != kAsanStackMidRedzoneMagic &&
- *shadow_ptr != kAsanStackRightRedzoneMagic))
- shadow_ptr--;
-
- return (uptr)shadow_ptr + 1;
-}
-
-bool AsanThread::AddrIsInStack(uptr addr) {
- const auto bounds = GetStackBounds();
- return addr >= bounds.bottom && addr < bounds.top;
-}
-
-static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
- void *addr) {
- AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
- AsanThread *t = tctx->thread;
- if (!t) return false;
- if (t->AddrIsInStack((uptr)addr)) return true;
- if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr))
- return true;
- return false;
-}
-
-AsanThread *GetCurrentThread() {
- if (SANITIZER_RTEMS && !asan_inited)
- return nullptr;
-
- AsanThreadContext *context =
- reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
- if (!context) {
- if (SANITIZER_ANDROID) {
- // On Android, libc constructor is called _after_ asan_init, and cleans up
- // TSD. Try to figure out if this is still the main thread by the stack
- // address. We are not entirely sure that we have correct main thread
- // limits, so only do this magic on Android, and only if the found thread
- // is the main thread.
- AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
- if (tctx && ThreadStackContainsAddress(tctx, &context)) {
- SetCurrentThread(tctx->thread);
- return tctx->thread;
- }
- }
- return nullptr;
- }
- return context->thread;
-}
-
-void SetCurrentThread(AsanThread *t) {
- CHECK(t->context());
- VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(),
- (void *)GetThreadSelf());
- // Make sure we do not reset the current AsanThread.
- CHECK_EQ(0, AsanTSDGet());
- AsanTSDSet(t->context());
- CHECK_EQ(t->context(), AsanTSDGet());
-}
-
-u32 GetCurrentTidOrInvalid() {
- AsanThread *t = GetCurrentThread();
- return t ? t->tid() : kInvalidTid;
-}
-
-AsanThread *FindThreadByStackAddress(uptr addr) {
- asanThreadRegistry().CheckLocked();
- AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
- asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
- (void *)addr));
- return tctx ? tctx->thread : nullptr;
-}
-
-void EnsureMainThreadIDIsCorrect() {
- AsanThreadContext *context =
- reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
- if (context && (context->tid == 0))
- context->os_id = GetTid();
-}
-
-__asan::AsanThread *GetAsanThreadByOsIDLocked(tid_t os_id) {
- __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
- __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
- if (!context) return nullptr;
- return context->thread;
-}
-} // namespace __asan
-
-// --- Implementation of LSan-specific functions --- {{{1
-namespace __lsan {
-bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
- uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
- uptr *cache_end, DTLS **dtls) {
- __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
- if (!t) return false;
- *stack_begin = t->stack_bottom();
- *stack_end = t->stack_top();
- *tls_begin = t->tls_begin();
- *tls_end = t->tls_end();
- // ASan doesn't keep allocator caches in TLS, so these are unused.
- *cache_begin = 0;
- *cache_end = 0;
- *dtls = t->dtls();
- return true;
-}
-
-void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
- void *arg) {
- __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
- if (t && t->has_fake_stack())
- t->fake_stack()->ForEachFakeFrame(callback, arg);
-}
-
-void LockThreadRegistry() {
- __asan::asanThreadRegistry().Lock();
-}
-
-void UnlockThreadRegistry() {
- __asan::asanThreadRegistry().Unlock();
-}
-
-ThreadRegistry *GetThreadRegistryLocked() {
- __asan::asanThreadRegistry().CheckLocked();
- return &__asan::asanThreadRegistry();
-}
-
-void EnsureMainThreadIDIsCorrect() {
- __asan::EnsureMainThreadIDIsCorrect();
-}
-} // namespace __lsan
-
-// ---------------------- Interface ---------------- {{{1
-using namespace __asan; // NOLINT
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom,
- uptr size) {
- AsanThread *t = GetCurrentThread();
- if (!t) {
- VReport(1, "__asan_start_switch_fiber called from unknown thread\n");
- return;
- }
- t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_finish_switch_fiber(void* fakestack,
- const void **bottom_old,
- uptr *size_old) {
- AsanThread *t = GetCurrentThread();
- if (!t) {
- VReport(1, "__asan_finish_switch_fiber called from unknown thread\n");
- return;
- }
- t->FinishSwitchFiber((FakeStack*)fakestack,
- (uptr*)bottom_old,
- (uptr*)size_old);
-}
-}
--- /dev/null
+//===-- asan_thread.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Thread-related code.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_interceptors.h"
+#include "asan_poisoning.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "asan_mapping.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+#include "lsan/lsan_common.h"
+
+namespace __asan {
+
+// AsanThreadContext implementation.
+
+void AsanThreadContext::OnCreated(void *arg) {
+ CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
+ if (args->stack)
+ stack_id = StackDepotPut(*args->stack);
+ thread = args->thread;
+ thread->set_context(this);
+}
+
+void AsanThreadContext::OnFinished() {
+ // Drop the link to the AsanThread object.
+ thread = nullptr;
+}
+
+// MIPS requires aligned address
+static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)];
+static ThreadRegistry *asan_thread_registry;
+
+static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED);
+static LowLevelAllocator allocator_for_thread_context;
+
+static ThreadContextBase *GetAsanThreadContext(u32 tid) {
+ BlockingMutexLock lock(&mu_for_thread_context);
+ return new(allocator_for_thread_context) AsanThreadContext(tid);
+}
+
+ThreadRegistry &asanThreadRegistry() {
+ static bool initialized;
+ // Don't worry about thread_safety - this should be called when there is
+ // a single thread.
+ if (!initialized) {
+ // Never reuse ASan threads: we store pointer to AsanThreadContext
+ // in TSD and can't reliably tell when no more TSD destructors will
+ // be called. It would be wrong to reuse AsanThreadContext for another
+ // thread before all TSD destructors will be called for it.
+ asan_thread_registry = new(thread_registry_placeholder) ThreadRegistry(
+ GetAsanThreadContext, kMaxNumberOfThreads, kMaxNumberOfThreads);
+ initialized = true;
+ }
+ return *asan_thread_registry;
+}
+
+AsanThreadContext *GetThreadContextByTidLocked(u32 tid) {
+ return static_cast<AsanThreadContext *>(
+ asanThreadRegistry().GetThreadLocked(tid));
+}
+
+// AsanThread implementation.
+
+AsanThread *AsanThread::Create(thread_callback_t start_routine, void *arg,
+ u32 parent_tid, StackTrace *stack,
+ bool detached) {
+ uptr PageSize = GetPageSizeCached();
+ uptr size = RoundUpTo(sizeof(AsanThread), PageSize);
+ AsanThread *thread = (AsanThread*)MmapOrDie(size, __func__);
+ thread->start_routine_ = start_routine;
+ thread->arg_ = arg;
+ AsanThreadContext::CreateThreadContextArgs args = {thread, stack};
+ asanThreadRegistry().CreateThread(*reinterpret_cast<uptr *>(thread), detached,
+ parent_tid, &args);
+
+ return thread;
+}
+
+void AsanThread::TSDDtor(void *tsd) {
+ AsanThreadContext *context = (AsanThreadContext*)tsd;
+ VReport(1, "T%d TSDDtor\n", context->tid);
+ if (context->thread)
+ context->thread->Destroy();
+}
+
+void AsanThread::Destroy() {
+ int tid = this->tid();
+ VReport(1, "T%d exited\n", tid);
+
+ malloc_storage().CommitBack();
+ if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack();
+ asanThreadRegistry().FinishThread(tid);
+ FlushToDeadThreadStats(&stats_);
+ // We also clear the shadow on thread destruction because
+ // some code may still be executing in later TSD destructors
+ // and we don't want it to have any poisoned stack.
+ ClearShadowForThreadStackAndTLS();
+ DeleteFakeStack(tid);
+ uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached());
+ UnmapOrDie(this, size);
+ DTLS_Destroy();
+}
+
+void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom,
+ uptr size) {
+ if (atomic_load(&stack_switching_, memory_order_relaxed)) {
+ Report("ERROR: starting fiber switch while in fiber switch\n");
+ Die();
+ }
+
+ next_stack_bottom_ = bottom;
+ next_stack_top_ = bottom + size;
+ atomic_store(&stack_switching_, 1, memory_order_release);
+
+ FakeStack *current_fake_stack = fake_stack_;
+ if (fake_stack_save)
+ *fake_stack_save = fake_stack_;
+ fake_stack_ = nullptr;
+ SetTLSFakeStack(nullptr);
+ // if fake_stack_save is null, the fiber will die, delete the fakestack
+ if (!fake_stack_save && current_fake_stack)
+ current_fake_stack->Destroy(this->tid());
+}
+
+void AsanThread::FinishSwitchFiber(FakeStack *fake_stack_save,
+ uptr *bottom_old,
+ uptr *size_old) {
+ if (!atomic_load(&stack_switching_, memory_order_relaxed)) {
+ Report("ERROR: finishing a fiber switch that has not started\n");
+ Die();
+ }
+
+ if (fake_stack_save) {
+ SetTLSFakeStack(fake_stack_save);
+ fake_stack_ = fake_stack_save;
+ }
+
+ if (bottom_old)
+ *bottom_old = stack_bottom_;
+ if (size_old)
+ *size_old = stack_top_ - stack_bottom_;
+ stack_bottom_ = next_stack_bottom_;
+ stack_top_ = next_stack_top_;
+ atomic_store(&stack_switching_, 0, memory_order_release);
+ next_stack_top_ = 0;
+ next_stack_bottom_ = 0;
+}
+
+inline AsanThread::StackBounds AsanThread::GetStackBounds() const {
+ if (!atomic_load(&stack_switching_, memory_order_acquire)) {
+ // Make sure the stack bounds are fully initialized.
+ if (stack_bottom_ >= stack_top_) return {0, 0};
+ return {stack_bottom_, stack_top_};
+ }
+ char local;
+ const uptr cur_stack = (uptr)&local;
+ // Note: need to check next stack first, because FinishSwitchFiber
+ // may be in process of overwriting stack_top_/bottom_. But in such case
+ // we are already on the next stack.
+ if (cur_stack >= next_stack_bottom_ && cur_stack < next_stack_top_)
+ return {next_stack_bottom_, next_stack_top_};
+ return {stack_bottom_, stack_top_};
+}
+
+uptr AsanThread::stack_top() {
+ return GetStackBounds().top;
+}
+
+uptr AsanThread::stack_bottom() {
+ return GetStackBounds().bottom;
+}
+
+uptr AsanThread::stack_size() {
+ const auto bounds = GetStackBounds();
+ return bounds.top - bounds.bottom;
+}
+
+// We want to create the FakeStack lazyly on the first use, but not eralier
+// than the stack size is known and the procedure has to be async-signal safe.
+FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
+ uptr stack_size = this->stack_size();
+ if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
+ return nullptr;
+ uptr old_val = 0;
+ // fake_stack_ has 3 states:
+ // 0 -- not initialized
+ // 1 -- being initialized
+ // ptr -- initialized
+ // This CAS checks if the state was 0 and if so changes it to state 1,
+ // if that was successful, it initializes the pointer.
+ if (atomic_compare_exchange_strong(
+ reinterpret_cast<atomic_uintptr_t *>(&fake_stack_), &old_val, 1UL,
+ memory_order_relaxed)) {
+ uptr stack_size_log = Log2(RoundUpToPowerOfTwo(stack_size));
+ CHECK_LE(flags()->min_uar_stack_size_log, flags()->max_uar_stack_size_log);
+ stack_size_log =
+ Min(stack_size_log, static_cast<uptr>(flags()->max_uar_stack_size_log));
+ stack_size_log =
+ Max(stack_size_log, static_cast<uptr>(flags()->min_uar_stack_size_log));
+ fake_stack_ = FakeStack::Create(stack_size_log);
+ SetTLSFakeStack(fake_stack_);
+ return fake_stack_;
+ }
+ return nullptr;
+}
+
+void AsanThread::Init(const InitOptions *options) {
+ next_stack_top_ = next_stack_bottom_ = 0;
+ atomic_store(&stack_switching_, false, memory_order_release);
+ CHECK_EQ(this->stack_size(), 0U);
+ SetThreadStackAndTls(options);
+ if (stack_top_ != stack_bottom_) {
+ CHECK_GT(this->stack_size(), 0U);
+ CHECK(AddrIsInMem(stack_bottom_));
+ CHECK(AddrIsInMem(stack_top_ - 1));
+ }
+ ClearShadowForThreadStackAndTLS();
+ fake_stack_ = nullptr;
+ if (__asan_option_detect_stack_use_after_return)
+ AsyncSignalSafeLazyInitFakeStack();
+ int local = 0;
+ VReport(1, "T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(),
+ (void *)stack_bottom_, (void *)stack_top_, stack_top_ - stack_bottom_,
+ &local);
+}
+
+// Fuchsia and RTEMS don't use ThreadStart.
+// asan_fuchsia.c/asan_rtems.c define CreateMainThread and
+// SetThreadStackAndTls.
+#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
+
+thread_return_t AsanThread::ThreadStart(
+ tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) {
+ Init();
+ asanThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular, nullptr);
+ if (signal_thread_is_registered)
+ atomic_store(signal_thread_is_registered, 1, memory_order_release);
+
+ if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
+
+ if (!start_routine_) {
+ // start_routine_ == 0 if we're on the main thread or on one of the
+ // OS X libdispatch worker threads. But nobody is supposed to call
+ // ThreadStart() for the worker threads.
+ CHECK_EQ(tid(), 0);
+ return 0;
+ }
+
+ thread_return_t res = start_routine_(arg_);
+
+ // On POSIX systems we defer this to the TSD destructor. LSan will consider
+ // the thread's memory as non-live from the moment we call Destroy(), even
+ // though that memory might contain pointers to heap objects which will be
+ // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before
+ // the TSD destructors have run might cause false positives in LSan.
+ if (!SANITIZER_POSIX)
+ this->Destroy();
+
+ return res;
+}
+
+AsanThread *CreateMainThread() {
+ AsanThread *main_thread = AsanThread::Create(
+ /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0,
+ /* stack */ nullptr, /* detached */ true);
+ SetCurrentThread(main_thread);
+ main_thread->ThreadStart(internal_getpid(),
+ /* signal_thread_is_registered */ nullptr);
+ return main_thread;
+}
+
+// This implementation doesn't use the argument, which is just passed down
+// from the caller of Init (which see, above). It's only there to support
+// OS-specific implementations that need more information passed through.
+void AsanThread::SetThreadStackAndTls(const InitOptions *options) {
+ DCHECK_EQ(options, nullptr);
+ uptr tls_size = 0;
+ uptr stack_size = 0;
+ GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_,
+ &tls_size);
+ stack_top_ = stack_bottom_ + stack_size;
+ tls_end_ = tls_begin_ + tls_size;
+ dtls_ = DTLS_Get();
+
+ if (stack_top_ != stack_bottom_) {
+ int local;
+ CHECK(AddrIsInStack((uptr)&local));
+ }
+}
+
+#endif // !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
+
+void AsanThread::ClearShadowForThreadStackAndTLS() {
+ if (stack_top_ != stack_bottom_)
+ PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0);
+ if (tls_begin_ != tls_end_) {
+ uptr tls_begin_aligned = RoundDownTo(tls_begin_, SHADOW_GRANULARITY);
+ uptr tls_end_aligned = RoundUpTo(tls_end_, SHADOW_GRANULARITY);
+ FastPoisonShadowPartialRightRedzone(tls_begin_aligned,
+ tls_end_ - tls_begin_aligned,
+ tls_end_aligned - tls_end_, 0);
+ }
+}
+
+bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
+ StackFrameAccess *access) {
+ if (stack_top_ == stack_bottom_)
+ return false;
+
+ uptr bottom = 0;
+ if (AddrIsInStack(addr)) {
+ bottom = stack_bottom();
+ } else if (has_fake_stack()) {
+ bottom = fake_stack()->AddrIsInFakeStack(addr);
+ CHECK(bottom);
+ access->offset = addr - bottom;
+ access->frame_pc = ((uptr*)bottom)[2];
+ access->frame_descr = (const char *)((uptr*)bottom)[1];
+ return true;
+ }
+ uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr.
+ uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY);
+ u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
+ u8 *shadow_bottom = (u8*)MemToShadow(bottom);
+
+ while (shadow_ptr >= shadow_bottom &&
+ *shadow_ptr != kAsanStackLeftRedzoneMagic) {
+ shadow_ptr--;
+ mem_ptr -= SHADOW_GRANULARITY;
+ }
+
+ while (shadow_ptr >= shadow_bottom &&
+ *shadow_ptr == kAsanStackLeftRedzoneMagic) {
+ shadow_ptr--;
+ mem_ptr -= SHADOW_GRANULARITY;
+ }
+
+ if (shadow_ptr < shadow_bottom) {
+ return false;
+ }
+
+ uptr* ptr = (uptr*)(mem_ptr + SHADOW_GRANULARITY);
+ CHECK(ptr[0] == kCurrentStackFrameMagic);
+ access->offset = addr - (uptr)ptr;
+ access->frame_pc = ptr[2];
+ access->frame_descr = (const char*)ptr[1];
+ return true;
+}
+
+uptr AsanThread::GetStackVariableShadowStart(uptr addr) {
+ uptr bottom = 0;
+ if (AddrIsInStack(addr)) {
+ bottom = stack_bottom();
+ } else if (has_fake_stack()) {
+ bottom = fake_stack()->AddrIsInFakeStack(addr);
+ CHECK(bottom);
+ } else
+ return 0;
+
+ uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr.
+ u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
+ u8 *shadow_bottom = (u8*)MemToShadow(bottom);
+
+ while (shadow_ptr >= shadow_bottom &&
+ (*shadow_ptr != kAsanStackLeftRedzoneMagic &&
+ *shadow_ptr != kAsanStackMidRedzoneMagic &&
+ *shadow_ptr != kAsanStackRightRedzoneMagic))
+ shadow_ptr--;
+
+ return (uptr)shadow_ptr + 1;
+}
+
+bool AsanThread::AddrIsInStack(uptr addr) {
+ const auto bounds = GetStackBounds();
+ return addr >= bounds.bottom && addr < bounds.top;
+}
+
+static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
+ void *addr) {
+ AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
+ AsanThread *t = tctx->thread;
+ if (!t) return false;
+ if (t->AddrIsInStack((uptr)addr)) return true;
+ if (t->has_fake_stack() && t->fake_stack()->AddrIsInFakeStack((uptr)addr))
+ return true;
+ return false;
+}
+
+AsanThread *GetCurrentThread() {
+ if (SANITIZER_RTEMS && !asan_inited)
+ return nullptr;
+
+ AsanThreadContext *context =
+ reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
+ if (!context) {
+ if (SANITIZER_ANDROID) {
+ // On Android, libc constructor is called _after_ asan_init, and cleans up
+ // TSD. Try to figure out if this is still the main thread by the stack
+ // address. We are not entirely sure that we have correct main thread
+ // limits, so only do this magic on Android, and only if the found thread
+ // is the main thread.
+ AsanThreadContext *tctx = GetThreadContextByTidLocked(0);
+ if (tctx && ThreadStackContainsAddress(tctx, &context)) {
+ SetCurrentThread(tctx->thread);
+ return tctx->thread;
+ }
+ }
+ return nullptr;
+ }
+ return context->thread;
+}
+
+void SetCurrentThread(AsanThread *t) {
+ CHECK(t->context());
+ VReport(2, "SetCurrentThread: %p for thread %p\n", t->context(),
+ (void *)GetThreadSelf());
+ // Make sure we do not reset the current AsanThread.
+ CHECK_EQ(0, AsanTSDGet());
+ AsanTSDSet(t->context());
+ CHECK_EQ(t->context(), AsanTSDGet());
+}
+
+u32 GetCurrentTidOrInvalid() {
+ AsanThread *t = GetCurrentThread();
+ return t ? t->tid() : kInvalidTid;
+}
+
+AsanThread *FindThreadByStackAddress(uptr addr) {
+ asanThreadRegistry().CheckLocked();
+ AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
+ asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
+ (void *)addr));
+ return tctx ? tctx->thread : nullptr;
+}
+
+void EnsureMainThreadIDIsCorrect() {
+ AsanThreadContext *context =
+ reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
+ if (context && (context->tid == 0))
+ context->os_id = GetTid();
+}
+
+__asan::AsanThread *GetAsanThreadByOsIDLocked(tid_t os_id) {
+ __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
+ __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
+ if (!context) return nullptr;
+ return context->thread;
+}
+} // namespace __asan
+
+// --- Implementation of LSan-specific functions --- {{{1
+namespace __lsan {
+bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
+ uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
+ uptr *cache_end, DTLS **dtls) {
+ __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
+ if (!t) return false;
+ *stack_begin = t->stack_bottom();
+ *stack_end = t->stack_top();
+ *tls_begin = t->tls_begin();
+ *tls_end = t->tls_end();
+ // ASan doesn't keep allocator caches in TLS, so these are unused.
+ *cache_begin = 0;
+ *cache_end = 0;
+ *dtls = t->dtls();
+ return true;
+}
+
+void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
+ void *arg) {
+ __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
+ if (t && t->has_fake_stack())
+ t->fake_stack()->ForEachFakeFrame(callback, arg);
+}
+
+void LockThreadRegistry() {
+ __asan::asanThreadRegistry().Lock();
+}
+
+void UnlockThreadRegistry() {
+ __asan::asanThreadRegistry().Unlock();
+}
+
+ThreadRegistry *GetThreadRegistryLocked() {
+ __asan::asanThreadRegistry().CheckLocked();
+ return &__asan::asanThreadRegistry();
+}
+
+void EnsureMainThreadIDIsCorrect() {
+ __asan::EnsureMainThreadIDIsCorrect();
+}
+} // namespace __lsan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_start_switch_fiber(void **fakestacksave, const void *bottom,
+ uptr size) {
+ AsanThread *t = GetCurrentThread();
+ if (!t) {
+ VReport(1, "__asan_start_switch_fiber called from unknown thread\n");
+ return;
+ }
+ t->StartSwitchFiber((FakeStack**)fakestacksave, (uptr)bottom, size);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_finish_switch_fiber(void* fakestack,
+ const void **bottom_old,
+ uptr *size_old) {
+ AsanThread *t = GetCurrentThread();
+ if (!t) {
+ VReport(1, "__asan_finish_switch_fiber called from unknown thread\n");
+ return;
+ }
+ t->FinishSwitchFiber((FakeStack*)fakestack,
+ (uptr*)bottom_old,
+ (uptr*)size_old);
+}
+}
//===-- asan_thread.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
-// ASan-private header for asan_thread.cc.
+// ASan-private header for asan_thread.cpp.
//===----------------------------------------------------------------------===//
#ifndef ASAN_THREAD_H
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
AsanStats &stats() { return stats_; }
+ void *extra_spill_area() { return &extra_spill_area_; }
+
private:
// NOTE: There is no AsanThread constructor. It is allocated
// via mmap() and *must* be valid in zero-initialized state.
AsanThreadLocalMallocStorage malloc_storage_;
AsanStats stats_;
bool unwinding_;
-};
-
-// ScopedUnwinding is a scope for stacktracing member of a context
-class ScopedUnwinding {
- public:
- explicit ScopedUnwinding(AsanThread *t) : thread(t) {
- t->setUnwinding(true);
- }
- ~ScopedUnwinding() { thread->setUnwinding(false); }
-
- private:
- AsanThread *thread;
+ uptr extra_spill_area_;
};
// Returns a single instance of registry.
+++ /dev/null
-//===-- asan_win.cc -------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Windows-specific details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-#include <stdlib.h>
-
-#include "asan_interceptors.h"
-#include "asan_internal.h"
-#include "asan_report.h"
-#include "asan_stack.h"
-#include "asan_thread.h"
-#include "asan_mapping.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "sanitizer_common/sanitizer_win.h"
-#include "sanitizer_common/sanitizer_win_defs.h"
-
-using namespace __asan; // NOLINT
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-int __asan_should_detect_stack_use_after_return() {
- __asan_init();
- return __asan_option_detect_stack_use_after_return;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __asan_get_shadow_memory_dynamic_address() {
- __asan_init();
- return __asan_shadow_memory_dynamic_address;
-}
-} // extern "C"
-
-// ---------------------- Windows-specific interceptors ---------------- {{{
-static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
-static LPTOP_LEVEL_EXCEPTION_FILTER user_seh_handler;
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) {
- EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
- CONTEXT *context = info->ContextRecord;
-
- // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
-
- SignalContext sig(exception_record, context);
- ReportDeadlySignal(sig);
- UNREACHABLE("returned from reporting deadly signal");
-}
-
-// Wrapper SEH Handler. If the exception should be handled by asan, we call
-// __asan_unhandled_exception_filter, otherwise, we execute the user provided
-// exception handler or the default.
-static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
- DWORD exception_code = info->ExceptionRecord->ExceptionCode;
- if (__sanitizer::IsHandledDeadlyException(exception_code))
- return __asan_unhandled_exception_filter(info);
- if (user_seh_handler)
- return user_seh_handler(info);
- // Bubble out to the default exception filter.
- if (default_seh_handler)
- return default_seh_handler(info);
- return EXCEPTION_CONTINUE_SEARCH;
-}
-
-INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter,
- LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
- CHECK(REAL(SetUnhandledExceptionFilter));
- if (ExceptionFilter == &SEHHandler)
- return REAL(SetUnhandledExceptionFilter)(ExceptionFilter);
- // We record the user provided exception handler to be called for all the
- // exceptions unhandled by asan.
- Swap(ExceptionFilter, user_seh_handler);
- return ExceptionFilter;
-}
-
-INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
- CHECK(REAL(RtlRaiseException));
- // This is a noreturn function, unless it's one of the exceptions raised to
- // communicate with the debugger, such as the one from OutputDebugString.
- if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
- __asan_handle_no_return();
- REAL(RtlRaiseException)(ExceptionRecord);
-}
-
-INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
- CHECK(REAL(RaiseException));
- __asan_handle_no_return();
- REAL(RaiseException)(a, b, c, d);
-}
-
-#ifdef _WIN64
-
-INTERCEPTOR_WINAPI(int, __C_specific_handler, void *a, void *b, void *c, void *d) { // NOLINT
- CHECK(REAL(__C_specific_handler));
- __asan_handle_no_return();
- return REAL(__C_specific_handler)(a, b, c, d);
-}
-
-#else
-
-INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
- CHECK(REAL(_except_handler3));
- __asan_handle_no_return();
- return REAL(_except_handler3)(a, b, c, d);
-}
-
-#if ASAN_DYNAMIC
-// This handler is named differently in -MT and -MD CRTs.
-#define _except_handler4 _except_handler4_common
-#endif
-INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
- CHECK(REAL(_except_handler4));
- __asan_handle_no_return();
- return REAL(_except_handler4)(a, b, c, d);
-}
-#endif
-
-static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
- AsanThread *t = (AsanThread*)arg;
- SetCurrentThread(t);
- return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
-}
-
-INTERCEPTOR_WINAPI(DWORD, CreateThread,
- void* security, uptr stack_size,
- DWORD (__stdcall *start_routine)(void*), void* arg,
- DWORD thr_flags, void* tid) {
- // Strict init-order checking is thread-hostile.
- if (flags()->strict_init_order)
- StopInitOrderChecking();
- GET_STACK_TRACE_THREAD;
- // FIXME: The CreateThread interceptor is not the same as a pthread_create
- // one. This is a bandaid fix for PR22025.
- bool detached = false; // FIXME: how can we determine it on Windows?
- u32 current_tid = GetCurrentTidOrInvalid();
- AsanThread *t =
- AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
- return REAL(CreateThread)(security, stack_size,
- asan_thread_start, t, thr_flags, tid);
-}
-
-// }}}
-
-namespace __asan {
-
-void InitializePlatformInterceptors() {
- // The interceptors were not designed to be removable, so we have to keep this
- // module alive for the life of the process.
- HMODULE pinned;
- CHECK(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
- GET_MODULE_HANDLE_EX_FLAG_PIN,
- (LPCWSTR)&InitializePlatformInterceptors,
- &pinned));
-
- ASAN_INTERCEPT_FUNC(CreateThread);
- ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
-
-#ifdef _WIN64
- ASAN_INTERCEPT_FUNC(__C_specific_handler);
-#else
- ASAN_INTERCEPT_FUNC(_except_handler3);
- ASAN_INTERCEPT_FUNC(_except_handler4);
-#endif
-
- // Try to intercept kernel32!RaiseException, and if that fails, intercept
- // ntdll!RtlRaiseException instead.
- if (!::__interception::OverrideFunction("RaiseException",
- (uptr)WRAP(RaiseException),
- (uptr *)&REAL(RaiseException))) {
- CHECK(::__interception::OverrideFunction("RtlRaiseException",
- (uptr)WRAP(RtlRaiseException),
- (uptr *)&REAL(RtlRaiseException)));
- }
-}
-
-void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
- UNIMPLEMENTED();
-}
-
-// ---------------------- TSD ---------------- {{{
-static bool tsd_key_inited = false;
-
-static __declspec(thread) void *fake_tsd = 0;
-
-void AsanTSDInit(void (*destructor)(void *tsd)) {
- // FIXME: we're ignoring the destructor for now.
- tsd_key_inited = true;
-}
-
-void *AsanTSDGet() {
- CHECK(tsd_key_inited);
- return fake_tsd;
-}
-
-void AsanTSDSet(void *tsd) {
- CHECK(tsd_key_inited);
- fake_tsd = tsd;
-}
-
-void PlatformTSDDtor(void *tsd) {
- AsanThread::TSDDtor(tsd);
-}
-// }}}
-
-// ---------------------- Various stuff ---------------- {{{
-void *AsanDoesNotSupportStaticLinkage() {
-#if defined(_DEBUG)
-#error Please build the runtime with a non-debug CRT: /MD or /MT
-#endif
- return 0;
-}
-
-uptr FindDynamicShadowStart() {
- uptr granularity = GetMmapGranularity();
- uptr alignment = 8 * granularity;
- uptr left_padding = granularity;
- uptr space_size = kHighShadowEnd + left_padding;
- uptr shadow_start = FindAvailableMemoryRange(space_size, alignment,
- granularity, nullptr, nullptr);
- CHECK_NE((uptr)0, shadow_start);
- CHECK(IsAligned(shadow_start, alignment));
- return shadow_start;
-}
-
-void AsanCheckDynamicRTPrereqs() {}
-
-void AsanCheckIncompatibleRT() {}
-
-void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
- UNIMPLEMENTED();
-}
-
-void AsanOnDeadlySignal(int, void *siginfo, void *context) {
- UNIMPLEMENTED();
-}
-
-#if SANITIZER_WINDOWS64
-// Exception handler for dealing with shadow memory.
-static LONG CALLBACK
-ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
- uptr page_size = GetPageSizeCached();
- // Only handle access violations.
- if (exception_pointers->ExceptionRecord->ExceptionCode !=
- EXCEPTION_ACCESS_VIOLATION) {
- return EXCEPTION_CONTINUE_SEARCH;
- }
-
- // Only handle access violations that land within the shadow memory.
- uptr addr =
- (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);
-
- // Check valid shadow range.
- if (!AddrIsInShadow(addr)) return EXCEPTION_CONTINUE_SEARCH;
-
- // This is an access violation while trying to read from the shadow. Commit
- // the relevant page and let execution continue.
-
- // Determine the address of the page that is being accessed.
- uptr page = RoundDownTo(addr, page_size);
-
- // Commit the page.
- uptr result =
- (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
- if (result != page) return EXCEPTION_CONTINUE_SEARCH;
-
- // The page mapping succeeded, so continue execution as usual.
- return EXCEPTION_CONTINUE_EXECUTION;
-}
-
-#endif
-
-void InitializePlatformExceptionHandlers() {
-#if SANITIZER_WINDOWS64
- // On Win64, we map memory on demand with access violation handler.
- // Install our exception handler.
- CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler));
-#endif
-}
-
-bool IsSystemHeapAddress(uptr addr) {
- return ::HeapValidate(GetProcessHeap(), 0, (void*)addr) != FALSE;
-}
-
-// We want to install our own exception handler (EH) to print helpful reports
-// on access violations and whatnot. Unfortunately, the CRT initializers assume
-// they are run before any user code and drop any previously-installed EHs on
-// the floor, so we can't install our handler inside __asan_init.
-// (See crt0dat.c in the CRT sources for the details)
-//
-// Things get even more complicated with the dynamic runtime, as it finishes its
-// initialization before the .exe module CRT begins to initialize.
-//
-// For the static runtime (-MT), it's enough to put a callback to
-// __asan_set_seh_filter in the last section for C initializers.
-//
-// For the dynamic runtime (-MD), we want link the same
-// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
-// will be called for each instrumented module. This ensures that at least one
-// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-int __asan_set_seh_filter() {
- // We should only store the previous handler if it's not our own handler in
- // order to avoid loops in the EH chain.
- auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
- if (prev_seh_handler != &SEHHandler)
- default_seh_handler = prev_seh_handler;
- return 0;
-}
-
-#if !ASAN_DYNAMIC
-// The CRT runs initializers in this order:
-// - C initializers, from XIA to XIZ
-// - C++ initializers, from XCA to XCZ
-// Prior to 2015, the CRT set the unhandled exception filter at priority XIY,
-// near the end of C initialization. Starting in 2015, it was moved to the
-// beginning of C++ initialization. We set our priority to XCAB to run
-// immediately after the CRT runs. This way, our exception filter is called
-// first and we can delegate to their filter if appropriate.
-#pragma section(".CRT$XCAB", long, read) // NOLINT
-__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() =
- __asan_set_seh_filter;
-
-// Piggyback on the TLS initialization callback directory to initialize asan as
-// early as possible. Initializers in .CRT$XL* are called directly by ntdll,
-// which run before the CRT. Users also add code to .CRT$XLC, so it's important
-// to run our initializers first.
-static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) {
- if (reason == DLL_PROCESS_ATTACH) __asan_init();
-}
-
-#pragma section(".CRT$XLAB", long, read) // NOLINT
-__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
- unsigned long, void *) = asan_thread_init;
-#endif
-
-WIN_FORCE_LINK(__asan_dso_reg_hook)
-
-// }}}
-} // namespace __asan
-
-#endif // SANITIZER_WINDOWS
--- /dev/null
+//===-- asan_win.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <stdlib.h>
+
+#include "asan_interceptors.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "asan_thread.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_win.h"
+#include "sanitizer_common/sanitizer_win_defs.h"
+
+using namespace __asan; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+int __asan_should_detect_stack_use_after_return() {
+ __asan_init();
+ return __asan_option_detect_stack_use_after_return;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_shadow_memory_dynamic_address() {
+ __asan_init();
+ return __asan_shadow_memory_dynamic_address;
+}
+} // extern "C"
+
+// ---------------------- Windows-specific interceptors ---------------- {{{
+static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler;
+static LPTOP_LEVEL_EXCEPTION_FILTER user_seh_handler;
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+long __asan_unhandled_exception_filter(EXCEPTION_POINTERS *info) {
+ EXCEPTION_RECORD *exception_record = info->ExceptionRecord;
+ CONTEXT *context = info->ContextRecord;
+
+ // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
+
+ SignalContext sig(exception_record, context);
+ ReportDeadlySignal(sig);
+ UNREACHABLE("returned from reporting deadly signal");
+}
+
+// Wrapper SEH Handler. If the exception should be handled by asan, we call
+// __asan_unhandled_exception_filter, otherwise, we execute the user provided
+// exception handler or the default.
+static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
+ DWORD exception_code = info->ExceptionRecord->ExceptionCode;
+ if (__sanitizer::IsHandledDeadlyException(exception_code))
+ return __asan_unhandled_exception_filter(info);
+ if (user_seh_handler)
+ return user_seh_handler(info);
+ // Bubble out to the default exception filter.
+ if (default_seh_handler)
+ return default_seh_handler(info);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+INTERCEPTOR_WINAPI(LPTOP_LEVEL_EXCEPTION_FILTER, SetUnhandledExceptionFilter,
+ LPTOP_LEVEL_EXCEPTION_FILTER ExceptionFilter) {
+ CHECK(REAL(SetUnhandledExceptionFilter));
+ if (ExceptionFilter == &SEHHandler)
+ return REAL(SetUnhandledExceptionFilter)(ExceptionFilter);
+ // We record the user provided exception handler to be called for all the
+ // exceptions unhandled by asan.
+ Swap(ExceptionFilter, user_seh_handler);
+ return ExceptionFilter;
+}
+
+INTERCEPTOR_WINAPI(void, RtlRaiseException, EXCEPTION_RECORD *ExceptionRecord) {
+ CHECK(REAL(RtlRaiseException));
+ // This is a noreturn function, unless it's one of the exceptions raised to
+ // communicate with the debugger, such as the one from OutputDebugString.
+ if (ExceptionRecord->ExceptionCode != DBG_PRINTEXCEPTION_C)
+ __asan_handle_no_return();
+ REAL(RtlRaiseException)(ExceptionRecord);
+}
+
+INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(RaiseException));
+ __asan_handle_no_return();
+ REAL(RaiseException)(a, b, c, d);
+}
+
+#ifdef _WIN64
+
+INTERCEPTOR_WINAPI(EXCEPTION_DISPOSITION, __C_specific_handler,
+ _EXCEPTION_RECORD *a, void *b, _CONTEXT *c,
+ _DISPATCHER_CONTEXT *d) { // NOLINT
+ CHECK(REAL(__C_specific_handler));
+ __asan_handle_no_return();
+ return REAL(__C_specific_handler)(a, b, c, d);
+}
+
+#else
+
+INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler3));
+ __asan_handle_no_return();
+ return REAL(_except_handler3)(a, b, c, d);
+}
+
+#if ASAN_DYNAMIC
+// This handler is named differently in -MT and -MD CRTs.
+#define _except_handler4 _except_handler4_common
+#endif
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ CHECK(REAL(_except_handler4));
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
+}
+#endif
+
+static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) {
+ AsanThread *t = (AsanThread *)arg;
+ SetCurrentThread(t);
+ return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr);
+}
+
+INTERCEPTOR_WINAPI(HANDLE, CreateThread, LPSECURITY_ATTRIBUTES security,
+ SIZE_T stack_size, LPTHREAD_START_ROUTINE start_routine,
+ void *arg, DWORD thr_flags, DWORD *tid) {
+ // Strict init-order checking is thread-hostile.
+ if (flags()->strict_init_order)
+ StopInitOrderChecking();
+ GET_STACK_TRACE_THREAD;
+ // FIXME: The CreateThread interceptor is not the same as a pthread_create
+ // one. This is a bandaid fix for PR22025.
+ bool detached = false; // FIXME: how can we determine it on Windows?
+ u32 current_tid = GetCurrentTidOrInvalid();
+ AsanThread *t =
+ AsanThread::Create(start_routine, arg, current_tid, &stack, detached);
+ return REAL(CreateThread)(security, stack_size, asan_thread_start, t,
+ thr_flags, tid);
+}
+
+// }}}
+
+namespace __asan {
+
+void InitializePlatformInterceptors() {
+ // The interceptors were not designed to be removable, so we have to keep this
+ // module alive for the life of the process.
+ HMODULE pinned;
+ CHECK(GetModuleHandleExW(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
+ (LPCWSTR)&InitializePlatformInterceptors, &pinned));
+
+ ASAN_INTERCEPT_FUNC(CreateThread);
+ ASAN_INTERCEPT_FUNC(SetUnhandledExceptionFilter);
+
+#ifdef _WIN64
+ ASAN_INTERCEPT_FUNC(__C_specific_handler);
+#else
+ ASAN_INTERCEPT_FUNC(_except_handler3);
+ ASAN_INTERCEPT_FUNC(_except_handler4);
+#endif
+
+ // Try to intercept kernel32!RaiseException, and if that fails, intercept
+ // ntdll!RtlRaiseException instead.
+ if (!::__interception::OverrideFunction("RaiseException",
+ (uptr)WRAP(RaiseException),
+ (uptr *)&REAL(RaiseException))) {
+ CHECK(::__interception::OverrideFunction("RtlRaiseException",
+ (uptr)WRAP(RtlRaiseException),
+ (uptr *)&REAL(RtlRaiseException)));
+ }
+}
+
+void AsanApplyToGlobals(globals_op_fptr op, const void *needle) {
+ UNIMPLEMENTED();
+}
+
+// ---------------------- TSD ---------------- {{{
+static bool tsd_key_inited = false;
+
+static __declspec(thread) void *fake_tsd = 0;
+
+// https://docs.microsoft.com/en-us/windows/desktop/api/winternl/ns-winternl-_teb
+// "[This structure may be altered in future versions of Windows. Applications
+// should use the alternate functions listed in this topic.]"
+typedef struct _TEB {
+ PVOID Reserved1[12];
+ // PVOID ThreadLocalStoragePointer; is here, at the last field in Reserved1.
+ PVOID ProcessEnvironmentBlock;
+ PVOID Reserved2[399];
+ BYTE Reserved3[1952];
+ PVOID TlsSlots[64];
+ BYTE Reserved4[8];
+ PVOID Reserved5[26];
+ PVOID ReservedForOle;
+ PVOID Reserved6[4];
+ PVOID TlsExpansionSlots;
+} TEB, *PTEB;
+
+constexpr size_t TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET = 11;
+BOOL IsTlsInitialized() {
+ PTEB teb = (PTEB)NtCurrentTeb();
+ return teb->Reserved1[TEB_RESERVED_FIELDS_THREAD_LOCAL_STORAGE_OFFSET] !=
+ nullptr;
+}
+
+void AsanTSDInit(void (*destructor)(void *tsd)) {
+ // FIXME: we're ignoring the destructor for now.
+ tsd_key_inited = true;
+}
+
+void *AsanTSDGet() {
+ CHECK(tsd_key_inited);
+ return IsTlsInitialized() ? fake_tsd : nullptr;
+}
+
+void AsanTSDSet(void *tsd) {
+ CHECK(tsd_key_inited);
+ fake_tsd = tsd;
+}
+
+void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); }
+// }}}
+
+// ---------------------- Various stuff ---------------- {{{
+void *AsanDoesNotSupportStaticLinkage() {
+#if defined(_DEBUG)
+#error Please build the runtime with a non-debug CRT: /MD or /MT
+#endif
+ return 0;
+}
+
+uptr FindDynamicShadowStart() {
+ uptr granularity = GetMmapGranularity();
+ uptr alignment = 8 * granularity;
+ uptr left_padding = granularity;
+ uptr space_size = kHighShadowEnd + left_padding;
+ uptr shadow_start = FindAvailableMemoryRange(space_size, alignment,
+ granularity, nullptr, nullptr);
+ CHECK_NE((uptr)0, shadow_start);
+ CHECK(IsAligned(shadow_start, alignment));
+ return shadow_start;
+}
+
+void AsanCheckDynamicRTPrereqs() {}
+
+void AsanCheckIncompatibleRT() {}
+
+void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
+ UNIMPLEMENTED();
+}
+
+void AsanOnDeadlySignal(int, void *siginfo, void *context) { UNIMPLEMENTED(); }
+
+#if SANITIZER_WINDOWS64
+// Exception handler for dealing with shadow memory.
+static LONG CALLBACK
+ShadowExceptionHandler(PEXCEPTION_POINTERS exception_pointers) {
+ uptr page_size = GetPageSizeCached();
+ // Only handle access violations.
+ if (exception_pointers->ExceptionRecord->ExceptionCode !=
+ EXCEPTION_ACCESS_VIOLATION ||
+ exception_pointers->ExceptionRecord->NumberParameters < 2) {
+ __asan_handle_no_return();
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // Only handle access violations that land within the shadow memory.
+ uptr addr =
+ (uptr)(exception_pointers->ExceptionRecord->ExceptionInformation[1]);
+
+ // Check valid shadow range.
+ if (!AddrIsInShadow(addr)) {
+ __asan_handle_no_return();
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ // This is an access violation while trying to read from the shadow. Commit
+ // the relevant page and let execution continue.
+
+ // Determine the address of the page that is being accessed.
+ uptr page = RoundDownTo(addr, page_size);
+
+ // Commit the page.
+ uptr result =
+ (uptr)::VirtualAlloc((LPVOID)page, page_size, MEM_COMMIT, PAGE_READWRITE);
+ if (result != page)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ // The page mapping succeeded, so continue execution as usual.
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+#endif
+
+void InitializePlatformExceptionHandlers() {
+#if SANITIZER_WINDOWS64
+ // On Win64, we map memory on demand with access violation handler.
+ // Install our exception handler.
+ CHECK(AddVectoredExceptionHandler(TRUE, &ShadowExceptionHandler));
+#endif
+}
+
+bool IsSystemHeapAddress(uptr addr) {
+ return ::HeapValidate(GetProcessHeap(), 0, (void *)addr) != FALSE;
+}
+
+// We want to install our own exception handler (EH) to print helpful reports
+// on access violations and whatnot. Unfortunately, the CRT initializers assume
+// they are run before any user code and drop any previously-installed EHs on
+// the floor, so we can't install our handler inside __asan_init.
+// (See crt0dat.c in the CRT sources for the details)
+//
+// Things get even more complicated with the dynamic runtime, as it finishes its
+// initialization before the .exe module CRT begins to initialize.
+//
+// For the static runtime (-MT), it's enough to put a callback to
+// __asan_set_seh_filter in the last section for C initializers.
+//
+// For the dynamic runtime (-MD), we want link the same
+// asan_dynamic_runtime_thunk.lib to all the modules, thus __asan_set_seh_filter
+// will be called for each instrumented module. This ensures that at least one
+// __asan_set_seh_filter call happens after the .exe module CRT is initialized.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_seh_filter() {
+ // We should only store the previous handler if it's not our own handler in
+ // order to avoid loops in the EH chain.
+ auto prev_seh_handler = SetUnhandledExceptionFilter(SEHHandler);
+ if (prev_seh_handler != &SEHHandler)
+ default_seh_handler = prev_seh_handler;
+ return 0;
+}
+
+bool HandleDlopenInit() {
+ // Not supported on this platform.
+ static_assert(!SANITIZER_SUPPORTS_INIT_FOR_DLOPEN,
+ "Expected SANITIZER_SUPPORTS_INIT_FOR_DLOPEN to be false");
+ return false;
+}
+
+#if !ASAN_DYNAMIC
+// The CRT runs initializers in this order:
+// - C initializers, from XIA to XIZ
+// - C++ initializers, from XCA to XCZ
+// Prior to 2015, the CRT set the unhandled exception filter at priority XIY,
+// near the end of C initialization. Starting in 2015, it was moved to the
+// beginning of C++ initialization. We set our priority to XCAB to run
+// immediately after the CRT runs. This way, our exception filter is called
+// first and we can delegate to their filter if appropriate.
+#pragma section(".CRT$XCAB", long, read) // NOLINT
+__declspec(allocate(".CRT$XCAB")) int (*__intercept_seh)() =
+ __asan_set_seh_filter;
+
+// Piggyback on the TLS initialization callback directory to initialize asan as
+// early as possible. Initializers in .CRT$XL* are called directly by ntdll,
+// which run before the CRT. Users also add code to .CRT$XLC, so it's important
+// to run our initializers first.
+static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) {
+ if (reason == DLL_PROCESS_ATTACH)
+ __asan_init();
+}
+
+#pragma section(".CRT$XLAB", long, read) // NOLINT
+__declspec(allocate(".CRT$XLAB")) void(NTAPI *__asan_tls_init)(
+ void *, unsigned long, void *) = asan_thread_init;
+#endif
+
+static void NTAPI asan_thread_exit(void *module, DWORD reason, void *reserved) {
+ if (reason == DLL_THREAD_DETACH) {
+ // Unpoison the thread's stack because the memory may be re-used.
+ NT_TIB *tib = (NT_TIB *)NtCurrentTeb();
+ uptr stackSize = (uptr)tib->StackBase - (uptr)tib->StackLimit;
+ __asan_unpoison_memory_region(tib->StackLimit, stackSize);
+ }
+}
+
+#pragma section(".CRT$XLY", long, read) // NOLINT
+__declspec(allocate(".CRT$XLY")) void(NTAPI *__asan_tls_exit)(
+ void *, unsigned long, void *) = asan_thread_exit;
+
+WIN_FORCE_LINK(__asan_dso_reg_hook)
+
+// }}}
+} // namespace __asan
+
+#endif // SANITIZER_WINDOWS
+++ /dev/null
-//===-- asan_win_dll_thunk.cc ---------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// This file defines a family of thunks that should be statically linked into
-// the DLLs that have ASan instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-
-#ifdef SANITIZER_DLL_THUNK
-#include "asan_init_version.h"
-#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_win_defs.h"
-#include "sanitizer_common/sanitizer_win_dll_thunk.h"
-#include "sanitizer_common/sanitizer_platform_interceptors.h"
-
-// ASan own interface functions.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "asan_interface.inc"
-
-// Memory allocation functions.
-INTERCEPT_WRAP_V_W(free)
-INTERCEPT_WRAP_V_W(_free_base)
-INTERCEPT_WRAP_V_WW(_free_dbg)
-
-INTERCEPT_WRAP_W_W(malloc)
-INTERCEPT_WRAP_W_W(_malloc_base)
-INTERCEPT_WRAP_W_WWWW(_malloc_dbg)
-
-INTERCEPT_WRAP_W_WW(calloc)
-INTERCEPT_WRAP_W_WW(_calloc_base)
-INTERCEPT_WRAP_W_WWWWW(_calloc_dbg)
-INTERCEPT_WRAP_W_WWW(_calloc_impl)
-
-INTERCEPT_WRAP_W_WW(realloc)
-INTERCEPT_WRAP_W_WW(_realloc_base)
-INTERCEPT_WRAP_W_WWW(_realloc_dbg)
-INTERCEPT_WRAP_W_WWW(_recalloc)
-INTERCEPT_WRAP_W_WWW(_recalloc_base)
-
-INTERCEPT_WRAP_W_W(_msize)
-INTERCEPT_WRAP_W_W(_expand)
-INTERCEPT_WRAP_W_W(_expand_dbg)
-
-// TODO(timurrrr): Might want to add support for _aligned_* allocation
-// functions to detect a bit more bugs. Those functions seem to wrap malloc().
-
-// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc).
-
-INTERCEPT_LIBRARY_FUNCTION(atoi);
-INTERCEPT_LIBRARY_FUNCTION(atol);
-INTERCEPT_LIBRARY_FUNCTION(frexp);
-INTERCEPT_LIBRARY_FUNCTION(longjmp);
-#if SANITIZER_INTERCEPT_MEMCHR
-INTERCEPT_LIBRARY_FUNCTION(memchr);
-#endif
-INTERCEPT_LIBRARY_FUNCTION(memcmp);
-INTERCEPT_LIBRARY_FUNCTION(memcpy);
-INTERCEPT_LIBRARY_FUNCTION(memmove);
-INTERCEPT_LIBRARY_FUNCTION(memset);
-INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
-INTERCEPT_LIBRARY_FUNCTION(strchr);
-INTERCEPT_LIBRARY_FUNCTION(strcmp);
-INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
-INTERCEPT_LIBRARY_FUNCTION(strcspn);
-INTERCEPT_LIBRARY_FUNCTION(strdup);
-INTERCEPT_LIBRARY_FUNCTION(strlen);
-INTERCEPT_LIBRARY_FUNCTION(strncat);
-INTERCEPT_LIBRARY_FUNCTION(strncmp);
-INTERCEPT_LIBRARY_FUNCTION(strncpy);
-INTERCEPT_LIBRARY_FUNCTION(strnlen);
-INTERCEPT_LIBRARY_FUNCTION(strpbrk);
-INTERCEPT_LIBRARY_FUNCTION(strrchr);
-INTERCEPT_LIBRARY_FUNCTION(strspn);
-INTERCEPT_LIBRARY_FUNCTION(strstr);
-INTERCEPT_LIBRARY_FUNCTION(strtok);
-INTERCEPT_LIBRARY_FUNCTION(strtol);
-INTERCEPT_LIBRARY_FUNCTION(wcslen);
-INTERCEPT_LIBRARY_FUNCTION(wcsnlen);
-
-#ifdef _WIN64
-INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler);
-#else
-INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
-// _except_handler4 checks -GS cookie which is different for each module, so we
-// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
-INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
- __asan_handle_no_return();
- return REAL(_except_handler4)(a, b, c, d);
-}
-#endif
-
-// Windows specific functions not included in asan_interface.inc.
-INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return)
-INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
-INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter)
-
-using namespace __sanitizer;
-
-extern "C" {
-int __asan_option_detect_stack_use_after_return;
-uptr __asan_shadow_memory_dynamic_address;
-} // extern "C"
-
-static int asan_dll_thunk_init() {
- typedef void (*fntype)();
- static fntype fn = 0;
- // asan_dll_thunk_init is expected to be called by only one thread.
- if (fn) return 0;
-
- // Ensure all interception was executed.
- __dll_thunk_init();
-
- fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init");
- fn();
- __asan_option_detect_stack_use_after_return =
- (__asan_should_detect_stack_use_after_return() != 0);
- __asan_shadow_memory_dynamic_address =
- (uptr)__asan_get_shadow_memory_dynamic_address();
-
-#ifndef _WIN64
- INTERCEPT_FUNCTION(_except_handler4);
-#endif
- // In DLLs, the callbacks are expected to return 0,
- // otherwise CRT initialization fails.
- return 0;
-}
-
-#pragma section(".CRT$XIB", long, read) // NOLINT
-__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init;
-
-static void WINAPI asan_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init();
-}
-
-#pragma section(".CRT$XLAB", long, read) // NOLINT
-__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *,
- unsigned long, void *) = asan_thread_init;
-
-WIN_FORCE_LINK(__asan_dso_reg_hook)
-
-#endif // SANITIZER_DLL_THUNK
--- /dev/null
+//===-- asan_win_dll_thunk.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file defines a family of thunks that should be statically linked into
+// the DLLs that have ASan instrumentation in order to delegate the calls to the
+// shared runtime that lives in the main binary.
+// See https://github.com/google/sanitizers/issues/209 for the details.
+//===----------------------------------------------------------------------===//
+
+#ifdef SANITIZER_DLL_THUNK
+#include "asan_init_version.h"
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_win_defs.h"
+#include "sanitizer_common/sanitizer_win_dll_thunk.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+
+// ASan own interface functions.
+#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "asan_interface.inc"
+
+// Memory allocation functions.
+INTERCEPT_WRAP_V_W(free)
+INTERCEPT_WRAP_V_W(_free_base)
+INTERCEPT_WRAP_V_WW(_free_dbg)
+
+INTERCEPT_WRAP_W_W(malloc)
+INTERCEPT_WRAP_W_W(_malloc_base)
+INTERCEPT_WRAP_W_WWWW(_malloc_dbg)
+
+INTERCEPT_WRAP_W_WW(calloc)
+INTERCEPT_WRAP_W_WW(_calloc_base)
+INTERCEPT_WRAP_W_WWWWW(_calloc_dbg)
+INTERCEPT_WRAP_W_WWW(_calloc_impl)
+
+INTERCEPT_WRAP_W_WW(realloc)
+INTERCEPT_WRAP_W_WW(_realloc_base)
+INTERCEPT_WRAP_W_WWW(_realloc_dbg)
+INTERCEPT_WRAP_W_WWW(_recalloc)
+INTERCEPT_WRAP_W_WWW(_recalloc_base)
+
+INTERCEPT_WRAP_W_W(_msize)
+INTERCEPT_WRAP_W_W(_msize_base)
+INTERCEPT_WRAP_W_W(_expand)
+INTERCEPT_WRAP_W_W(_expand_dbg)
+
+// TODO(timurrrr): Might want to add support for _aligned_* allocation
+// functions to detect a bit more bugs. Those functions seem to wrap malloc().
+
+// TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cpp)
+
+INTERCEPT_LIBRARY_FUNCTION(atoi);
+INTERCEPT_LIBRARY_FUNCTION(atol);
+INTERCEPT_LIBRARY_FUNCTION(frexp);
+INTERCEPT_LIBRARY_FUNCTION(longjmp);
+#if SANITIZER_INTERCEPT_MEMCHR
+INTERCEPT_LIBRARY_FUNCTION(memchr);
+#endif
+INTERCEPT_LIBRARY_FUNCTION(memcmp);
+INTERCEPT_LIBRARY_FUNCTION(memcpy);
+INTERCEPT_LIBRARY_FUNCTION(memmove);
+INTERCEPT_LIBRARY_FUNCTION(memset);
+INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT
+INTERCEPT_LIBRARY_FUNCTION(strchr);
+INTERCEPT_LIBRARY_FUNCTION(strcmp);
+INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT
+INTERCEPT_LIBRARY_FUNCTION(strcspn);
+INTERCEPT_LIBRARY_FUNCTION(strdup);
+INTERCEPT_LIBRARY_FUNCTION(strlen);
+INTERCEPT_LIBRARY_FUNCTION(strncat);
+INTERCEPT_LIBRARY_FUNCTION(strncmp);
+INTERCEPT_LIBRARY_FUNCTION(strncpy);
+INTERCEPT_LIBRARY_FUNCTION(strnlen);
+INTERCEPT_LIBRARY_FUNCTION(strpbrk);
+INTERCEPT_LIBRARY_FUNCTION(strrchr);
+INTERCEPT_LIBRARY_FUNCTION(strspn);
+INTERCEPT_LIBRARY_FUNCTION(strstr);
+INTERCEPT_LIBRARY_FUNCTION(strtok);
+INTERCEPT_LIBRARY_FUNCTION(strtol);
+INTERCEPT_LIBRARY_FUNCTION(wcslen);
+INTERCEPT_LIBRARY_FUNCTION(wcsnlen);
+
+#ifdef _WIN64
+INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler);
+#else
+INTERCEPT_LIBRARY_FUNCTION(_except_handler3);
+// _except_handler4 checks -GS cookie which is different for each module, so we
+// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4).
+INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) {
+ __asan_handle_no_return();
+ return REAL(_except_handler4)(a, b, c, d);
+}
+#endif
+
+// Windows specific functions not included in asan_interface.inc.
+INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return)
+INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address)
+INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter)
+
+using namespace __sanitizer;
+
+extern "C" {
+int __asan_option_detect_stack_use_after_return;
+uptr __asan_shadow_memory_dynamic_address;
+} // extern "C"
+
+static int asan_dll_thunk_init() {
+ typedef void (*fntype)();
+ static fntype fn = 0;
+ // asan_dll_thunk_init is expected to be called by only one thread.
+ if (fn) return 0;
+
+ // Ensure all interception was executed.
+ __dll_thunk_init();
+
+ fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init");
+ fn();
+ __asan_option_detect_stack_use_after_return =
+ (__asan_should_detect_stack_use_after_return() != 0);
+ __asan_shadow_memory_dynamic_address =
+ (uptr)__asan_get_shadow_memory_dynamic_address();
+
+#ifndef _WIN64
+ INTERCEPT_FUNCTION(_except_handler4);
+#endif
+ // In DLLs, the callbacks are expected to return 0,
+ // otherwise CRT initialization fails.
+ return 0;
+}
+
+#pragma section(".CRT$XIB", long, read) // NOLINT
+__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init;
+
+static void WINAPI asan_thread_init(void *mod, unsigned long reason,
+ void *reserved) {
+ if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init();
+}
+
+#pragma section(".CRT$XLAB", long, read) // NOLINT
+__declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *,
+ unsigned long, void *) = asan_thread_init;
+
+WIN_FORCE_LINK(__asan_dso_reg_hook)
+
+#endif // SANITIZER_DLL_THUNK
+++ /dev/null
-//===-- asan_win_dynamic_runtime_thunk.cc ---------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// This file defines things that need to be present in the application modules
-// to interact with the ASan DLL runtime correctly and can't be implemented
-// using the default "import library" generated when linking the DLL RTL.
-//
-// This includes:
-// - creating weak aliases to default implementation imported from asan dll.
-// - forwarding the detect_stack_use_after_return runtime option
-// - working around deficiencies of the MD runtime
-// - installing a custom SEH handler
-//
-//===----------------------------------------------------------------------===//
-
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_common/sanitizer_win_defs.h"
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-// Define weak alias for all weak functions imported from asan dll.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "asan_interface.inc"
-
-// First, declare CRT sections we'll be using in this file
-#pragma section(".CRT$XIB", long, read) // NOLINT
-#pragma section(".CRT$XID", long, read) // NOLINT
-#pragma section(".CRT$XCAB", long, read) // NOLINT
-#pragma section(".CRT$XTW", long, read) // NOLINT
-#pragma section(".CRT$XTY", long, read) // NOLINT
-#pragma section(".CRT$XLAB", long, read) // NOLINT
-
-////////////////////////////////////////////////////////////////////////////////
-// Define a copy of __asan_option_detect_stack_use_after_return that should be
-// used when linking an MD runtime with a set of object files on Windows.
-//
-// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
-// so normally we would just dllimport it. Unfortunately, the dllimport
-// attribute adds __imp_ prefix to the symbol name of a variable.
-// Since in general we don't know if a given TU is going to be used
-// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
-// just to work around this issue, let's clone the variable that is constant
-// after initialization anyways.
-extern "C" {
-__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
-int __asan_option_detect_stack_use_after_return;
-
-__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address();
-void* __asan_shadow_memory_dynamic_address;
-}
-
-static int InitializeClonedVariables() {
- __asan_option_detect_stack_use_after_return =
- __asan_should_detect_stack_use_after_return();
- __asan_shadow_memory_dynamic_address =
- __asan_get_shadow_memory_dynamic_address();
- return 0;
-}
-
-static void NTAPI asan_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables();
-}
-
-// Our cloned variables must be initialized before C/C++ constructors. If TLS
-// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB
-// initializer is needed as a backup.
-__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() =
- InitializeClonedVariables;
-__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
- unsigned long, void *) = asan_thread_init;
-
-////////////////////////////////////////////////////////////////////////////////
-// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
-// unload or on exit. ASan relies on LLVM global_dtors to call
-// __asan_unregister_globals on these events, which unfortunately doesn't work
-// with the MD runtime, see PR22545 for the details.
-// To work around this, for each DLL we schedule a call to UnregisterGlobals
-// using atexit() that calls a small subset of C terminators
-// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
-// are there.
-extern "C" int __cdecl atexit(void (__cdecl *f)(void));
-extern "C" void __cdecl _initterm(void *a, void *b);
-
-namespace {
-__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0;
-__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0;
-
-void UnregisterGlobals() {
- _initterm(&before_global_dtors, &after_global_dtors);
-}
-
-int ScheduleUnregisterGlobals() {
- return atexit(UnregisterGlobals);
-}
-} // namespace
-
-// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
-// atexit() is initialized (.CRT$XIC). As this is executed before C++
-// initializers (think ctors for globals), UnregisterGlobals gets executed after
-// dtors for C++ globals.
-__declspec(allocate(".CRT$XID"))
-int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
-
-////////////////////////////////////////////////////////////////////////////////
-// ASan SEH handling.
-// We need to set the ASan-specific SEH handler at the end of CRT initialization
-// of each module (see also asan_win.cc).
-extern "C" {
-__declspec(dllimport) int __asan_set_seh_filter();
-static int SetSEHFilter() { return __asan_set_seh_filter(); }
-
-// Unfortunately, putting a pointer to __asan_set_seh_filter into
-// __asan_intercept_seh gets optimized out, so we have to use an extra function.
-__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
- SetSEHFilter;
-}
-
-WIN_FORCE_LINK(__asan_dso_reg_hook)
-
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
--- /dev/null
+//===-- asan_win_dynamic_runtime_thunk.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file defines things that need to be present in the application modules
+// to interact with the ASan DLL runtime correctly and can't be implemented
+// using the default "import library" generated when linking the DLL RTL.
+//
+// This includes:
+// - creating weak aliases to default implementation imported from asan dll.
+// - forwarding the detect_stack_use_after_return runtime option
+// - working around deficiencies of the MD runtime
+// - installing a custom SEH handler
+//
+//===----------------------------------------------------------------------===//
+
+#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
+#define SANITIZER_IMPORT_INTERFACE 1
+#include "sanitizer_common/sanitizer_win_defs.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+// Define weak alias for all weak functions imported from asan dll.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
+#include "asan_interface.inc"
+
+// First, declare CRT sections we'll be using in this file
+#pragma section(".CRT$XIB", long, read) // NOLINT
+#pragma section(".CRT$XID", long, read) // NOLINT
+#pragma section(".CRT$XCAB", long, read) // NOLINT
+#pragma section(".CRT$XTW", long, read) // NOLINT
+#pragma section(".CRT$XTY", long, read) // NOLINT
+#pragma section(".CRT$XLAB", long, read) // NOLINT
+
+////////////////////////////////////////////////////////////////////////////////
+// Define a copy of __asan_option_detect_stack_use_after_return that should be
+// used when linking an MD runtime with a set of object files on Windows.
+//
+// The ASan MD runtime dllexports '__asan_option_detect_stack_use_after_return',
+// so normally we would just dllimport it. Unfortunately, the dllimport
+// attribute adds __imp_ prefix to the symbol name of a variable.
+// Since in general we don't know if a given TU is going to be used
+// with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows
+// just to work around this issue, let's clone the variable that is constant
+// after initialization anyways.
+extern "C" {
+__declspec(dllimport) int __asan_should_detect_stack_use_after_return();
+int __asan_option_detect_stack_use_after_return;
+
+__declspec(dllimport) void* __asan_get_shadow_memory_dynamic_address();
+void* __asan_shadow_memory_dynamic_address;
+}
+
+static int InitializeClonedVariables() {
+ __asan_option_detect_stack_use_after_return =
+ __asan_should_detect_stack_use_after_return();
+ __asan_shadow_memory_dynamic_address =
+ __asan_get_shadow_memory_dynamic_address();
+ return 0;
+}
+
+static void NTAPI asan_thread_init(void *mod, unsigned long reason,
+ void *reserved) {
+ if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables();
+}
+
+// Our cloned variables must be initialized before C/C++ constructors. If TLS
+// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB
+// initializer is needed as a backup.
+__declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() =
+ InitializeClonedVariables;
+__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)(void *,
+ unsigned long, void *) = asan_thread_init;
+
+////////////////////////////////////////////////////////////////////////////////
+// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL
+// unload or on exit. ASan relies on LLVM global_dtors to call
+// __asan_unregister_globals on these events, which unfortunately doesn't work
+// with the MD runtime, see PR22545 for the details.
+// To work around this, for each DLL we schedule a call to UnregisterGlobals
+// using atexit() that calls a small subset of C terminators
+// where LLVM global_dtors is placed. Fingers crossed, no other C terminators
+// are there.
+extern "C" int __cdecl atexit(void (__cdecl *f)(void));
+extern "C" void __cdecl _initterm(void *a, void *b);
+
+namespace {
+__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0;
+__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0;
+
+void UnregisterGlobals() {
+ _initterm(&before_global_dtors, &after_global_dtors);
+}
+
+int ScheduleUnregisterGlobals() {
+ return atexit(UnregisterGlobals);
+}
+} // namespace
+
+// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after
+// atexit() is initialized (.CRT$XIC). As this is executed before C++
+// initializers (think ctors for globals), UnregisterGlobals gets executed after
+// dtors for C++ globals.
+__declspec(allocate(".CRT$XID"))
+int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals;
+
+////////////////////////////////////////////////////////////////////////////////
+// ASan SEH handling.
+// We need to set the ASan-specific SEH handler at the end of CRT initialization
+// of each module (see also asan_win.cpp).
+extern "C" {
+__declspec(dllimport) int __asan_set_seh_filter();
+static int SetSEHFilter() { return __asan_set_seh_filter(); }
+
+// Unfortunately, putting a pointer to __asan_set_seh_filter into
+// __asan_intercept_seh gets optimized out, so we have to use an extra function.
+__declspec(allocate(".CRT$XCAB")) int (*__asan_seh_interceptor)() =
+ SetSEHFilter;
+}
+
+WIN_FORCE_LINK(__asan_dso_reg_hook)
+
+#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+++ /dev/null
-//===-- asan_win_weak_interception.cc -------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// This module should be included in Address Sanitizer when it is implemented as
-// a shared library on Windows (dll), in order to delegate the calls of weak
-// functions to the implementation in the main executable when a strong
-// definition is provided.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC
-#include "sanitizer_common/sanitizer_win_weak_interception.h"
-#include "asan_interface_internal.h"
-// Check if strong definitions for weak functions are present in the main
-// executable. If that is the case, override dll functions to point to strong
-// implementations.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "asan_interface.inc"
-#endif // SANITIZER_DYNAMIC
--- /dev/null
+//===-- asan_win_weak_interception.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This module should be included in Address Sanitizer when it is implemented as
+// a shared library on Windows (dll), in order to delegate the calls of weak
+// functions to the implementation in the main executable when a strong
+// definition is provided.
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DYNAMIC
+#include "sanitizer_common/sanitizer_win_weak_interception.h"
+#include "asan_interface_internal.h"
+// Check if strong definitions for weak functions are present in the main
+// executable. If that is the case, override dll functions to point to strong
+// implementations.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "asan_interface.inc"
+#endif // SANITIZER_DYNAMIC
-/* ===-- assembly.h - compiler-rt assembler support macros -----------------===
- *
- * The LLVM Compiler Infrastructure
- *
- * This file is dual licensed under the MIT and the University of Illinois Open
- * Source Licenses. See LICENSE.TXT for details.
- *
- * ===----------------------------------------------------------------------===
- *
- * This file defines macros for use in compiler-rt assembler source.
- * This file is not part of the interface of this library.
- *
- * ===----------------------------------------------------------------------===
- */
+//===-- assembly.h - compiler-rt assembler support macros -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines macros for use in compiler-rt assembler source.
+// This file is not part of the interface of this library.
+//
+//===----------------------------------------------------------------------===//
#ifndef COMPILERRT_ASSEMBLY_H
#define COMPILERRT_ASSEMBLY_H
#if defined(__arm__)
-/*
- * Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros:
- * - for '-mthumb -march=armv6' compiler defines '__thumb__'
- * - for '-mthumb -march=armv7' compiler defines '__thumb__' and '__thumb2__'
- */
+// Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros:
+// - for '-mthumb -march=armv6' compiler defines '__thumb__'
+// - for '-mthumb -march=armv7' compiler defines '__thumb__' and '__thumb2__'
#if defined(__thumb2__) || defined(__thumb__)
#define DEFINE_CODE_STATE .thumb SEPARATOR
#define DECLARE_FUNC_ENCODING .thumb_func SEPARATOR
#define END_COMPILERRT_FUNCTION(name)
#endif
-#endif /* COMPILERRT_ASSEMBLY_H */
+#endif // COMPILERRT_ASSEMBLY_H
AMDEPBACKSLASH
AMDEP_FALSE
AMDEP_TRUE
-am__quote
am__include
DEPDIR
am__untar
PACKAGE_TARNAME
PACKAGE_NAME
PATH_SEPARATOR
-SHELL'
+SHELL
+am__quote'
ac_subst_files=''
ac_user_opts='
enable_option_checking
-am__api_version='1.15'
+am__api_version='1.16'
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
ac_config_commands="$ac_config_commands depfiles"
-
-am_make=${MAKE-make}
-cat > confinc << 'END'
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5
+$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; }
+cat > confinc.mk << 'END'
am__doit:
- @echo this is the am__doit target
+ @echo this is the am__doit target >confinc.out
.PHONY: am__doit
END
-# If we don't find an include directive, just comment out the code.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5
-$as_echo_n "checking for style of include used by $am_make... " >&6; }
am__include="#"
am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# Ignore all kinds of additional output from 'make'.
-case `$am_make -s -f confmf 2> /dev/null` in #(
-*the\ am__doit\ target*)
- am__include=include
- am__quote=
- _am_result=GNU
- ;;
-esac
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
- echo '.include "confinc"' > confmf
- case `$am_make -s -f confmf 2> /dev/null` in #(
- *the\ am__doit\ target*)
- am__include=.include
- am__quote="\""
- _am_result=BSD
+# BSD make does it like this.
+echo '.include "confinc.mk" # ignored' > confmf.BSD
+# Other make implementations (GNU, Solaris 10, AIX) do it like this.
+echo 'include confinc.mk # ignored' > confmf.GNU
+_am_result=no
+for s in GNU BSD; do
+ { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5
+ (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); }
+ case $?:`cat confinc.out 2>/dev/null` in #(
+ '0:this is the am__doit target') :
+ case $s in #(
+ BSD) :
+ am__include='.include' am__quote='"' ;; #(
+ *) :
+ am__include='include' am__quote='' ;;
+esac ;; #(
+ *) :
;;
- esac
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5
-$as_echo "$_am_result" >&6; }
-rm -f confinc confmf
+esac
+ if test "$am__include" != "#"; then
+ _am_result="yes ($s style)"
+ break
+ fi
+done
+rm -f confinc.* confmf.*
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5
+$as_echo "${_am_result}" >&6; }
# Check whether --enable-dependency-tracking was given.
if test "${enable_dependency_tracking+set}" = set; then :
# For better backward compatibility. To be removed once Automake 1.9.x
# dies out for good. For more background, see:
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
-# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <https://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
mkdir_p='$(MKDIR_P)'
# We need awk for the "check" target (and possibly the TAP driver). The
Aborting the configuration process, to ensure you take notice of the issue.
You can download and install GNU coreutils to get an 'rm' implementation
-that behaves properly: <http://www.gnu.org/software/coreutils/>.
+that behaves properly: <https://www.gnu.org/software/coreutils/>.
If you want to complete the configuration process using your problematic
'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
CXX="$CXX"
GFORTRAN="$GFORTRAN"
GDC="$GDC"
-AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"
# The HP-UX ksh and POSIX shell print the target directory to stdout
# Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
- case $CONFIG_FILES in
- *\'*) eval set x "$CONFIG_FILES" ;;
- *) set x $CONFIG_FILES ;;
- esac
+ # TODO: see whether this extra hack can be removed once we start
+ # requiring Autoconf 2.70 or later.
+ case $CONFIG_FILES in #(
+ *\'*) :
+ eval set x "$CONFIG_FILES" ;; #(
+ *) :
+ set x $CONFIG_FILES ;; #(
+ *) :
+ ;;
+esac
shift
- for mf
+ # Used to flag and report bootstrapping failures.
+ am_rc=0
+ for am_mf
do
# Strip MF so we end up with the name of the file.
- mf=`echo "$mf" | sed -e 's/:.*$//'`
- # Check whether this is an Automake generated Makefile or not.
- # We used to match only the files named 'Makefile.in', but
- # some people rename them; so instead we look at the file content.
- # Grep'ing the first line is not enough: some people post-process
- # each Makefile.in and add a new line on top of each file to say so.
- # Grep'ing the whole file is not good either: AIX grep has a line
+ am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'`
+ # Check whether this is an Automake generated Makefile which includes
+ # dependency-tracking related rules and includes.
+ # Grep'ing the whole file directly is not great: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
- if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then
- dirpart=`$as_dirname -- "$mf" ||
-$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$mf" : 'X\(//\)[^/]' \| \
- X"$mf" : 'X\(//\)$' \| \
- X"$mf" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$mf" |
+ sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \
+ || continue
+ am_dirpart=`$as_dirname -- "$am_mf" ||
+$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$am_mf" : 'X\(//\)[^/]' \| \
+ X"$am_mf" : 'X\(//\)$' \| \
+ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$am_mf" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
q
}
s/.*/./; q'`
- else
- continue
- fi
- # Extract the definition of DEPDIR, am__include, and am__quote
- # from the Makefile without running 'make'.
- DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
- test -z "$DEPDIR" && continue
- am__include=`sed -n 's/^am__include = //p' < "$mf"`
- test -z "$am__include" && continue
- am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
- # Find all dependency output files, they are included files with
- # $(DEPDIR) in their names. We invoke sed twice because it is the
- # simplest approach to changing $(DEPDIR) to its actual value in the
- # expansion.
- for file in `sed -n "
- s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
- # Make sure the directory exists.
- test -f "$dirpart/$file" && continue
- fdir=`$as_dirname -- "$file" ||
-$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
- X"$file" : 'X\(//\)[^/]' \| \
- X"$file" : 'X\(//\)$' \| \
- X"$file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$file" |
- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
- s//\1/
- q
- }
- /^X\(\/\/\)[^/].*/{
+ am_filepart=`$as_basename -- "$am_mf" ||
+$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$am_mf" : 'X\(//\)$' \| \
+ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$am_mf" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
}
- /^X\(\/\/\)$/{
+ /^X\/\(\/\/\)$/{
s//\1/
q
}
- /^X\(\/\).*/{
+ /^X\/\(\/\).*/{
s//\1/
q
}
s/.*/./; q'`
- as_dir=$dirpart/$fdir; as_fn_mkdir_p
- # echo "creating $dirpart/$file"
- echo '# dummy' > "$dirpart/$file"
- done
+ { echo "$as_me:$LINENO: cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles" >&5
+ (cd "$am_dirpart" \
+ && sed -e '/# am--include-marker/d' "$am_filepart" \
+ | $MAKE -f - am--depfiles) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } || am_rc=$?
done
+ if test $am_rc -ne 0; then
+ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "Something went wrong bootstrapping makefile fragments
+ for automatic dependency tracking. Try re-running configure with the
+ '--disable-dependency-tracking' option to at least be able to build
+ the package (albeit without support for automatic dependency tracking).
+See \`config.log' for more details" "$LINENO" 5; }
+ fi
+ { am_dirpart=; unset am_dirpart;}
+ { am_filepart=; unset am_filepart;}
+ { am_mf=; unset am_mf;}
+ { am_rc=; unset am_rc;}
+ rm -f conftest-deps.mk
}
;;
"libtool":C)
LSAN_SUPPORTED=yes
TSAN_TARGET_DEPENDENT_OBJECTS=tsan_rtl_amd64.lo
fi
- if echo "int x = __x86_64__;" | $CC -c -x c -o /dev/null - > /dev/null 2>&1; then
- SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS=sanitizer_linux_x86_64.lo
- fi
;;
powerpc*-*-linux*)
if test x$ac_cv_sizeof_void_p = x8; then
//===-- allocator_interface.h ---------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
-// This file is a part of AddressSanitizer.
+// This file is a part of AddressSanitizer (ASan).
//
// Public interface header.
//===----------------------------------------------------------------------===//
#ifdef __cplusplus
extern "C" {
#endif
- // Marks memory region [addr, addr+size) as unaddressable.
- // This memory must be previously allocated by the user program. Accessing
- // addresses in this region from instrumented code is forbidden until
- // this region is unpoisoned. This function is not guaranteed to poison
- // the whole region - it may poison only subregion of [addr, addr+size) due
- // to ASan alignment restrictions.
- // Method is NOT thread-safe in the sense that no two threads can
- // (un)poison memory in the same memory region simultaneously.
- void __asan_poison_memory_region(void const volatile *addr, size_t size);
- // Marks memory region [addr, addr+size) as addressable.
- // This memory must be previously allocated by the user program. Accessing
- // addresses in this region is allowed until this region is poisoned again.
- // This function may unpoison a superregion of [addr, addr+size) due to
- // ASan alignment restrictions.
- // Method is NOT thread-safe in the sense that no two threads can
- // (un)poison memory in the same memory region simultaneously.
- void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
-
-// User code should use macros instead of functions.
+/// Marks a memory region (<c>[addr, addr+size)</c>) as unaddressable.
+///
+/// This memory must be previously allocated by your program. Instrumented
+/// code is forbidden from accessing addresses in this region until it is
+/// unpoisoned. This function is not guaranteed to poison the entire region -
+/// it could poison only a subregion of <c>[addr, addr+size)</c> due to ASan
+/// alignment restrictions.
+///
+/// \note This function is not thread-safe because no two threads can poison or
+/// unpoison memory in the same memory region simultaneously.
+///
+/// \param addr Start of memory region.
+/// \param size Size of memory region.
+void __asan_poison_memory_region(void const volatile *addr, size_t size);
+
+/// Marks a memory region (<c>[addr, addr+size)</c>) as addressable.
+///
+/// This memory must be previously allocated by your program. Accessing
+/// addresses in this region is allowed until this region is poisoned again.
+/// This function could unpoison a super-region of <c>[addr, addr+size)</c> due
+/// to ASan alignment restrictions.
+///
+/// \note This function is not thread-safe because no two threads can
+/// poison or unpoison memory in the same memory region simultaneously.
+///
+/// \param addr Start of memory region.
+/// \param size Size of memory region.
+void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
+
+// Macros provided for convenience.
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
+/// Marks a memory region as unaddressable.
+///
+/// \note Macro provided for convenience; defined as a no-op if ASan is not
+/// enabled.
+///
+/// \param addr Start of memory region.
+/// \param size Size of memory region.
#define ASAN_POISON_MEMORY_REGION(addr, size) \
__asan_poison_memory_region((addr), (size))
+
+/// Marks a memory region as addressable.
+///
+/// \note Macro provided for convenience; defined as a no-op if ASan is not
+/// enabled.
+///
+/// \param addr Start of memory region.
+/// \param size Size of memory region.
#define ASAN_UNPOISON_MEMORY_REGION(addr, size) \
__asan_unpoison_memory_region((addr), (size))
#else
((void)(addr), (void)(size))
#endif
- // Returns 1 if addr is poisoned (i.e. 1-byte read/write access to this
- // address will result in error report from AddressSanitizer).
- // Otherwise returns 0.
- int __asan_address_is_poisoned(void const volatile *addr);
-
- // If at least one byte in [beg, beg+size) is poisoned, return the address
- // of the first such byte. Otherwise return 0.
- void *__asan_region_is_poisoned(void *beg, size_t size);
-
- // Print the description of addr (useful when debugging in gdb).
- void __asan_describe_address(void *addr);
-
- // Useful for calling from a debugger to get information about an ASan error.
- // Returns 1 if an error has been (or is being) reported, otherwise returns 0.
- int __asan_report_present(void);
-
- // Useful for calling from a debugger to get information about an ASan error.
- // If an error has been (or is being) reported, the following functions return
- // the pc, bp, sp, address, access type (0 = read, 1 = write), access size and
- // bug description (e.g. "heap-use-after-free"). Otherwise they return 0.
- void *__asan_get_report_pc(void);
- void *__asan_get_report_bp(void);
- void *__asan_get_report_sp(void);
- void *__asan_get_report_address(void);
- int __asan_get_report_access_type(void);
- size_t __asan_get_report_access_size(void);
- const char *__asan_get_report_description(void);
-
- // Useful for calling from the debugger to get information about a pointer.
- // Returns the category of the given pointer as a constant string.
- // Possible return values are "global", "stack", "stack-fake", "heap",
- // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown".
- // If global or stack, tries to also return the variable name, address and
- // size. If heap, tries to return the chunk address and size. 'name' should
- // point to an allocated buffer of size 'name_size'.
- const char *__asan_locate_address(void *addr, char *name, size_t name_size,
- void **region_address, size_t *region_size);
-
- // Useful for calling from the debugger to get the allocation stack trace
- // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
- // returns the number of stored frames or 0 on error.
- size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
- int *thread_id);
-
- // Useful for calling from the debugger to get the free stack trace
- // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
- // returns the number of stored frames or 0 on error.
- size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
- int *thread_id);
-
- // Useful for calling from the debugger to get the current shadow memory
- // mapping.
- void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
-
- // This is an internal function that is called to report an error.
- // However it is still a part of the interface because users may want to
- // set a breakpoint on this function in a debugger.
- void __asan_report_error(void *pc, void *bp, void *sp,
- void *addr, int is_write, size_t access_size);
-
- // Deprecated. Call __sanitizer_set_death_callback instead.
- void __asan_set_death_callback(void (*callback)(void));
-
- void __asan_set_error_report_callback(void (*callback)(const char*));
-
- // User may provide function that would be called right when ASan detects
- // an error. This can be used to notice cases when ASan detects an error, but
- // the program crashes before ASan report is printed.
- void __asan_on_error(void);
-
- // Prints accumulated stats to stderr. Used for debugging.
- void __asan_print_accumulated_stats(void);
-
- // This function may be optionally provided by user and should return
- // a string containing ASan runtime options. See asan_flags.h for details.
- const char* __asan_default_options(void);
-
- // The following 2 functions facilitate garbage collection in presence of
- // asan's fake stack.
-
- // Returns an opaque handler to be used later in __asan_addr_is_in_fake_stack.
- // Returns NULL if the current thread does not have a fake stack.
- void *__asan_get_current_fake_stack(void);
-
- // If fake_stack is non-NULL and addr belongs to a fake frame in
- // fake_stack, returns the address on real stack that corresponds to
- // the fake frame and sets beg/end to the boundaries of this fake frame.
- // Otherwise returns NULL and does not touch beg/end.
- // If beg/end are NULL, they are not touched.
- // This function may be called from a thread other than the owner of
- // fake_stack, but the owner thread need to be alive.
- void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
- void **end);
-
- // Performs cleanup before a [[noreturn]] function. Must be called
- // before things like _exit and execl to avoid false positives on stack.
- void __asan_handle_no_return(void);
+/// Checks if an address is poisoned.
+///
+/// Returns 1 if <c><i>addr</i></c> is poisoned (that is, 1-byte read/write
+/// access to this address would result in an error report from ASan).
+/// Otherwise returns 0.
+///
+/// \param addr Address to check.
+///
+/// \retval 1 Address is poisoned.
+/// \retval 0 Address is not poisoned.
+int __asan_address_is_poisoned(void const volatile *addr);
+
+/// Checks if a region is poisoned.
+///
+/// If at least one byte in <c>[beg, beg+size)</c> is poisoned, returns the
+/// address of the first such byte. Otherwise returns 0.
+///
+/// \param beg Start of memory region.
+/// \param size Start of memory region.
+/// \returns Address of first poisoned byte.
+void *__asan_region_is_poisoned(void *beg, size_t size);
+
+/// Describes an address (useful for calling from the debugger).
+///
+/// Prints the description of <c><i>addr</i></c>.
+///
+/// \param addr Address to describe.
+void __asan_describe_address(void *addr);
+
+/// Checks if an error has been or is being reported (useful for calling from
+/// the debugger to get information about an ASan error).
+///
+/// Returns 1 if an error has been (or is being) reported. Otherwise returns 0.
+///
+/// \returns 1 if an error has been (or is being) reported. Otherwise returns
+/// 0.
+int __asan_report_present(void);
+
+/// Gets the PC (program counter) register value of an ASan error (useful for
+/// calling from the debugger).
+///
+/// Returns PC if an error has been (or is being) reported.
+/// Otherwise returns 0.
+///
+/// \returns PC value.
+void *__asan_get_report_pc(void);
+
+/// Gets the BP (base pointer) register value of an ASan error (useful for
+/// calling from the debugger).
+///
+/// Returns BP if an error has been (or is being) reported.
+/// Otherwise returns 0.
+///
+/// \returns BP value.
+void *__asan_get_report_bp(void);
+
+/// Gets the SP (stack pointer) register value of an ASan error (useful for
+/// calling from the debugger).
+///
+/// If an error has been (or is being) reported, returns SP.
+/// Otherwise returns 0.
+///
+/// \returns SP value.
+void *__asan_get_report_sp(void);
+
+/// Gets the address of the report buffer of an ASan error (useful for calling
+/// from the debugger).
+///
+/// Returns the address of the report buffer if an error has been (or is being)
+/// reported. Otherwise returns 0.
+///
+/// \returns Address of report buffer.
+void *__asan_get_report_address(void);
+
+/// Gets access type of an ASan error (useful for calling from the debugger).
+///
+/// Returns access type (read or write) if an error has been (or is being)
+/// reported. Otherwise returns 0.
+///
+/// \returns Access type (0 = read, 1 = write).
+int __asan_get_report_access_type(void);
+
+/// Gets access size of an ASan error (useful for calling from the debugger).
+///
+/// Returns access size if an error has been (or is being) reported. Otherwise
+/// returns 0.
+///
+/// \returns Access size in bytes.
+size_t __asan_get_report_access_size(void);
+
+/// Gets the bug description of an ASan error (useful for calling from a
+/// debugger).
+///
+/// \returns Returns a bug description if an error has been (or is being)
+/// reported - for example, "heap-use-after-free". Otherwise returns an empty
+/// string.
+const char *__asan_get_report_description(void);
+
+/// Gets information about a pointer (useful for calling from the debugger).
+///
+/// Returns the category of the given pointer as a constant string.
+/// Possible return values are <c>global</c>, <c>stack</c>, <c>stack-fake</c>,
+/// <c>heap</c>, <c>heap-invalid</c>, <c>shadow-low</c>, <c>shadow-gap</c>,
+/// <c>shadow-high</c>, and <c>unknown</c>.
+///
+/// If the return value is <c>global</c> or <c>stack</c>, tries to also return
+/// the variable name, address, and size. If the return value is <c>heap</c>,
+/// tries to return the chunk address and size. <c><i>name</i></c> should point
+/// to an allocated buffer of size <c><i>name_size</i></c>.
+///
+/// \param addr Address to locate.
+/// \param name Buffer to store the variable's name.
+/// \param name_size Size in bytes of the variable's name buffer.
+/// \param region_address [out] Address of the region.
+/// \param region_size [out] Size of the region in bytes.
+///
+/// \returns Returns the category of the given pointer as a constant string.
+const char *__asan_locate_address(void *addr, char *name, size_t name_size,
+ void **region_address, size_t *region_size);
+
+/// Gets the allocation stack trace and thread ID for a heap address (useful
+/// for calling from the debugger).
+///
+/// Stores up to <c><i>size</i></c> frames in <c><i>trace</i></c>. Returns
+/// the number of stored frames or 0 on error.
+///
+/// \param addr A heap address.
+/// \param trace A buffer to store the stack trace.
+/// \param size Size in bytes of the trace buffer.
+/// \param thread_id [out] The thread ID of the address.
+///
+/// \returns Returns the number of stored frames or 0 on error.
+size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
+ int *thread_id);
+
+/// Gets the free stack trace and thread ID for a heap address (useful for
+/// calling from the debugger).
+///
+/// Stores up to <c><i>size</i></c> frames in <c><i>trace</i></c>. Returns
+/// the number of stored frames or 0 on error.
+///
+/// \param addr A heap address.
+/// \param trace A buffer to store the stack trace.
+/// \param size Size in bytes of the trace buffer.
+/// \param thread_id [out] The thread ID of the address.
+///
+/// \returns Returns the number of stored frames or 0 on error.
+size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
+ int *thread_id);
+
+/// Gets the current shadow memory mapping (useful for calling from the
+/// debugger).
+///
+/// \param shadow_scale [out] Shadow scale value.
+/// \param shadow_offset [out] Offset value.
+void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
+
+/// This is an internal function that is called to report an error. However,
+/// it is still a part of the interface because you might want to set a
+/// breakpoint on this function in the debugger.
+///
+/// \param pc <c><i>pc</i></c> value of the ASan error.
+/// \param bp <c><i>bp</i></c> value of the ASan error.
+/// \param sp <c><i>sp</i></c> value of the ASan error.
+/// \param addr Address of the ASan error.
+/// \param is_write True if the error is a write error; false otherwise.
+/// \param access_size Size of the memory access of the ASan error.
+void __asan_report_error(void *pc, void *bp, void *sp,
+ void *addr, int is_write, size_t access_size);
+
+// Deprecated. Call __sanitizer_set_death_callback instead.
+void __asan_set_death_callback(void (*callback)(void));
+
+/// Sets the callback function to be called during ASan error reporting.
+///
+/// The callback provides a string pointer to the report.
+///
+/// \param callback User-provided function.
+void __asan_set_error_report_callback(void (*callback)(const char *));
+
+/// User-provided callback on ASan errors.
+///
+/// You can provide a function that would be called immediately when ASan
+/// detects an error. This is useful in cases when ASan detects an error but
+/// your program crashes before the ASan report is printed.
+void __asan_on_error(void);
+
+/// Prints accumulated statistics to <c>stderr</c> (useful for calling from the
+/// debugger).
+void __asan_print_accumulated_stats(void);
+
+/// User-provided default option settings.
+///
+/// You can provide your own implementation of this function to return a string
+/// containing ASan runtime options (for example,
+/// <c>verbosity=1:halt_on_error=0</c>).
+///
+/// \returns Default options string.
+const char* __asan_default_options(void);
+
+// The following two functions facilitate garbage collection in presence of
+// ASan's fake stack.
+
+/// Gets an opaque handler to the current thread's fake stack.
+///
+/// Returns an opaque handler to be used by
+/// <c>__asan_addr_is_in_fake_stack()</c>. Returns NULL if the current thread
+/// does not have a fake stack.
+///
+/// \returns An opaque handler to the fake stack or NULL.
+void *__asan_get_current_fake_stack(void);
+
+/// Checks if an address belongs to a given fake stack.
+///
+/// If <c><i>fake_stack</i></c> is non-NULL and <c><i>addr</i></c> belongs to a
+/// fake frame in <c><i>fake_stack</i></c>, returns the address of the real
+/// stack that corresponds to the fake frame and sets <c><i>beg</i></c> and
+/// <c><i>end</i></c> to the boundaries of this fake frame. Otherwise returns
+/// NULL and does not touch <c><i>beg</i></c> and <c><i>end</i></c>.
+///
+/// If <c><i>beg</i></c> or <c><i>end</i></c> are NULL, they are not touched.
+///
+/// \note This function can be called from a thread other than the owner of
+/// <c><i>fake_stack</i></c>, but the owner thread needs to be alive.
+///
+/// \param fake_stack An opaque handler to a fake stack.
+/// \param addr Address to test.
+/// \param beg [out] Beginning of fake frame.
+/// \param end [out] End of fake frame.
+/// \returns Stack address or NULL.
+void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg,
+ void **end);
+
+/// Performs shadow memory cleanup of the current thread's stack before a
+/// function marked with the <c>[[noreturn]]</c> attribute is called.
+///
+/// To avoid false positives on the stack, must be called before no-return
+/// functions like <c>_exit()</c> and <c>execl()</c>.
+void __asan_handle_no_return(void);
#ifdef __cplusplus
} // extern "C"
//===-- sanitizer/common_interface_defs.h -----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// GCC does not understand __has_feature.
#if !defined(__has_feature)
-# define __has_feature(x) 0
+#define __has_feature(x) 0
#endif
#ifdef __cplusplus
extern "C" {
#endif
- // Arguments for __sanitizer_sandbox_on_notify() below.
- typedef struct {
- // Enable sandbox support in sanitizer coverage.
- int coverage_sandboxed;
- // File descriptor to write coverage data to. If -1 is passed, a file will
- // be pre-opened by __sanitizer_sandobx_on_notify(). This field has no
- // effect if coverage_sandboxed == 0.
- intptr_t coverage_fd;
- // If non-zero, split the coverage data into well-formed blocks. This is
- // useful when coverage_fd is a socket descriptor. Each block will contain
- // a header, allowing data from multiple processes to be sent over the same
- // socket.
- unsigned int coverage_max_block_size;
- } __sanitizer_sandbox_arguments;
-
- // Tell the tools to write their reports to "path.<pid>" instead of stderr.
- void __sanitizer_set_report_path(const char *path);
- // Tell the tools to write their reports to the provided file descriptor
- // (casted to void *).
- void __sanitizer_set_report_fd(void *fd);
-
- // Notify the tools that the sandbox is going to be turned on. The reserved
- // parameter will be used in the future to hold a structure with functions
- // that the tools may call to bypass the sandbox.
- void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
-
- // This function is called by the tool when it has just finished reporting
- // an error. 'error_summary' is a one-line string that summarizes
- // the error message. This function can be overridden by the client.
- void __sanitizer_report_error_summary(const char *error_summary);
-
- // Some of the sanitizers (e.g. asan/tsan) may miss bugs that happen
- // in unaligned loads/stores. In order to find such bugs reliably one needs
- // to replace plain unaligned loads/stores with these calls.
- uint16_t __sanitizer_unaligned_load16(const void *p);
- uint32_t __sanitizer_unaligned_load32(const void *p);
- uint64_t __sanitizer_unaligned_load64(const void *p);
- void __sanitizer_unaligned_store16(void *p, uint16_t x);
- void __sanitizer_unaligned_store32(void *p, uint32_t x);
- void __sanitizer_unaligned_store64(void *p, uint64_t x);
-
- // Returns 1 on the first call, then returns 0 thereafter. Called by the tool
- // to ensure only one report is printed when multiple errors occur
- // simultaneously.
- int __sanitizer_acquire_crash_state();
-
- // Annotate the current state of a contiguous container, such as
- // std::vector, std::string or similar.
- // A contiguous container is a container that keeps all of its elements
- // in a contiguous region of memory. The container owns the region of memory
- // [beg, end); the memory [beg, mid) is used to store the current elements
- // and the memory [mid, end) is reserved for future elements;
- // beg <= mid <= end. For example, in "std::vector<> v"
- // beg = &v[0];
- // end = beg + v.capacity() * sizeof(v[0]);
- // mid = beg + v.size() * sizeof(v[0]);
- //
- // This annotation tells the Sanitizer tool about the current state of the
- // container so that the tool can report errors when memory from [mid, end)
- // is accessed. Insert this annotation into methods like push_back/pop_back.
- // Supply the old and the new values of mid (old_mid/new_mid).
- // In the initial state mid == end and so should be the final
- // state when the container is destroyed or when it reallocates the storage.
- //
- // Use with caution and don't use for anything other than vector-like classes.
- //
- // For AddressSanitizer, 'beg' should be 8-aligned and 'end' should
- // be either 8-aligned or it should point to the end of a separate heap-,
- // stack-, or global- allocated buffer. I.e. the following will not work:
- // int64_t x[2]; // 16 bytes, 8-aligned.
- // char *beg = (char *)&x[0];
- // char *end = beg + 12; // Not 8 aligned, not the end of the buffer.
- // This however will work fine:
- // int32_t x[3]; // 12 bytes, but 8-aligned under AddressSanitizer.
- // char *beg = (char*)&x[0];
- // char *end = beg + 12; // Not 8-aligned, but is the end of the buffer.
- void __sanitizer_annotate_contiguous_container(const void *beg,
- const void *end,
- const void *old_mid,
- const void *new_mid);
- // Returns true if the contiguous container [beg, end) is properly poisoned
- // (e.g. with __sanitizer_annotate_contiguous_container), i.e. if
- // - [beg, mid) is addressable,
- // - [mid, end) is unaddressable.
- // Full verification requires O(end-beg) time; this function tries to avoid
- // such complexity by touching only parts of the container around beg/mid/end.
- int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
- const void *end);
-
- // Similar to __sanitizer_verify_contiguous_container but returns the address
- // of the first improperly poisoned byte otherwise. Returns null if the area
- // is poisoned properly.
- const void *__sanitizer_contiguous_container_find_bad_address(
- const void *beg, const void *mid, const void *end);
-
- // Print the stack trace leading to this call. Useful for debugging user code.
- void __sanitizer_print_stack_trace(void);
-
- // Symbolizes the supplied 'pc' using the format string 'fmt'.
- // Outputs at most 'out_buf_size' bytes into 'out_buf'.
- // If 'out_buf' is not empty then output is zero or more non empty C strings
- // followed by single empty C string. Multiple strings can be returned if PC
- // corresponds to inlined function. Inlined frames are printed in the order
- // from "most-inlined" to the "least-inlined", so the last frame should be the
- // not inlined function.
- // Inlined frames can be removed with 'symbolize_inline_frames=0'.
- // The format syntax is described in
- // lib/sanitizer_common/sanitizer_stacktrace_printer.h.
- void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
- size_t out_buf_size);
- // Same as __sanitizer_symbolize_pc, but for data section (i.e. globals).
- void __sanitizer_symbolize_global(void *data_ptr, const char *fmt,
- char *out_buf, size_t out_buf_size);
-
- // Sets the callback to be called right before death on error.
- // Passing 0 will unset the callback.
- void __sanitizer_set_death_callback(void (*callback)(void));
-
- // Interceptor hooks.
- // Whenever a libc function interceptor is called it checks if the
- // corresponding weak hook is defined, and it so -- calls it.
- // The primary use case is data-flow-guided fuzzing, where the fuzzer needs
- // to know what is being passed to libc functions, e.g. memcmp.
- // FIXME: implement more hooks.
- void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1,
- const void *s2, size_t n, int result);
- void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1,
- const char *s2, size_t n, int result);
- void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
- const char *s2, size_t n, int result);
- void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
- const char *s2, int result);
- void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
- const char *s2, int result);
- void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
- const char *s2, char *result);
- void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
- const char *s2, char *result);
- void __sanitizer_weak_hook_memmem(void *called_pc,
- const void *s1, size_t len1,
- const void *s2, size_t len2, void *result);
-
- // Prints stack traces for all live heap allocations ordered by total
- // allocation size until `top_percent` of total live heap is shown.
- // `top_percent` should be between 1 and 100.
- // At most `max_number_of_contexts` contexts (stack traces) is printed.
- // Experimental feature currently available only with asan on Linux/x86_64.
- void __sanitizer_print_memory_profile(size_t top_percent,
- size_t max_number_of_contexts);
-
- // Fiber annotation interface.
- // Before switching to a different stack, one must call
- // __sanitizer_start_switch_fiber with a pointer to the bottom of the
- // destination stack and its size. When code starts running on the new stack,
- // it must call __sanitizer_finish_switch_fiber to finalize the switch.
- // The start_switch function takes a void** to store the current fake stack if
- // there is one (it is needed when detect_stack_use_after_return is enabled).
- // When restoring a stack, this pointer must be given to the finish_switch
- // function. In most cases, this void* can be stored on the stack just before
- // switching. When leaving a fiber definitely, null must be passed as first
- // argument to the start_switch function so that the fake stack is destroyed.
- // If you do not want support for stack use-after-return detection, you can
- // always pass null to these two functions.
- // Note that the fake stack mechanism is disabled during fiber switch, so if a
- // signal callback runs during the switch, it will not benefit from the stack
- // use-after-return detection.
- void __sanitizer_start_switch_fiber(void **fake_stack_save,
- const void *bottom, size_t size);
- void __sanitizer_finish_switch_fiber(void *fake_stack_save,
- const void **bottom_old,
- size_t *size_old);
-
- // Get full module name and calculate pc offset within it.
- // Returns 1 if pc belongs to some module, 0 if module was not found.
- int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path,
- size_t module_path_len,
- void **pc_offset);
+// Arguments for __sanitizer_sandbox_on_notify() below.
+typedef struct {
+ // Enable sandbox support in sanitizer coverage.
+ int coverage_sandboxed;
+ // File descriptor to write coverage data to. If -1 is passed, a file will
+ // be pre-opened by __sanitizer_sandobx_on_notify(). This field has no
+ // effect if coverage_sandboxed == 0.
+ intptr_t coverage_fd;
+ // If non-zero, split the coverage data into well-formed blocks. This is
+ // useful when coverage_fd is a socket descriptor. Each block will contain
+ // a header, allowing data from multiple processes to be sent over the same
+ // socket.
+ unsigned int coverage_max_block_size;
+} __sanitizer_sandbox_arguments;
+
+// Tell the tools to write their reports to "path.<pid>" instead of stderr.
+void __sanitizer_set_report_path(const char *path);
+// Tell the tools to write their reports to the provided file descriptor
+// (casted to void *).
+void __sanitizer_set_report_fd(void *fd);
+
+// Notify the tools that the sandbox is going to be turned on. The reserved
+// parameter will be used in the future to hold a structure with functions
+// that the tools may call to bypass the sandbox.
+void __sanitizer_sandbox_on_notify(__sanitizer_sandbox_arguments *args);
+
+// This function is called by the tool when it has just finished reporting
+// an error. 'error_summary' is a one-line string that summarizes
+// the error message. This function can be overridden by the client.
+void __sanitizer_report_error_summary(const char *error_summary);
+
+// Some of the sanitizers (for example ASan/TSan) could miss bugs that happen
+// in unaligned loads/stores. To find such bugs reliably, you need to replace
+// plain unaligned loads/stores with these calls.
+
+/// Loads a 16-bit unaligned value.
+///
+/// \param p Pointer to unaligned memory.
+///
+/// \returns Loaded value.
+uint16_t __sanitizer_unaligned_load16(const void *p);
+
+/// Loads a 32-bit unaligned value.
+///
+/// \param p Pointer to unaligned memory.
+///
+/// \returns Loaded value.
+uint32_t __sanitizer_unaligned_load32(const void *p);
+
+/// Loads a 64-bit unaligned value.
+///
+/// \param p Pointer to unaligned memory.
+///
+/// \returns Loaded value.
+uint64_t __sanitizer_unaligned_load64(const void *p);
+
+/// Stores a 16-bit unaligned value.
+///
+/// \param p Pointer to unaligned memory.
+/// \param x 16-bit value to store.
+void __sanitizer_unaligned_store16(void *p, uint16_t x);
+
+/// Stores a 32-bit unaligned value.
+///
+/// \param p Pointer to unaligned memory.
+/// \param x 32-bit value to store.
+void __sanitizer_unaligned_store32(void *p, uint32_t x);
+
+/// Stores a 64-bit unaligned value.
+///
+/// \param p Pointer to unaligned memory.
+/// \param x 64-bit value to store.
+void __sanitizer_unaligned_store64(void *p, uint64_t x);
+
+// Returns 1 on the first call, then returns 0 thereafter. Called by the tool
+// to ensure only one report is printed when multiple errors occur
+// simultaneously.
+int __sanitizer_acquire_crash_state();
+
+/// Annotates the current state of a contiguous container, such as
+/// <c>std::vector</c>, <c>std::string</c>, or similar.
+///
+/// A contiguous container is a container that keeps all of its elements
+/// in a contiguous region of memory. The container owns the region of memory
+/// <c>[beg, end)</c>; the memory <c>[beg, mid)</c> is used to store the
+/// current elements, and the memory <c>[mid, end)</c> is reserved for future
+/// elements (<c>beg <= mid <= end</c>). For example, in
+/// <c>std::vector<> v</c>:
+///
+/// \code
+/// beg = &v[0];
+/// end = beg + v.capacity() * sizeof(v[0]);
+/// mid = beg + v.size() * sizeof(v[0]);
+/// \endcode
+///
+/// This annotation tells the Sanitizer tool about the current state of the
+/// container so that the tool can report errors when memory from
+/// <c>[mid, end)</c> is accessed. Insert this annotation into methods like
+/// <c>push_back()</c> or <c>pop_back()</c>. Supply the old and new values of
+/// <c>mid</c>(<c><i>old_mid</i></c> and <c><i>new_mid</i></c>). In the initial
+/// state <c>mid == end</c>, so that should be the final state when the
+/// container is destroyed or when the container reallocates the storage.
+///
+/// For ASan, <c><i>beg</i></c> should be 8-aligned and <c><i>end</i></c>
+/// should be either 8-aligned or it should point to the end of a separate
+/// heap-, stack-, or global-allocated buffer. So the following example will
+/// not work:
+///
+/// \code
+/// int64_t x[2]; // 16 bytes, 8-aligned
+/// char *beg = (char *)&x[0];
+/// char *end = beg + 12; // Not 8-aligned, not the end of the buffer
+/// \endcode
+///
+/// The following, however, will work:
+/// \code
+/// int32_t x[3]; // 12 bytes, but 8-aligned under ASan.
+/// char *beg = (char*)&x[0];
+/// char *end = beg + 12; // Not 8-aligned, but is the end of the buffer
+/// \endcode
+///
+/// \note Use this function with caution and do not use for anything other
+/// than vector-like classes.
+///
+/// \param beg Beginning of memory region.
+/// \param end End of memory region.
+/// \param old_mid Old middle of memory region.
+/// \param new_mid New middle of memory region.
+void __sanitizer_annotate_contiguous_container(const void *beg,
+ const void *end,
+ const void *old_mid,
+ const void *new_mid);
+
+/// Returns true if the contiguous container <c>[beg, end)</c> is properly
+/// poisoned.
+///
+/// Proper poisoning could occur, for example, with
+/// <c>__sanitizer_annotate_contiguous_container</c>), that is, if
+/// <c>[beg, mid)</c> is addressable and <c>[mid, end)</c> is unaddressable.
+/// Full verification requires O (<c>end - beg</c>) time; this function tries
+/// to avoid such complexity by touching only parts of the container around
+/// <c><i>beg</i></c>, <c><i>mid</i></c>, and <c><i>end</i></c>.
+///
+/// \param beg Beginning of memory region.
+/// \param mid Middle of memory region.
+/// \param end Old end of memory region.
+///
+/// \returns True if the contiguous container <c>[beg, end)</c> is properly
+/// poisoned.
+int __sanitizer_verify_contiguous_container(const void *beg, const void *mid,
+ const void *end);
+
+/// Similar to <c>__sanitizer_verify_contiguous_container()</c> but also
+/// returns the address of the first improperly poisoned byte.
+///
+/// Returns NULL if the area is poisoned properly.
+///
+/// \param beg Beginning of memory region.
+/// \param mid Middle of memory region.
+/// \param end Old end of memory region.
+///
+/// \returns The bad address or NULL.
+const void *__sanitizer_contiguous_container_find_bad_address(const void *beg,
+ const void *mid,
+ const void *end);
+
+/// Prints the stack trace leading to this call (useful for calling from the
+/// debugger).
+void __sanitizer_print_stack_trace(void);
+
+// Symbolizes the supplied 'pc' using the format string 'fmt'.
+// Outputs at most 'out_buf_size' bytes into 'out_buf'.
+// If 'out_buf' is not empty then output is zero or more non empty C strings
+// followed by single empty C string. Multiple strings can be returned if PC
+// corresponds to inlined function. Inlined frames are printed in the order
+// from "most-inlined" to the "least-inlined", so the last frame should be the
+// not inlined function.
+// Inlined frames can be removed with 'symbolize_inline_frames=0'.
+// The format syntax is described in
+// lib/sanitizer_common/sanitizer_stacktrace_printer.h.
+void __sanitizer_symbolize_pc(void *pc, const char *fmt, char *out_buf,
+ size_t out_buf_size);
+// Same as __sanitizer_symbolize_pc, but for data section (i.e. globals).
+void __sanitizer_symbolize_global(void *data_ptr, const char *fmt,
+ char *out_buf, size_t out_buf_size);
+
+/// Sets the callback to be called immediately before death on error.
+///
+/// Passing 0 will unset the callback.
+///
+/// \param callback User-provided callback.
+void __sanitizer_set_death_callback(void (*callback)(void));
+
+
+// Interceptor hooks.
+// Whenever a libc function interceptor is called, it checks if the
+// corresponding weak hook is defined, and calls it if it is indeed defined.
+// The primary use-case is data-flow-guided fuzzing, where the fuzzer needs
+// to know what is being passed to libc functions (for example memcmp).
+// FIXME: implement more hooks.
+
+/// Interceptor hook for <c>memcmp()</c>.
+///
+/// \param called_pc PC (program counter) address of the original call.
+/// \param s1 Pointer to block of memory.
+/// \param s2 Pointer to block of memory.
+/// \param n Number of bytes to compare.
+/// \param result Value returned by the intercepted function.
+void __sanitizer_weak_hook_memcmp(void *called_pc, const void *s1,
+ const void *s2, size_t n, int result);
+
+/// Interceptor hook for <c>strncmp()</c>.
+///
+/// \param called_pc PC (program counter) address of the original call.
+/// \param s1 Pointer to block of memory.
+/// \param s2 Pointer to block of memory.
+/// \param n Number of bytes to compare.
+/// \param result Value returned by the intercepted function.
+void __sanitizer_weak_hook_strncmp(void *called_pc, const char *s1,
+ const char *s2, size_t n, int result);
+
+/// Interceptor hook for <c>strncasecmp()</c>.
+///
+/// \param called_pc PC (program counter) address of the original call.
+/// \param s1 Pointer to block of memory.
+/// \param s2 Pointer to block of memory.
+/// \param n Number of bytes to compare.
+/// \param result Value returned by the intercepted function.
+void __sanitizer_weak_hook_strncasecmp(void *called_pc, const char *s1,
+ const char *s2, size_t n, int result);
+
+/// Interceptor hook for <c>strcmp()</c>.
+///
+/// \param called_pc PC (program counter) address of the original call.
+/// \param s1 Pointer to block of memory.
+/// \param s2 Pointer to block of memory.
+/// \param result Value returned by the intercepted function.
+void __sanitizer_weak_hook_strcmp(void *called_pc, const char *s1,
+ const char *s2, int result);
+
+/// Interceptor hook for <c>strcasecmp()</c>.
+///
+/// \param called_pc PC (program counter) address of the original call.
+/// \param s1 Pointer to block of memory.
+/// \param s2 Pointer to block of memory.
+/// \param result Value returned by the intercepted function.
+void __sanitizer_weak_hook_strcasecmp(void *called_pc, const char *s1,
+ const char *s2, int result);
+
+/// Interceptor hook for <c>strstr()</c>.
+///
+/// \param called_pc PC (program counter) address of the original call.
+/// \param s1 Pointer to block of memory.
+/// \param s2 Pointer to block of memory.
+/// \param result Value returned by the intercepted function.
+void __sanitizer_weak_hook_strstr(void *called_pc, const char *s1,
+ const char *s2, char *result);
+
+void __sanitizer_weak_hook_strcasestr(void *called_pc, const char *s1,
+ const char *s2, char *result);
+
+void __sanitizer_weak_hook_memmem(void *called_pc,
+ const void *s1, size_t len1,
+ const void *s2, size_t len2, void *result);
+
+// Prints stack traces for all live heap allocations ordered by total
+// allocation size until top_percent of total live heap is shown. top_percent
+// should be between 1 and 100. At most max_number_of_contexts contexts
+// (stack traces) are printed.
+// Experimental feature currently available only with ASan on Linux/x86_64.
+void __sanitizer_print_memory_profile(size_t top_percent,
+ size_t max_number_of_contexts);
+
+/// Notify ASan that a fiber switch has started (required only if implementing
+/// your own fiber library).
+///
+/// Before switching to a different stack, you must call
+/// <c>__sanitizer_start_switch_fiber()</c> with a pointer to the bottom of the
+/// destination stack and with its size. When code starts running on the new
+/// stack, it must call <c>__sanitizer_finish_switch_fiber()</c> to finalize
+/// the switch. The <c>__sanitizer_start_switch_fiber()</c> function takes a
+/// <c>void**</c> pointer argument to store the current fake stack if there is
+/// one (it is necessary when the runtime option
+/// <c>detect_stack_use_after_return</c> is enabled).
+///
+/// When restoring a stack, this <c>void**</c> pointer must be given to the
+/// <c>__sanitizer_finish_switch_fiber()</c> function. In most cases, this
+/// pointer can be stored on the stack immediately before switching. When
+/// leaving a fiber definitely, NULL must be passed as the first argument to
+/// the <c>__sanitizer_start_switch_fiber()</c> function so that the fake stack
+/// is destroyed. If your program does not need stack use-after-return
+/// detection, you can always pass NULL to these two functions.
+///
+/// \note The fake stack mechanism is disabled during fiber switch, so if a
+/// signal callback runs during the switch, it will not benefit from stack
+/// use-after-return detection.
+///
+/// \param fake_stack_save [out] Fake stack save location.
+/// \param bottom Bottom address of stack.
+/// \param size Size of stack in bytes.
+void __sanitizer_start_switch_fiber(void **fake_stack_save,
+ const void *bottom, size_t size);
+
+/// Notify ASan that a fiber switch has completed (required only if
+/// implementing your own fiber library).
+///
+/// When code starts running on the new stack, it must call
+/// <c>__sanitizer_finish_switch_fiber()</c> to finalize
+/// the switch. For usage details, see the description of
+/// <c>__sanitizer_start_switch_fiber()</c>.
+///
+/// \param fake_stack_save Fake stack save location.
+/// \param bottom_old [out] Bottom address of old stack.
+/// \param size_old [out] Size of old stack in bytes.
+void __sanitizer_finish_switch_fiber(void *fake_stack_save,
+ const void **bottom_old,
+ size_t *size_old);
+
+// Get full module name and calculate pc offset within it.
+// Returns 1 if pc belongs to some module, 0 if module was not found.
+int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_path,
+ size_t module_path_len,
+ void **pc_offset);
#ifdef __cplusplus
} // extern "C"
//===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- dfsan_interface.h -------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// Returns the number of labels allocated.
size_t dfsan_get_label_count(void);
+/// Flushes the DFSan shadow, i.e. forgets about all labels currently associated
+/// with the application memory. Will work only if there are no other
+/// threads executing DFSan-instrumented code concurrently.
+/// Use this call to start over the taint tracking within the same procces.
+void dfsan_flush(void);
+
/// Sets a callback to be invoked on calls to write(). The callback is invoked
/// before the write is done. The write is not guaranteed to succeed when the
/// callback executes. Pass in NULL to remove any callback.
+++ /dev/null
-//===-- sanitizer/esan_interface.h ------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of EfficiencySanitizer, a family of performance tuners.
-//
-// Public interface header.
-//===----------------------------------------------------------------------===//
-#ifndef SANITIZER_ESAN_INTERFACE_H
-#define SANITIZER_ESAN_INTERFACE_H
-
-#include <sanitizer/common_interface_defs.h>
-
-// We declare our interface routines as weak to allow the user to avoid
-// ifdefs and instead use this pattern to allow building the same sources
-// with and without our runtime library:
-// if (__esan_report)
-// __esan_report();
-#ifdef _MSC_VER
-/* selectany is as close to weak as we'll get. */
-#define COMPILER_RT_WEAK __declspec(selectany)
-#elif __GNUC__
-#define COMPILER_RT_WEAK __attribute__((weak))
-#else
-#define COMPILER_RT_WEAK
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// This function can be called mid-run (or at the end of a run for
-// a server process that doesn't shut down normally) to request that
-// data for that point in the run be reported from the tool.
-void COMPILER_RT_WEAK __esan_report(void);
-
-// This function returns the number of samples that the esan tool has collected
-// to this point. This is useful for testing.
-unsigned int COMPILER_RT_WEAK __esan_get_sample_count(void);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // SANITIZER_ESAN_INTERFACE_H
//===-- sanitizer/asan_interface.h ------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#ifdef __cplusplus
extern "C" {
#endif
- // Initialize shadow but not the rest of the runtime.
+ // Libc hook for program startup in statically linked executables.
+ // Initializes enough of the runtime to run instrumented code. This function
+ // should only be called in statically linked executables because it modifies
+ // the GOT, which won't work in regular binaries because RELRO will already
+ // have been applied by the time the function is called. This also means that
+ // the function should be called before libc applies RELRO.
// Does not call libc unless there is an error.
- // Can be called multiple times, or not at all (in which case shadow will
- // be initialized in compiler-inserted __hwasan_init() call).
- void __hwasan_shadow_init(void);
+ // Can be called multiple times.
+ void __hwasan_init_static(void);
// This function may be optionally provided by user and should return
// a string containing HWASan runtime options. See asan_flags.h for details.
// does would cause false reports.
void __hwasan_handle_longjmp(const void *sp_dst);
+ // Set memory tag for the part of the current thread stack below sp_dst to
+ // zero. Call this in vfork() before returning in the parent process.
+ void __hwasan_handle_vfork(const void *sp_dst);
+
// Libc hook for thread creation. Should be called in the child thread before
// any instrumented code.
void __hwasan_thread_enter();
// Print one-line report about the memory usage of the current process.
void __hwasan_print_memory_usage();
+ /* Returns the offset of the first byte in the memory range that can not be
+ * accessed through the pointer in x, or -1 if the whole range is good. */
+ intptr_t __hwasan_test_shadow(const volatile void *x, size_t size);
+
int __sanitizer_posix_memalign(void **memptr, size_t alignment, size_t size);
void * __sanitizer_memalign(size_t alignment, size_t size);
void * __sanitizer_aligned_alloc(size_t alignment, size_t size);
void __sanitizer_malloc_stats(void);
void * __sanitizer_calloc(size_t nmemb, size_t size);
void * __sanitizer_realloc(void *ptr, size_t size);
+ void * __sanitizer_reallocarray(void *ptr, size_t nmemb, size_t size);
void * __sanitizer_malloc(size_t size);
#ifdef __cplusplus
} // extern "C"
//===-- linux_syscall_hooks.h ---------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer/lsan_interface.h ------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- msan_interface.h --------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
contents). */
void __msan_unpoison_string(const volatile char *a);
+ /* Make first n parameters of the next function call fully initialized. */
+ void __msan_unpoison_param(size_t n);
+
/* Make memory region fully uninitialized (without changing its contents).
This is a legacy interface that does not update origin information. Use
__msan_allocated_memory() instead. */
//===-- netbsd_syscall_hooks.h --------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// DO NOT EDIT! THIS FILE HAS BEEN GENERATED!
//
// Generated with: generate_netbsd_syscalls.awk
-// Generated date: 2018-03-03
-// Generated from: syscalls.master,v 1.291 2018/01/06 16:41:23 kamil Exp
+// Generated date: 2018-10-30
+// Generated from: syscalls.master,v 1.293 2018/07/31 13:00:13 rjs Exp
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_NETBSD_SYSCALL_HOOKS_H
#define __sanitizer_syscall_post_fpathconf(res, fd, name) \
__sanitizer_syscall_post_impl_fpathconf(res, (long long)(fd), \
(long long)(name))
-/* syscall 193 has been skipped */
+#define __sanitizer_syscall_pre_getsockopt2(s, level, name, val, avalsize) \
+ __sanitizer_syscall_pre_impl_getsockopt2( \
+ (long long)(s), (long long)(level), (long long)(name), (long long)(val), \
+ (long long)(avalsize))
+#define __sanitizer_syscall_post_getsockopt2(res, s, level, name, val, \
+ avalsize) \
+ __sanitizer_syscall_post_impl_getsockopt2( \
+ res, (long long)(s), (long long)(level), (long long)(name), \
+ (long long)(val), (long long)(avalsize))
#define __sanitizer_syscall_pre_getrlimit(which, rlp) \
__sanitizer_syscall_pre_impl_getrlimit((long long)(which), (long long)(rlp))
#define __sanitizer_syscall_post_getrlimit(res, which, rlp) \
__sanitizer_syscall_post_impl___sigaction_sigtramp( \
res, (long long)(signum), (long long)(nsa), (long long)(osa), \
(long long)(tramp), (long long)(vers))
-#define __sanitizer_syscall_pre_pmc_get_info(ctr, op, args) \
- __sanitizer_syscall_pre_impl_pmc_get_info((long long)(ctr), (long long)(op), \
- (long long)(args))
-#define __sanitizer_syscall_post_pmc_get_info(res, ctr, op, args) \
- __sanitizer_syscall_post_impl_pmc_get_info( \
- res, (long long)(ctr), (long long)(op), (long long)(args))
-#define __sanitizer_syscall_pre_pmc_control(ctr, op, args) \
- __sanitizer_syscall_pre_impl_pmc_control((long long)(ctr), (long long)(op), \
- (long long)(args))
-#define __sanitizer_syscall_post_pmc_control(res, ctr, op, args) \
- __sanitizer_syscall_post_impl_pmc_control( \
- res, (long long)(ctr), (long long)(op), (long long)(args))
+/* syscall 341 has been skipped */
+/* syscall 342 has been skipped */
#define __sanitizer_syscall_pre_rasctl(addr, len, op) \
__sanitizer_syscall_pre_impl_rasctl((long long)(addr), (long long)(len), \
(long long)(op))
void __sanitizer_syscall_pre_impl_fpathconf(long long fd, long long name);
void __sanitizer_syscall_post_impl_fpathconf(long long res, long long fd,
long long name);
-/* syscall 193 has been skipped */
+void __sanitizer_syscall_pre_impl_getsockopt2(long long s, long long level,
+ long long name, long long val,
+ long long avalsize);
+void __sanitizer_syscall_post_impl_getsockopt2(long long res, long long s,
+ long long level, long long name,
+ long long val,
+ long long avalsize);
void __sanitizer_syscall_pre_impl_getrlimit(long long which, long long rlp);
void __sanitizer_syscall_post_impl_getrlimit(long long res, long long which,
long long rlp);
void __sanitizer_syscall_post_impl___sigaction_sigtramp(
long long res, long long signum, long long nsa, long long osa,
long long tramp, long long vers);
-void __sanitizer_syscall_pre_impl_pmc_get_info(long long ctr, long long op,
- long long args);
-void __sanitizer_syscall_post_impl_pmc_get_info(long long res, long long ctr,
- long long op, long long args);
-void __sanitizer_syscall_pre_impl_pmc_control(long long ctr, long long op,
- long long args);
-void __sanitizer_syscall_post_impl_pmc_control(long long res, long long ctr,
- long long op, long long args);
+/* syscall 341 has been skipped */
+/* syscall 342 has been skipped */
void __sanitizer_syscall_pre_impl_rasctl(long long addr, long long len,
long long op);
void __sanitizer_syscall_post_impl_rasctl(long long res, long long addr,
//===-- sanitizer/scudo_interface.h -----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- tsan_interface.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void __tsan_external_read(void *addr, void *caller_pc, void *tag);
void __tsan_external_write(void *addr, void *caller_pc, void *tag);
+// Fiber switching API.
+// - TSAN context for fiber can be created by __tsan_create_fiber
+// and freed by __tsan_destroy_fiber.
+// - TSAN context of current fiber or thread can be obtained
+// by calling __tsan_get_current_fiber.
+// - __tsan_switch_to_fiber should be called immediatly before switch
+// to fiber, such as call of swapcontext.
+// - Fiber name can be set by __tsan_set_fiber_name.
+void *__tsan_get_current_fiber(void);
+void *__tsan_create_fiber(unsigned flags);
+void __tsan_destroy_fiber(void *fiber);
+void __tsan_switch_to_fiber(void *fiber, unsigned flags);
+void __tsan_set_fiber_name(void *fiber, const char *name);
+
+// Flags for __tsan_switch_to_fiber:
+// Do not establish a happens-before relation between fibers
+const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0;
+
#ifdef __cplusplus
} // extern "C"
#endif
//===-- tsan_interface_atomic.h ---------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#endif
// Part of ABI, do not change.
-// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
+// https://github.com/llvm/llvm-project/blob/master/libcxx/include/atomic
typedef enum {
__tsan_memory_order_relaxed,
__tsan_memory_order_consume,
noinst_LTLIBRARIES = libinterception.la
interception_files = \
- interception_linux.cc \
- interception_mac.cc \
- interception_win.cc \
- interception_type_test.cc
+ interception_linux.cpp \
+ interception_mac.cpp \
+ interception_win.cpp \
+ interception_type_test.cpp
libinterception_la_SOURCES = $(interception_files)
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/interception_linux.Plo \
+ ./$(DEPDIR)/interception_mac.Plo \
+ ./$(DEPDIR)/interception_type_test.Plo \
+ ./$(DEPDIR)/interception_win.Plo
am__mv = mv -f
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libinterception.la
interception_files = \
- interception_linux.cc \
- interception_mac.cc \
- interception_win.cc \
- interception_type_test.cc
+ interception_linux.cpp \
+ interception_mac.cpp \
+ interception_win.cpp \
+ interception_type_test.cpp
libinterception_la_SOURCES = $(interception_files)
all: all-am
.SUFFIXES:
-.SUFFIXES: .cc .lo .o .obj
+.SUFFIXES: .cpp .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_type_test.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_type_test.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interception_win.Plo@am__quote@ # am--include-marker
-.cc.o:
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
-.cc.obj:
+.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-.cc.lo:
+.cpp.lo:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/interception_linux.Plo
+ -rm -f ./$(DEPDIR)/interception_mac.Plo
+ -rm -f ./$(DEPDIR)/interception_type_test.Plo
+ -rm -f ./$(DEPDIR)/interception_win.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/interception_linux.Plo
+ -rm -f ./$(DEPDIR)/interception_mac.Plo
+ -rm -f ./$(DEPDIR)/interception_type_test.Plo
+ -rm -f ./$(DEPDIR)/interception_win.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags dvi dvi-am \
+ html html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
//===-- interception.h ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#endif // SANITIZER_MAC
#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS
-#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
+# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \
DECLARE_REAL(ret_type, func, __VA_ARGS__) \
extern "C" ret_type WRAP(func)(__VA_ARGS__);
+// Declare an interceptor and its wrapper defined in a different translation
+// unit (ex. asm).
+# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...) \
+ extern "C" ret_type WRAP(func)(__VA_ARGS__); \
+ extern "C" ret_type func(__VA_ARGS__);
#else
-#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...)
+# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...)
+# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...)
#endif
// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
+++ /dev/null
-//===-- interception_linux.cc -----------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Linux-specific interception methods.
-//===----------------------------------------------------------------------===//
-
-#include "interception.h"
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
-
-#include <dlfcn.h> // for dlsym() and dlvsym()
-
-#if SANITIZER_NETBSD
-#include "sanitizer_common/sanitizer_libc.h"
-#endif
-
-namespace __interception {
-bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
- uptr real, uptr wrapper) {
-#if SANITIZER_NETBSD
- // XXX: Find a better way to handle renames
- if (internal_strcmp(func_name, "sigaction") == 0) func_name = "__sigaction14";
-#endif
- *func_addr = (uptr)dlsym(RTLD_NEXT, func_name);
- if (!*func_addr) {
- // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is
- // later in the library search order than the DSO that we are trying to
- // intercept, which means that we cannot intercept this function. We still
- // want the address of the real definition, though, so look it up using
- // RTLD_DEFAULT.
- *func_addr = (uptr)dlsym(RTLD_DEFAULT, func_name);
- }
- return real == wrapper;
-}
-
-// Android and Solaris do not have dlvsym
-#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD
-void *GetFuncAddrVer(const char *func_name, const char *ver) {
- return dlvsym(RTLD_NEXT, func_name, ver);
-}
-#endif // !SANITIZER_ANDROID
-
-} // namespace __interception
-
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD ||
- // SANITIZER_OPENBSD || SANITIZER_SOLARIS
--- /dev/null
+//===-- interception_linux.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Linux-specific interception methods.
+//===----------------------------------------------------------------------===//
+
+#include "interception.h"
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \
+ SANITIZER_OPENBSD || SANITIZER_SOLARIS
+
+#include <dlfcn.h> // for dlsym() and dlvsym()
+
+namespace __interception {
+
+#if SANITIZER_NETBSD
+static int StrCmp(const char *s1, const char *s2) {
+ while (true) {
+ if (*s1 != *s2)
+ return false;
+ if (*s1 == 0)
+ return true;
+ s1++;
+ s2++;
+ }
+}
+#endif
+
+static void *GetFuncAddr(const char *name, uptr wrapper_addr) {
+#if SANITIZER_NETBSD
+ // FIXME: Find a better way to handle renames
+ if (StrCmp(name, "sigaction"))
+ name = "__sigaction14";
+#endif
+ void *addr = dlsym(RTLD_NEXT, name);
+ if (!addr) {
+ // If the lookup using RTLD_NEXT failed, the sanitizer runtime library is
+ // later in the library search order than the DSO that we are trying to
+ // intercept, which means that we cannot intercept this function. We still
+ // want the address of the real definition, though, so look it up using
+ // RTLD_DEFAULT.
+ addr = dlsym(RTLD_DEFAULT, name);
+
+ // In case `name' is not loaded, dlsym ends up finding the actual wrapper.
+ // We don't want to intercept the wrapper and have it point to itself.
+ if ((uptr)addr == wrapper_addr)
+ addr = nullptr;
+ }
+ return addr;
+}
+
+bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,
+ uptr wrapper) {
+ void *addr = GetFuncAddr(name, wrapper);
+ *ptr_to_real = (uptr)addr;
+ return addr && (func == wrapper);
+}
+
+// Android and Solaris do not have dlvsym
+#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD
+static void *GetFuncAddr(const char *name, const char *ver) {
+ return dlvsym(RTLD_NEXT, name, ver);
+}
+
+bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
+ uptr func, uptr wrapper) {
+ void *addr = GetFuncAddr(name, ver);
+ *ptr_to_real = (uptr)addr;
+ return addr && (func == wrapper);
+}
+#endif // !SANITIZER_ANDROID
+
+} // namespace __interception
+
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD ||
+ // SANITIZER_OPENBSD || SANITIZER_SOLARIS
//===-- interception_linux.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#define INTERCEPTION_LINUX_H
namespace __interception {
-// returns true if a function with the given name was found.
-bool GetRealFunctionAddress(const char *func_name, uptr *func_addr,
- uptr real, uptr wrapper);
-void *GetFuncAddrVer(const char *func_name, const char *ver);
+bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func,
+ uptr wrapper);
+bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real,
+ uptr func, uptr wrapper);
} // namespace __interception
-#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
- ::__interception::GetRealFunctionAddress( \
- #func, (::__interception::uptr *)&__interception::PTR_TO_REAL(func), \
- (::__interception::uptr) & (func), \
+#define INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) \
+ ::__interception::InterceptFunction( \
+ #func, \
+ (::__interception::uptr *) & REAL(func), \
+ (::__interception::uptr) & (func), \
(::__interception::uptr) & WRAP(func))
// Android, Solaris and OpenBSD do not have dlvsym
#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS && !SANITIZER_OPENBSD
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
- (::__interception::real_##func = (func##_type)( \
- unsigned long)::__interception::GetFuncAddrVer(#func, symver))
+ ::__interception::InterceptFunction( \
+ #func, symver, \
+ (::__interception::uptr *) & REAL(func), \
+ (::__interception::uptr) & (func), \
+ (::__interception::uptr) & WRAP(func))
#else
#define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \
INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
+++ /dev/null
-//===-- interception_mac.cc -------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Mac-specific interception methods.
-//===----------------------------------------------------------------------===//
-
-#include "interception.h"
-
-#if SANITIZER_MAC
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- interception_mac.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Mac-specific interception methods.
+//===----------------------------------------------------------------------===//
+
+#include "interception.h"
+
+#if SANITIZER_MAC
+
+#endif // SANITIZER_MAC
//===-- interception_mac.h --------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- interception_type_test.cc -------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Compile-time tests of the internal type definitions.
-//===----------------------------------------------------------------------===//
-
-#include "interception.h"
-
-#if SANITIZER_LINUX || SANITIZER_MAC
-
-#include <sys/types.h>
-#include <stddef.h>
-#include <stdint.h>
-
-COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t));
-COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t));
-COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t));
-COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t));
-
-#if !SANITIZER_MAC
-COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t));
-#endif
-
-// The following are the cases when pread (and friends) is used instead of
-// pread64. In those cases we need OFF_T to match off_t. We don't care about the
-// rest (they depend on _FILE_OFFSET_BITS setting when building an application).
-# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \
- _FILE_OFFSET_BITS != 64
-COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t));
-# endif
-
-#endif
--- /dev/null
+//===-- interception_type_test.cpp ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Compile-time tests of the internal type definitions.
+//===----------------------------------------------------------------------===//
+
+#include "interception.h"
+
+#if SANITIZER_LINUX || SANITIZER_MAC
+
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdint.h>
+
+COMPILER_CHECK(sizeof(::SIZE_T) == sizeof(size_t));
+COMPILER_CHECK(sizeof(::SSIZE_T) == sizeof(ssize_t));
+COMPILER_CHECK(sizeof(::PTRDIFF_T) == sizeof(ptrdiff_t));
+COMPILER_CHECK(sizeof(::INTMAX_T) == sizeof(intmax_t));
+
+#if !SANITIZER_MAC
+COMPILER_CHECK(sizeof(::OFF64_T) == sizeof(off64_t));
+#endif
+
+// The following are the cases when pread (and friends) is used instead of
+// pread64. In those cases we need OFF_T to match off_t. We don't care about the
+// rest (they depend on _FILE_OFFSET_BITS setting when building an application).
+# if SANITIZER_ANDROID || !defined _FILE_OFFSET_BITS || \
+ _FILE_OFFSET_BITS != 64
+COMPILER_CHECK(sizeof(::OFF_T) == sizeof(off_t));
+# endif
+
+#endif
+++ /dev/null
-//===-- interception_linux.cc -----------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of AddressSanitizer, an address sanity checker.
-//
-// Windows-specific interception methods.
-//
-// This file is implementing several hooking techniques to intercept calls
-// to functions. The hooks are dynamically installed by modifying the assembly
-// code.
-//
-// The hooking techniques are making assumptions on the way the code is
-// generated and are safe under these assumptions.
-//
-// On 64-bit architecture, there is no direct 64-bit jump instruction. To allow
-// arbitrary branching on the whole memory space, the notion of trampoline
-// region is used. A trampoline region is a memory space withing 2G boundary
-// where it is safe to add custom assembly code to build 64-bit jumps.
-//
-// Hooking techniques
-// ==================
-//
-// 1) Detour
-//
-// The Detour hooking technique is assuming the presence of an header with
-// padding and an overridable 2-bytes nop instruction (mov edi, edi). The
-// nop instruction can safely be replaced by a 2-bytes jump without any need
-// to save the instruction. A jump to the target is encoded in the function
-// header and the nop instruction is replaced by a short jump to the header.
-//
-// head: 5 x nop head: jmp <hook>
-// func: mov edi, edi --> func: jmp short <head>
-// [...] real: [...]
-//
-// This technique is only implemented on 32-bit architecture.
-// Most of the time, Windows API are hookable with the detour technique.
-//
-// 2) Redirect Jump
-//
-// The redirect jump is applicable when the first instruction is a direct
-// jump. The instruction is replaced by jump to the hook.
-//
-// func: jmp <label> --> func: jmp <hook>
-//
-// On an 64-bit architecture, a trampoline is inserted.
-//
-// func: jmp <label> --> func: jmp <tramp>
-// [...]
-//
-// [trampoline]
-// tramp: jmp QWORD [addr]
-// addr: .bytes <hook>
-//
-// Note: <real> is equilavent to <label>.
-//
-// 3) HotPatch
-//
-// The HotPatch hooking is assuming the presence of an header with padding
-// and a first instruction with at least 2-bytes.
-//
-// The reason to enforce the 2-bytes limitation is to provide the minimal
-// space to encode a short jump. HotPatch technique is only rewriting one
-// instruction to avoid breaking a sequence of instructions containing a
-// branching target.
-//
-// Assumptions are enforced by MSVC compiler by using the /HOTPATCH flag.
-// see: https://msdn.microsoft.com/en-us/library/ms173507.aspx
-// Default padding length is 5 bytes in 32-bits and 6 bytes in 64-bits.
-//
-// head: 5 x nop head: jmp <hook>
-// func: <instr> --> func: jmp short <head>
-// [...] body: [...]
-//
-// [trampoline]
-// real: <instr>
-// jmp <body>
-//
-// On an 64-bit architecture:
-//
-// head: 6 x nop head: jmp QWORD [addr1]
-// func: <instr> --> func: jmp short <head>
-// [...] body: [...]
-//
-// [trampoline]
-// addr1: .bytes <hook>
-// real: <instr>
-// jmp QWORD [addr2]
-// addr2: .bytes <body>
-//
-// 4) Trampoline
-//
-// The Trampoline hooking technique is the most aggressive one. It is
-// assuming that there is a sequence of instructions that can be safely
-// replaced by a jump (enough room and no incoming branches).
-//
-// Unfortunately, these assumptions can't be safely presumed and code may
-// be broken after hooking.
-//
-// func: <instr> --> func: jmp <hook>
-// <instr>
-// [...] body: [...]
-//
-// [trampoline]
-// real: <instr>
-// <instr>
-// jmp <body>
-//
-// On an 64-bit architecture:
-//
-// func: <instr> --> func: jmp QWORD [addr1]
-// <instr>
-// [...] body: [...]
-//
-// [trampoline]
-// addr1: .bytes <hook>
-// real: <instr>
-// <instr>
-// jmp QWORD [addr2]
-// addr2: .bytes <body>
-//===----------------------------------------------------------------------===//
-
-#include "interception.h"
-
-#if SANITIZER_WINDOWS
-#include "sanitizer_common/sanitizer_platform.h"
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-
-namespace __interception {
-
-static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
-static const int kJumpInstructionLength = 5;
-static const int kShortJumpInstructionLength = 2;
-static const int kIndirectJumpInstructionLength = 6;
-static const int kBranchLength =
- FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength);
-static const int kDirectBranchLength = kBranchLength + kAddressLength;
-
-static void InterceptionFailed() {
- // Do we have a good way to abort with an error message here?
- __debugbreak();
-}
-
-static bool DistanceIsWithin2Gig(uptr from, uptr target) {
-#if SANITIZER_WINDOWS64
- if (from < target)
- return target - from <= (uptr)0x7FFFFFFFU;
- else
- return from - target <= (uptr)0x80000000U;
-#else
- // In a 32-bit address space, the address calculation will wrap, so this check
- // is unnecessary.
- return true;
-#endif
-}
-
-static uptr GetMmapGranularity() {
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return si.dwAllocationGranularity;
-}
-
-static uptr RoundUpTo(uptr size, uptr boundary) {
- return (size + boundary - 1) & ~(boundary - 1);
-}
-
-// FIXME: internal_str* and internal_mem* functions should be moved from the
-// ASan sources into interception/.
-
-static size_t _strlen(const char *str) {
- const char* p = str;
- while (*p != '\0') ++p;
- return p - str;
-}
-
-static char* _strchr(char* str, char c) {
- while (*str) {
- if (*str == c)
- return str;
- ++str;
- }
- return nullptr;
-}
-
-static void _memset(void *p, int value, size_t sz) {
- for (size_t i = 0; i < sz; ++i)
- ((char*)p)[i] = (char)value;
-}
-
-static void _memcpy(void *dst, void *src, size_t sz) {
- char *dst_c = (char*)dst,
- *src_c = (char*)src;
- for (size_t i = 0; i < sz; ++i)
- dst_c[i] = src_c[i];
-}
-
-static bool ChangeMemoryProtection(
- uptr address, uptr size, DWORD *old_protection) {
- return ::VirtualProtect((void*)address, size,
- PAGE_EXECUTE_READWRITE,
- old_protection) != FALSE;
-}
-
-static bool RestoreMemoryProtection(
- uptr address, uptr size, DWORD old_protection) {
- DWORD unused;
- return ::VirtualProtect((void*)address, size,
- old_protection,
- &unused) != FALSE;
-}
-
-static bool IsMemoryPadding(uptr address, uptr size) {
- u8* function = (u8*)address;
- for (size_t i = 0; i < size; ++i)
- if (function[i] != 0x90 && function[i] != 0xCC)
- return false;
- return true;
-}
-
-static const u8 kHintNop8Bytes[] = {
- 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-template<class T>
-static bool FunctionHasPrefix(uptr address, const T &pattern) {
- u8* function = (u8*)address - sizeof(pattern);
- for (size_t i = 0; i < sizeof(pattern); ++i)
- if (function[i] != pattern[i])
- return false;
- return true;
-}
-
-static bool FunctionHasPadding(uptr address, uptr size) {
- if (IsMemoryPadding(address - size, size))
- return true;
- if (size <= sizeof(kHintNop8Bytes) &&
- FunctionHasPrefix(address, kHintNop8Bytes))
- return true;
- return false;
-}
-
-static void WritePadding(uptr from, uptr size) {
- _memset((void*)from, 0xCC, (size_t)size);
-}
-
-static void WriteJumpInstruction(uptr from, uptr target) {
- if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target))
- InterceptionFailed();
- ptrdiff_t offset = target - from - kJumpInstructionLength;
- *(u8*)from = 0xE9;
- *(u32*)(from + 1) = offset;
-}
-
-static void WriteShortJumpInstruction(uptr from, uptr target) {
- sptr offset = target - from - kShortJumpInstructionLength;
- if (offset < -128 || offset > 127)
- InterceptionFailed();
- *(u8*)from = 0xEB;
- *(u8*)(from + 1) = (u8)offset;
-}
-
-#if SANITIZER_WINDOWS64
-static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) {
- // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative
- // offset.
- // The offset is the distance from then end of the jump instruction to the
- // memory location containing the targeted address. The displacement is still
- // 32-bit in x64, so indirect_target must be located within +/- 2GB range.
- int offset = indirect_target - from - kIndirectJumpInstructionLength;
- if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength,
- indirect_target)) {
- InterceptionFailed();
- }
- *(u16*)from = 0x25FF;
- *(u32*)(from + 2) = offset;
-}
-#endif
-
-static void WriteBranch(
- uptr from, uptr indirect_target, uptr target) {
-#if SANITIZER_WINDOWS64
- WriteIndirectJumpInstruction(from, indirect_target);
- *(u64*)indirect_target = target;
-#else
- (void)indirect_target;
- WriteJumpInstruction(from, target);
-#endif
-}
-
-static void WriteDirectBranch(uptr from, uptr target) {
-#if SANITIZER_WINDOWS64
- // Emit an indirect jump through immediately following bytes:
- // jmp [rip + kBranchLength]
- // .quad <target>
- WriteBranch(from, from + kBranchLength, target);
-#else
- WriteJumpInstruction(from, target);
-#endif
-}
-
-struct TrampolineMemoryRegion {
- uptr content;
- uptr allocated_size;
- uptr max_size;
-};
-
-static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig
-static const int kMaxTrampolineRegion = 1024;
-static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
-
-static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
-#if SANITIZER_WINDOWS64
- uptr address = image_address;
- uptr scanned = 0;
- while (scanned < kTrampolineScanLimitRange) {
- MEMORY_BASIC_INFORMATION info;
- if (!::VirtualQuery((void*)address, &info, sizeof(info)))
- return nullptr;
-
- // Check whether a region can be allocated at |address|.
- if (info.State == MEM_FREE && info.RegionSize >= granularity) {
- void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
- granularity,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_EXECUTE_READWRITE);
- return page;
- }
-
- // Move to the next region.
- address = (uptr)info.BaseAddress + info.RegionSize;
- scanned += info.RegionSize;
- }
- return nullptr;
-#else
- return ::VirtualAlloc(nullptr,
- granularity,
- MEM_RESERVE | MEM_COMMIT,
- PAGE_EXECUTE_READWRITE);
-#endif
-}
-
-// Used by unittests to release mapped memory space.
-void TestOnlyReleaseTrampolineRegions() {
- for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
- TrampolineMemoryRegion *current = &TrampolineRegions[bucket];
- if (current->content == 0)
- return;
- ::VirtualFree((void*)current->content, 0, MEM_RELEASE);
- current->content = 0;
- }
-}
-
-static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
- // Find a region within 2G with enough space to allocate |size| bytes.
- TrampolineMemoryRegion *region = nullptr;
- for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
- TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
- if (current->content == 0) {
- // No valid region found, allocate a new region.
- size_t bucket_size = GetMmapGranularity();
- void *content = AllocateTrampolineRegion(image_address, bucket_size);
- if (content == nullptr)
- return 0U;
-
- current->content = (uptr)content;
- current->allocated_size = 0;
- current->max_size = bucket_size;
- region = current;
- break;
- } else if (current->max_size - current->allocated_size > size) {
-#if SANITIZER_WINDOWS64
- // In 64-bits, the memory space must be allocated within 2G boundary.
- uptr next_address = current->content + current->allocated_size;
- if (next_address < image_address ||
- next_address - image_address >= 0x7FFF0000)
- continue;
-#endif
- // The space can be allocated in the current region.
- region = current;
- break;
- }
- }
-
- // Failed to find a region.
- if (region == nullptr)
- return 0U;
-
- // Allocate the space in the current region.
- uptr allocated_space = region->content + region->allocated_size;
- region->allocated_size += size;
- WritePadding(allocated_space, size);
-
- return allocated_space;
-}
-
-// Returns 0 on error.
-static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
- switch (*(u64*)address) {
- case 0x90909090909006EB: // stub: jmp over 6 x nop.
- return 8;
- }
-
- switch (*(u8*)address) {
- case 0x90: // 90 : nop
- return 1;
-
- case 0x50: // push eax / rax
- case 0x51: // push ecx / rcx
- case 0x52: // push edx / rdx
- case 0x53: // push ebx / rbx
- case 0x54: // push esp / rsp
- case 0x55: // push ebp / rbp
- case 0x56: // push esi / rsi
- case 0x57: // push edi / rdi
- case 0x5D: // pop ebp / rbp
- return 1;
-
- case 0x6A: // 6A XX = push XX
- return 2;
-
- case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX
- case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX
- return 5;
-
- // Cannot overwrite control-instruction. Return 0 to indicate failure.
- case 0xE9: // E9 XX XX XX XX : jmp <label>
- case 0xE8: // E8 XX XX XX XX : call <func>
- case 0xC3: // C3 : ret
- case 0xEB: // EB XX : jmp XX (short jump)
- case 0x70: // 7Y YY : jy XX (short conditional jump)
- case 0x71:
- case 0x72:
- case 0x73:
- case 0x74:
- case 0x75:
- case 0x76:
- case 0x77:
- case 0x78:
- case 0x79:
- case 0x7A:
- case 0x7B:
- case 0x7C:
- case 0x7D:
- case 0x7E:
- case 0x7F:
- return 0;
- }
-
- switch (*(u16*)(address)) {
- case 0x018A: // 8A 01 : mov al, byte ptr [ecx]
- case 0xFF8B: // 8B FF : mov edi, edi
- case 0xEC8B: // 8B EC : mov ebp, esp
- case 0xc889: // 89 C8 : mov eax, ecx
- case 0xC18B: // 8B C1 : mov eax, ecx
- case 0xC033: // 33 C0 : xor eax, eax
- case 0xC933: // 33 C9 : xor ecx, ecx
- case 0xD233: // 33 D2 : xor edx, edx
- return 2;
-
- // Cannot overwrite control-instruction. Return 0 to indicate failure.
- case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX]
- return 0;
- }
-
- switch (0x00FFFFFF & *(u32*)address) {
- case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]
- return 7;
- }
-
-#if SANITIZER_WINDOWS64
- switch (*(u8*)address) {
- case 0xA1: // A1 XX XX XX XX XX XX XX XX :
- // movabs eax, dword ptr ds:[XXXXXXXX]
- return 9;
- }
-
- switch (*(u16*)address) {
- case 0x5040: // push rax
- case 0x5140: // push rcx
- case 0x5240: // push rdx
- case 0x5340: // push rbx
- case 0x5440: // push rsp
- case 0x5540: // push rbp
- case 0x5640: // push rsi
- case 0x5740: // push rdi
- case 0x5441: // push r12
- case 0x5541: // push r13
- case 0x5641: // push r14
- case 0x5741: // push r15
- case 0x9066: // Two-byte NOP
- return 2;
-
- case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
- if (rel_offset)
- *rel_offset = 2;
- return 6;
- }
-
- switch (0x00FFFFFF & *(u32*)address) {
- case 0xe58948: // 48 8b c4 : mov rbp, rsp
- case 0xc18b48: // 48 8b c1 : mov rax, rcx
- case 0xc48b48: // 48 8b c4 : mov rax, rsp
- case 0xd9f748: // 48 f7 d9 : neg rcx
- case 0xd12b48: // 48 2b d1 : sub rdx, rcx
- case 0x07c1f6: // f6 c1 07 : test cl, 0x7
- case 0xc98548: // 48 85 C9 : test rcx, rcx
- case 0xc0854d: // 4d 85 c0 : test r8, r8
- case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
- case 0xc03345: // 45 33 c0 : xor r8d, r8d
- case 0xdb3345: // 45 33 DB : xor r11d, r11d
- case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
- case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
- case 0xc98b4c: // 4C 8B C9 : mov r9, rcx
- case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
- case 0xca2b48: // 48 2b ca : sub rcx, rdx
- case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
- case 0xc00b4d: // 3d 0b c0 : or r8, r8
- case 0xd18b48: // 48 8b d1 : mov rdx, rcx
- case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
- case 0xd18b4c: // 4c 8b d1 : mov r10, rcx
- return 3;
-
- case 0xec8348: // 48 83 ec XX : sub rsp, XX
- case 0xf88349: // 49 83 f8 XX : cmp r8, XX
- case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
- return 4;
-
- case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
- return 7;
-
- case 0x058b48: // 48 8b 05 XX XX XX XX :
- // mov rax, QWORD PTR [rip + XXXXXXXX]
- case 0x25ff48: // 48 ff 25 XX XX XX XX :
- // rex.W jmp QWORD PTR [rip + XXXXXXXX]
-
- // Instructions having offset relative to 'rip' need offset adjustment.
- if (rel_offset)
- *rel_offset = 3;
- return 7;
-
- case 0x2444c7: // C7 44 24 XX YY YY YY YY
- // mov dword ptr [rsp + XX], YYYYYYYY
- return 8;
- }
-
- switch (*(u32*)(address)) {
- case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
- case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
- case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
- case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi
- case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx
- return 5;
- case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
- return 6;
- }
-
-#else
-
- switch (*(u8*)address) {
- case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX]
- return 5;
- }
- switch (*(u16*)address) {
- case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX]
- case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
- case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX]
- case 0xEC83: // 83 EC XX : sub esp, XX
- case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX]
- return 3;
- case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX
- case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
- return 6;
- case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX
- return 7;
- case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY
- return 4;
- }
-
- switch (0x00FFFFFF & *(u32*)address) {
- case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX]
- case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX]
- case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX]
- case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX]
- case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX]
- case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX]
- return 4;
- }
-
- switch (*(u32*)address) {
- case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX]
- return 5;
- }
-#endif
-
- // Unknown instruction!
- // FIXME: Unknown instruction failures might happen when we add a new
- // interceptor or a new compiler version. In either case, they should result
- // in visible and readable error messages. However, merely calling abort()
- // leads to an infinite recursion in CheckFailed.
- InterceptionFailed();
- return 0;
-}
-
-// Returns 0 on error.
-static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
- size_t cursor = 0;
- while (cursor < size) {
- size_t instruction_size = GetInstructionSize(address + cursor);
- if (!instruction_size)
- return 0;
- cursor += instruction_size;
- }
- return cursor;
-}
-
-static bool CopyInstructions(uptr to, uptr from, size_t size) {
- size_t cursor = 0;
- while (cursor != size) {
- size_t rel_offset = 0;
- size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset);
- _memcpy((void*)(to + cursor), (void*)(from + cursor),
- (size_t)instruction_size);
- if (rel_offset) {
- uptr delta = to - from;
- uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta;
-#if SANITIZER_WINDOWS64
- if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU)
- return false;
-#endif
- *(u32*)(to + cursor + rel_offset) = relocated_offset;
- }
- cursor += instruction_size;
- }
- return true;
-}
-
-
-#if !SANITIZER_WINDOWS64
-bool OverrideFunctionWithDetour(
- uptr old_func, uptr new_func, uptr *orig_old_func) {
- const int kDetourHeaderLen = 5;
- const u16 kDetourInstruction = 0xFF8B;
-
- uptr header = (uptr)old_func - kDetourHeaderLen;
- uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength;
-
- // Validate that the function is hookable.
- if (*(u16*)old_func != kDetourInstruction ||
- !IsMemoryPadding(header, kDetourHeaderLen))
- return false;
-
- // Change memory protection to writable.
- DWORD protection = 0;
- if (!ChangeMemoryProtection(header, patch_length, &protection))
- return false;
-
- // Write a relative jump to the redirected function.
- WriteJumpInstruction(header, new_func);
-
- // Write the short jump to the function prefix.
- WriteShortJumpInstruction(old_func, header);
-
- // Restore previous memory protection.
- if (!RestoreMemoryProtection(header, patch_length, protection))
- return false;
-
- if (orig_old_func)
- *orig_old_func = old_func + kShortJumpInstructionLength;
-
- return true;
-}
-#endif
-
-bool OverrideFunctionWithRedirectJump(
- uptr old_func, uptr new_func, uptr *orig_old_func) {
- // Check whether the first instruction is a relative jump.
- if (*(u8*)old_func != 0xE9)
- return false;
-
- if (orig_old_func) {
- uptr relative_offset = *(u32*)(old_func + 1);
- uptr absolute_target = old_func + relative_offset + kJumpInstructionLength;
- *orig_old_func = absolute_target;
- }
-
-#if SANITIZER_WINDOWS64
- // If needed, get memory space for a trampoline jump.
- uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength);
- if (!trampoline)
- return false;
- WriteDirectBranch(trampoline, new_func);
-#endif
-
- // Change memory protection to writable.
- DWORD protection = 0;
- if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection))
- return false;
-
- // Write a relative jump to the redirected function.
- WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline));
-
- // Restore previous memory protection.
- if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection))
- return false;
-
- return true;
-}
-
-bool OverrideFunctionWithHotPatch(
- uptr old_func, uptr new_func, uptr *orig_old_func) {
- const int kHotPatchHeaderLen = kBranchLength;
-
- uptr header = (uptr)old_func - kHotPatchHeaderLen;
- uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength;
-
- // Validate that the function is hot patchable.
- size_t instruction_size = GetInstructionSize(old_func);
- if (instruction_size < kShortJumpInstructionLength ||
- !FunctionHasPadding(old_func, kHotPatchHeaderLen))
- return false;
-
- if (orig_old_func) {
- // Put the needed instructions into the trampoline bytes.
- uptr trampoline_length = instruction_size + kDirectBranchLength;
- uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
- if (!trampoline)
- return false;
- if (!CopyInstructions(trampoline, old_func, instruction_size))
- return false;
- WriteDirectBranch(trampoline + instruction_size,
- old_func + instruction_size);
- *orig_old_func = trampoline;
- }
-
- // If needed, get memory space for indirect address.
- uptr indirect_address = 0;
-#if SANITIZER_WINDOWS64
- indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
- if (!indirect_address)
- return false;
-#endif
-
- // Change memory protection to writable.
- DWORD protection = 0;
- if (!ChangeMemoryProtection(header, patch_length, &protection))
- return false;
-
- // Write jumps to the redirected function.
- WriteBranch(header, indirect_address, new_func);
- WriteShortJumpInstruction(old_func, header);
-
- // Restore previous memory protection.
- if (!RestoreMemoryProtection(header, patch_length, protection))
- return false;
-
- return true;
-}
-
-bool OverrideFunctionWithTrampoline(
- uptr old_func, uptr new_func, uptr *orig_old_func) {
-
- size_t instructions_length = kBranchLength;
- size_t padding_length = 0;
- uptr indirect_address = 0;
-
- if (orig_old_func) {
- // Find out the number of bytes of the instructions we need to copy
- // to the trampoline.
- instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func);
- if (!instructions_length)
- return false;
-
- // Put the needed instructions into the trampoline bytes.
- uptr trampoline_length = instructions_length + kDirectBranchLength;
- uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
- if (!trampoline)
- return false;
- if (!CopyInstructions(trampoline, old_func, instructions_length))
- return false;
- WriteDirectBranch(trampoline + instructions_length,
- old_func + instructions_length);
- *orig_old_func = trampoline;
- }
-
-#if SANITIZER_WINDOWS64
- // Check if the targeted address can be encoded in the function padding.
- // Otherwise, allocate it in the trampoline region.
- if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) {
- indirect_address = old_func - kAddressLength;
- padding_length = kAddressLength;
- } else {
- indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
- if (!indirect_address)
- return false;
- }
-#endif
-
- // Change memory protection to writable.
- uptr patch_address = old_func - padding_length;
- uptr patch_length = instructions_length + padding_length;
- DWORD protection = 0;
- if (!ChangeMemoryProtection(patch_address, patch_length, &protection))
- return false;
-
- // Patch the original function.
- WriteBranch(old_func, indirect_address, new_func);
-
- // Restore previous memory protection.
- if (!RestoreMemoryProtection(patch_address, patch_length, protection))
- return false;
-
- return true;
-}
-
-bool OverrideFunction(
- uptr old_func, uptr new_func, uptr *orig_old_func) {
-#if !SANITIZER_WINDOWS64
- if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func))
- return true;
-#endif
- if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func))
- return true;
- if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func))
- return true;
- if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func))
- return true;
- return false;
-}
-
-static void **InterestingDLLsAvailable() {
- static const char *InterestingDLLs[] = {
- "kernel32.dll",
- "msvcr100.dll", // VS2010
- "msvcr110.dll", // VS2012
- "msvcr120.dll", // VS2013
- "vcruntime140.dll", // VS2015
- "ucrtbase.dll", // Universal CRT
- // NTDLL should go last as it exports some functions that we should
- // override in the CRT [presumably only used internally].
- "ntdll.dll", NULL};
- static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
- if (!result[0]) {
- for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
- if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
- result[j++] = (void *)h;
- }
- }
- return &result[0];
-}
-
-namespace {
-// Utility for reading loaded PE images.
-template <typename T> class RVAPtr {
- public:
- RVAPtr(void *module, uptr rva)
- : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {}
- operator T *() { return ptr_; }
- T *operator->() { return ptr_; }
- T *operator++() { return ++ptr_; }
-
- private:
- T *ptr_;
-};
-} // namespace
-
-// Internal implementation of GetProcAddress. At least since Windows 8,
-// GetProcAddress appears to initialize DLLs before returning function pointers
-// into them. This is problematic for the sanitizers, because they typically
-// want to intercept malloc *before* MSVCRT initializes. Our internal
-// implementation walks the export list manually without doing initialization.
-uptr InternalGetProcAddress(void *module, const char *func_name) {
- // Check that the module header is full and present.
- RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
- RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
- if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
- headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
- headers->FileHeader.SizeOfOptionalHeader <
- sizeof(IMAGE_OPTIONAL_HEADER)) {
- return 0;
- }
-
- IMAGE_DATA_DIRECTORY *export_directory =
- &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
- if (export_directory->Size == 0)
- return 0;
- RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module,
- export_directory->VirtualAddress);
- RVAPtr<DWORD> functions(module, exports->AddressOfFunctions);
- RVAPtr<DWORD> names(module, exports->AddressOfNames);
- RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals);
-
- for (DWORD i = 0; i < exports->NumberOfNames; i++) {
- RVAPtr<char> name(module, names[i]);
- if (!strcmp(func_name, name)) {
- DWORD index = ordinals[i];
- RVAPtr<char> func(module, functions[index]);
-
- // Handle forwarded functions.
- DWORD offset = functions[index];
- if (offset >= export_directory->VirtualAddress &&
- offset < export_directory->VirtualAddress + export_directory->Size) {
- // An entry for a forwarded function is a string with the following
- // format: "<module> . <function_name>" that is stored into the
- // exported directory.
- char function_name[256];
- size_t funtion_name_length = _strlen(func);
- if (funtion_name_length >= sizeof(function_name) - 1)
- InterceptionFailed();
-
- _memcpy(function_name, func, funtion_name_length);
- function_name[funtion_name_length] = '\0';
- char* separator = _strchr(function_name, '.');
- if (!separator)
- InterceptionFailed();
- *separator = '\0';
-
- void* redirected_module = GetModuleHandleA(function_name);
- if (!redirected_module)
- InterceptionFailed();
- return InternalGetProcAddress(redirected_module, separator + 1);
- }
-
- return (uptr)(char *)func;
- }
- }
-
- return 0;
-}
-
-bool OverrideFunction(
- const char *func_name, uptr new_func, uptr *orig_old_func) {
- bool hooked = false;
- void **DLLs = InterestingDLLsAvailable();
- for (size_t i = 0; DLLs[i]; ++i) {
- uptr func_addr = InternalGetProcAddress(DLLs[i], func_name);
- if (func_addr &&
- OverrideFunction(func_addr, new_func, orig_old_func)) {
- hooked = true;
- }
- }
- return hooked;
-}
-
-bool OverrideImportedFunction(const char *module_to_patch,
- const char *imported_module,
- const char *function_name, uptr new_function,
- uptr *orig_old_func) {
- HMODULE module = GetModuleHandleA(module_to_patch);
- if (!module)
- return false;
-
- // Check that the module header is full and present.
- RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
- RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
- if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
- headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
- headers->FileHeader.SizeOfOptionalHeader <
- sizeof(IMAGE_OPTIONAL_HEADER)) {
- return false;
- }
-
- IMAGE_DATA_DIRECTORY *import_directory =
- &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
-
- // Iterate the list of imported DLLs. FirstThunk will be null for the last
- // entry.
- RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module,
- import_directory->VirtualAddress);
- for (; imports->FirstThunk != 0; ++imports) {
- RVAPtr<const char> modname(module, imports->Name);
- if (_stricmp(&*modname, imported_module) == 0)
- break;
- }
- if (imports->FirstThunk == 0)
- return false;
-
- // We have two parallel arrays: the import address table (IAT) and the table
- // of names. They start out containing the same data, but the loader rewrites
- // the IAT to hold imported addresses and leaves the name table in
- // OriginalFirstThunk alone.
- RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk);
- RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk);
- for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) {
- if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
- RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name(
- module, name_table->u1.ForwarderString);
- const char *funcname = &import_by_name->Name[0];
- if (strcmp(funcname, function_name) == 0)
- break;
- }
- }
- if (name_table->u1.Ordinal == 0)
- return false;
-
- // Now we have the correct IAT entry. Do the swap. We have to make the page
- // read/write first.
- if (orig_old_func)
- *orig_old_func = iat->u1.AddressOfData;
- DWORD old_prot, unused_prot;
- if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE,
- &old_prot))
- return false;
- iat->u1.AddressOfData = new_function;
- if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot))
- return false; // Not clear if this failure bothers us.
- return true;
-}
-
-} // namespace __interception
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- interception_linux.cpp ----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// Windows-specific interception methods.
+//
+// This file is implementing several hooking techniques to intercept calls
+// to functions. The hooks are dynamically installed by modifying the assembly
+// code.
+//
+// The hooking techniques are making assumptions on the way the code is
+// generated and are safe under these assumptions.
+//
+// On 64-bit architecture, there is no direct 64-bit jump instruction. To allow
+// arbitrary branching on the whole memory space, the notion of trampoline
+// region is used. A trampoline region is a memory space withing 2G boundary
+// where it is safe to add custom assembly code to build 64-bit jumps.
+//
+// Hooking techniques
+// ==================
+//
+// 1) Detour
+//
+// The Detour hooking technique is assuming the presence of an header with
+// padding and an overridable 2-bytes nop instruction (mov edi, edi). The
+// nop instruction can safely be replaced by a 2-bytes jump without any need
+// to save the instruction. A jump to the target is encoded in the function
+// header and the nop instruction is replaced by a short jump to the header.
+//
+// head: 5 x nop head: jmp <hook>
+// func: mov edi, edi --> func: jmp short <head>
+// [...] real: [...]
+//
+// This technique is only implemented on 32-bit architecture.
+// Most of the time, Windows API are hookable with the detour technique.
+//
+// 2) Redirect Jump
+//
+// The redirect jump is applicable when the first instruction is a direct
+// jump. The instruction is replaced by jump to the hook.
+//
+// func: jmp <label> --> func: jmp <hook>
+//
+// On an 64-bit architecture, a trampoline is inserted.
+//
+// func: jmp <label> --> func: jmp <tramp>
+// [...]
+//
+// [trampoline]
+// tramp: jmp QWORD [addr]
+// addr: .bytes <hook>
+//
+// Note: <real> is equilavent to <label>.
+//
+// 3) HotPatch
+//
+// The HotPatch hooking is assuming the presence of an header with padding
+// and a first instruction with at least 2-bytes.
+//
+// The reason to enforce the 2-bytes limitation is to provide the minimal
+// space to encode a short jump. HotPatch technique is only rewriting one
+// instruction to avoid breaking a sequence of instructions containing a
+// branching target.
+//
+// Assumptions are enforced by MSVC compiler by using the /HOTPATCH flag.
+// see: https://msdn.microsoft.com/en-us/library/ms173507.aspx
+// Default padding length is 5 bytes in 32-bits and 6 bytes in 64-bits.
+//
+// head: 5 x nop head: jmp <hook>
+// func: <instr> --> func: jmp short <head>
+// [...] body: [...]
+//
+// [trampoline]
+// real: <instr>
+// jmp <body>
+//
+// On an 64-bit architecture:
+//
+// head: 6 x nop head: jmp QWORD [addr1]
+// func: <instr> --> func: jmp short <head>
+// [...] body: [...]
+//
+// [trampoline]
+// addr1: .bytes <hook>
+// real: <instr>
+// jmp QWORD [addr2]
+// addr2: .bytes <body>
+//
+// 4) Trampoline
+//
+// The Trampoline hooking technique is the most aggressive one. It is
+// assuming that there is a sequence of instructions that can be safely
+// replaced by a jump (enough room and no incoming branches).
+//
+// Unfortunately, these assumptions can't be safely presumed and code may
+// be broken after hooking.
+//
+// func: <instr> --> func: jmp <hook>
+// <instr>
+// [...] body: [...]
+//
+// [trampoline]
+// real: <instr>
+// <instr>
+// jmp <body>
+//
+// On an 64-bit architecture:
+//
+// func: <instr> --> func: jmp QWORD [addr1]
+// <instr>
+// [...] body: [...]
+//
+// [trampoline]
+// addr1: .bytes <hook>
+// real: <instr>
+// <instr>
+// jmp QWORD [addr2]
+// addr2: .bytes <body>
+//===----------------------------------------------------------------------===//
+
+#include "interception.h"
+
+#if SANITIZER_WINDOWS
+#include "sanitizer_common/sanitizer_platform.h"
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+namespace __interception {
+
+static const int kAddressLength = FIRST_32_SECOND_64(4, 8);
+static const int kJumpInstructionLength = 5;
+static const int kShortJumpInstructionLength = 2;
+static const int kIndirectJumpInstructionLength = 6;
+static const int kBranchLength =
+ FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength);
+static const int kDirectBranchLength = kBranchLength + kAddressLength;
+
+static void InterceptionFailed() {
+ // Do we have a good way to abort with an error message here?
+ __debugbreak();
+}
+
+static bool DistanceIsWithin2Gig(uptr from, uptr target) {
+#if SANITIZER_WINDOWS64
+ if (from < target)
+ return target - from <= (uptr)0x7FFFFFFFU;
+ else
+ return from - target <= (uptr)0x80000000U;
+#else
+ // In a 32-bit address space, the address calculation will wrap, so this check
+ // is unnecessary.
+ return true;
+#endif
+}
+
+static uptr GetMmapGranularity() {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwAllocationGranularity;
+}
+
+static uptr RoundUpTo(uptr size, uptr boundary) {
+ return (size + boundary - 1) & ~(boundary - 1);
+}
+
+// FIXME: internal_str* and internal_mem* functions should be moved from the
+// ASan sources into interception/.
+
+static size_t _strlen(const char *str) {
+ const char* p = str;
+ while (*p != '\0') ++p;
+ return p - str;
+}
+
+static char* _strchr(char* str, char c) {
+ while (*str) {
+ if (*str == c)
+ return str;
+ ++str;
+ }
+ return nullptr;
+}
+
+static void _memset(void *p, int value, size_t sz) {
+ for (size_t i = 0; i < sz; ++i)
+ ((char*)p)[i] = (char)value;
+}
+
+static void _memcpy(void *dst, void *src, size_t sz) {
+ char *dst_c = (char*)dst,
+ *src_c = (char*)src;
+ for (size_t i = 0; i < sz; ++i)
+ dst_c[i] = src_c[i];
+}
+
+static bool ChangeMemoryProtection(
+ uptr address, uptr size, DWORD *old_protection) {
+ return ::VirtualProtect((void*)address, size,
+ PAGE_EXECUTE_READWRITE,
+ old_protection) != FALSE;
+}
+
+static bool RestoreMemoryProtection(
+ uptr address, uptr size, DWORD old_protection) {
+ DWORD unused;
+ return ::VirtualProtect((void*)address, size,
+ old_protection,
+ &unused) != FALSE;
+}
+
+static bool IsMemoryPadding(uptr address, uptr size) {
+ u8* function = (u8*)address;
+ for (size_t i = 0; i < size; ++i)
+ if (function[i] != 0x90 && function[i] != 0xCC)
+ return false;
+ return true;
+}
+
+static const u8 kHintNop8Bytes[] = {
+ 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+template<class T>
+static bool FunctionHasPrefix(uptr address, const T &pattern) {
+ u8* function = (u8*)address - sizeof(pattern);
+ for (size_t i = 0; i < sizeof(pattern); ++i)
+ if (function[i] != pattern[i])
+ return false;
+ return true;
+}
+
+static bool FunctionHasPadding(uptr address, uptr size) {
+ if (IsMemoryPadding(address - size, size))
+ return true;
+ if (size <= sizeof(kHintNop8Bytes) &&
+ FunctionHasPrefix(address, kHintNop8Bytes))
+ return true;
+ return false;
+}
+
+static void WritePadding(uptr from, uptr size) {
+ _memset((void*)from, 0xCC, (size_t)size);
+}
+
+static void WriteJumpInstruction(uptr from, uptr target) {
+ if (!DistanceIsWithin2Gig(from + kJumpInstructionLength, target))
+ InterceptionFailed();
+ ptrdiff_t offset = target - from - kJumpInstructionLength;
+ *(u8*)from = 0xE9;
+ *(u32*)(from + 1) = offset;
+}
+
+static void WriteShortJumpInstruction(uptr from, uptr target) {
+ sptr offset = target - from - kShortJumpInstructionLength;
+ if (offset < -128 || offset > 127)
+ InterceptionFailed();
+ *(u8*)from = 0xEB;
+ *(u8*)(from + 1) = (u8)offset;
+}
+
+#if SANITIZER_WINDOWS64
+static void WriteIndirectJumpInstruction(uptr from, uptr indirect_target) {
+ // jmp [rip + <offset>] = FF 25 <offset> where <offset> is a relative
+ // offset.
+ // The offset is the distance from then end of the jump instruction to the
+ // memory location containing the targeted address. The displacement is still
+ // 32-bit in x64, so indirect_target must be located within +/- 2GB range.
+ int offset = indirect_target - from - kIndirectJumpInstructionLength;
+ if (!DistanceIsWithin2Gig(from + kIndirectJumpInstructionLength,
+ indirect_target)) {
+ InterceptionFailed();
+ }
+ *(u16*)from = 0x25FF;
+ *(u32*)(from + 2) = offset;
+}
+#endif
+
+static void WriteBranch(
+ uptr from, uptr indirect_target, uptr target) {
+#if SANITIZER_WINDOWS64
+ WriteIndirectJumpInstruction(from, indirect_target);
+ *(u64*)indirect_target = target;
+#else
+ (void)indirect_target;
+ WriteJumpInstruction(from, target);
+#endif
+}
+
+static void WriteDirectBranch(uptr from, uptr target) {
+#if SANITIZER_WINDOWS64
+ // Emit an indirect jump through immediately following bytes:
+ // jmp [rip + kBranchLength]
+ // .quad <target>
+ WriteBranch(from, from + kBranchLength, target);
+#else
+ WriteJumpInstruction(from, target);
+#endif
+}
+
+struct TrampolineMemoryRegion {
+ uptr content;
+ uptr allocated_size;
+ uptr max_size;
+};
+
+static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig
+static const int kMaxTrampolineRegion = 1024;
+static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion];
+
+static void *AllocateTrampolineRegion(uptr image_address, size_t granularity) {
+#if SANITIZER_WINDOWS64
+ uptr address = image_address;
+ uptr scanned = 0;
+ while (scanned < kTrampolineScanLimitRange) {
+ MEMORY_BASIC_INFORMATION info;
+ if (!::VirtualQuery((void*)address, &info, sizeof(info)))
+ return nullptr;
+
+ // Check whether a region can be allocated at |address|.
+ if (info.State == MEM_FREE && info.RegionSize >= granularity) {
+ void *page = ::VirtualAlloc((void*)RoundUpTo(address, granularity),
+ granularity,
+ MEM_RESERVE | MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE);
+ return page;
+ }
+
+ // Move to the next region.
+ address = (uptr)info.BaseAddress + info.RegionSize;
+ scanned += info.RegionSize;
+ }
+ return nullptr;
+#else
+ return ::VirtualAlloc(nullptr,
+ granularity,
+ MEM_RESERVE | MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE);
+#endif
+}
+
+// Used by unittests to release mapped memory space.
+void TestOnlyReleaseTrampolineRegions() {
+ for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
+ TrampolineMemoryRegion *current = &TrampolineRegions[bucket];
+ if (current->content == 0)
+ return;
+ ::VirtualFree((void*)current->content, 0, MEM_RELEASE);
+ current->content = 0;
+ }
+}
+
+static uptr AllocateMemoryForTrampoline(uptr image_address, size_t size) {
+ // Find a region within 2G with enough space to allocate |size| bytes.
+ TrampolineMemoryRegion *region = nullptr;
+ for (size_t bucket = 0; bucket < kMaxTrampolineRegion; ++bucket) {
+ TrampolineMemoryRegion* current = &TrampolineRegions[bucket];
+ if (current->content == 0) {
+ // No valid region found, allocate a new region.
+ size_t bucket_size = GetMmapGranularity();
+ void *content = AllocateTrampolineRegion(image_address, bucket_size);
+ if (content == nullptr)
+ return 0U;
+
+ current->content = (uptr)content;
+ current->allocated_size = 0;
+ current->max_size = bucket_size;
+ region = current;
+ break;
+ } else if (current->max_size - current->allocated_size > size) {
+#if SANITIZER_WINDOWS64
+ // In 64-bits, the memory space must be allocated within 2G boundary.
+ uptr next_address = current->content + current->allocated_size;
+ if (next_address < image_address ||
+ next_address - image_address >= 0x7FFF0000)
+ continue;
+#endif
+ // The space can be allocated in the current region.
+ region = current;
+ break;
+ }
+ }
+
+ // Failed to find a region.
+ if (region == nullptr)
+ return 0U;
+
+ // Allocate the space in the current region.
+ uptr allocated_space = region->content + region->allocated_size;
+ region->allocated_size += size;
+ WritePadding(allocated_space, size);
+
+ return allocated_space;
+}
+
+// Returns 0 on error.
+static size_t GetInstructionSize(uptr address, size_t* rel_offset = nullptr) {
+ switch (*(u64*)address) {
+ case 0x90909090909006EB: // stub: jmp over 6 x nop.
+ return 8;
+ }
+
+ switch (*(u8*)address) {
+ case 0x90: // 90 : nop
+ return 1;
+
+ case 0x50: // push eax / rax
+ case 0x51: // push ecx / rcx
+ case 0x52: // push edx / rdx
+ case 0x53: // push ebx / rbx
+ case 0x54: // push esp / rsp
+ case 0x55: // push ebp / rbp
+ case 0x56: // push esi / rsi
+ case 0x57: // push edi / rdi
+ case 0x5D: // pop ebp / rbp
+ return 1;
+
+ case 0x6A: // 6A XX = push XX
+ return 2;
+
+ case 0xb8: // b8 XX XX XX XX : mov eax, XX XX XX XX
+ case 0xB9: // b9 XX XX XX XX : mov ecx, XX XX XX XX
+ return 5;
+
+ // Cannot overwrite control-instruction. Return 0 to indicate failure.
+ case 0xE9: // E9 XX XX XX XX : jmp <label>
+ case 0xE8: // E8 XX XX XX XX : call <func>
+ case 0xC3: // C3 : ret
+ case 0xEB: // EB XX : jmp XX (short jump)
+ case 0x70: // 7Y YY : jy XX (short conditional jump)
+ case 0x71:
+ case 0x72:
+ case 0x73:
+ case 0x74:
+ case 0x75:
+ case 0x76:
+ case 0x77:
+ case 0x78:
+ case 0x79:
+ case 0x7A:
+ case 0x7B:
+ case 0x7C:
+ case 0x7D:
+ case 0x7E:
+ case 0x7F:
+ return 0;
+ }
+
+ switch (*(u16*)(address)) {
+ case 0x018A: // 8A 01 : mov al, byte ptr [ecx]
+ case 0xFF8B: // 8B FF : mov edi, edi
+ case 0xEC8B: // 8B EC : mov ebp, esp
+ case 0xc889: // 89 C8 : mov eax, ecx
+ case 0xC18B: // 8B C1 : mov eax, ecx
+ case 0xC033: // 33 C0 : xor eax, eax
+ case 0xC933: // 33 C9 : xor ecx, ecx
+ case 0xD233: // 33 D2 : xor edx, edx
+ return 2;
+
+ // Cannot overwrite control-instruction. Return 0 to indicate failure.
+ case 0x25FF: // FF 25 XX XX XX XX : jmp [XXXXXXXX]
+ return 0;
+ }
+
+ switch (0x00FFFFFF & *(u32*)address) {
+ case 0x24A48D: // 8D A4 24 XX XX XX XX : lea esp, [esp + XX XX XX XX]
+ return 7;
+ }
+
+#if SANITIZER_WINDOWS64
+ switch (*(u8*)address) {
+ case 0xA1: // A1 XX XX XX XX XX XX XX XX :
+ // movabs eax, dword ptr ds:[XXXXXXXX]
+ return 9;
+ }
+
+ switch (*(u16*)address) {
+ case 0x5040: // push rax
+ case 0x5140: // push rcx
+ case 0x5240: // push rdx
+ case 0x5340: // push rbx
+ case 0x5440: // push rsp
+ case 0x5540: // push rbp
+ case 0x5640: // push rsi
+ case 0x5740: // push rdi
+ case 0x5441: // push r12
+ case 0x5541: // push r13
+ case 0x5641: // push r14
+ case 0x5741: // push r15
+ case 0x9066: // Two-byte NOP
+ return 2;
+
+ case 0x058B: // 8B 05 XX XX XX XX : mov eax, dword ptr [XX XX XX XX]
+ if (rel_offset)
+ *rel_offset = 2;
+ return 6;
+ }
+
+ switch (0x00FFFFFF & *(u32*)address) {
+ case 0xe58948: // 48 8b c4 : mov rbp, rsp
+ case 0xc18b48: // 48 8b c1 : mov rax, rcx
+ case 0xc48b48: // 48 8b c4 : mov rax, rsp
+ case 0xd9f748: // 48 f7 d9 : neg rcx
+ case 0xd12b48: // 48 2b d1 : sub rdx, rcx
+ case 0x07c1f6: // f6 c1 07 : test cl, 0x7
+ case 0xc98548: // 48 85 C9 : test rcx, rcx
+ case 0xc0854d: // 4d 85 c0 : test r8, r8
+ case 0xc2b60f: // 0f b6 c2 : movzx eax, dl
+ case 0xc03345: // 45 33 c0 : xor r8d, r8d
+ case 0xc93345: // 45 33 c9 : xor r9d, r9d
+ case 0xdb3345: // 45 33 DB : xor r11d, r11d
+ case 0xd98b4c: // 4c 8b d9 : mov r11, rcx
+ case 0xd28b4c: // 4c 8b d2 : mov r10, rdx
+ case 0xc98b4c: // 4C 8B C9 : mov r9, rcx
+ case 0xc18b4c: // 4C 8B C1 : mov r8, rcx
+ case 0xd2b60f: // 0f b6 d2 : movzx edx, dl
+ case 0xca2b48: // 48 2b ca : sub rcx, rdx
+ case 0x10b70f: // 0f b7 10 : movzx edx, WORD PTR [rax]
+ case 0xc00b4d: // 3d 0b c0 : or r8, r8
+ case 0xd18b48: // 48 8b d1 : mov rdx, rcx
+ case 0xdc8b4c: // 4c 8b dc : mov r11, rsp
+ case 0xd18b4c: // 4c 8b d1 : mov r10, rcx
+ case 0xE0E483: // 83 E4 E0 : and esp, 0xFFFFFFE0
+ return 3;
+
+ case 0xec8348: // 48 83 ec XX : sub rsp, XX
+ case 0xf88349: // 49 83 f8 XX : cmp r8, XX
+ case 0x588948: // 48 89 58 XX : mov QWORD PTR[rax + XX], rbx
+ return 4;
+
+ case 0xec8148: // 48 81 EC XX XX XX XX : sub rsp, XXXXXXXX
+ return 7;
+
+ case 0x058b48: // 48 8b 05 XX XX XX XX :
+ // mov rax, QWORD PTR [rip + XXXXXXXX]
+ case 0x25ff48: // 48 ff 25 XX XX XX XX :
+ // rex.W jmp QWORD PTR [rip + XXXXXXXX]
+
+ // Instructions having offset relative to 'rip' need offset adjustment.
+ if (rel_offset)
+ *rel_offset = 3;
+ return 7;
+
+ case 0x2444c7: // C7 44 24 XX YY YY YY YY
+ // mov dword ptr [rsp + XX], YYYYYYYY
+ return 8;
+ }
+
+ switch (*(u32*)(address)) {
+ case 0x24448b48: // 48 8b 44 24 XX : mov rax, QWORD ptr [rsp + XX]
+ case 0x246c8948: // 48 89 6C 24 XX : mov QWORD ptr [rsp + XX], rbp
+ case 0x245c8948: // 48 89 5c 24 XX : mov QWORD PTR [rsp + XX], rbx
+ case 0x24748948: // 48 89 74 24 XX : mov QWORD PTR [rsp + XX], rsi
+ case 0x244C8948: // 48 89 4C 24 XX : mov QWORD PTR [rsp + XX], rcx
+ case 0x24548948: // 48 89 54 24 XX : mov QWORD PTR [rsp + XX], rdx
+ case 0x244c894c: // 4c 89 4c 24 XX : mov QWORD PTR [rsp + XX], r9
+ case 0x2444894c: // 4c 89 44 24 XX : mov QWORD PTR [rsp + XX], r8
+ return 5;
+ case 0x24648348: // 48 83 64 24 XX : and QWORD PTR [rsp + XX], YY
+ return 6;
+ }
+
+#else
+
+ switch (*(u8*)address) {
+ case 0xA1: // A1 XX XX XX XX : mov eax, dword ptr ds:[XXXXXXXX]
+ return 5;
+ }
+ switch (*(u16*)address) {
+ case 0x458B: // 8B 45 XX : mov eax, dword ptr [ebp + XX]
+ case 0x5D8B: // 8B 5D XX : mov ebx, dword ptr [ebp + XX]
+ case 0x7D8B: // 8B 7D XX : mov edi, dword ptr [ebp + XX]
+ case 0xEC83: // 83 EC XX : sub esp, XX
+ case 0x75FF: // FF 75 XX : push dword ptr [ebp + XX]
+ return 3;
+ case 0xC1F7: // F7 C1 XX YY ZZ WW : test ecx, WWZZYYXX
+ case 0x25FF: // FF 25 XX YY ZZ WW : jmp dword ptr ds:[WWZZYYXX]
+ return 6;
+ case 0x3D83: // 83 3D XX YY ZZ WW TT : cmp TT, WWZZYYXX
+ return 7;
+ case 0x7D83: // 83 7D XX YY : cmp dword ptr [ebp + XX], YY
+ return 4;
+ }
+
+ switch (0x00FFFFFF & *(u32*)address) {
+ case 0x24448A: // 8A 44 24 XX : mov eal, dword ptr [esp + XX]
+ case 0x24448B: // 8B 44 24 XX : mov eax, dword ptr [esp + XX]
+ case 0x244C8B: // 8B 4C 24 XX : mov ecx, dword ptr [esp + XX]
+ case 0x24548B: // 8B 54 24 XX : mov edx, dword ptr [esp + XX]
+ case 0x24748B: // 8B 74 24 XX : mov esi, dword ptr [esp + XX]
+ case 0x247C8B: // 8B 7C 24 XX : mov edi, dword ptr [esp + XX]
+ return 4;
+ }
+
+ switch (*(u32*)address) {
+ case 0x2444B60F: // 0F B6 44 24 XX : movzx eax, byte ptr [esp + XX]
+ return 5;
+ }
+#endif
+
+ // Unknown instruction!
+ // FIXME: Unknown instruction failures might happen when we add a new
+ // interceptor or a new compiler version. In either case, they should result
+ // in visible and readable error messages. However, merely calling abort()
+ // leads to an infinite recursion in CheckFailed.
+ InterceptionFailed();
+ return 0;
+}
+
+// Returns 0 on error.
+static size_t RoundUpToInstrBoundary(size_t size, uptr address) {
+ size_t cursor = 0;
+ while (cursor < size) {
+ size_t instruction_size = GetInstructionSize(address + cursor);
+ if (!instruction_size)
+ return 0;
+ cursor += instruction_size;
+ }
+ return cursor;
+}
+
+static bool CopyInstructions(uptr to, uptr from, size_t size) {
+ size_t cursor = 0;
+ while (cursor != size) {
+ size_t rel_offset = 0;
+ size_t instruction_size = GetInstructionSize(from + cursor, &rel_offset);
+ _memcpy((void*)(to + cursor), (void*)(from + cursor),
+ (size_t)instruction_size);
+ if (rel_offset) {
+ uptr delta = to - from;
+ uptr relocated_offset = *(u32*)(to + cursor + rel_offset) - delta;
+#if SANITIZER_WINDOWS64
+ if (relocated_offset + 0x80000000U >= 0xFFFFFFFFU)
+ return false;
+#endif
+ *(u32*)(to + cursor + rel_offset) = relocated_offset;
+ }
+ cursor += instruction_size;
+ }
+ return true;
+}
+
+
+#if !SANITIZER_WINDOWS64
+bool OverrideFunctionWithDetour(
+ uptr old_func, uptr new_func, uptr *orig_old_func) {
+ const int kDetourHeaderLen = 5;
+ const u16 kDetourInstruction = 0xFF8B;
+
+ uptr header = (uptr)old_func - kDetourHeaderLen;
+ uptr patch_length = kDetourHeaderLen + kShortJumpInstructionLength;
+
+ // Validate that the function is hookable.
+ if (*(u16*)old_func != kDetourInstruction ||
+ !IsMemoryPadding(header, kDetourHeaderLen))
+ return false;
+
+ // Change memory protection to writable.
+ DWORD protection = 0;
+ if (!ChangeMemoryProtection(header, patch_length, &protection))
+ return false;
+
+ // Write a relative jump to the redirected function.
+ WriteJumpInstruction(header, new_func);
+
+ // Write the short jump to the function prefix.
+ WriteShortJumpInstruction(old_func, header);
+
+ // Restore previous memory protection.
+ if (!RestoreMemoryProtection(header, patch_length, protection))
+ return false;
+
+ if (orig_old_func)
+ *orig_old_func = old_func + kShortJumpInstructionLength;
+
+ return true;
+}
+#endif
+
+bool OverrideFunctionWithRedirectJump(
+ uptr old_func, uptr new_func, uptr *orig_old_func) {
+ // Check whether the first instruction is a relative jump.
+ if (*(u8*)old_func != 0xE9)
+ return false;
+
+ if (orig_old_func) {
+ uptr relative_offset = *(u32*)(old_func + 1);
+ uptr absolute_target = old_func + relative_offset + kJumpInstructionLength;
+ *orig_old_func = absolute_target;
+ }
+
+#if SANITIZER_WINDOWS64
+ // If needed, get memory space for a trampoline jump.
+ uptr trampoline = AllocateMemoryForTrampoline(old_func, kDirectBranchLength);
+ if (!trampoline)
+ return false;
+ WriteDirectBranch(trampoline, new_func);
+#endif
+
+ // Change memory protection to writable.
+ DWORD protection = 0;
+ if (!ChangeMemoryProtection(old_func, kJumpInstructionLength, &protection))
+ return false;
+
+ // Write a relative jump to the redirected function.
+ WriteJumpInstruction(old_func, FIRST_32_SECOND_64(new_func, trampoline));
+
+ // Restore previous memory protection.
+ if (!RestoreMemoryProtection(old_func, kJumpInstructionLength, protection))
+ return false;
+
+ return true;
+}
+
+bool OverrideFunctionWithHotPatch(
+ uptr old_func, uptr new_func, uptr *orig_old_func) {
+ const int kHotPatchHeaderLen = kBranchLength;
+
+ uptr header = (uptr)old_func - kHotPatchHeaderLen;
+ uptr patch_length = kHotPatchHeaderLen + kShortJumpInstructionLength;
+
+ // Validate that the function is hot patchable.
+ size_t instruction_size = GetInstructionSize(old_func);
+ if (instruction_size < kShortJumpInstructionLength ||
+ !FunctionHasPadding(old_func, kHotPatchHeaderLen))
+ return false;
+
+ if (orig_old_func) {
+ // Put the needed instructions into the trampoline bytes.
+ uptr trampoline_length = instruction_size + kDirectBranchLength;
+ uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
+ if (!trampoline)
+ return false;
+ if (!CopyInstructions(trampoline, old_func, instruction_size))
+ return false;
+ WriteDirectBranch(trampoline + instruction_size,
+ old_func + instruction_size);
+ *orig_old_func = trampoline;
+ }
+
+ // If needed, get memory space for indirect address.
+ uptr indirect_address = 0;
+#if SANITIZER_WINDOWS64
+ indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
+ if (!indirect_address)
+ return false;
+#endif
+
+ // Change memory protection to writable.
+ DWORD protection = 0;
+ if (!ChangeMemoryProtection(header, patch_length, &protection))
+ return false;
+
+ // Write jumps to the redirected function.
+ WriteBranch(header, indirect_address, new_func);
+ WriteShortJumpInstruction(old_func, header);
+
+ // Restore previous memory protection.
+ if (!RestoreMemoryProtection(header, patch_length, protection))
+ return false;
+
+ return true;
+}
+
+bool OverrideFunctionWithTrampoline(
+ uptr old_func, uptr new_func, uptr *orig_old_func) {
+
+ size_t instructions_length = kBranchLength;
+ size_t padding_length = 0;
+ uptr indirect_address = 0;
+
+ if (orig_old_func) {
+ // Find out the number of bytes of the instructions we need to copy
+ // to the trampoline.
+ instructions_length = RoundUpToInstrBoundary(kBranchLength, old_func);
+ if (!instructions_length)
+ return false;
+
+ // Put the needed instructions into the trampoline bytes.
+ uptr trampoline_length = instructions_length + kDirectBranchLength;
+ uptr trampoline = AllocateMemoryForTrampoline(old_func, trampoline_length);
+ if (!trampoline)
+ return false;
+ if (!CopyInstructions(trampoline, old_func, instructions_length))
+ return false;
+ WriteDirectBranch(trampoline + instructions_length,
+ old_func + instructions_length);
+ *orig_old_func = trampoline;
+ }
+
+#if SANITIZER_WINDOWS64
+ // Check if the targeted address can be encoded in the function padding.
+ // Otherwise, allocate it in the trampoline region.
+ if (IsMemoryPadding(old_func - kAddressLength, kAddressLength)) {
+ indirect_address = old_func - kAddressLength;
+ padding_length = kAddressLength;
+ } else {
+ indirect_address = AllocateMemoryForTrampoline(old_func, kAddressLength);
+ if (!indirect_address)
+ return false;
+ }
+#endif
+
+ // Change memory protection to writable.
+ uptr patch_address = old_func - padding_length;
+ uptr patch_length = instructions_length + padding_length;
+ DWORD protection = 0;
+ if (!ChangeMemoryProtection(patch_address, patch_length, &protection))
+ return false;
+
+ // Patch the original function.
+ WriteBranch(old_func, indirect_address, new_func);
+
+ // Restore previous memory protection.
+ if (!RestoreMemoryProtection(patch_address, patch_length, protection))
+ return false;
+
+ return true;
+}
+
+bool OverrideFunction(
+ uptr old_func, uptr new_func, uptr *orig_old_func) {
+#if !SANITIZER_WINDOWS64
+ if (OverrideFunctionWithDetour(old_func, new_func, orig_old_func))
+ return true;
+#endif
+ if (OverrideFunctionWithRedirectJump(old_func, new_func, orig_old_func))
+ return true;
+ if (OverrideFunctionWithHotPatch(old_func, new_func, orig_old_func))
+ return true;
+ if (OverrideFunctionWithTrampoline(old_func, new_func, orig_old_func))
+ return true;
+ return false;
+}
+
+static void **InterestingDLLsAvailable() {
+ static const char *InterestingDLLs[] = {
+ "kernel32.dll",
+ "msvcr100.dll", // VS2010
+ "msvcr110.dll", // VS2012
+ "msvcr120.dll", // VS2013
+ "vcruntime140.dll", // VS2015
+ "ucrtbase.dll", // Universal CRT
+ // NTDLL should go last as it exports some functions that we should
+ // override in the CRT [presumably only used internally].
+ "ntdll.dll", NULL};
+ static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
+ if (!result[0]) {
+ for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {
+ if (HMODULE h = GetModuleHandleA(InterestingDLLs[i]))
+ result[j++] = (void *)h;
+ }
+ }
+ return &result[0];
+}
+
+namespace {
+// Utility for reading loaded PE images.
+template <typename T> class RVAPtr {
+ public:
+ RVAPtr(void *module, uptr rva)
+ : ptr_(reinterpret_cast<T *>(reinterpret_cast<char *>(module) + rva)) {}
+ operator T *() { return ptr_; }
+ T *operator->() { return ptr_; }
+ T *operator++() { return ++ptr_; }
+
+ private:
+ T *ptr_;
+};
+} // namespace
+
+// Internal implementation of GetProcAddress. At least since Windows 8,
+// GetProcAddress appears to initialize DLLs before returning function pointers
+// into them. This is problematic for the sanitizers, because they typically
+// want to intercept malloc *before* MSVCRT initializes. Our internal
+// implementation walks the export list manually without doing initialization.
+uptr InternalGetProcAddress(void *module, const char *func_name) {
+ // Check that the module header is full and present.
+ RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
+ RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
+ if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
+ headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
+ headers->FileHeader.SizeOfOptionalHeader <
+ sizeof(IMAGE_OPTIONAL_HEADER)) {
+ return 0;
+ }
+
+ IMAGE_DATA_DIRECTORY *export_directory =
+ &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
+ if (export_directory->Size == 0)
+ return 0;
+ RVAPtr<IMAGE_EXPORT_DIRECTORY> exports(module,
+ export_directory->VirtualAddress);
+ RVAPtr<DWORD> functions(module, exports->AddressOfFunctions);
+ RVAPtr<DWORD> names(module, exports->AddressOfNames);
+ RVAPtr<WORD> ordinals(module, exports->AddressOfNameOrdinals);
+
+ for (DWORD i = 0; i < exports->NumberOfNames; i++) {
+ RVAPtr<char> name(module, names[i]);
+ if (!strcmp(func_name, name)) {
+ DWORD index = ordinals[i];
+ RVAPtr<char> func(module, functions[index]);
+
+ // Handle forwarded functions.
+ DWORD offset = functions[index];
+ if (offset >= export_directory->VirtualAddress &&
+ offset < export_directory->VirtualAddress + export_directory->Size) {
+ // An entry for a forwarded function is a string with the following
+ // format: "<module> . <function_name>" that is stored into the
+ // exported directory.
+ char function_name[256];
+ size_t funtion_name_length = _strlen(func);
+ if (funtion_name_length >= sizeof(function_name) - 1)
+ InterceptionFailed();
+
+ _memcpy(function_name, func, funtion_name_length);
+ function_name[funtion_name_length] = '\0';
+ char* separator = _strchr(function_name, '.');
+ if (!separator)
+ InterceptionFailed();
+ *separator = '\0';
+
+ void* redirected_module = GetModuleHandleA(function_name);
+ if (!redirected_module)
+ InterceptionFailed();
+ return InternalGetProcAddress(redirected_module, separator + 1);
+ }
+
+ return (uptr)(char *)func;
+ }
+ }
+
+ return 0;
+}
+
+bool OverrideFunction(
+ const char *func_name, uptr new_func, uptr *orig_old_func) {
+ bool hooked = false;
+ void **DLLs = InterestingDLLsAvailable();
+ for (size_t i = 0; DLLs[i]; ++i) {
+ uptr func_addr = InternalGetProcAddress(DLLs[i], func_name);
+ if (func_addr &&
+ OverrideFunction(func_addr, new_func, orig_old_func)) {
+ hooked = true;
+ }
+ }
+ return hooked;
+}
+
+bool OverrideImportedFunction(const char *module_to_patch,
+ const char *imported_module,
+ const char *function_name, uptr new_function,
+ uptr *orig_old_func) {
+ HMODULE module = GetModuleHandleA(module_to_patch);
+ if (!module)
+ return false;
+
+ // Check that the module header is full and present.
+ RVAPtr<IMAGE_DOS_HEADER> dos_stub(module, 0);
+ RVAPtr<IMAGE_NT_HEADERS> headers(module, dos_stub->e_lfanew);
+ if (!module || dos_stub->e_magic != IMAGE_DOS_SIGNATURE || // "MZ"
+ headers->Signature != IMAGE_NT_SIGNATURE || // "PE\0\0"
+ headers->FileHeader.SizeOfOptionalHeader <
+ sizeof(IMAGE_OPTIONAL_HEADER)) {
+ return false;
+ }
+
+ IMAGE_DATA_DIRECTORY *import_directory =
+ &headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+
+ // Iterate the list of imported DLLs. FirstThunk will be null for the last
+ // entry.
+ RVAPtr<IMAGE_IMPORT_DESCRIPTOR> imports(module,
+ import_directory->VirtualAddress);
+ for (; imports->FirstThunk != 0; ++imports) {
+ RVAPtr<const char> modname(module, imports->Name);
+ if (_stricmp(&*modname, imported_module) == 0)
+ break;
+ }
+ if (imports->FirstThunk == 0)
+ return false;
+
+ // We have two parallel arrays: the import address table (IAT) and the table
+ // of names. They start out containing the same data, but the loader rewrites
+ // the IAT to hold imported addresses and leaves the name table in
+ // OriginalFirstThunk alone.
+ RVAPtr<IMAGE_THUNK_DATA> name_table(module, imports->OriginalFirstThunk);
+ RVAPtr<IMAGE_THUNK_DATA> iat(module, imports->FirstThunk);
+ for (; name_table->u1.Ordinal != 0; ++name_table, ++iat) {
+ if (!IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ RVAPtr<IMAGE_IMPORT_BY_NAME> import_by_name(
+ module, name_table->u1.ForwarderString);
+ const char *funcname = &import_by_name->Name[0];
+ if (strcmp(funcname, function_name) == 0)
+ break;
+ }
+ }
+ if (name_table->u1.Ordinal == 0)
+ return false;
+
+ // Now we have the correct IAT entry. Do the swap. We have to make the page
+ // read/write first.
+ if (orig_old_func)
+ *orig_old_func = iat->u1.AddressOfData;
+ DWORD old_prot, unused_prot;
+ if (!VirtualProtect(&iat->u1.AddressOfData, 4, PAGE_EXECUTE_READWRITE,
+ &old_prot))
+ return false;
+ iat->u1.AddressOfData = new_function;
+ if (!VirtualProtect(&iat->u1.AddressOfData, 4, old_prot, &unused_prot))
+ return false; // Not clear if this failure bothers us.
+ return true;
+}
+
+} // namespace __interception
+
+#endif // SANITIZER_MAC
//===-- interception_linux.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/alloc.Plo ./$(DEPDIR)/atomic.Plo \
+ ./$(DEPDIR)/bridge.Plo ./$(DEPDIR)/cp-demangle.Plo \
+ ./$(DEPDIR)/dwarf.Plo ./$(DEPDIR)/elf.Plo \
+ ./$(DEPDIR)/fileline.Plo ./$(DEPDIR)/mmap.Plo \
+ ./$(DEPDIR)/mmapio.Plo ./$(DEPDIR)/posix.Plo \
+ ./$(DEPDIR)/read.Plo ./$(DEPDIR)/sort.Plo \
+ ./$(DEPDIR)/state.Plo ./$(DEPDIR)/unknown.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomic.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bridge.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cp-demangle.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwarf.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileline.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmapio.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/posix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sort.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/state.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unknown.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alloc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atomic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bridge.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cp-demangle.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dwarf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileline.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmap.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmapio.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/posix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sort.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/state.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unknown.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/alloc.Plo
+ -rm -f ./$(DEPDIR)/atomic.Plo
+ -rm -f ./$(DEPDIR)/bridge.Plo
+ -rm -f ./$(DEPDIR)/cp-demangle.Plo
+ -rm -f ./$(DEPDIR)/dwarf.Plo
+ -rm -f ./$(DEPDIR)/elf.Plo
+ -rm -f ./$(DEPDIR)/fileline.Plo
+ -rm -f ./$(DEPDIR)/mmap.Plo
+ -rm -f ./$(DEPDIR)/mmapio.Plo
+ -rm -f ./$(DEPDIR)/posix.Plo
+ -rm -f ./$(DEPDIR)/read.Plo
+ -rm -f ./$(DEPDIR)/sort.Plo
+ -rm -f ./$(DEPDIR)/state.Plo
+ -rm -f ./$(DEPDIR)/unknown.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/alloc.Plo
+ -rm -f ./$(DEPDIR)/atomic.Plo
+ -rm -f ./$(DEPDIR)/bridge.Plo
+ -rm -f ./$(DEPDIR)/cp-demangle.Plo
+ -rm -f ./$(DEPDIR)/dwarf.Plo
+ -rm -f ./$(DEPDIR)/elf.Plo
+ -rm -f ./$(DEPDIR)/fileline.Plo
+ -rm -f ./$(DEPDIR)/mmap.Plo
+ -rm -f ./$(DEPDIR)/mmapio.Plo
+ -rm -f ./$(DEPDIR)/posix.Plo
+ -rm -f ./$(DEPDIR)/read.Plo
+ -rm -f ./$(DEPDIR)/sort.Plo
+ -rm -f ./$(DEPDIR)/state.Plo
+ -rm -f ./$(DEPDIR)/unknown.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags dvi dvi-am \
+ html html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
endif
sanitizer_lsan_files = \
- lsan_common.cc \
- lsan_common_linux.cc \
- lsan_common_mac.cc
+ lsan_common.cpp \
+ lsan_common_linux.cpp \
+ lsan_common_mac.cpp
lsan_files = \
$(sanitizer_lsan_files) \
- lsan.cc \
- lsan_linux.cc \
- lsan_mac.cc \
- lsan_malloc_mac.cc \
- lsan_allocator.cc \
- lsan_interceptors.cc \
- lsan_thread.cc
+ lsan.cpp \
+ lsan_linux.cpp \
+ lsan_mac.cpp \
+ lsan_malloc_mac.cpp \
+ lsan_allocator.cpp \
+ lsan_interceptors.cpp \
+ lsan_thread.cpp
libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files)
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/lsan.Plo \
+ ./$(DEPDIR)/lsan_allocator.Plo ./$(DEPDIR)/lsan_common.Plo \
+ ./$(DEPDIR)/lsan_common_linux.Plo \
+ ./$(DEPDIR)/lsan_common_mac.Plo \
+ ./$(DEPDIR)/lsan_interceptors.Plo ./$(DEPDIR)/lsan_linux.Plo \
+ ./$(DEPDIR)/lsan_mac.Plo ./$(DEPDIR)/lsan_malloc_mac.Plo \
+ ./$(DEPDIR)/lsan_thread.Plo
am__mv = mv -f
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
@LSAN_SUPPORTED_TRUE@toolexeclib_LTLIBRARIES = liblsan.la
@LSAN_SUPPORTED_TRUE@nodist_toolexeclib_HEADERS = liblsan_preinit.o
sanitizer_lsan_files = \
- lsan_common.cc \
- lsan_common_linux.cc \
- lsan_common_mac.cc
+ lsan_common.cpp \
+ lsan_common_linux.cpp \
+ lsan_common_mac.cpp
lsan_files = \
$(sanitizer_lsan_files) \
- lsan.cc \
- lsan_linux.cc \
- lsan_mac.cc \
- lsan_malloc_mac.cc \
- lsan_allocator.cc \
- lsan_interceptors.cc \
- lsan_thread.cc
+ lsan.cpp \
+ lsan_linux.cpp \
+ lsan_mac.cpp \
+ lsan_malloc_mac.cpp \
+ lsan_allocator.cpp \
+ lsan_interceptors.cpp \
+ lsan_thread.cpp
libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files)
liblsan_la_SOURCES = $(lsan_files)
all: all-am
.SUFFIXES:
-.SUFFIXES: .cc .lo .o .obj
+.SUFFIXES: .cpp .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_allocator.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_interceptors.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_malloc_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_thread.Plo@am__quote@
-
-.cc.o:
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_allocator.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_interceptors.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_malloc_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_thread.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
-.cc.obj:
+.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-.cc.lo:
+.cpp.lo:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
clean-toolexeclibLTLIBRARIES mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/lsan.Plo
+ -rm -f ./$(DEPDIR)/lsan_allocator.Plo
+ -rm -f ./$(DEPDIR)/lsan_common.Plo
+ -rm -f ./$(DEPDIR)/lsan_common_linux.Plo
+ -rm -f ./$(DEPDIR)/lsan_common_mac.Plo
+ -rm -f ./$(DEPDIR)/lsan_interceptors.Plo
+ -rm -f ./$(DEPDIR)/lsan_linux.Plo
+ -rm -f ./$(DEPDIR)/lsan_mac.Plo
+ -rm -f ./$(DEPDIR)/lsan_malloc_mac.Plo
+ -rm -f ./$(DEPDIR)/lsan_thread.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/lsan.Plo
+ -rm -f ./$(DEPDIR)/lsan_allocator.Plo
+ -rm -f ./$(DEPDIR)/lsan_common.Plo
+ -rm -f ./$(DEPDIR)/lsan_common_linux.Plo
+ -rm -f ./$(DEPDIR)/lsan_common_mac.Plo
+ -rm -f ./$(DEPDIR)/lsan_interceptors.Plo
+ -rm -f ./$(DEPDIR)/lsan_linux.Plo
+ -rm -f ./$(DEPDIR)/lsan_mac.Plo
+ -rm -f ./$(DEPDIR)/lsan_malloc_mac.Plo
+ -rm -f ./$(DEPDIR)/lsan_thread.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-noinstLTLIBRARIES \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
clean-toolexeclibLTLIBRARIES cscopelist-am ctags ctags-am \
distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags dvi dvi-am html html-am info \
+++ /dev/null
-//=-- lsan.cc -------------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// Standalone LSan RTL.
-//
-//===----------------------------------------------------------------------===//
-
-#include "lsan.h"
-
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_flag_parser.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "lsan_allocator.h"
-#include "lsan_common.h"
-#include "lsan_thread.h"
-
-bool lsan_inited;
-bool lsan_init_is_running;
-
-namespace __lsan {
-
-///// Interface to the common LSan module. /////
-bool WordIsPoisoned(uptr addr) {
- return false;
-}
-
-} // namespace __lsan
-
-using namespace __lsan; // NOLINT
-
-static void InitializeFlags() {
- // Set all the default values.
- SetCommonFlagsDefaults();
- {
- CommonFlags cf;
- cf.CopyFrom(*common_flags());
- cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
- cf.malloc_context_size = 30;
- cf.intercept_tls_get_addr = true;
- cf.detect_leaks = true;
- cf.exitcode = 23;
- OverrideCommonFlags(cf);
- }
-
- Flags *f = flags();
- f->SetDefaults();
-
- FlagParser parser;
- RegisterLsanFlags(&parser, f);
- RegisterCommonFlags(&parser);
-
- // Override from user-specified string.
- const char *lsan_default_options = MaybeCallLsanDefaultOptions();
- parser.ParseString(lsan_default_options);
- parser.ParseString(GetEnv("LSAN_OPTIONS"));
-
- SetVerbosity(common_flags()->verbosity);
-
- if (Verbosity()) ReportUnrecognizedFlags();
-
- if (common_flags()->help) parser.PrintFlagDescriptions();
-
- __sanitizer_set_report_path(common_flags()->log_path);
-}
-
-static void OnStackUnwind(const SignalContext &sig, const void *,
- BufferedStackTrace *stack) {
- GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context,
- common_flags()->fast_unwind_on_fatal);
-}
-
-static void LsanOnDeadlySignal(int signo, void *siginfo, void *context) {
- HandleDeadlySignal(siginfo, context, GetCurrentThread(), &OnStackUnwind,
- nullptr);
-}
-
-extern "C" void __lsan_init() {
- CHECK(!lsan_init_is_running);
- if (lsan_inited)
- return;
- lsan_init_is_running = true;
- SanitizerToolName = "LeakSanitizer";
- CacheBinaryName();
- AvoidCVE_2016_2143();
- InitializeFlags();
- InitCommonLsan();
- InitializeAllocator();
- ReplaceSystemMalloc();
- InitTlsSize();
- InitializeInterceptors();
- InitializeThreadRegistry();
- InstallDeadlySignalHandlers(LsanOnDeadlySignal);
- u32 tid = ThreadCreate(0, 0, true);
- CHECK_EQ(tid, 0);
- ThreadStart(tid, GetTid());
- SetCurrentThread(tid);
-
- if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
- Atexit(DoLeakCheck);
-
- InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
-
- lsan_inited = true;
- lsan_init_is_running = false;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_print_stack_trace() {
- GET_STACK_TRACE_FATAL;
- stack.Print();
-}
--- /dev/null
+//=-- lsan.cpp ------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Standalone LSan RTL.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan.h"
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "lsan_allocator.h"
+#include "lsan_common.h"
+#include "lsan_thread.h"
+
+bool lsan_inited;
+bool lsan_init_is_running;
+
+namespace __lsan {
+
+///// Interface to the common LSan module. /////
+bool WordIsPoisoned(uptr addr) {
+ return false;
+}
+
+} // namespace __lsan
+
+void __sanitizer::BufferedStackTrace::UnwindImpl(
+ uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+ using namespace __lsan;
+ uptr stack_top = 0, stack_bottom = 0;
+ ThreadContext *t;
+ if (StackTrace::WillUseFastUnwind(request_fast) &&
+ (t = CurrentThreadContext())) {
+ stack_top = t->stack_end();
+ stack_bottom = t->stack_begin();
+ }
+ if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) {
+ if (StackTrace::WillUseFastUnwind(request_fast))
+ Unwind(max_depth, pc, bp, nullptr, stack_top, stack_bottom, true);
+ else
+ Unwind(max_depth, pc, 0, context, 0, 0, false);
+ }
+}
+
+using namespace __lsan; // NOLINT
+
+static void InitializeFlags() {
+ // Set all the default values.
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH");
+ cf.malloc_context_size = 30;
+ cf.intercept_tls_get_addr = true;
+ cf.detect_leaks = true;
+ cf.exitcode = 23;
+ OverrideCommonFlags(cf);
+ }
+
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterLsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+ // Override from user-specified string.
+ const char *lsan_default_options = MaybeCallLsanDefaultOptions();
+ parser.ParseString(lsan_default_options);
+ parser.ParseStringFromEnv("LSAN_OPTIONS");
+
+ SetVerbosity(common_flags()->verbosity);
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+
+ __sanitizer_set_report_path(common_flags()->log_path);
+}
+
+static void OnStackUnwind(const SignalContext &sig, const void *,
+ BufferedStackTrace *stack) {
+ stack->Unwind(sig.pc, sig.bp, sig.context,
+ common_flags()->fast_unwind_on_fatal);
+}
+
+static void LsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ HandleDeadlySignal(siginfo, context, GetCurrentThread(), &OnStackUnwind,
+ nullptr);
+}
+
+extern "C" void __lsan_init() {
+ CHECK(!lsan_init_is_running);
+ if (lsan_inited)
+ return;
+ lsan_init_is_running = true;
+ SanitizerToolName = "LeakSanitizer";
+ CacheBinaryName();
+ AvoidCVE_2016_2143();
+ InitializeFlags();
+ InitCommonLsan();
+ InitializeAllocator();
+ ReplaceSystemMalloc();
+ InitTlsSize();
+ InitializeInterceptors();
+ InitializeThreadRegistry();
+ InstallDeadlySignalHandlers(LsanOnDeadlySignal);
+ u32 tid = ThreadCreate(0, 0, true);
+ CHECK_EQ(tid, 0);
+ ThreadStart(tid, GetTid());
+ SetCurrentThread(tid);
+
+ if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
+ Atexit(DoLeakCheck);
+
+ InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+
+ lsan_inited = true;
+ lsan_init_is_running = false;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ GET_STACK_TRACE_FATAL;
+ stack.Print();
+}
//=-- lsan.h --------------------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#define GET_STACK_TRACE(max_size, fast) \
__sanitizer::BufferedStackTrace stack; \
- GetStackTrace(&stack, max_size, StackTrace::GetCurrentPc(), \
- GET_CURRENT_FRAME(), nullptr, fast);
+ stack.Unwind(StackTrace::GetCurrentPc(), \
+ GET_CURRENT_FRAME(), nullptr, fast, max_size);
#define GET_STACK_TRACE_FATAL \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
__lsan_init(); \
} while (0)
-// Get the stack trace with the given pc and bp.
-// The pc will be in the position 0 of the resulting stack trace.
-// The bp may refer to the current frame or to the caller's frame.
-ALWAYS_INLINE
-void GetStackTrace(__sanitizer::BufferedStackTrace *stack,
- __sanitizer::uptr max_depth, __sanitizer::uptr pc,
- __sanitizer::uptr bp, void *context, bool fast) {
- uptr stack_top = 0, stack_bottom = 0;
- ThreadContext *t;
- if (fast && (t = CurrentThreadContext())) {
- stack_top = t->stack_end();
- stack_bottom = t->stack_begin();
- }
- if (!SANITIZER_MIPS || IsValidFrame(bp, stack_top, stack_bottom)) {
- stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast);
- }
-}
-
} // namespace __lsan
extern bool lsan_inited;
+++ /dev/null
-//=-- lsan_allocator.cc ---------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// See lsan_allocator.h for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "lsan_allocator.h"
-
-#include "sanitizer_common/sanitizer_allocator.h"
-#include "sanitizer_common/sanitizer_allocator_checks.h"
-#include "sanitizer_common/sanitizer_allocator_interface.h"
-#include "sanitizer_common/sanitizer_allocator_report.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "lsan_common.h"
-
-extern "C" void *memset(void *ptr, int value, uptr num);
-
-namespace __lsan {
-#if defined(__i386__) || defined(__arm__)
-static const uptr kMaxAllowedMallocSize = 1UL << 30;
-#elif defined(__mips64) || defined(__aarch64__)
-static const uptr kMaxAllowedMallocSize = 4UL << 30;
-#else
-static const uptr kMaxAllowedMallocSize = 8UL << 30;
-#endif
-typedef LargeMmapAllocator<> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
-
-static Allocator allocator;
-
-void InitializeAllocator() {
- SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
- allocator.InitLinkerInitialized(
- common_flags()->allocator_release_to_os_interval_ms);
-}
-
-void AllocatorThreadFinish() {
- allocator.SwallowCache(GetAllocatorCache());
-}
-
-static ChunkMetadata *Metadata(const void *p) {
- return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
-}
-
-static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
- if (!p) return;
- ChunkMetadata *m = Metadata(p);
- CHECK(m);
- m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
- m->stack_trace_id = StackDepotPut(stack);
- m->requested_size = size;
- atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
-}
-
-static void RegisterDeallocation(void *p) {
- if (!p) return;
- ChunkMetadata *m = Metadata(p);
- CHECK(m);
- atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed);
-}
-
-static void *ReportAllocationSizeTooBig(uptr size, const StackTrace &stack) {
- if (AllocatorMayReturnNull()) {
- Report("WARNING: LeakSanitizer failed to allocate 0x%zx bytes\n", size);
- return nullptr;
- }
- ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, &stack);
-}
-
-void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
- bool cleared) {
- if (size == 0)
- size = 1;
- if (size > kMaxAllowedMallocSize)
- return ReportAllocationSizeTooBig(size, stack);
- void *p = allocator.Allocate(GetAllocatorCache(), size, alignment);
- if (UNLIKELY(!p)) {
- SetAllocatorOutOfMemory();
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportOutOfMemory(size, &stack);
- }
- // Do not rely on the allocator to clear the memory (it's slow).
- if (cleared && allocator.FromPrimary(p))
- memset(p, 0, size);
- RegisterAllocation(stack, p, size);
- if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
- RunMallocHooks(p, size);
- return p;
-}
-
-static void *Calloc(uptr nmemb, uptr size, const StackTrace &stack) {
- if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportCallocOverflow(nmemb, size, &stack);
- }
- size *= nmemb;
- return Allocate(stack, size, 1, true);
-}
-
-void Deallocate(void *p) {
- if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
- RunFreeHooks(p);
- RegisterDeallocation(p);
- allocator.Deallocate(GetAllocatorCache(), p);
-}
-
-void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
- uptr alignment) {
- RegisterDeallocation(p);
- if (new_size > kMaxAllowedMallocSize) {
- allocator.Deallocate(GetAllocatorCache(), p);
- return ReportAllocationSizeTooBig(new_size, stack);
- }
- p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment);
- RegisterAllocation(stack, p, new_size);
- return p;
-}
-
-void GetAllocatorCacheRange(uptr *begin, uptr *end) {
- *begin = (uptr)GetAllocatorCache();
- *end = *begin + sizeof(AllocatorCache);
-}
-
-uptr GetMallocUsableSize(const void *p) {
- ChunkMetadata *m = Metadata(p);
- if (!m) return 0;
- return m->requested_size;
-}
-
-int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
- const StackTrace &stack) {
- if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
- if (AllocatorMayReturnNull())
- return errno_EINVAL;
- ReportInvalidPosixMemalignAlignment(alignment, &stack);
- }
- void *ptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
- if (UNLIKELY(!ptr))
- // OOM error is already taken care of by Allocate.
- return errno_ENOMEM;
- CHECK(IsAligned((uptr)ptr, alignment));
- *memptr = ptr;
- return 0;
-}
-
-void *lsan_aligned_alloc(uptr alignment, uptr size, const StackTrace &stack) {
- if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
- errno = errno_EINVAL;
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportInvalidAlignedAllocAlignment(size, alignment, &stack);
- }
- return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
-}
-
-void *lsan_memalign(uptr alignment, uptr size, const StackTrace &stack) {
- if (UNLIKELY(!IsPowerOfTwo(alignment))) {
- errno = errno_EINVAL;
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportInvalidAllocationAlignment(alignment, &stack);
- }
- return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
-}
-
-void *lsan_malloc(uptr size, const StackTrace &stack) {
- return SetErrnoOnNull(Allocate(stack, size, 1, kAlwaysClearMemory));
-}
-
-void lsan_free(void *p) {
- Deallocate(p);
-}
-
-void *lsan_realloc(void *p, uptr size, const StackTrace &stack) {
- return SetErrnoOnNull(Reallocate(stack, p, size, 1));
-}
-
-void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack) {
- return SetErrnoOnNull(Calloc(nmemb, size, stack));
-}
-
-void *lsan_valloc(uptr size, const StackTrace &stack) {
- return SetErrnoOnNull(
- Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory));
-}
-
-void *lsan_pvalloc(uptr size, const StackTrace &stack) {
- uptr PageSize = GetPageSizeCached();
- if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
- errno = errno_ENOMEM;
- if (AllocatorMayReturnNull())
- return nullptr;
- ReportPvallocOverflow(size, &stack);
- }
- // pvalloc(0) should allocate one page.
- size = size ? RoundUpTo(size, PageSize) : PageSize;
- return SetErrnoOnNull(Allocate(stack, size, PageSize, kAlwaysClearMemory));
-}
-
-uptr lsan_mz_size(const void *p) {
- return GetMallocUsableSize(p);
-}
-
-///// Interface to the common LSan module. /////
-
-void LockAllocator() {
- allocator.ForceLock();
-}
-
-void UnlockAllocator() {
- allocator.ForceUnlock();
-}
-
-void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
- *begin = (uptr)&allocator;
- *end = *begin + sizeof(allocator);
-}
-
-uptr PointsIntoChunk(void* p) {
- uptr addr = reinterpret_cast<uptr>(p);
- uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p));
- if (!chunk) return 0;
- // LargeMmapAllocator considers pointers to the meta-region of a chunk to be
- // valid, but we don't want that.
- if (addr < chunk) return 0;
- ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
- CHECK(m);
- if (!m->allocated)
- return 0;
- if (addr < chunk + m->requested_size)
- return chunk;
- if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr))
- return chunk;
- return 0;
-}
-
-uptr GetUserBegin(uptr chunk) {
- return chunk;
-}
-
-LsanMetadata::LsanMetadata(uptr chunk) {
- metadata_ = Metadata(reinterpret_cast<void *>(chunk));
- CHECK(metadata_);
-}
-
-bool LsanMetadata::allocated() const {
- return reinterpret_cast<ChunkMetadata *>(metadata_)->allocated;
-}
-
-ChunkTag LsanMetadata::tag() const {
- return reinterpret_cast<ChunkMetadata *>(metadata_)->tag;
-}
-
-void LsanMetadata::set_tag(ChunkTag value) {
- reinterpret_cast<ChunkMetadata *>(metadata_)->tag = value;
-}
-
-uptr LsanMetadata::requested_size() const {
- return reinterpret_cast<ChunkMetadata *>(metadata_)->requested_size;
-}
-
-u32 LsanMetadata::stack_trace_id() const {
- return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id;
-}
-
-void ForEachChunk(ForEachChunkCallback callback, void *arg) {
- allocator.ForEachChunk(callback, arg);
-}
-
-IgnoreObjectResult IgnoreObjectLocked(const void *p) {
- void *chunk = allocator.GetBlockBegin(p);
- if (!chunk || p < chunk) return kIgnoreObjectInvalid;
- ChunkMetadata *m = Metadata(chunk);
- CHECK(m);
- if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) {
- if (m->tag == kIgnored)
- return kIgnoreObjectAlreadyIgnored;
- m->tag = kIgnored;
- return kIgnoreObjectSuccess;
- } else {
- return kIgnoreObjectInvalid;
- }
-}
-} // namespace __lsan
-
-using namespace __lsan;
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __sanitizer_get_current_allocated_bytes() {
- uptr stats[AllocatorStatCount];
- allocator.GetStats(stats);
- return stats[AllocatorStatAllocated];
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __sanitizer_get_heap_size() {
- uptr stats[AllocatorStatCount];
- allocator.GetStats(stats);
- return stats[AllocatorStatMapped];
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __sanitizer_get_free_bytes() { return 0; }
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __sanitizer_get_unmapped_bytes() { return 0; }
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; }
-
-SANITIZER_INTERFACE_ATTRIBUTE
-uptr __sanitizer_get_allocated_size(const void *p) {
- return GetMallocUsableSize(p);
-}
-
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
-// Provide default (no-op) implementation of malloc hooks.
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __sanitizer_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __sanitizer_free_hook(void *ptr) {
- (void)ptr;
-}
-#endif
-} // extern "C"
--- /dev/null
+//=-- lsan_allocator.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// See lsan_allocator.h for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_allocator.h"
+
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "lsan_common.h"
+
+extern "C" void *memset(void *ptr, int value, uptr num);
+
+namespace __lsan {
+#if defined(__i386__) || defined(__arm__)
+static const uptr kMaxAllowedMallocSize = 1UL << 30;
+#elif defined(__mips64) || defined(__aarch64__)
+static const uptr kMaxAllowedMallocSize = 4UL << 30;
+#else
+static const uptr kMaxAllowedMallocSize = 8UL << 30;
+#endif
+
+static Allocator allocator;
+
+void InitializeAllocator() {
+ SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
+ allocator.InitLinkerInitialized(
+ common_flags()->allocator_release_to_os_interval_ms);
+}
+
+void AllocatorThreadFinish() {
+ allocator.SwallowCache(GetAllocatorCache());
+}
+
+static ChunkMetadata *Metadata(const void *p) {
+ return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p));
+}
+
+static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
+ if (!p) return;
+ ChunkMetadata *m = Metadata(p);
+ CHECK(m);
+ m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
+ m->stack_trace_id = StackDepotPut(stack);
+ m->requested_size = size;
+ atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
+}
+
+static void RegisterDeallocation(void *p) {
+ if (!p) return;
+ ChunkMetadata *m = Metadata(p);
+ CHECK(m);
+ atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed);
+}
+
+static void *ReportAllocationSizeTooBig(uptr size, const StackTrace &stack) {
+ if (AllocatorMayReturnNull()) {
+ Report("WARNING: LeakSanitizer failed to allocate 0x%zx bytes\n", size);
+ return nullptr;
+ }
+ ReportAllocationSizeTooBig(size, kMaxAllowedMallocSize, &stack);
+}
+
+void *Allocate(const StackTrace &stack, uptr size, uptr alignment,
+ bool cleared) {
+ if (size == 0)
+ size = 1;
+ if (size > kMaxAllowedMallocSize)
+ return ReportAllocationSizeTooBig(size, stack);
+ void *p = allocator.Allocate(GetAllocatorCache(), size, alignment);
+ if (UNLIKELY(!p)) {
+ SetAllocatorOutOfMemory();
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportOutOfMemory(size, &stack);
+ }
+ // Do not rely on the allocator to clear the memory (it's slow).
+ if (cleared && allocator.FromPrimary(p))
+ memset(p, 0, size);
+ RegisterAllocation(stack, p, size);
+ if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size);
+ RunMallocHooks(p, size);
+ return p;
+}
+
+static void *Calloc(uptr nmemb, uptr size, const StackTrace &stack) {
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportCallocOverflow(nmemb, size, &stack);
+ }
+ size *= nmemb;
+ return Allocate(stack, size, 1, true);
+}
+
+void Deallocate(void *p) {
+ if (&__sanitizer_free_hook) __sanitizer_free_hook(p);
+ RunFreeHooks(p);
+ RegisterDeallocation(p);
+ allocator.Deallocate(GetAllocatorCache(), p);
+}
+
+void *Reallocate(const StackTrace &stack, void *p, uptr new_size,
+ uptr alignment) {
+ RegisterDeallocation(p);
+ if (new_size > kMaxAllowedMallocSize) {
+ allocator.Deallocate(GetAllocatorCache(), p);
+ return ReportAllocationSizeTooBig(new_size, stack);
+ }
+ p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment);
+ RegisterAllocation(stack, p, new_size);
+ return p;
+}
+
+void GetAllocatorCacheRange(uptr *begin, uptr *end) {
+ *begin = (uptr)GetAllocatorCache();
+ *end = *begin + sizeof(AllocatorCache);
+}
+
+uptr GetMallocUsableSize(const void *p) {
+ ChunkMetadata *m = Metadata(p);
+ if (!m) return 0;
+ return m->requested_size;
+}
+
+int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
+ const StackTrace &stack) {
+ if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
+ if (AllocatorMayReturnNull())
+ return errno_EINVAL;
+ ReportInvalidPosixMemalignAlignment(alignment, &stack);
+ }
+ void *ptr = Allocate(stack, size, alignment, kAlwaysClearMemory);
+ if (UNLIKELY(!ptr))
+ // OOM error is already taken care of by Allocate.
+ return errno_ENOMEM;
+ CHECK(IsAligned((uptr)ptr, alignment));
+ *memptr = ptr;
+ return 0;
+}
+
+void *lsan_aligned_alloc(uptr alignment, uptr size, const StackTrace &stack) {
+ if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportInvalidAlignedAllocAlignment(size, alignment, &stack);
+ }
+ return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
+}
+
+void *lsan_memalign(uptr alignment, uptr size, const StackTrace &stack) {
+ if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportInvalidAllocationAlignment(alignment, &stack);
+ }
+ return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory));
+}
+
+void *lsan_malloc(uptr size, const StackTrace &stack) {
+ return SetErrnoOnNull(Allocate(stack, size, 1, kAlwaysClearMemory));
+}
+
+void lsan_free(void *p) {
+ Deallocate(p);
+}
+
+void *lsan_realloc(void *p, uptr size, const StackTrace &stack) {
+ return SetErrnoOnNull(Reallocate(stack, p, size, 1));
+}
+
+void *lsan_reallocarray(void *ptr, uptr nmemb, uptr size,
+ const StackTrace &stack) {
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportReallocArrayOverflow(nmemb, size, &stack);
+ }
+ return lsan_realloc(ptr, nmemb * size, stack);
+}
+
+void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack) {
+ return SetErrnoOnNull(Calloc(nmemb, size, stack));
+}
+
+void *lsan_valloc(uptr size, const StackTrace &stack) {
+ return SetErrnoOnNull(
+ Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory));
+}
+
+void *lsan_pvalloc(uptr size, const StackTrace &stack) {
+ uptr PageSize = GetPageSizeCached();
+ if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ ReportPvallocOverflow(size, &stack);
+ }
+ // pvalloc(0) should allocate one page.
+ size = size ? RoundUpTo(size, PageSize) : PageSize;
+ return SetErrnoOnNull(Allocate(stack, size, PageSize, kAlwaysClearMemory));
+}
+
+uptr lsan_mz_size(const void *p) {
+ return GetMallocUsableSize(p);
+}
+
+///// Interface to the common LSan module. /////
+
+void LockAllocator() {
+ allocator.ForceLock();
+}
+
+void UnlockAllocator() {
+ allocator.ForceUnlock();
+}
+
+void GetAllocatorGlobalRange(uptr *begin, uptr *end) {
+ *begin = (uptr)&allocator;
+ *end = *begin + sizeof(allocator);
+}
+
+uptr PointsIntoChunk(void* p) {
+ uptr addr = reinterpret_cast<uptr>(p);
+ uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p));
+ if (!chunk) return 0;
+ // LargeMmapAllocator considers pointers to the meta-region of a chunk to be
+ // valid, but we don't want that.
+ if (addr < chunk) return 0;
+ ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk));
+ CHECK(m);
+ if (!m->allocated)
+ return 0;
+ if (addr < chunk + m->requested_size)
+ return chunk;
+ if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr))
+ return chunk;
+ return 0;
+}
+
+uptr GetUserBegin(uptr chunk) {
+ return chunk;
+}
+
+LsanMetadata::LsanMetadata(uptr chunk) {
+ metadata_ = Metadata(reinterpret_cast<void *>(chunk));
+ CHECK(metadata_);
+}
+
+bool LsanMetadata::allocated() const {
+ return reinterpret_cast<ChunkMetadata *>(metadata_)->allocated;
+}
+
+ChunkTag LsanMetadata::tag() const {
+ return reinterpret_cast<ChunkMetadata *>(metadata_)->tag;
+}
+
+void LsanMetadata::set_tag(ChunkTag value) {
+ reinterpret_cast<ChunkMetadata *>(metadata_)->tag = value;
+}
+
+uptr LsanMetadata::requested_size() const {
+ return reinterpret_cast<ChunkMetadata *>(metadata_)->requested_size;
+}
+
+u32 LsanMetadata::stack_trace_id() const {
+ return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id;
+}
+
+void ForEachChunk(ForEachChunkCallback callback, void *arg) {
+ allocator.ForEachChunk(callback, arg);
+}
+
+IgnoreObjectResult IgnoreObjectLocked(const void *p) {
+ void *chunk = allocator.GetBlockBegin(p);
+ if (!chunk || p < chunk) return kIgnoreObjectInvalid;
+ ChunkMetadata *m = Metadata(chunk);
+ CHECK(m);
+ if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) {
+ if (m->tag == kIgnored)
+ return kIgnoreObjectAlreadyIgnored;
+ m->tag = kIgnored;
+ return kIgnoreObjectSuccess;
+ } else {
+ return kIgnoreObjectInvalid;
+ }
+}
+} // namespace __lsan
+
+using namespace __lsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_free_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_unmapped_bytes() { return 0; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; }
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return GetMallocUsableSize(p);
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+// Provide default (no-op) implementation of malloc hooks.
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_free_hook(void *ptr) {
+ (void)ptr;
+}
+#endif
+} // extern "C"
//=-- lsan_allocator.h ----------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#if defined(__mips64) || defined(__aarch64__) || defined(__i386__) || \
defined(__arm__)
-static const uptr kRegionSizeLog = 20;
-static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
-typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
-
+template <typename AddressSpaceViewTy>
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = sizeof(ChunkMetadata);
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
- static const uptr kRegionSizeLog = __lsan::kRegionSizeLog;
- typedef __lsan::ByteMap ByteMap;
+ static const uptr kRegionSizeLog = 20;
+ using AddressSpaceView = AddressSpaceViewTy;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
-typedef SizeClassAllocator32<AP32> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator32<AP32<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#elif defined(__x86_64__) || defined(__powerpc64__)
# if defined(__powerpc64__)
const uptr kAllocatorSpace = 0xa0000000000ULL;
const uptr kAllocatorSpace = 0x600000000000ULL;
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
# endif
+template <typename AddressSpaceViewTy>
struct AP64 { // Allocator64 parameters. Deliberately using a short name.
static const uptr kSpaceBeg = kAllocatorSpace;
static const uptr kSpaceSize = kAllocatorSize;
typedef DefaultSizeClassMap SizeClassMap;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = AddressSpaceViewTy;
};
-typedef SizeClassAllocator64<AP64> PrimaryAllocator;
+template <typename AddressSpaceView>
+using PrimaryAllocatorASVT = SizeClassAllocator64<AP64<AddressSpaceView>>;
+using PrimaryAllocator = PrimaryAllocatorASVT<LocalAddressSpaceView>;
#endif
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-AllocatorCache *GetAllocatorCache();
+template <typename AddressSpaceView>
+using AllocatorASVT = CombinedAllocator<PrimaryAllocatorASVT<AddressSpaceView>>;
+using Allocator = AllocatorASVT<LocalAddressSpaceView>;
+using AllocatorCache = Allocator::AllocatorCache;
+
+Allocator::AllocatorCache *GetAllocatorCache();
int lsan_posix_memalign(void **memptr, uptr alignment, uptr size,
const StackTrace &stack);
void *lsan_malloc(uptr size, const StackTrace &stack);
void lsan_free(void *p);
void *lsan_realloc(void *p, uptr size, const StackTrace &stack);
+void *lsan_reallocarray(void *p, uptr nmemb, uptr size,
+ const StackTrace &stack);
void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack);
void *lsan_valloc(uptr size, const StackTrace &stack);
void *lsan_pvalloc(uptr size, const StackTrace &stack);
+++ /dev/null
-//=-- lsan_common.cc ------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// Implementation of common leak checking functionality.
-//
-//===----------------------------------------------------------------------===//
-
-#include "lsan_common.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flag_parser.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-#include "sanitizer_common/sanitizer_report_decorator.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
-#include "sanitizer_common/sanitizer_thread_registry.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
-
-#if CAN_SANITIZE_LEAKS
-namespace __lsan {
-
-// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
-// also to protect the global list of root regions.
-BlockingMutex global_mutex(LINKER_INITIALIZED);
-
-Flags lsan_flags;
-
-void DisableCounterUnderflow() {
- if (common_flags()->detect_leaks) {
- Report("Unmatched call to __lsan_enable().\n");
- Die();
- }
-}
-
-void Flags::SetDefaults() {
-#define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
-#include "lsan_flags.inc"
-#undef LSAN_FLAG
-}
-
-void RegisterLsanFlags(FlagParser *parser, Flags *f) {
-#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &f->Name);
-#include "lsan_flags.inc"
-#undef LSAN_FLAG
-}
-
-#define LOG_POINTERS(...) \
- do { \
- if (flags()->log_pointers) Report(__VA_ARGS__); \
- } while (0)
-
-#define LOG_THREADS(...) \
- do { \
- if (flags()->log_threads) Report(__VA_ARGS__); \
- } while (0)
-
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
-static SuppressionContext *suppression_ctx = nullptr;
-static const char kSuppressionLeak[] = "leak";
-static const char *kSuppressionTypes[] = { kSuppressionLeak };
-static const char kStdSuppressions[] =
-#if SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
- // For more details refer to the SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
- // definition.
- "leak:*pthread_exit*\n"
-#endif // SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
-#if SANITIZER_MAC
- // For Darwin and os_log/os_trace: https://reviews.llvm.org/D35173
- "leak:*_os_trace*\n"
-#endif
- // TLS leak in some glibc versions, described in
- // https://sourceware.org/bugzilla/show_bug.cgi?id=12650.
- "leak:*tls_get_addr*\n";
-
-void InitializeSuppressions() {
- CHECK_EQ(nullptr, suppression_ctx);
- suppression_ctx = new (suppression_placeholder) // NOLINT
- SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
- suppression_ctx->ParseFromFile(flags()->suppressions);
- if (&__lsan_default_suppressions)
- suppression_ctx->Parse(__lsan_default_suppressions());
- suppression_ctx->Parse(kStdSuppressions);
-}
-
-static SuppressionContext *GetSuppressionContext() {
- CHECK(suppression_ctx);
- return suppression_ctx;
-}
-
-static InternalMmapVector<RootRegion> *root_regions;
-
-InternalMmapVector<RootRegion> const *GetRootRegions() { return root_regions; }
-
-void InitializeRootRegions() {
- CHECK(!root_regions);
- ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
- root_regions = new (placeholder) InternalMmapVector<RootRegion>(); // NOLINT
-}
-
-const char *MaybeCallLsanDefaultOptions() {
- return (&__lsan_default_options) ? __lsan_default_options() : "";
-}
-
-void InitCommonLsan() {
- InitializeRootRegions();
- if (common_flags()->detect_leaks) {
- // Initialization which can fail or print warnings should only be done if
- // LSan is actually enabled.
- InitializeSuppressions();
- InitializePlatformSpecificModules();
- }
-}
-
-class Decorator: public __sanitizer::SanitizerCommonDecorator {
- public:
- Decorator() : SanitizerCommonDecorator() { }
- const char *Error() { return Red(); }
- const char *Leak() { return Blue(); }
-};
-
-static inline bool CanBeAHeapPointer(uptr p) {
- // Since our heap is located in mmap-ed memory, we can assume a sensible lower
- // bound on heap addresses.
- const uptr kMinAddress = 4 * 4096;
- if (p < kMinAddress) return false;
-#if defined(__x86_64__)
- // Accept only canonical form user-space addresses.
- return ((p >> 47) == 0);
-#elif defined(__mips64)
- return ((p >> 40) == 0);
-#elif defined(__aarch64__)
- unsigned runtimeVMA =
- (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
- return ((p >> runtimeVMA) == 0);
-#else
- return true;
-#endif
-}
-
-// Scans the memory range, looking for byte patterns that point into allocator
-// chunks. Marks those chunks with |tag| and adds them to |frontier|.
-// There are two usage modes for this function: finding reachable chunks
-// (|tag| = kReachable) and finding indirectly leaked chunks
-// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
-// so |frontier| = 0.
-void ScanRangeForPointers(uptr begin, uptr end,
- Frontier *frontier,
- const char *region_type, ChunkTag tag) {
- CHECK(tag == kReachable || tag == kIndirectlyLeaked);
- const uptr alignment = flags()->pointer_alignment();
- LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
- uptr pp = begin;
- if (pp % alignment)
- pp = pp + alignment - pp % alignment;
- for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT
- void *p = *reinterpret_cast<void **>(pp);
- if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue;
- uptr chunk = PointsIntoChunk(p);
- if (!chunk) continue;
- // Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
- if (chunk == begin) continue;
- LsanMetadata m(chunk);
- if (m.tag() == kReachable || m.tag() == kIgnored) continue;
-
- // Do this check relatively late so we can log only the interesting cases.
- if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
- LOG_POINTERS(
- "%p is poisoned: ignoring %p pointing into chunk %p-%p of size "
- "%zu.\n",
- pp, p, chunk, chunk + m.requested_size(), m.requested_size());
- continue;
- }
-
- m.set_tag(tag);
- LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
- chunk, chunk + m.requested_size(), m.requested_size());
- if (frontier)
- frontier->push_back(chunk);
- }
-}
-
-// Scans a global range for pointers
-void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) {
- uptr allocator_begin = 0, allocator_end = 0;
- GetAllocatorGlobalRange(&allocator_begin, &allocator_end);
- if (begin <= allocator_begin && allocator_begin < end) {
- CHECK_LE(allocator_begin, allocator_end);
- CHECK_LE(allocator_end, end);
- if (begin < allocator_begin)
- ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL",
- kReachable);
- if (allocator_end < end)
- ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL", kReachable);
- } else {
- ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable);
- }
-}
-
-void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
- Frontier *frontier = reinterpret_cast<Frontier *>(arg);
- ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
-}
-
-// Scans thread data (stacks and TLS) for heap pointers.
-static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
- Frontier *frontier) {
- InternalMmapVector<uptr> registers(suspended_threads.RegisterCount());
- uptr registers_begin = reinterpret_cast<uptr>(registers.data());
- uptr registers_end =
- reinterpret_cast<uptr>(registers.data() + registers.size());
- for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) {
- tid_t os_id = static_cast<tid_t>(suspended_threads.GetThreadID(i));
- LOG_THREADS("Processing thread %d.\n", os_id);
- uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
- DTLS *dtls;
- bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
- &tls_begin, &tls_end,
- &cache_begin, &cache_end, &dtls);
- if (!thread_found) {
- // If a thread can't be found in the thread registry, it's probably in the
- // process of destruction. Log this event and move on.
- LOG_THREADS("Thread %d not found in registry.\n", os_id);
- continue;
- }
- uptr sp;
- PtraceRegistersStatus have_registers =
- suspended_threads.GetRegistersAndSP(i, registers.data(), &sp);
- if (have_registers != REGISTERS_AVAILABLE) {
- Report("Unable to get registers from thread %d.\n", os_id);
- // If unable to get SP, consider the entire stack to be reachable unless
- // GetRegistersAndSP failed with ESRCH.
- if (have_registers == REGISTERS_UNAVAILABLE_FATAL) continue;
- sp = stack_begin;
- }
-
- if (flags()->use_registers && have_registers)
- ScanRangeForPointers(registers_begin, registers_end, frontier,
- "REGISTERS", kReachable);
-
- if (flags()->use_stacks) {
- LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
- if (sp < stack_begin || sp >= stack_end) {
- // SP is outside the recorded stack range (e.g. the thread is running a
- // signal handler on alternate stack, or swapcontext was used).
- // Again, consider the entire stack range to be reachable.
- LOG_THREADS("WARNING: stack pointer not in stack range.\n");
- uptr page_size = GetPageSizeCached();
- int skipped = 0;
- while (stack_begin < stack_end &&
- !IsAccessibleMemoryRange(stack_begin, 1)) {
- skipped++;
- stack_begin += page_size;
- }
- LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n",
- skipped, stack_begin, stack_end);
- } else {
- // Shrink the stack range to ignore out-of-scope values.
- stack_begin = sp;
- }
- ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
- kReachable);
- ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
- }
-
- if (flags()->use_tls) {
- if (tls_begin) {
- LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
- // If the tls and cache ranges don't overlap, scan full tls range,
- // otherwise, only scan the non-overlapping portions
- if (cache_begin == cache_end || tls_end < cache_begin ||
- tls_begin > cache_end) {
- ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
- } else {
- if (tls_begin < cache_begin)
- ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
- kReachable);
- if (tls_end > cache_end)
- ScanRangeForPointers(cache_end, tls_end, frontier, "TLS",
- kReachable);
- }
- }
- if (dtls && !DTLSInDestruction(dtls)) {
- for (uptr j = 0; j < dtls->dtv_size; ++j) {
- uptr dtls_beg = dtls->dtv[j].beg;
- uptr dtls_end = dtls_beg + dtls->dtv[j].size;
- if (dtls_beg < dtls_end) {
- LOG_THREADS("DTLS %zu at %p-%p.\n", j, dtls_beg, dtls_end);
- ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS",
- kReachable);
- }
- }
- } else {
- // We are handling a thread with DTLS under destruction. Log about
- // this and continue.
- LOG_THREADS("Thread %d has DTLS under destruction.\n", os_id);
- }
- }
- }
-}
-
-void ScanRootRegion(Frontier *frontier, const RootRegion &root_region,
- uptr region_begin, uptr region_end, bool is_readable) {
- uptr intersection_begin = Max(root_region.begin, region_begin);
- uptr intersection_end = Min(region_end, root_region.begin + root_region.size);
- if (intersection_begin >= intersection_end) return;
- LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
- root_region.begin, root_region.begin + root_region.size,
- region_begin, region_end,
- is_readable ? "readable" : "unreadable");
- if (is_readable)
- ScanRangeForPointers(intersection_begin, intersection_end, frontier, "ROOT",
- kReachable);
-}
-
-static void ProcessRootRegion(Frontier *frontier,
- const RootRegion &root_region) {
- MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
- MemoryMappedSegment segment;
- while (proc_maps.Next(&segment)) {
- ScanRootRegion(frontier, root_region, segment.start, segment.end,
- segment.IsReadable());
- }
-}
-
-// Scans root regions for heap pointers.
-static void ProcessRootRegions(Frontier *frontier) {
- if (!flags()->use_root_regions) return;
- CHECK(root_regions);
- for (uptr i = 0; i < root_regions->size(); i++) {
- ProcessRootRegion(frontier, (*root_regions)[i]);
- }
-}
-
-static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
- while (frontier->size()) {
- uptr next_chunk = frontier->back();
- frontier->pop_back();
- LsanMetadata m(next_chunk);
- ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier,
- "HEAP", tag);
- }
-}
-
-// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks
-// which are reachable from it as indirectly leaked.
-static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (m.allocated() && m.tag() != kReachable) {
- ScanRangeForPointers(chunk, chunk + m.requested_size(),
- /* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
- }
-}
-
-// ForEachChunk callback. If chunk is marked as ignored, adds its address to
-// frontier.
-static void CollectIgnoredCb(uptr chunk, void *arg) {
- CHECK(arg);
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (m.allocated() && m.tag() == kIgnored) {
- LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
- chunk, chunk + m.requested_size(), m.requested_size());
- reinterpret_cast<Frontier *>(arg)->push_back(chunk);
- }
-}
-
-static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
- CHECK(stack_id);
- StackTrace stack = map->Get(stack_id);
- // The top frame is our malloc/calloc/etc. The next frame is the caller.
- if (stack.size >= 2)
- return stack.trace[1];
- return 0;
-}
-
-struct InvalidPCParam {
- Frontier *frontier;
- StackDepotReverseMap *stack_depot_reverse_map;
- bool skip_linker_allocations;
-};
-
-// ForEachChunk callback. If the caller pc is invalid or is within the linker,
-// mark as reachable. Called by ProcessPlatformSpecificAllocations.
-static void MarkInvalidPCCb(uptr chunk, void *arg) {
- CHECK(arg);
- InvalidPCParam *param = reinterpret_cast<InvalidPCParam *>(arg);
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
- u32 stack_id = m.stack_trace_id();
- uptr caller_pc = 0;
- if (stack_id > 0)
- caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map);
- // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
- // it as reachable, as we can't properly report its allocation stack anyway.
- if (caller_pc == 0 || (param->skip_linker_allocations &&
- GetLinker()->containsAddress(caller_pc))) {
- m.set_tag(kReachable);
- param->frontier->push_back(chunk);
- }
- }
-}
-
-// On Linux, treats all chunks allocated from ld-linux.so as reachable, which
-// covers dynamically allocated TLS blocks, internal dynamic loader's loaded
-// modules accounting etc.
-// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
-// They are allocated with a __libc_memalign() call in allocate_and_init()
-// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
-// blocks, but we can make sure they come from our own allocator by intercepting
-// __libc_memalign(). On top of that, there is no easy way to reach them. Their
-// addresses are stored in a dynamically allocated array (the DTV) which is
-// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
-// being reachable from the static TLS, and the dynamic TLS being reachable from
-// the DTV. This is because the initial DTV is allocated before our interception
-// mechanism kicks in, and thus we don't recognize it as allocated memory. We
-// can't special-case it either, since we don't know its size.
-// Our solution is to include in the root set all allocations made from
-// ld-linux.so (which is where allocate_and_init() is implemented). This is
-// guaranteed to include all dynamic TLS blocks (and possibly other allocations
-// which we don't care about).
-// On all other platforms, this simply checks to ensure that the caller pc is
-// valid before reporting chunks as leaked.
-void ProcessPC(Frontier *frontier) {
- StackDepotReverseMap stack_depot_reverse_map;
- InvalidPCParam arg;
- arg.frontier = frontier;
- arg.stack_depot_reverse_map = &stack_depot_reverse_map;
- arg.skip_linker_allocations =
- flags()->use_tls && flags()->use_ld_allocations && GetLinker() != nullptr;
- ForEachChunk(MarkInvalidPCCb, &arg);
-}
-
-// Sets the appropriate tag on each chunk.
-static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
- // Holds the flood fill frontier.
- Frontier frontier;
-
- ForEachChunk(CollectIgnoredCb, &frontier);
- ProcessGlobalRegions(&frontier);
- ProcessThreads(suspended_threads, &frontier);
- ProcessRootRegions(&frontier);
- FloodFillTag(&frontier, kReachable);
-
- CHECK_EQ(0, frontier.size());
- ProcessPC(&frontier);
-
- // The check here is relatively expensive, so we do this in a separate flood
- // fill. That way we can skip the check for chunks that are reachable
- // otherwise.
- LOG_POINTERS("Processing platform-specific allocations.\n");
- ProcessPlatformSpecificAllocations(&frontier);
- FloodFillTag(&frontier, kReachable);
-
- // Iterate over leaked chunks and mark those that are reachable from other
- // leaked chunks.
- LOG_POINTERS("Scanning leaked chunks.\n");
- ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
-}
-
-// ForEachChunk callback. Resets the tags to pre-leak-check state.
-static void ResetTagsCb(uptr chunk, void *arg) {
- (void)arg;
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (m.allocated() && m.tag() != kIgnored)
- m.set_tag(kDirectlyLeaked);
-}
-
-static void PrintStackTraceById(u32 stack_trace_id) {
- CHECK(stack_trace_id);
- StackDepotGet(stack_trace_id).Print();
-}
-
-// ForEachChunk callback. Aggregates information about unreachable chunks into
-// a LeakReport.
-static void CollectLeaksCb(uptr chunk, void *arg) {
- CHECK(arg);
- LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg);
- chunk = GetUserBegin(chunk);
- LsanMetadata m(chunk);
- if (!m.allocated()) return;
- if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
- u32 resolution = flags()->resolution;
- u32 stack_trace_id = 0;
- if (resolution > 0) {
- StackTrace stack = StackDepotGet(m.stack_trace_id());
- stack.size = Min(stack.size, resolution);
- stack_trace_id = StackDepotPut(stack);
- } else {
- stack_trace_id = m.stack_trace_id();
- }
- leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(),
- m.tag());
- }
-}
-
-static void PrintMatchedSuppressions() {
- InternalMmapVector<Suppression *> matched;
- GetSuppressionContext()->GetMatched(&matched);
- if (!matched.size())
- return;
- const char *line = "-----------------------------------------------------";
- Printf("%s\n", line);
- Printf("Suppressions used:\n");
- Printf(" count bytes template\n");
- for (uptr i = 0; i < matched.size(); i++)
- Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed(
- &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ);
- Printf("%s\n\n", line);
-}
-
-struct CheckForLeaksParam {
- bool success;
- LeakReport leak_report;
-};
-
-static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) {
- const InternalMmapVector<tid_t> &suspended_threads =
- *(const InternalMmapVector<tid_t> *)arg;
- if (tctx->status == ThreadStatusRunning) {
- uptr i = InternalLowerBound(suspended_threads, 0, suspended_threads.size(),
- tctx->os_id, CompareLess<int>());
- if (i >= suspended_threads.size() || suspended_threads[i] != tctx->os_id)
- Report("Running thread %d was not suspended. False leaks are possible.\n",
- tctx->os_id);
- };
-}
-
-static void ReportUnsuspendedThreads(
- const SuspendedThreadsList &suspended_threads) {
- InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount());
- for (uptr i = 0; i < suspended_threads.ThreadCount(); ++i)
- threads[i] = suspended_threads.GetThreadID(i);
-
- Sort(threads.data(), threads.size());
-
- GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
- &ReportIfNotSuspended, &threads);
-}
-
-static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
- void *arg) {
- CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
- CHECK(param);
- CHECK(!param->success);
- ReportUnsuspendedThreads(suspended_threads);
- ClassifyAllChunks(suspended_threads);
- ForEachChunk(CollectLeaksCb, ¶m->leak_report);
- // Clean up for subsequent leak checks. This assumes we did not overwrite any
- // kIgnored tags.
- ForEachChunk(ResetTagsCb, nullptr);
- param->success = true;
-}
-
-static bool CheckForLeaks() {
- if (&__lsan_is_turned_off && __lsan_is_turned_off())
- return false;
- EnsureMainThreadIDIsCorrect();
- CheckForLeaksParam param;
- param.success = false;
- LockThreadRegistry();
- LockAllocator();
- DoStopTheWorld(CheckForLeaksCallback, ¶m);
- UnlockAllocator();
- UnlockThreadRegistry();
-
- if (!param.success) {
- Report("LeakSanitizer has encountered a fatal error.\n");
- Report(
- "HINT: For debugging, try setting environment variable "
- "LSAN_OPTIONS=verbosity=1:log_threads=1\n");
- Report(
- "HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)\n");
- Die();
- }
- param.leak_report.ApplySuppressions();
- uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
- if (unsuppressed_count > 0) {
- Decorator d;
- Printf("\n"
- "================================================================="
- "\n");
- Printf("%s", d.Error());
- Report("ERROR: LeakSanitizer: detected memory leaks\n");
- Printf("%s", d.Default());
- param.leak_report.ReportTopLeaks(flags()->max_leaks);
- }
- if (common_flags()->print_suppressions)
- PrintMatchedSuppressions();
- if (unsuppressed_count > 0) {
- param.leak_report.PrintSummary();
- return true;
- }
- return false;
-}
-
-static bool has_reported_leaks = false;
-bool HasReportedLeaks() { return has_reported_leaks; }
-
-void DoLeakCheck() {
- BlockingMutexLock l(&global_mutex);
- static bool already_done;
- if (already_done) return;
- already_done = true;
- has_reported_leaks = CheckForLeaks();
- if (has_reported_leaks) HandleLeaks();
-}
-
-static int DoRecoverableLeakCheck() {
- BlockingMutexLock l(&global_mutex);
- bool have_leaks = CheckForLeaks();
- return have_leaks ? 1 : 0;
-}
-
-void DoRecoverableLeakCheckVoid() { DoRecoverableLeakCheck(); }
-
-static Suppression *GetSuppressionForAddr(uptr addr) {
- Suppression *s = nullptr;
-
- // Suppress by module name.
- SuppressionContext *suppressions = GetSuppressionContext();
- if (const char *module_name =
- Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
- if (suppressions->Match(module_name, kSuppressionLeak, &s))
- return s;
-
- // Suppress by file or function name.
- SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
- if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) ||
- suppressions->Match(cur->info.file, kSuppressionLeak, &s)) {
- break;
- }
- }
- frames->ClearAll();
- return s;
-}
-
-static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
- StackTrace stack = StackDepotGet(stack_trace_id);
- for (uptr i = 0; i < stack.size; i++) {
- Suppression *s = GetSuppressionForAddr(
- StackTrace::GetPreviousInstructionPc(stack.trace[i]));
- if (s) return s;
- }
- return nullptr;
-}
-
-///// LeakReport implementation. /////
-
-// A hard limit on the number of distinct leaks, to avoid quadratic complexity
-// in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks
-// in real-world applications.
-// FIXME: Get rid of this limit by changing the implementation of LeakReport to
-// use a hash table.
-const uptr kMaxLeaksConsidered = 5000;
-
-void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
- uptr leaked_size, ChunkTag tag) {
- CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
- bool is_directly_leaked = (tag == kDirectlyLeaked);
- uptr i;
- for (i = 0; i < leaks_.size(); i++) {
- if (leaks_[i].stack_trace_id == stack_trace_id &&
- leaks_[i].is_directly_leaked == is_directly_leaked) {
- leaks_[i].hit_count++;
- leaks_[i].total_size += leaked_size;
- break;
- }
- }
- if (i == leaks_.size()) {
- if (leaks_.size() == kMaxLeaksConsidered) return;
- Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id,
- is_directly_leaked, /* is_suppressed */ false };
- leaks_.push_back(leak);
- }
- if (flags()->report_objects) {
- LeakedObject obj = {leaks_[i].id, chunk, leaked_size};
- leaked_objects_.push_back(obj);
- }
-}
-
-static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
- if (leak1.is_directly_leaked == leak2.is_directly_leaked)
- return leak1.total_size > leak2.total_size;
- else
- return leak1.is_directly_leaked;
-}
-
-void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) {
- CHECK(leaks_.size() <= kMaxLeaksConsidered);
- Printf("\n");
- if (leaks_.size() == kMaxLeaksConsidered)
- Printf("Too many leaks! Only the first %zu leaks encountered will be "
- "reported.\n",
- kMaxLeaksConsidered);
-
- uptr unsuppressed_count = UnsuppressedLeakCount();
- if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count)
- Printf("The %zu top leak(s):\n", num_leaks_to_report);
- Sort(leaks_.data(), leaks_.size(), &LeakComparator);
- uptr leaks_reported = 0;
- for (uptr i = 0; i < leaks_.size(); i++) {
- if (leaks_[i].is_suppressed) continue;
- PrintReportForLeak(i);
- leaks_reported++;
- if (leaks_reported == num_leaks_to_report) break;
- }
- if (leaks_reported < unsuppressed_count) {
- uptr remaining = unsuppressed_count - leaks_reported;
- Printf("Omitting %zu more leak(s).\n", remaining);
- }
-}
-
-void LeakReport::PrintReportForLeak(uptr index) {
- Decorator d;
- Printf("%s", d.Leak());
- Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
- leaks_[index].is_directly_leaked ? "Direct" : "Indirect",
- leaks_[index].total_size, leaks_[index].hit_count);
- Printf("%s", d.Default());
-
- PrintStackTraceById(leaks_[index].stack_trace_id);
-
- if (flags()->report_objects) {
- Printf("Objects leaked above:\n");
- PrintLeakedObjectsForLeak(index);
- Printf("\n");
- }
-}
-
-void LeakReport::PrintLeakedObjectsForLeak(uptr index) {
- u32 leak_id = leaks_[index].id;
- for (uptr j = 0; j < leaked_objects_.size(); j++) {
- if (leaked_objects_[j].leak_id == leak_id)
- Printf("%p (%zu bytes)\n", leaked_objects_[j].addr,
- leaked_objects_[j].size);
- }
-}
-
-void LeakReport::PrintSummary() {
- CHECK(leaks_.size() <= kMaxLeaksConsidered);
- uptr bytes = 0, allocations = 0;
- for (uptr i = 0; i < leaks_.size(); i++) {
- if (leaks_[i].is_suppressed) continue;
- bytes += leaks_[i].total_size;
- allocations += leaks_[i].hit_count;
- }
- InternalScopedString summary(kMaxSummaryLength);
- summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes,
- allocations);
- ReportErrorSummary(summary.data());
-}
-
-void LeakReport::ApplySuppressions() {
- for (uptr i = 0; i < leaks_.size(); i++) {
- Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
- if (s) {
- s->weight += leaks_[i].total_size;
- atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
- leaks_[i].hit_count);
- leaks_[i].is_suppressed = true;
- }
- }
-}
-
-uptr LeakReport::UnsuppressedLeakCount() {
- uptr result = 0;
- for (uptr i = 0; i < leaks_.size(); i++)
- if (!leaks_[i].is_suppressed) result++;
- return result;
-}
-
-} // namespace __lsan
-#else // CAN_SANITIZE_LEAKS
-namespace __lsan {
-void InitCommonLsan() { }
-void DoLeakCheck() { }
-void DoRecoverableLeakCheckVoid() { }
-void DisableInThisThread() { }
-void EnableInThisThread() { }
-}
-#endif // CAN_SANITIZE_LEAKS
-
-using namespace __lsan; // NOLINT
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __lsan_ignore_object(const void *p) {
-#if CAN_SANITIZE_LEAKS
- if (!common_flags()->detect_leaks)
- return;
- // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not
- // locked.
- BlockingMutexLock l(&global_mutex);
- IgnoreObjectResult res = IgnoreObjectLocked(p);
- if (res == kIgnoreObjectInvalid)
- VReport(1, "__lsan_ignore_object(): no heap object found at %p", p);
- if (res == kIgnoreObjectAlreadyIgnored)
- VReport(1, "__lsan_ignore_object(): "
- "heap object at %p is already being ignored\n", p);
- if (res == kIgnoreObjectSuccess)
- VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p);
-#endif // CAN_SANITIZE_LEAKS
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __lsan_register_root_region(const void *begin, uptr size) {
-#if CAN_SANITIZE_LEAKS
- BlockingMutexLock l(&global_mutex);
- CHECK(root_regions);
- RootRegion region = {reinterpret_cast<uptr>(begin), size};
- root_regions->push_back(region);
- VReport(1, "Registered root region at %p of size %llu\n", begin, size);
-#endif // CAN_SANITIZE_LEAKS
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __lsan_unregister_root_region(const void *begin, uptr size) {
-#if CAN_SANITIZE_LEAKS
- BlockingMutexLock l(&global_mutex);
- CHECK(root_regions);
- bool removed = false;
- for (uptr i = 0; i < root_regions->size(); i++) {
- RootRegion region = (*root_regions)[i];
- if (region.begin == reinterpret_cast<uptr>(begin) && region.size == size) {
- removed = true;
- uptr last_index = root_regions->size() - 1;
- (*root_regions)[i] = (*root_regions)[last_index];
- root_regions->pop_back();
- VReport(1, "Unregistered root region at %p of size %llu\n", begin, size);
- break;
- }
- }
- if (!removed) {
- Report(
- "__lsan_unregister_root_region(): region at %p of size %llu has not "
- "been registered.\n",
- begin, size);
- Die();
- }
-#endif // CAN_SANITIZE_LEAKS
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __lsan_disable() {
-#if CAN_SANITIZE_LEAKS
- __lsan::DisableInThisThread();
-#endif
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __lsan_enable() {
-#if CAN_SANITIZE_LEAKS
- __lsan::EnableInThisThread();
-#endif
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __lsan_do_leak_check() {
-#if CAN_SANITIZE_LEAKS
- if (common_flags()->detect_leaks)
- __lsan::DoLeakCheck();
-#endif // CAN_SANITIZE_LEAKS
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __lsan_do_recoverable_leak_check() {
-#if CAN_SANITIZE_LEAKS
- if (common_flags()->detect_leaks)
- return __lsan::DoRecoverableLeakCheck();
-#endif // CAN_SANITIZE_LEAKS
- return 0;
-}
-
-#if !SANITIZER_SUPPORTS_WEAK_HOOKS
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-const char * __lsan_default_options() {
- return "";
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-int __lsan_is_turned_off() {
- return 0;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-const char *__lsan_default_suppressions() {
- return "";
-}
-#endif
-} // extern "C"
--- /dev/null
+//=-- lsan_common.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_common.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_thread_registry.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+
+#if CAN_SANITIZE_LEAKS
+namespace __lsan {
+
+// This mutex is used to prevent races between DoLeakCheck and IgnoreObject, and
+// also to protect the global list of root regions.
+BlockingMutex global_mutex(LINKER_INITIALIZED);
+
+Flags lsan_flags;
+
+void DisableCounterUnderflow() {
+ if (common_flags()->detect_leaks) {
+ Report("Unmatched call to __lsan_enable().\n");
+ Die();
+ }
+}
+
+void Flags::SetDefaults() {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+}
+
+void RegisterLsanFlags(FlagParser *parser, Flags *f) {
+#define LSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "lsan_flags.inc"
+#undef LSAN_FLAG
+}
+
+#define LOG_POINTERS(...) \
+ do { \
+ if (flags()->log_pointers) Report(__VA_ARGS__); \
+ } while (0)
+
+#define LOG_THREADS(...) \
+ do { \
+ if (flags()->log_threads) Report(__VA_ARGS__); \
+ } while (0)
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kSuppressionLeak[] = "leak";
+static const char *kSuppressionTypes[] = { kSuppressionLeak };
+static const char kStdSuppressions[] =
+#if SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
+ // For more details refer to the SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
+ // definition.
+ "leak:*pthread_exit*\n"
+#endif // SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT
+#if SANITIZER_MAC
+ // For Darwin and os_log/os_trace: https://reviews.llvm.org/D35173
+ "leak:*_os_trace*\n"
+#endif
+ // TLS leak in some glibc versions, described in
+ // https://sourceware.org/bugzilla/show_bug.cgi?id=12650.
+ "leak:*tls_get_addr*\n";
+
+void InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+ if (&__lsan_default_suppressions)
+ suppression_ctx->Parse(__lsan_default_suppressions());
+ suppression_ctx->Parse(kStdSuppressions);
+}
+
+static SuppressionContext *GetSuppressionContext() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
+}
+
+static InternalMmapVector<RootRegion> *root_regions;
+
+InternalMmapVector<RootRegion> const *GetRootRegions() { return root_regions; }
+
+void InitializeRootRegions() {
+ CHECK(!root_regions);
+ ALIGNED(64) static char placeholder[sizeof(InternalMmapVector<RootRegion>)];
+ root_regions = new (placeholder) InternalMmapVector<RootRegion>(); // NOLINT
+}
+
+const char *MaybeCallLsanDefaultOptions() {
+ return (&__lsan_default_options) ? __lsan_default_options() : "";
+}
+
+void InitCommonLsan() {
+ InitializeRootRegions();
+ if (common_flags()->detect_leaks) {
+ // Initialization which can fail or print warnings should only be done if
+ // LSan is actually enabled.
+ InitializeSuppressions();
+ InitializePlatformSpecificModules();
+ }
+}
+
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
+ public:
+ Decorator() : SanitizerCommonDecorator() { }
+ const char *Error() { return Red(); }
+ const char *Leak() { return Blue(); }
+};
+
+static inline bool CanBeAHeapPointer(uptr p) {
+ // Since our heap is located in mmap-ed memory, we can assume a sensible lower
+ // bound on heap addresses.
+ const uptr kMinAddress = 4 * 4096;
+ if (p < kMinAddress) return false;
+#if defined(__x86_64__)
+ // Accept only canonical form user-space addresses.
+ return ((p >> 47) == 0);
+#elif defined(__mips64)
+ return ((p >> 40) == 0);
+#elif defined(__aarch64__)
+ unsigned runtimeVMA =
+ (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+ return ((p >> runtimeVMA) == 0);
+#else
+ return true;
+#endif
+}
+
+// Scans the memory range, looking for byte patterns that point into allocator
+// chunks. Marks those chunks with |tag| and adds them to |frontier|.
+// There are two usage modes for this function: finding reachable chunks
+// (|tag| = kReachable) and finding indirectly leaked chunks
+// (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill,
+// so |frontier| = 0.
+void ScanRangeForPointers(uptr begin, uptr end,
+ Frontier *frontier,
+ const char *region_type, ChunkTag tag) {
+ CHECK(tag == kReachable || tag == kIndirectlyLeaked);
+ const uptr alignment = flags()->pointer_alignment();
+ LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end);
+ uptr pp = begin;
+ if (pp % alignment)
+ pp = pp + alignment - pp % alignment;
+ for (; pp + sizeof(void *) <= end; pp += alignment) { // NOLINT
+ void *p = *reinterpret_cast<void **>(pp);
+ if (!CanBeAHeapPointer(reinterpret_cast<uptr>(p))) continue;
+ uptr chunk = PointsIntoChunk(p);
+ if (!chunk) continue;
+ // Pointers to self don't count. This matters when tag == kIndirectlyLeaked.
+ if (chunk == begin) continue;
+ LsanMetadata m(chunk);
+ if (m.tag() == kReachable || m.tag() == kIgnored) continue;
+
+ // Do this check relatively late so we can log only the interesting cases.
+ if (!flags()->use_poisoned && WordIsPoisoned(pp)) {
+ LOG_POINTERS(
+ "%p is poisoned: ignoring %p pointing into chunk %p-%p of size "
+ "%zu.\n",
+ pp, p, chunk, chunk + m.requested_size(), m.requested_size());
+ continue;
+ }
+
+ m.set_tag(tag);
+ LOG_POINTERS("%p: found %p pointing into chunk %p-%p of size %zu.\n", pp, p,
+ chunk, chunk + m.requested_size(), m.requested_size());
+ if (frontier)
+ frontier->push_back(chunk);
+ }
+}
+
+// Scans a global range for pointers
+void ScanGlobalRange(uptr begin, uptr end, Frontier *frontier) {
+ uptr allocator_begin = 0, allocator_end = 0;
+ GetAllocatorGlobalRange(&allocator_begin, &allocator_end);
+ if (begin <= allocator_begin && allocator_begin < end) {
+ CHECK_LE(allocator_begin, allocator_end);
+ CHECK_LE(allocator_end, end);
+ if (begin < allocator_begin)
+ ScanRangeForPointers(begin, allocator_begin, frontier, "GLOBAL",
+ kReachable);
+ if (allocator_end < end)
+ ScanRangeForPointers(allocator_end, end, frontier, "GLOBAL", kReachable);
+ } else {
+ ScanRangeForPointers(begin, end, frontier, "GLOBAL", kReachable);
+ }
+}
+
+void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
+ Frontier *frontier = reinterpret_cast<Frontier *>(arg);
+ ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
+}
+
+// Scans thread data (stacks and TLS) for heap pointers.
+static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
+ Frontier *frontier) {
+ InternalMmapVector<uptr> registers(suspended_threads.RegisterCount());
+ uptr registers_begin = reinterpret_cast<uptr>(registers.data());
+ uptr registers_end =
+ reinterpret_cast<uptr>(registers.data() + registers.size());
+ for (uptr i = 0; i < suspended_threads.ThreadCount(); i++) {
+ tid_t os_id = static_cast<tid_t>(suspended_threads.GetThreadID(i));
+ LOG_THREADS("Processing thread %d.\n", os_id);
+ uptr stack_begin, stack_end, tls_begin, tls_end, cache_begin, cache_end;
+ DTLS *dtls;
+ bool thread_found = GetThreadRangesLocked(os_id, &stack_begin, &stack_end,
+ &tls_begin, &tls_end,
+ &cache_begin, &cache_end, &dtls);
+ if (!thread_found) {
+ // If a thread can't be found in the thread registry, it's probably in the
+ // process of destruction. Log this event and move on.
+ LOG_THREADS("Thread %d not found in registry.\n", os_id);
+ continue;
+ }
+ uptr sp;
+ PtraceRegistersStatus have_registers =
+ suspended_threads.GetRegistersAndSP(i, registers.data(), &sp);
+ if (have_registers != REGISTERS_AVAILABLE) {
+ Report("Unable to get registers from thread %d.\n", os_id);
+ // If unable to get SP, consider the entire stack to be reachable unless
+ // GetRegistersAndSP failed with ESRCH.
+ if (have_registers == REGISTERS_UNAVAILABLE_FATAL) continue;
+ sp = stack_begin;
+ }
+
+ if (flags()->use_registers && have_registers)
+ ScanRangeForPointers(registers_begin, registers_end, frontier,
+ "REGISTERS", kReachable);
+
+ if (flags()->use_stacks) {
+ LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
+ if (sp < stack_begin || sp >= stack_end) {
+ // SP is outside the recorded stack range (e.g. the thread is running a
+ // signal handler on alternate stack, or swapcontext was used).
+ // Again, consider the entire stack range to be reachable.
+ LOG_THREADS("WARNING: stack pointer not in stack range.\n");
+ uptr page_size = GetPageSizeCached();
+ int skipped = 0;
+ while (stack_begin < stack_end &&
+ !IsAccessibleMemoryRange(stack_begin, 1)) {
+ skipped++;
+ stack_begin += page_size;
+ }
+ LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n",
+ skipped, stack_begin, stack_end);
+ } else {
+ // Shrink the stack range to ignore out-of-scope values.
+ stack_begin = sp;
+ }
+ ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
+ kReachable);
+ ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
+ }
+
+ if (flags()->use_tls) {
+ if (tls_begin) {
+ LOG_THREADS("TLS at %p-%p.\n", tls_begin, tls_end);
+ // If the tls and cache ranges don't overlap, scan full tls range,
+ // otherwise, only scan the non-overlapping portions
+ if (cache_begin == cache_end || tls_end < cache_begin ||
+ tls_begin > cache_end) {
+ ScanRangeForPointers(tls_begin, tls_end, frontier, "TLS", kReachable);
+ } else {
+ if (tls_begin < cache_begin)
+ ScanRangeForPointers(tls_begin, cache_begin, frontier, "TLS",
+ kReachable);
+ if (tls_end > cache_end)
+ ScanRangeForPointers(cache_end, tls_end, frontier, "TLS",
+ kReachable);
+ }
+ }
+ if (dtls && !DTLSInDestruction(dtls)) {
+ for (uptr j = 0; j < dtls->dtv_size; ++j) {
+ uptr dtls_beg = dtls->dtv[j].beg;
+ uptr dtls_end = dtls_beg + dtls->dtv[j].size;
+ if (dtls_beg < dtls_end) {
+ LOG_THREADS("DTLS %zu at %p-%p.\n", j, dtls_beg, dtls_end);
+ ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS",
+ kReachable);
+ }
+ }
+ } else {
+ // We are handling a thread with DTLS under destruction. Log about
+ // this and continue.
+ LOG_THREADS("Thread %d has DTLS under destruction.\n", os_id);
+ }
+ }
+ }
+}
+
+void ScanRootRegion(Frontier *frontier, const RootRegion &root_region,
+ uptr region_begin, uptr region_end, bool is_readable) {
+ uptr intersection_begin = Max(root_region.begin, region_begin);
+ uptr intersection_end = Min(region_end, root_region.begin + root_region.size);
+ if (intersection_begin >= intersection_end) return;
+ LOG_POINTERS("Root region %p-%p intersects with mapped region %p-%p (%s)\n",
+ root_region.begin, root_region.begin + root_region.size,
+ region_begin, region_end,
+ is_readable ? "readable" : "unreadable");
+ if (is_readable)
+ ScanRangeForPointers(intersection_begin, intersection_end, frontier, "ROOT",
+ kReachable);
+}
+
+static void ProcessRootRegion(Frontier *frontier,
+ const RootRegion &root_region) {
+ MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
+ MemoryMappedSegment segment;
+ while (proc_maps.Next(&segment)) {
+ ScanRootRegion(frontier, root_region, segment.start, segment.end,
+ segment.IsReadable());
+ }
+}
+
+// Scans root regions for heap pointers.
+static void ProcessRootRegions(Frontier *frontier) {
+ if (!flags()->use_root_regions) return;
+ CHECK(root_regions);
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ ProcessRootRegion(frontier, (*root_regions)[i]);
+ }
+}
+
+static void FloodFillTag(Frontier *frontier, ChunkTag tag) {
+ while (frontier->size()) {
+ uptr next_chunk = frontier->back();
+ frontier->pop_back();
+ LsanMetadata m(next_chunk);
+ ScanRangeForPointers(next_chunk, next_chunk + m.requested_size(), frontier,
+ "HEAP", tag);
+ }
+}
+
+// ForEachChunk callback. If the chunk is marked as leaked, marks all chunks
+// which are reachable from it as indirectly leaked.
+static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) {
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (m.allocated() && m.tag() != kReachable) {
+ ScanRangeForPointers(chunk, chunk + m.requested_size(),
+ /* frontier */ nullptr, "HEAP", kIndirectlyLeaked);
+ }
+}
+
+// ForEachChunk callback. If chunk is marked as ignored, adds its address to
+// frontier.
+static void CollectIgnoredCb(uptr chunk, void *arg) {
+ CHECK(arg);
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (m.allocated() && m.tag() == kIgnored) {
+ LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n",
+ chunk, chunk + m.requested_size(), m.requested_size());
+ reinterpret_cast<Frontier *>(arg)->push_back(chunk);
+ }
+}
+
+static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
+ CHECK(stack_id);
+ StackTrace stack = map->Get(stack_id);
+ // The top frame is our malloc/calloc/etc. The next frame is the caller.
+ if (stack.size >= 2)
+ return stack.trace[1];
+ return 0;
+}
+
+struct InvalidPCParam {
+ Frontier *frontier;
+ StackDepotReverseMap *stack_depot_reverse_map;
+ bool skip_linker_allocations;
+};
+
+// ForEachChunk callback. If the caller pc is invalid or is within the linker,
+// mark as reachable. Called by ProcessPlatformSpecificAllocations.
+static void MarkInvalidPCCb(uptr chunk, void *arg) {
+ CHECK(arg);
+ InvalidPCParam *param = reinterpret_cast<InvalidPCParam *>(arg);
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) {
+ u32 stack_id = m.stack_trace_id();
+ uptr caller_pc = 0;
+ if (stack_id > 0)
+ caller_pc = GetCallerPC(stack_id, param->stack_depot_reverse_map);
+ // If caller_pc is unknown, this chunk may be allocated in a coroutine. Mark
+ // it as reachable, as we can't properly report its allocation stack anyway.
+ if (caller_pc == 0 || (param->skip_linker_allocations &&
+ GetLinker()->containsAddress(caller_pc))) {
+ m.set_tag(kReachable);
+ param->frontier->push_back(chunk);
+ }
+ }
+}
+
+// On Linux, treats all chunks allocated from ld-linux.so as reachable, which
+// covers dynamically allocated TLS blocks, internal dynamic loader's loaded
+// modules accounting etc.
+// Dynamic TLS blocks contain the TLS variables of dynamically loaded modules.
+// They are allocated with a __libc_memalign() call in allocate_and_init()
+// (elf/dl-tls.c). Glibc won't tell us the address ranges occupied by those
+// blocks, but we can make sure they come from our own allocator by intercepting
+// __libc_memalign(). On top of that, there is no easy way to reach them. Their
+// addresses are stored in a dynamically allocated array (the DTV) which is
+// referenced from the static TLS. Unfortunately, we can't just rely on the DTV
+// being reachable from the static TLS, and the dynamic TLS being reachable from
+// the DTV. This is because the initial DTV is allocated before our interception
+// mechanism kicks in, and thus we don't recognize it as allocated memory. We
+// can't special-case it either, since we don't know its size.
+// Our solution is to include in the root set all allocations made from
+// ld-linux.so (which is where allocate_and_init() is implemented). This is
+// guaranteed to include all dynamic TLS blocks (and possibly other allocations
+// which we don't care about).
+// On all other platforms, this simply checks to ensure that the caller pc is
+// valid before reporting chunks as leaked.
+void ProcessPC(Frontier *frontier) {
+ StackDepotReverseMap stack_depot_reverse_map;
+ InvalidPCParam arg;
+ arg.frontier = frontier;
+ arg.stack_depot_reverse_map = &stack_depot_reverse_map;
+ arg.skip_linker_allocations =
+ flags()->use_tls && flags()->use_ld_allocations && GetLinker() != nullptr;
+ ForEachChunk(MarkInvalidPCCb, &arg);
+}
+
+// Sets the appropriate tag on each chunk.
+static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
+ // Holds the flood fill frontier.
+ Frontier frontier;
+
+ ForEachChunk(CollectIgnoredCb, &frontier);
+ ProcessGlobalRegions(&frontier);
+ ProcessThreads(suspended_threads, &frontier);
+ ProcessRootRegions(&frontier);
+ FloodFillTag(&frontier, kReachable);
+
+ CHECK_EQ(0, frontier.size());
+ ProcessPC(&frontier);
+
+ // The check here is relatively expensive, so we do this in a separate flood
+ // fill. That way we can skip the check for chunks that are reachable
+ // otherwise.
+ LOG_POINTERS("Processing platform-specific allocations.\n");
+ ProcessPlatformSpecificAllocations(&frontier);
+ FloodFillTag(&frontier, kReachable);
+
+ // Iterate over leaked chunks and mark those that are reachable from other
+ // leaked chunks.
+ LOG_POINTERS("Scanning leaked chunks.\n");
+ ForEachChunk(MarkIndirectlyLeakedCb, nullptr);
+}
+
+// ForEachChunk callback. Resets the tags to pre-leak-check state.
+static void ResetTagsCb(uptr chunk, void *arg) {
+ (void)arg;
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (m.allocated() && m.tag() != kIgnored)
+ m.set_tag(kDirectlyLeaked);
+}
+
+static void PrintStackTraceById(u32 stack_trace_id) {
+ CHECK(stack_trace_id);
+ StackDepotGet(stack_trace_id).Print();
+}
+
+// ForEachChunk callback. Aggregates information about unreachable chunks into
+// a LeakReport.
+static void CollectLeaksCb(uptr chunk, void *arg) {
+ CHECK(arg);
+ LeakReport *leak_report = reinterpret_cast<LeakReport *>(arg);
+ chunk = GetUserBegin(chunk);
+ LsanMetadata m(chunk);
+ if (!m.allocated()) return;
+ if (m.tag() == kDirectlyLeaked || m.tag() == kIndirectlyLeaked) {
+ u32 resolution = flags()->resolution;
+ u32 stack_trace_id = 0;
+ if (resolution > 0) {
+ StackTrace stack = StackDepotGet(m.stack_trace_id());
+ stack.size = Min(stack.size, resolution);
+ stack_trace_id = StackDepotPut(stack);
+ } else {
+ stack_trace_id = m.stack_trace_id();
+ }
+ leak_report->AddLeakedChunk(chunk, stack_trace_id, m.requested_size(),
+ m.tag());
+ }
+}
+
+static void PrintMatchedSuppressions() {
+ InternalMmapVector<Suppression *> matched;
+ GetSuppressionContext()->GetMatched(&matched);
+ if (!matched.size())
+ return;
+ const char *line = "-----------------------------------------------------";
+ Printf("%s\n", line);
+ Printf("Suppressions used:\n");
+ Printf(" count bytes template\n");
+ for (uptr i = 0; i < matched.size(); i++)
+ Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed(
+ &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ);
+ Printf("%s\n\n", line);
+}
+
+struct CheckForLeaksParam {
+ bool success;
+ LeakReport leak_report;
+};
+
+static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) {
+ const InternalMmapVector<tid_t> &suspended_threads =
+ *(const InternalMmapVector<tid_t> *)arg;
+ if (tctx->status == ThreadStatusRunning) {
+ uptr i = InternalLowerBound(suspended_threads, 0, suspended_threads.size(),
+ tctx->os_id, CompareLess<int>());
+ if (i >= suspended_threads.size() || suspended_threads[i] != tctx->os_id)
+ Report("Running thread %d was not suspended. False leaks are possible.\n",
+ tctx->os_id);
+ };
+}
+
+static void ReportUnsuspendedThreads(
+ const SuspendedThreadsList &suspended_threads) {
+ InternalMmapVector<tid_t> threads(suspended_threads.ThreadCount());
+ for (uptr i = 0; i < suspended_threads.ThreadCount(); ++i)
+ threads[i] = suspended_threads.GetThreadID(i);
+
+ Sort(threads.data(), threads.size());
+
+ GetThreadRegistryLocked()->RunCallbackForEachThreadLocked(
+ &ReportIfNotSuspended, &threads);
+}
+
+static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads,
+ void *arg) {
+ CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg);
+ CHECK(param);
+ CHECK(!param->success);
+ ReportUnsuspendedThreads(suspended_threads);
+ ClassifyAllChunks(suspended_threads);
+ ForEachChunk(CollectLeaksCb, ¶m->leak_report);
+ // Clean up for subsequent leak checks. This assumes we did not overwrite any
+ // kIgnored tags.
+ ForEachChunk(ResetTagsCb, nullptr);
+ param->success = true;
+}
+
+static bool CheckForLeaks() {
+ if (&__lsan_is_turned_off && __lsan_is_turned_off())
+ return false;
+ EnsureMainThreadIDIsCorrect();
+ CheckForLeaksParam param;
+ param.success = false;
+ LockThreadRegistry();
+ LockAllocator();
+ DoStopTheWorld(CheckForLeaksCallback, ¶m);
+ UnlockAllocator();
+ UnlockThreadRegistry();
+
+ if (!param.success) {
+ Report("LeakSanitizer has encountered a fatal error.\n");
+ Report(
+ "HINT: For debugging, try setting environment variable "
+ "LSAN_OPTIONS=verbosity=1:log_threads=1\n");
+ Report(
+ "HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)\n");
+ Die();
+ }
+ param.leak_report.ApplySuppressions();
+ uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
+ if (unsuppressed_count > 0) {
+ Decorator d;
+ Printf("\n"
+ "================================================================="
+ "\n");
+ Printf("%s", d.Error());
+ Report("ERROR: LeakSanitizer: detected memory leaks\n");
+ Printf("%s", d.Default());
+ param.leak_report.ReportTopLeaks(flags()->max_leaks);
+ }
+ if (common_flags()->print_suppressions)
+ PrintMatchedSuppressions();
+ if (unsuppressed_count > 0) {
+ param.leak_report.PrintSummary();
+ return true;
+ }
+ return false;
+}
+
+static bool has_reported_leaks = false;
+bool HasReportedLeaks() { return has_reported_leaks; }
+
+void DoLeakCheck() {
+ BlockingMutexLock l(&global_mutex);
+ static bool already_done;
+ if (already_done) return;
+ already_done = true;
+ has_reported_leaks = CheckForLeaks();
+ if (has_reported_leaks) HandleLeaks();
+}
+
+static int DoRecoverableLeakCheck() {
+ BlockingMutexLock l(&global_mutex);
+ bool have_leaks = CheckForLeaks();
+ return have_leaks ? 1 : 0;
+}
+
+void DoRecoverableLeakCheckVoid() { DoRecoverableLeakCheck(); }
+
+static Suppression *GetSuppressionForAddr(uptr addr) {
+ Suppression *s = nullptr;
+
+ // Suppress by module name.
+ SuppressionContext *suppressions = GetSuppressionContext();
+ if (const char *module_name =
+ Symbolizer::GetOrInit()->GetModuleNameForPc(addr))
+ if (suppressions->Match(module_name, kSuppressionLeak, &s))
+ return s;
+
+ // Suppress by file or function name.
+ SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) ||
+ suppressions->Match(cur->info.file, kSuppressionLeak, &s)) {
+ break;
+ }
+ }
+ frames->ClearAll();
+ return s;
+}
+
+static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
+ StackTrace stack = StackDepotGet(stack_trace_id);
+ for (uptr i = 0; i < stack.size; i++) {
+ Suppression *s = GetSuppressionForAddr(
+ StackTrace::GetPreviousInstructionPc(stack.trace[i]));
+ if (s) return s;
+ }
+ return nullptr;
+}
+
+///// LeakReport implementation. /////
+
+// A hard limit on the number of distinct leaks, to avoid quadratic complexity
+// in LeakReport::AddLeakedChunk(). We don't expect to ever see this many leaks
+// in real-world applications.
+// FIXME: Get rid of this limit by changing the implementation of LeakReport to
+// use a hash table.
+const uptr kMaxLeaksConsidered = 5000;
+
+void LeakReport::AddLeakedChunk(uptr chunk, u32 stack_trace_id,
+ uptr leaked_size, ChunkTag tag) {
+ CHECK(tag == kDirectlyLeaked || tag == kIndirectlyLeaked);
+ bool is_directly_leaked = (tag == kDirectlyLeaked);
+ uptr i;
+ for (i = 0; i < leaks_.size(); i++) {
+ if (leaks_[i].stack_trace_id == stack_trace_id &&
+ leaks_[i].is_directly_leaked == is_directly_leaked) {
+ leaks_[i].hit_count++;
+ leaks_[i].total_size += leaked_size;
+ break;
+ }
+ }
+ if (i == leaks_.size()) {
+ if (leaks_.size() == kMaxLeaksConsidered) return;
+ Leak leak = { next_id_++, /* hit_count */ 1, leaked_size, stack_trace_id,
+ is_directly_leaked, /* is_suppressed */ false };
+ leaks_.push_back(leak);
+ }
+ if (flags()->report_objects) {
+ LeakedObject obj = {leaks_[i].id, chunk, leaked_size};
+ leaked_objects_.push_back(obj);
+ }
+}
+
+static bool LeakComparator(const Leak &leak1, const Leak &leak2) {
+ if (leak1.is_directly_leaked == leak2.is_directly_leaked)
+ return leak1.total_size > leak2.total_size;
+ else
+ return leak1.is_directly_leaked;
+}
+
+void LeakReport::ReportTopLeaks(uptr num_leaks_to_report) {
+ CHECK(leaks_.size() <= kMaxLeaksConsidered);
+ Printf("\n");
+ if (leaks_.size() == kMaxLeaksConsidered)
+ Printf("Too many leaks! Only the first %zu leaks encountered will be "
+ "reported.\n",
+ kMaxLeaksConsidered);
+
+ uptr unsuppressed_count = UnsuppressedLeakCount();
+ if (num_leaks_to_report > 0 && num_leaks_to_report < unsuppressed_count)
+ Printf("The %zu top leak(s):\n", num_leaks_to_report);
+ Sort(leaks_.data(), leaks_.size(), &LeakComparator);
+ uptr leaks_reported = 0;
+ for (uptr i = 0; i < leaks_.size(); i++) {
+ if (leaks_[i].is_suppressed) continue;
+ PrintReportForLeak(i);
+ leaks_reported++;
+ if (leaks_reported == num_leaks_to_report) break;
+ }
+ if (leaks_reported < unsuppressed_count) {
+ uptr remaining = unsuppressed_count - leaks_reported;
+ Printf("Omitting %zu more leak(s).\n", remaining);
+ }
+}
+
+void LeakReport::PrintReportForLeak(uptr index) {
+ Decorator d;
+ Printf("%s", d.Leak());
+ Printf("%s leak of %zu byte(s) in %zu object(s) allocated from:\n",
+ leaks_[index].is_directly_leaked ? "Direct" : "Indirect",
+ leaks_[index].total_size, leaks_[index].hit_count);
+ Printf("%s", d.Default());
+
+ PrintStackTraceById(leaks_[index].stack_trace_id);
+
+ if (flags()->report_objects) {
+ Printf("Objects leaked above:\n");
+ PrintLeakedObjectsForLeak(index);
+ Printf("\n");
+ }
+}
+
+void LeakReport::PrintLeakedObjectsForLeak(uptr index) {
+ u32 leak_id = leaks_[index].id;
+ for (uptr j = 0; j < leaked_objects_.size(); j++) {
+ if (leaked_objects_[j].leak_id == leak_id)
+ Printf("%p (%zu bytes)\n", leaked_objects_[j].addr,
+ leaked_objects_[j].size);
+ }
+}
+
+void LeakReport::PrintSummary() {
+ CHECK(leaks_.size() <= kMaxLeaksConsidered);
+ uptr bytes = 0, allocations = 0;
+ for (uptr i = 0; i < leaks_.size(); i++) {
+ if (leaks_[i].is_suppressed) continue;
+ bytes += leaks_[i].total_size;
+ allocations += leaks_[i].hit_count;
+ }
+ InternalScopedString summary(kMaxSummaryLength);
+ summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes,
+ allocations);
+ ReportErrorSummary(summary.data());
+}
+
+void LeakReport::ApplySuppressions() {
+ for (uptr i = 0; i < leaks_.size(); i++) {
+ Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id);
+ if (s) {
+ s->weight += leaks_[i].total_size;
+ atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) +
+ leaks_[i].hit_count);
+ leaks_[i].is_suppressed = true;
+ }
+ }
+}
+
+uptr LeakReport::UnsuppressedLeakCount() {
+ uptr result = 0;
+ for (uptr i = 0; i < leaks_.size(); i++)
+ if (!leaks_[i].is_suppressed) result++;
+ return result;
+}
+
+} // namespace __lsan
+#else // CAN_SANITIZE_LEAKS
+namespace __lsan {
+void InitCommonLsan() { }
+void DoLeakCheck() { }
+void DoRecoverableLeakCheckVoid() { }
+void DisableInThisThread() { }
+void EnableInThisThread() { }
+}
+#endif // CAN_SANITIZE_LEAKS
+
+using namespace __lsan; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_ignore_object(const void *p) {
+#if CAN_SANITIZE_LEAKS
+ if (!common_flags()->detect_leaks)
+ return;
+ // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not
+ // locked.
+ BlockingMutexLock l(&global_mutex);
+ IgnoreObjectResult res = IgnoreObjectLocked(p);
+ if (res == kIgnoreObjectInvalid)
+ VReport(1, "__lsan_ignore_object(): no heap object found at %p", p);
+ if (res == kIgnoreObjectAlreadyIgnored)
+ VReport(1, "__lsan_ignore_object(): "
+ "heap object at %p is already being ignored\n", p);
+ if (res == kIgnoreObjectSuccess)
+ VReport(1, "__lsan_ignore_object(): ignoring heap object at %p\n", p);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_register_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+ BlockingMutexLock l(&global_mutex);
+ CHECK(root_regions);
+ RootRegion region = {reinterpret_cast<uptr>(begin), size};
+ root_regions->push_back(region);
+ VReport(1, "Registered root region at %p of size %llu\n", begin, size);
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_unregister_root_region(const void *begin, uptr size) {
+#if CAN_SANITIZE_LEAKS
+ BlockingMutexLock l(&global_mutex);
+ CHECK(root_regions);
+ bool removed = false;
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ RootRegion region = (*root_regions)[i];
+ if (region.begin == reinterpret_cast<uptr>(begin) && region.size == size) {
+ removed = true;
+ uptr last_index = root_regions->size() - 1;
+ (*root_regions)[i] = (*root_regions)[last_index];
+ root_regions->pop_back();
+ VReport(1, "Unregistered root region at %p of size %llu\n", begin, size);
+ break;
+ }
+ }
+ if (!removed) {
+ Report(
+ "__lsan_unregister_root_region(): region at %p of size %llu has not "
+ "been registered.\n",
+ begin, size);
+ Die();
+ }
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_disable() {
+#if CAN_SANITIZE_LEAKS
+ __lsan::DisableInThisThread();
+#endif
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_enable() {
+#if CAN_SANITIZE_LEAKS
+ __lsan::EnableInThisThread();
+#endif
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __lsan_do_leak_check() {
+#if CAN_SANITIZE_LEAKS
+ if (common_flags()->detect_leaks)
+ __lsan::DoLeakCheck();
+#endif // CAN_SANITIZE_LEAKS
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __lsan_do_recoverable_leak_check() {
+#if CAN_SANITIZE_LEAKS
+ if (common_flags()->detect_leaks)
+ return __lsan::DoRecoverableLeakCheck();
+#endif // CAN_SANITIZE_LEAKS
+ return 0;
+}
+
+#if !SANITIZER_SUPPORTS_WEAK_HOOKS
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char * __lsan_default_options() {
+ return "";
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+int __lsan_is_turned_off() {
+ return 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+const char *__lsan_default_suppressions() {
+ return "";
+}
+#endif
+} // extern "C"
//=-- lsan_common.h -------------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
-// LeakSanitizer relies on some Glibc's internals (e.g. TLS machinery) thus
-// supported for Linux only. Also, LSan doesn't like 32 bit architectures
+// LeakSanitizer relies on some Glibc's internals (e.g. TLS machinery) on Linux.
+// Also, LSan doesn't like 32 bit architectures
// because of "small" (4 bytes) pointer size that leads to high false negative
// ratio on large leaks. But we still want to have it for some 32 bit arches
// (e.g. x86), see https://github.com/google/sanitizers/issues/403.
#elif defined(__arm__) && \
SANITIZER_LINUX && !SANITIZER_ANDROID
#define CAN_SANITIZE_LEAKS 1
+#elif SANITIZER_NETBSD
+#define CAN_SANITIZE_LEAKS 1
#else
#define CAN_SANITIZE_LEAKS 0
#endif
+++ /dev/null
-//=-- lsan_common_linux.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// Implementation of common leak checking functionality. Linux-specific code.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#include "lsan_common.h"
-
-#if CAN_SANITIZE_LEAKS && SANITIZER_LINUX
-#include <link.h>
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_getauxval.h"
-#include "sanitizer_common/sanitizer_linux.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-
-namespace __lsan {
-
-static const char kLinkerName[] = "ld";
-
-static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
-static LoadedModule *linker = nullptr;
-
-static bool IsLinker(const LoadedModule& module) {
-#if SANITIZER_USE_GETAUXVAL
- return module.base_address() == getauxval(AT_BASE);
-#else
- return LibraryNameIs(module.full_name(), kLinkerName);
-#endif // SANITIZER_USE_GETAUXVAL
-}
-
-__attribute__((tls_model("initial-exec")))
-THREADLOCAL int disable_counter;
-bool DisabledInThisThread() { return disable_counter > 0; }
-void DisableInThisThread() { disable_counter++; }
-void EnableInThisThread() {
- if (disable_counter == 0) {
- DisableCounterUnderflow();
- }
- disable_counter--;
-}
-
-void InitializePlatformSpecificModules() {
- ListOfModules modules;
- modules.init();
- for (LoadedModule &module : modules) {
- if (!IsLinker(module))
- continue;
- if (linker == nullptr) {
- linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
- *linker = module;
- module = LoadedModule();
- } else {
- VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
- "TLS and other allocations originating from linker might be "
- "falsely reported as leaks.\n", kLinkerName);
- linker->clear();
- linker = nullptr;
- return;
- }
- }
- if (linker == nullptr) {
- VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other "
- "allocations originating from linker might be falsely reported "
- "as leaks.\n");
- }
-}
-
-static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
- void *data) {
- Frontier *frontier = reinterpret_cast<Frontier *>(data);
- for (uptr j = 0; j < info->dlpi_phnum; j++) {
- const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
- // We're looking for .data and .bss sections, which reside in writeable,
- // loadable segments.
- if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
- (phdr->p_memsz == 0))
- continue;
- uptr begin = info->dlpi_addr + phdr->p_vaddr;
- uptr end = begin + phdr->p_memsz;
- ScanGlobalRange(begin, end, frontier);
- }
- return 0;
-}
-
-// Scans global variables for heap pointers.
-void ProcessGlobalRegions(Frontier *frontier) {
- if (!flags()->use_globals) return;
- dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
-}
-
-LoadedModule *GetLinker() { return linker; }
-
-void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
-
-struct DoStopTheWorldParam {
- StopTheWorldCallback callback;
- void *argument;
-};
-
-// While calling Die() here is undefined behavior and can potentially
-// cause race conditions, it isn't possible to intercept exit on linux,
-// so we have no choice but to call Die() from the atexit handler.
-void HandleLeaks() {
- if (common_flags()->exitcode) Die();
-}
-
-static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
- void *data) {
- DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
- StopTheWorld(param->callback, param->argument);
- return 1;
-}
-
-// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
-// of the threads is frozen while holding the libdl lock, the tracer will hang
-// in dl_iterate_phdr() forever.
-// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
-// tracer task and the thread that spawned it. Thus, if we run the tracer task
-// while holding the libdl lock in the parent thread, we can safely reenter it
-// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
-// callback in the parent thread.
-void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
- DoStopTheWorldParam param = {callback, argument};
- dl_iterate_phdr(DoStopTheWorldCallback, ¶m);
-}
-
-} // namespace __lsan
-
-#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
--- /dev/null
+//=-- lsan_common_linux.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality. Linux/NetBSD-specific
+// code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "lsan_common.h"
+
+#if CAN_SANITIZE_LEAKS && (SANITIZER_LINUX || SANITIZER_NETBSD)
+#include <link.h>
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_getauxval.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __lsan {
+
+static const char kLinkerName[] = "ld";
+
+static char linker_placeholder[sizeof(LoadedModule)] ALIGNED(64);
+static LoadedModule *linker = nullptr;
+
+static bool IsLinker(const LoadedModule& module) {
+#if SANITIZER_USE_GETAUXVAL
+ return module.base_address() == getauxval(AT_BASE);
+#else
+ return LibraryNameIs(module.full_name(), kLinkerName);
+#endif // SANITIZER_USE_GETAUXVAL
+}
+
+__attribute__((tls_model("initial-exec")))
+THREADLOCAL int disable_counter;
+bool DisabledInThisThread() { return disable_counter > 0; }
+void DisableInThisThread() { disable_counter++; }
+void EnableInThisThread() {
+ if (disable_counter == 0) {
+ DisableCounterUnderflow();
+ }
+ disable_counter--;
+}
+
+void InitializePlatformSpecificModules() {
+ ListOfModules modules;
+ modules.init();
+ for (LoadedModule &module : modules) {
+ if (!IsLinker(module))
+ continue;
+ if (linker == nullptr) {
+ linker = reinterpret_cast<LoadedModule *>(linker_placeholder);
+ *linker = module;
+ module = LoadedModule();
+ } else {
+ VReport(1, "LeakSanitizer: Multiple modules match \"%s\". "
+ "TLS and other allocations originating from linker might be "
+ "falsely reported as leaks.\n", kLinkerName);
+ linker->clear();
+ linker = nullptr;
+ return;
+ }
+ }
+ if (linker == nullptr) {
+ VReport(1, "LeakSanitizer: Dynamic linker not found. TLS and other "
+ "allocations originating from linker might be falsely reported "
+ "as leaks.\n");
+ }
+}
+
+static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ Frontier *frontier = reinterpret_cast<Frontier *>(data);
+ for (uptr j = 0; j < info->dlpi_phnum; j++) {
+ const ElfW(Phdr) *phdr = &(info->dlpi_phdr[j]);
+ // We're looking for .data and .bss sections, which reside in writeable,
+ // loadable segments.
+ if (!(phdr->p_flags & PF_W) || (phdr->p_type != PT_LOAD) ||
+ (phdr->p_memsz == 0))
+ continue;
+ uptr begin = info->dlpi_addr + phdr->p_vaddr;
+ uptr end = begin + phdr->p_memsz;
+ ScanGlobalRange(begin, end, frontier);
+ }
+ return 0;
+}
+
+// Scans global variables for heap pointers.
+void ProcessGlobalRegions(Frontier *frontier) {
+ if (!flags()->use_globals) return;
+ dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier);
+}
+
+LoadedModule *GetLinker() { return linker; }
+
+void ProcessPlatformSpecificAllocations(Frontier *frontier) {}
+
+struct DoStopTheWorldParam {
+ StopTheWorldCallback callback;
+ void *argument;
+};
+
+// While calling Die() here is undefined behavior and can potentially
+// cause race conditions, it isn't possible to intercept exit on linux,
+// so we have no choice but to call Die() from the atexit handler.
+void HandleLeaks() {
+ if (common_flags()->exitcode) Die();
+}
+
+static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data);
+ StopTheWorld(param->callback, param->argument);
+ return 1;
+}
+
+// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one
+// of the threads is frozen while holding the libdl lock, the tracer will hang
+// in dl_iterate_phdr() forever.
+// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the
+// tracer task and the thread that spawned it. Thus, if we run the tracer task
+// while holding the libdl lock in the parent thread, we can safely reenter it
+// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr()
+// callback in the parent thread.
+void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
+ DoStopTheWorldParam param = {callback, argument};
+ dl_iterate_phdr(DoStopTheWorldCallback, ¶m);
+}
+
+} // namespace __lsan
+
+#endif
+++ /dev/null
-//=-- lsan_common_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 LeakSanitizer.
-// Implementation of common leak checking functionality. Darwin-specific code.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#include "lsan_common.h"
-
-#if CAN_SANITIZE_LEAKS && SANITIZER_MAC
-
-#include "sanitizer_common/sanitizer_allocator_internal.h"
-#include "lsan_allocator.h"
-
-#include <pthread.h>
-
-#include <mach/mach.h>
-
-// Only introduced in Mac OS X 10.9.
-#ifdef VM_MEMORY_OS_ALLOC_ONCE
-static const int kSanitizerVmMemoryOsAllocOnce = VM_MEMORY_OS_ALLOC_ONCE;
-#else
-static const int kSanitizerVmMemoryOsAllocOnce = 73;
-#endif
-
-namespace __lsan {
-
-typedef struct {
- int disable_counter;
- u32 current_thread_id;
- AllocatorCache cache;
-} thread_local_data_t;
-
-static pthread_key_t key;
-static pthread_once_t key_once = PTHREAD_ONCE_INIT;
-
-// The main thread destructor requires the current thread id,
-// so we can't destroy it until it's been used and reset to invalid tid
-void restore_tid_data(void *ptr) {
- thread_local_data_t *data = (thread_local_data_t *)ptr;
- if (data->current_thread_id != kInvalidTid)
- pthread_setspecific(key, data);
-}
-
-static void make_tls_key() {
- CHECK_EQ(pthread_key_create(&key, restore_tid_data), 0);
-}
-
-static thread_local_data_t *get_tls_val(bool alloc) {
- pthread_once(&key_once, make_tls_key);
-
- thread_local_data_t *ptr = (thread_local_data_t *)pthread_getspecific(key);
- if (ptr == NULL && alloc) {
- ptr = (thread_local_data_t *)InternalAlloc(sizeof(*ptr));
- ptr->disable_counter = 0;
- ptr->current_thread_id = kInvalidTid;
- ptr->cache = AllocatorCache();
- pthread_setspecific(key, ptr);
- }
-
- return ptr;
-}
-
-bool DisabledInThisThread() {
- thread_local_data_t *data = get_tls_val(false);
- return data ? data->disable_counter > 0 : false;
-}
-
-void DisableInThisThread() { ++get_tls_val(true)->disable_counter; }
-
-void EnableInThisThread() {
- int *disable_counter = &get_tls_val(true)->disable_counter;
- if (*disable_counter == 0) {
- DisableCounterUnderflow();
- }
- --*disable_counter;
-}
-
-u32 GetCurrentThread() {
- thread_local_data_t *data = get_tls_val(false);
- return data ? data->current_thread_id : kInvalidTid;
-}
-
-void SetCurrentThread(u32 tid) { get_tls_val(true)->current_thread_id = tid; }
-
-AllocatorCache *GetAllocatorCache() { return &get_tls_val(true)->cache; }
-
-LoadedModule *GetLinker() { return nullptr; }
-
-// Required on Linux for initialization of TLS behavior, but should not be
-// required on Darwin.
-void InitializePlatformSpecificModules() {}
-
-// Sections which can't contain contain global pointers. This list errs on the
-// side of caution to avoid false positives, at the expense of performance.
-//
-// Other potentially safe sections include:
-// __all_image_info, __crash_info, __const, __got, __interpose, __objc_msg_break
-//
-// Sections which definitely cannot be included here are:
-// __objc_data, __objc_const, __data, __bss, __common, __thread_data,
-// __thread_bss, __thread_vars, __objc_opt_rw, __objc_opt_ptrs
-static const char *kSkippedSecNames[] = {
- "__cfstring", "__la_symbol_ptr", "__mod_init_func",
- "__mod_term_func", "__nl_symbol_ptr", "__objc_classlist",
- "__objc_classrefs", "__objc_imageinfo", "__objc_nlclslist",
- "__objc_protolist", "__objc_selrefs", "__objc_superrefs"};
-
-// Scans global variables for heap pointers.
-void ProcessGlobalRegions(Frontier *frontier) {
- for (auto name : kSkippedSecNames) CHECK(ARRAY_SIZE(name) < kMaxSegName);
-
- MemoryMappingLayout memory_mapping(false);
- InternalMmapVector<LoadedModule> modules;
- modules.reserve(128);
- memory_mapping.DumpListOfModules(&modules);
- for (uptr i = 0; i < modules.size(); ++i) {
- // Even when global scanning is disabled, we still need to scan
- // system libraries for stashed pointers
- if (!flags()->use_globals && modules[i].instrumented()) continue;
-
- for (const __sanitizer::LoadedModule::AddressRange &range :
- modules[i].ranges()) {
- // Sections storing global variables are writable and non-executable
- if (range.executable || !range.writable) continue;
-
- for (auto name : kSkippedSecNames) {
- if (!internal_strcmp(range.name, name)) continue;
- }
-
- ScanGlobalRange(range.beg, range.end, frontier);
- }
- }
-}
-
-void ProcessPlatformSpecificAllocations(Frontier *frontier) {
- unsigned depth = 1;
- vm_size_t size = 0;
- vm_address_t address = 0;
- kern_return_t err = KERN_SUCCESS;
- mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
-
- InternalMmapVector<RootRegion> const *root_regions = GetRootRegions();
-
- while (err == KERN_SUCCESS) {
- struct vm_region_submap_info_64 info;
- err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth,
- (vm_region_info_t)&info, &count);
-
- uptr end_address = address + size;
-
- // libxpc stashes some pointers in the Kernel Alloc Once page,
- // make sure not to report those as leaks.
- if (info.user_tag == kSanitizerVmMemoryOsAllocOnce) {
- ScanRangeForPointers(address, end_address, frontier, "GLOBAL",
- kReachable);
-
- // Recursing over the full memory map is very slow, break out
- // early if we don't need the full iteration.
- if (!flags()->use_root_regions || !root_regions->size())
- break;
- }
-
- // This additional root region scan is required on Darwin in order to
- // detect root regions contained within mmap'd memory regions, because
- // the Darwin implementation of sanitizer_procmaps traverses images
- // as loaded by dyld, and not the complete set of all memory regions.
- //
- // TODO(fjricci) - remove this once sanitizer_procmaps_mac has the same
- // behavior as sanitizer_procmaps_linux and traverses all memory regions
- if (flags()->use_root_regions) {
- for (uptr i = 0; i < root_regions->size(); i++) {
- ScanRootRegion(frontier, (*root_regions)[i], address, end_address,
- info.protection & kProtectionRead);
- }
- }
-
- address = end_address;
- }
-}
-
-// On darwin, we can intercept _exit gracefully, and return a failing exit code
-// if required at that point. Calling Die() here is undefined behavior and
-// causes rare race conditions.
-void HandleLeaks() {}
-
-void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
- StopTheWorld(callback, argument);
-}
-
-} // namespace __lsan
-
-#endif // CAN_SANITIZE_LEAKS && SANITIZER_MAC
--- /dev/null
+//=-- lsan_common_mac.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Implementation of common leak checking functionality. Darwin-specific code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "lsan_common.h"
+
+#if CAN_SANITIZE_LEAKS && SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_allocator_internal.h"
+#include "lsan_allocator.h"
+
+#include <pthread.h>
+
+#include <mach/mach.h>
+
+// Only introduced in Mac OS X 10.9.
+#ifdef VM_MEMORY_OS_ALLOC_ONCE
+static const int kSanitizerVmMemoryOsAllocOnce = VM_MEMORY_OS_ALLOC_ONCE;
+#else
+static const int kSanitizerVmMemoryOsAllocOnce = 73;
+#endif
+
+namespace __lsan {
+
+typedef struct {
+ int disable_counter;
+ u32 current_thread_id;
+ AllocatorCache cache;
+} thread_local_data_t;
+
+static pthread_key_t key;
+static pthread_once_t key_once = PTHREAD_ONCE_INIT;
+
+// The main thread destructor requires the current thread id,
+// so we can't destroy it until it's been used and reset to invalid tid
+void restore_tid_data(void *ptr) {
+ thread_local_data_t *data = (thread_local_data_t *)ptr;
+ if (data->current_thread_id != kInvalidTid)
+ pthread_setspecific(key, data);
+}
+
+static void make_tls_key() {
+ CHECK_EQ(pthread_key_create(&key, restore_tid_data), 0);
+}
+
+static thread_local_data_t *get_tls_val(bool alloc) {
+ pthread_once(&key_once, make_tls_key);
+
+ thread_local_data_t *ptr = (thread_local_data_t *)pthread_getspecific(key);
+ if (ptr == NULL && alloc) {
+ ptr = (thread_local_data_t *)InternalAlloc(sizeof(*ptr));
+ ptr->disable_counter = 0;
+ ptr->current_thread_id = kInvalidTid;
+ ptr->cache = AllocatorCache();
+ pthread_setspecific(key, ptr);
+ }
+
+ return ptr;
+}
+
+bool DisabledInThisThread() {
+ thread_local_data_t *data = get_tls_val(false);
+ return data ? data->disable_counter > 0 : false;
+}
+
+void DisableInThisThread() { ++get_tls_val(true)->disable_counter; }
+
+void EnableInThisThread() {
+ int *disable_counter = &get_tls_val(true)->disable_counter;
+ if (*disable_counter == 0) {
+ DisableCounterUnderflow();
+ }
+ --*disable_counter;
+}
+
+u32 GetCurrentThread() {
+ thread_local_data_t *data = get_tls_val(false);
+ return data ? data->current_thread_id : kInvalidTid;
+}
+
+void SetCurrentThread(u32 tid) { get_tls_val(true)->current_thread_id = tid; }
+
+AllocatorCache *GetAllocatorCache() { return &get_tls_val(true)->cache; }
+
+LoadedModule *GetLinker() { return nullptr; }
+
+// Required on Linux for initialization of TLS behavior, but should not be
+// required on Darwin.
+void InitializePlatformSpecificModules() {}
+
+// Sections which can't contain contain global pointers. This list errs on the
+// side of caution to avoid false positives, at the expense of performance.
+//
+// Other potentially safe sections include:
+// __all_image_info, __crash_info, __const, __got, __interpose, __objc_msg_break
+//
+// Sections which definitely cannot be included here are:
+// __objc_data, __objc_const, __data, __bss, __common, __thread_data,
+// __thread_bss, __thread_vars, __objc_opt_rw, __objc_opt_ptrs
+static const char *kSkippedSecNames[] = {
+ "__cfstring", "__la_symbol_ptr", "__mod_init_func",
+ "__mod_term_func", "__nl_symbol_ptr", "__objc_classlist",
+ "__objc_classrefs", "__objc_imageinfo", "__objc_nlclslist",
+ "__objc_protolist", "__objc_selrefs", "__objc_superrefs"};
+
+// Scans global variables for heap pointers.
+void ProcessGlobalRegions(Frontier *frontier) {
+ for (auto name : kSkippedSecNames)
+ CHECK(internal_strnlen(name, kMaxSegName + 1) <= kMaxSegName);
+
+ MemoryMappingLayout memory_mapping(false);
+ InternalMmapVector<LoadedModule> modules;
+ modules.reserve(128);
+ memory_mapping.DumpListOfModules(&modules);
+ for (uptr i = 0; i < modules.size(); ++i) {
+ // Even when global scanning is disabled, we still need to scan
+ // system libraries for stashed pointers
+ if (!flags()->use_globals && modules[i].instrumented()) continue;
+
+ for (const __sanitizer::LoadedModule::AddressRange &range :
+ modules[i].ranges()) {
+ // Sections storing global variables are writable and non-executable
+ if (range.executable || !range.writable) continue;
+
+ for (auto name : kSkippedSecNames) {
+ if (!internal_strcmp(range.name, name)) continue;
+ }
+
+ ScanGlobalRange(range.beg, range.end, frontier);
+ }
+ }
+}
+
+void ProcessPlatformSpecificAllocations(Frontier *frontier) {
+ unsigned depth = 1;
+ vm_size_t size = 0;
+ vm_address_t address = 0;
+ kern_return_t err = KERN_SUCCESS;
+ mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ InternalMmapVector<RootRegion> const *root_regions = GetRootRegions();
+
+ while (err == KERN_SUCCESS) {
+ struct vm_region_submap_info_64 info;
+ err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth,
+ (vm_region_info_t)&info, &count);
+
+ uptr end_address = address + size;
+
+ // libxpc stashes some pointers in the Kernel Alloc Once page,
+ // make sure not to report those as leaks.
+ if (info.user_tag == kSanitizerVmMemoryOsAllocOnce) {
+ ScanRangeForPointers(address, end_address, frontier, "GLOBAL",
+ kReachable);
+
+ // Recursing over the full memory map is very slow, break out
+ // early if we don't need the full iteration.
+ if (!flags()->use_root_regions || !root_regions->size())
+ break;
+ }
+
+ // This additional root region scan is required on Darwin in order to
+ // detect root regions contained within mmap'd memory regions, because
+ // the Darwin implementation of sanitizer_procmaps traverses images
+ // as loaded by dyld, and not the complete set of all memory regions.
+ //
+ // TODO(fjricci) - remove this once sanitizer_procmaps_mac has the same
+ // behavior as sanitizer_procmaps_linux and traverses all memory regions
+ if (flags()->use_root_regions) {
+ for (uptr i = 0; i < root_regions->size(); i++) {
+ ScanRootRegion(frontier, (*root_regions)[i], address, end_address,
+ info.protection & kProtectionRead);
+ }
+ }
+
+ address = end_address;
+ }
+}
+
+// On darwin, we can intercept _exit gracefully, and return a failing exit code
+// if required at that point. Calling Die() here is undefined behavior and
+// causes rare race conditions.
+void HandleLeaks() {}
+
+void DoStopTheWorld(StopTheWorldCallback callback, void *argument) {
+ StopTheWorld(callback, argument);
+}
+
+} // namespace __lsan
+
+#endif // CAN_SANITIZE_LEAKS && SANITIZER_MAC
//===-- lsan_flags.inc ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//=-- lsan_interceptors.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// Interceptors for standalone LSan.
-//
-//===----------------------------------------------------------------------===//
-
-#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_allocator.h"
-#include "sanitizer_common/sanitizer_allocator_report.h"
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_linux.h"
-#include "sanitizer_common/sanitizer_platform_interceptors.h"
-#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_common/sanitizer_platform_limits_posix.h"
-#include "sanitizer_common/sanitizer_posix.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
-#include "lsan.h"
-#include "lsan_allocator.h"
-#include "lsan_common.h"
-#include "lsan_thread.h"
-
-#include <stddef.h>
-
-using namespace __lsan;
-
-extern "C" {
-int pthread_attr_init(void *attr);
-int pthread_attr_destroy(void *attr);
-int pthread_attr_getdetachstate(void *attr, int *v);
-int pthread_key_create(unsigned *key, void (*destructor)(void* v));
-int pthread_setspecific(unsigned key, const void *v);
-}
-
-///// Malloc/free interceptors. /////
-
-namespace std {
- struct nothrow_t;
- enum class align_val_t: size_t;
-}
-
-#if !SANITIZER_MAC
-INTERCEPTOR(void*, malloc, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_malloc(size, stack);
-}
-
-INTERCEPTOR(void, free, void *p) {
- ENSURE_LSAN_INITED;
- lsan_free(p);
-}
-
-INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
- if (lsan_init_is_running) {
- // 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;
- }
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_calloc(nmemb, size, stack);
-}
-
-INTERCEPTOR(void*, realloc, void *q, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_realloc(q, size, stack);
-}
-
-INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_posix_memalign(memptr, alignment, size, stack);
-}
-
-INTERCEPTOR(void*, valloc, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_valloc(size, stack);
-}
-#endif
-
-#if SANITIZER_INTERCEPT_MEMALIGN
-INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_memalign(alignment, size, stack);
-}
-#define LSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
-
-INTERCEPTOR(void *, __libc_memalign, uptr alignment, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- void *res = lsan_memalign(alignment, size, stack);
- DTLS_on_libc_memalign(res, size);
- return res;
-}
-#define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN INTERCEPT_FUNCTION(__libc_memalign)
-#else
-#define LSAN_MAYBE_INTERCEPT_MEMALIGN
-#define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN
-#endif // SANITIZER_INTERCEPT_MEMALIGN
-
-#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
-INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_aligned_alloc(alignment, size, stack);
-}
-#define LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc)
-#else
-#define LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC
-#endif
-
-#if SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE
-INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
- ENSURE_LSAN_INITED;
- return GetMallocUsableSize(ptr);
-}
-#define LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE \
- INTERCEPT_FUNCTION(malloc_usable_size)
-#else
-#define LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE
-#endif
-
-#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
-struct fake_mallinfo {
- int x[10];
-};
-
-INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
- struct fake_mallinfo res;
- internal_memset(&res, 0, sizeof(res));
- return res;
-}
-#define LSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
-
-INTERCEPTOR(int, mallopt, int cmd, int value) {
- return -1;
-}
-#define LSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
-#else
-#define LSAN_MAYBE_INTERCEPT_MALLINFO
-#define LSAN_MAYBE_INTERCEPT_MALLOPT
-#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
-
-#if SANITIZER_INTERCEPT_PVALLOC
-INTERCEPTOR(void*, pvalloc, uptr size) {
- ENSURE_LSAN_INITED;
- GET_STACK_TRACE_MALLOC;
- return lsan_pvalloc(size, stack);
-}
-#define LSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
-#else
-#define LSAN_MAYBE_INTERCEPT_PVALLOC
-#endif // SANITIZER_INTERCEPT_PVALLOC
-
-#if SANITIZER_INTERCEPT_CFREE
-INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
-#define LSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
-#else
-#define LSAN_MAYBE_INTERCEPT_CFREE
-#endif // SANITIZER_INTERCEPT_CFREE
-
-#if SANITIZER_INTERCEPT_MCHECK_MPROBE
-INTERCEPTOR(int, mcheck, void (*abortfunc)(int mstatus)) {
- return 0;
-}
-
-INTERCEPTOR(int, mcheck_pedantic, void (*abortfunc)(int mstatus)) {
- return 0;
-}
-
-INTERCEPTOR(int, mprobe, void *ptr) {
- return 0;
-}
-#endif // SANITIZER_INTERCEPT_MCHECK_MPROBE
-
-
-// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
-#define OPERATOR_NEW_BODY(nothrow)\
- ENSURE_LSAN_INITED;\
- GET_STACK_TRACE_MALLOC;\
- void *res = lsan_malloc(size, stack);\
- if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
- return res;
-#define OPERATOR_NEW_BODY_ALIGN(nothrow)\
- ENSURE_LSAN_INITED;\
- GET_STACK_TRACE_MALLOC;\
- void *res = lsan_memalign((uptr)align, size, stack);\
- if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
- return res;
-
-#define OPERATOR_DELETE_BODY\
- ENSURE_LSAN_INITED;\
- lsan_free(ptr);
-
-// On OS X it's not enough to just provide our own 'operator new' and
-// 'operator delete' implementations, because they're going to be in the runtime
-// dylib, and the main executable will depend on both the runtime dylib and
-// libstdc++, each of has its implementation of new and delete.
-// To make sure that C++ allocation/deallocation operators are overridden on
-// OS X we need to intercept them using their mangled names.
-#if !SANITIZER_MAC
-
-INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(true /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(true /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size, std::align_val_t align)
-{ OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size, std::align_val_t align)
-{ OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/); }
-INTERCEPTOR_ATTRIBUTE
-void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/); }
-
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr, std::nothrow_t const &)
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr, size_t size) NOEXCEPT
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr, size_t size) NOEXCEPT
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr, std::align_val_t) NOEXCEPT
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr, std::align_val_t) NOEXCEPT
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr, std::align_val_t, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr, std::align_val_t, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete(void *ptr, size_t size, std::align_val_t) NOEXCEPT
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR_ATTRIBUTE
-void operator delete[](void *ptr, size_t size, std::align_val_t) NOEXCEPT
-{ OPERATOR_DELETE_BODY; }
-
-#else // SANITIZER_MAC
-
-INTERCEPTOR(void *, _Znwm, size_t size)
-{ OPERATOR_NEW_BODY(false /*nothrow*/); }
-INTERCEPTOR(void *, _Znam, size_t size)
-{ OPERATOR_NEW_BODY(false /*nothrow*/); }
-INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(true /*nothrow*/); }
-INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&)
-{ OPERATOR_NEW_BODY(true /*nothrow*/); }
-
-INTERCEPTOR(void, _ZdlPv, void *ptr)
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR(void, _ZdaPv, void *ptr)
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY; }
-INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
-{ OPERATOR_DELETE_BODY; }
-
-#endif // !SANITIZER_MAC
-
-
-///// Thread initialization and finalization. /////
-
-#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
-static unsigned g_thread_finalize_key;
-
-static void thread_finalize(void *v) {
- uptr iter = (uptr)v;
- if (iter > 1) {
- if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) {
- Report("LeakSanitizer: failed to set thread key.\n");
- Die();
- }
- return;
- }
- ThreadFinish();
-}
-#endif
-
-#if SANITIZER_NETBSD
-INTERCEPTOR(void, _lwp_exit) {
- ENSURE_LSAN_INITED;
- ThreadFinish();
- REAL(_lwp_exit)();
-}
-#define LSAN_MAYBE_INTERCEPT__LWP_EXIT INTERCEPT_FUNCTION(_lwp_exit)
-#else
-#define LSAN_MAYBE_INTERCEPT__LWP_EXIT
-#endif
-
-#if SANITIZER_INTERCEPT_THR_EXIT
-INTERCEPTOR(void, thr_exit, tid_t *state) {
- ENSURE_LSAN_INITED;
- ThreadFinish();
- REAL(thr_exit)(state);
-}
-#define LSAN_MAYBE_INTERCEPT_THR_EXIT INTERCEPT_FUNCTION(thr_exit)
-#else
-#define LSAN_MAYBE_INTERCEPT_THR_EXIT
-#endif
-
-struct ThreadParam {
- void *(*callback)(void *arg);
- void *param;
- atomic_uintptr_t tid;
-};
-
-extern "C" void *__lsan_thread_start_func(void *arg) {
- ThreadParam *p = (ThreadParam*)arg;
- void* (*callback)(void *arg) = p->callback;
- void *param = p->param;
- // Wait until the last iteration to maximize the chance that we are the last
- // destructor to run.
-#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
- if (pthread_setspecific(g_thread_finalize_key,
- (void*)GetPthreadDestructorIterations())) {
- Report("LeakSanitizer: failed to set thread key.\n");
- Die();
- }
-#endif
- int tid = 0;
- while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
- internal_sched_yield();
- SetCurrentThread(tid);
- ThreadStart(tid, GetTid());
- atomic_store(&p->tid, 0, memory_order_release);
- return callback(param);
-}
-
-INTERCEPTOR(int, pthread_create, void *th, void *attr,
- void *(*callback)(void *), void *param) {
- ENSURE_LSAN_INITED;
- EnsureMainThreadIDIsCorrect();
- __sanitizer_pthread_attr_t myattr;
- if (!attr) {
- pthread_attr_init(&myattr);
- attr = &myattr;
- }
- AdjustStackSize(attr);
- int detached = 0;
- pthread_attr_getdetachstate(attr, &detached);
- ThreadParam p;
- p.callback = callback;
- p.param = param;
- atomic_store(&p.tid, 0, memory_order_relaxed);
- int res;
- {
- // Ignore all allocations made by pthread_create: thread stack/TLS may be
- // stored by pthread for future reuse even after thread destruction, and
- // the linked list it's stored in doesn't even hold valid pointers to the
- // objects, the latter are calculated by obscure pointer arithmetic.
- ScopedInterceptorDisabler disabler;
- res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p);
- }
- if (res == 0) {
- int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th,
- IsStateDetached(detached));
- CHECK_NE(tid, 0);
- atomic_store(&p.tid, tid, memory_order_release);
- while (atomic_load(&p.tid, memory_order_acquire) != 0)
- internal_sched_yield();
- }
- if (attr == &myattr)
- pthread_attr_destroy(&myattr);
- return res;
-}
-
-INTERCEPTOR(int, pthread_join, void *th, void **ret) {
- ENSURE_LSAN_INITED;
- int tid = ThreadTid((uptr)th);
- int res = REAL(pthread_join)(th, ret);
- if (res == 0)
- ThreadJoin(tid);
- return res;
-}
-
-INTERCEPTOR(void, _exit, int status) {
- if (status == 0 && HasReportedLeaks()) status = common_flags()->exitcode;
- REAL(_exit)(status);
-}
-
-#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
-#include "sanitizer_common/sanitizer_signal_interceptors.inc"
-
-namespace __lsan {
-
-void InitializeInterceptors() {
- InitializeSignalInterceptors();
-
- INTERCEPT_FUNCTION(malloc);
- INTERCEPT_FUNCTION(free);
- LSAN_MAYBE_INTERCEPT_CFREE;
- INTERCEPT_FUNCTION(calloc);
- INTERCEPT_FUNCTION(realloc);
- LSAN_MAYBE_INTERCEPT_MEMALIGN;
- LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN;
- LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC;
- INTERCEPT_FUNCTION(posix_memalign);
- INTERCEPT_FUNCTION(valloc);
- LSAN_MAYBE_INTERCEPT_PVALLOC;
- LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE;
- LSAN_MAYBE_INTERCEPT_MALLINFO;
- LSAN_MAYBE_INTERCEPT_MALLOPT;
- INTERCEPT_FUNCTION(pthread_create);
- INTERCEPT_FUNCTION(pthread_join);
- INTERCEPT_FUNCTION(_exit);
-
- LSAN_MAYBE_INTERCEPT__LWP_EXIT;
- LSAN_MAYBE_INTERCEPT_THR_EXIT;
-
-#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
- if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
- Report("LeakSanitizer: failed to create thread key.\n");
- Die();
- }
-#endif
-}
-
-} // namespace __lsan
--- /dev/null
+//=-- lsan_interceptors.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// Interceptors for standalone LSan.
+//
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_posix.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+#include "lsan.h"
+#include "lsan_allocator.h"
+#include "lsan_common.h"
+#include "lsan_thread.h"
+
+#include <stddef.h>
+
+using namespace __lsan;
+
+extern "C" {
+int pthread_attr_init(void *attr);
+int pthread_attr_destroy(void *attr);
+int pthread_attr_getdetachstate(void *attr, int *v);
+int pthread_key_create(unsigned *key, void (*destructor)(void* v));
+int pthread_setspecific(unsigned key, const void *v);
+}
+
+///// Malloc/free interceptors. /////
+
+namespace std {
+ struct nothrow_t;
+ enum class align_val_t: size_t;
+}
+
+#if !SANITIZER_MAC
+INTERCEPTOR(void*, malloc, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_malloc(size, stack);
+}
+
+INTERCEPTOR(void, free, void *p) {
+ ENSURE_LSAN_INITED;
+ lsan_free(p);
+}
+
+INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
+ if (lsan_init_is_running) {
+ // 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;
+ }
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_calloc(nmemb, size, stack);
+}
+
+INTERCEPTOR(void*, realloc, void *q, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_realloc(q, size, stack);
+}
+
+INTERCEPTOR(void*, reallocarray, void *q, uptr nmemb, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_reallocarray(q, nmemb, size, stack);
+}
+
+INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_posix_memalign(memptr, alignment, size, stack);
+}
+
+INTERCEPTOR(void*, valloc, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_valloc(size, stack);
+}
+#endif
+
+#if SANITIZER_INTERCEPT_MEMALIGN
+INTERCEPTOR(void*, memalign, uptr alignment, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_memalign(alignment, size, stack);
+}
+#define LSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign)
+
+INTERCEPTOR(void *, __libc_memalign, uptr alignment, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ void *res = lsan_memalign(alignment, size, stack);
+ DTLS_on_libc_memalign(res, size);
+ return res;
+}
+#define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN INTERCEPT_FUNCTION(__libc_memalign)
+#else
+#define LSAN_MAYBE_INTERCEPT_MEMALIGN
+#define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN
+#endif // SANITIZER_INTERCEPT_MEMALIGN
+
+#if SANITIZER_INTERCEPT_ALIGNED_ALLOC
+INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_aligned_alloc(alignment, size, stack);
+}
+#define LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc)
+#else
+#define LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC
+#endif
+
+#if SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE
+INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
+ ENSURE_LSAN_INITED;
+ return GetMallocUsableSize(ptr);
+}
+#define LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE \
+ INTERCEPT_FUNCTION(malloc_usable_size)
+#else
+#define LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE
+#endif
+
+#if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
+struct fake_mallinfo {
+ int x[10];
+};
+
+INTERCEPTOR(struct fake_mallinfo, mallinfo, void) {
+ struct fake_mallinfo res;
+ internal_memset(&res, 0, sizeof(res));
+ return res;
+}
+#define LSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo)
+
+INTERCEPTOR(int, mallopt, int cmd, int value) {
+ return 0;
+}
+#define LSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt)
+#else
+#define LSAN_MAYBE_INTERCEPT_MALLINFO
+#define LSAN_MAYBE_INTERCEPT_MALLOPT
+#endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO
+
+#if SANITIZER_INTERCEPT_PVALLOC
+INTERCEPTOR(void*, pvalloc, uptr size) {
+ ENSURE_LSAN_INITED;
+ GET_STACK_TRACE_MALLOC;
+ return lsan_pvalloc(size, stack);
+}
+#define LSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc)
+#else
+#define LSAN_MAYBE_INTERCEPT_PVALLOC
+#endif // SANITIZER_INTERCEPT_PVALLOC
+
+#if SANITIZER_INTERCEPT_CFREE
+INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free));
+#define LSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree)
+#else
+#define LSAN_MAYBE_INTERCEPT_CFREE
+#endif // SANITIZER_INTERCEPT_CFREE
+
+#if SANITIZER_INTERCEPT_MCHECK_MPROBE
+INTERCEPTOR(int, mcheck, void (*abortfunc)(int mstatus)) {
+ return 0;
+}
+
+INTERCEPTOR(int, mcheck_pedantic, void (*abortfunc)(int mstatus)) {
+ return 0;
+}
+
+INTERCEPTOR(int, mprobe, void *ptr) {
+ return 0;
+}
+#endif // SANITIZER_INTERCEPT_MCHECK_MPROBE
+
+
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(nothrow)\
+ ENSURE_LSAN_INITED;\
+ GET_STACK_TRACE_MALLOC;\
+ void *res = lsan_malloc(size, stack);\
+ if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
+ return res;
+#define OPERATOR_NEW_BODY_ALIGN(nothrow)\
+ ENSURE_LSAN_INITED;\
+ GET_STACK_TRACE_MALLOC;\
+ void *res = lsan_memalign((uptr)align, size, stack);\
+ if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\
+ return res;
+
+#define OPERATOR_DELETE_BODY\
+ ENSURE_LSAN_INITED;\
+ lsan_free(ptr);
+
+// On OS X it's not enough to just provide our own 'operator new' and
+// 'operator delete' implementations, because they're going to be in the runtime
+// dylib, and the main executable will depend on both the runtime dylib and
+// libstdc++, each of has its implementation of new and delete.
+// To make sure that C++ allocation/deallocation operators are overridden on
+// OS X we need to intercept them using their mangled names.
+#if !SANITIZER_MAC
+
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(true /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(true /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align)
+{ OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align)
+{ OPERATOR_NEW_BODY_ALIGN(false /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY_ALIGN(true /*nothrow*/); }
+
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const &)
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size) NOEXCEPT
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) NOEXCEPT
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t) NOEXCEPT
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t) NOEXCEPT
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size, std::align_val_t) NOEXCEPT
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size, std::align_val_t) NOEXCEPT
+{ OPERATOR_DELETE_BODY; }
+
+#else // SANITIZER_MAC
+
+INTERCEPTOR(void *, _Znwm, size_t size)
+{ OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR(void *, _Znam, size_t size)
+{ OPERATOR_NEW_BODY(false /*nothrow*/); }
+INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(true /*nothrow*/); }
+INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&)
+{ OPERATOR_NEW_BODY(true /*nothrow*/); }
+
+INTERCEPTOR(void, _ZdlPv, void *ptr)
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR(void, _ZdaPv, void *ptr)
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY; }
+INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&)
+{ OPERATOR_DELETE_BODY; }
+
+#endif // !SANITIZER_MAC
+
+
+///// Thread initialization and finalization. /////
+
+#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
+static unsigned g_thread_finalize_key;
+
+static void thread_finalize(void *v) {
+ uptr iter = (uptr)v;
+ if (iter > 1) {
+ if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) {
+ Report("LeakSanitizer: failed to set thread key.\n");
+ Die();
+ }
+ return;
+ }
+ ThreadFinish();
+}
+#endif
+
+#if SANITIZER_NETBSD
+INTERCEPTOR(void, _lwp_exit) {
+ ENSURE_LSAN_INITED;
+ ThreadFinish();
+ REAL(_lwp_exit)();
+}
+#define LSAN_MAYBE_INTERCEPT__LWP_EXIT INTERCEPT_FUNCTION(_lwp_exit)
+#else
+#define LSAN_MAYBE_INTERCEPT__LWP_EXIT
+#endif
+
+#if SANITIZER_INTERCEPT_THR_EXIT
+INTERCEPTOR(void, thr_exit, tid_t *state) {
+ ENSURE_LSAN_INITED;
+ ThreadFinish();
+ REAL(thr_exit)(state);
+}
+#define LSAN_MAYBE_INTERCEPT_THR_EXIT INTERCEPT_FUNCTION(thr_exit)
+#else
+#define LSAN_MAYBE_INTERCEPT_THR_EXIT
+#endif
+
+struct ThreadParam {
+ void *(*callback)(void *arg);
+ void *param;
+ atomic_uintptr_t tid;
+};
+
+extern "C" void *__lsan_thread_start_func(void *arg) {
+ ThreadParam *p = (ThreadParam*)arg;
+ void* (*callback)(void *arg) = p->callback;
+ void *param = p->param;
+ // Wait until the last iteration to maximize the chance that we are the last
+ // destructor to run.
+#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
+ if (pthread_setspecific(g_thread_finalize_key,
+ (void*)GetPthreadDestructorIterations())) {
+ Report("LeakSanitizer: failed to set thread key.\n");
+ Die();
+ }
+#endif
+ int tid = 0;
+ while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
+ internal_sched_yield();
+ SetCurrentThread(tid);
+ ThreadStart(tid, GetTid());
+ atomic_store(&p->tid, 0, memory_order_release);
+ return callback(param);
+}
+
+INTERCEPTOR(int, pthread_create, void *th, void *attr,
+ void *(*callback)(void *), void *param) {
+ ENSURE_LSAN_INITED;
+ EnsureMainThreadIDIsCorrect();
+ __sanitizer_pthread_attr_t myattr;
+ if (!attr) {
+ pthread_attr_init(&myattr);
+ attr = &myattr;
+ }
+ AdjustStackSize(attr);
+ int detached = 0;
+ pthread_attr_getdetachstate(attr, &detached);
+ ThreadParam p;
+ p.callback = callback;
+ p.param = param;
+ atomic_store(&p.tid, 0, memory_order_relaxed);
+ int res;
+ {
+ // Ignore all allocations made by pthread_create: thread stack/TLS may be
+ // stored by pthread for future reuse even after thread destruction, and
+ // the linked list it's stored in doesn't even hold valid pointers to the
+ // objects, the latter are calculated by obscure pointer arithmetic.
+ ScopedInterceptorDisabler disabler;
+ res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p);
+ }
+ if (res == 0) {
+ int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th,
+ IsStateDetached(detached));
+ CHECK_NE(tid, 0);
+ atomic_store(&p.tid, tid, memory_order_release);
+ while (atomic_load(&p.tid, memory_order_acquire) != 0)
+ internal_sched_yield();
+ }
+ if (attr == &myattr)
+ pthread_attr_destroy(&myattr);
+ return res;
+}
+
+INTERCEPTOR(int, pthread_join, void *th, void **ret) {
+ ENSURE_LSAN_INITED;
+ int tid = ThreadTid((uptr)th);
+ int res = REAL(pthread_join)(th, ret);
+ if (res == 0)
+ ThreadJoin(tid);
+ return res;
+}
+
+INTERCEPTOR(void, _exit, int status) {
+ if (status == 0 && HasReportedLeaks()) status = common_flags()->exitcode;
+ REAL(_exit)(status);
+}
+
+#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
+#include "sanitizer_common/sanitizer_signal_interceptors.inc"
+
+namespace __lsan {
+
+void InitializeInterceptors() {
+ InitializeSignalInterceptors();
+
+ INTERCEPT_FUNCTION(malloc);
+ INTERCEPT_FUNCTION(free);
+ LSAN_MAYBE_INTERCEPT_CFREE;
+ INTERCEPT_FUNCTION(calloc);
+ INTERCEPT_FUNCTION(realloc);
+ LSAN_MAYBE_INTERCEPT_MEMALIGN;
+ LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN;
+ LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC;
+ INTERCEPT_FUNCTION(posix_memalign);
+ INTERCEPT_FUNCTION(valloc);
+ LSAN_MAYBE_INTERCEPT_PVALLOC;
+ LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE;
+ LSAN_MAYBE_INTERCEPT_MALLINFO;
+ LSAN_MAYBE_INTERCEPT_MALLOPT;
+ INTERCEPT_FUNCTION(pthread_create);
+ INTERCEPT_FUNCTION(pthread_join);
+ INTERCEPT_FUNCTION(_exit);
+
+ LSAN_MAYBE_INTERCEPT__LWP_EXIT;
+ LSAN_MAYBE_INTERCEPT_THR_EXIT;
+
+#if !SANITIZER_NETBSD && !SANITIZER_FREEBSD
+ if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) {
+ Report("LeakSanitizer: failed to create thread key.\n");
+ Die();
+ }
+#endif
+}
+
+} // namespace __lsan
+++ /dev/null
-//=-- lsan_linux.cc -------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer. Linux-specific code.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-
-#if SANITIZER_LINUX
-
-#include "lsan_allocator.h"
-
-namespace __lsan {
-
-static THREADLOCAL u32 current_thread_tid = kInvalidTid;
-u32 GetCurrentThread() { return current_thread_tid; }
-void SetCurrentThread(u32 tid) { current_thread_tid = tid; }
-
-static THREADLOCAL AllocatorCache allocator_cache;
-AllocatorCache *GetAllocatorCache() { return &allocator_cache; }
-
-void ReplaceSystemMalloc() {}
-
-} // namespace __lsan
-
-#endif // SANITIZER_LINUX
--- /dev/null
+//=-- lsan_linux.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer. Linux/NetBSD-specific code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+
+#if SANITIZER_LINUX || SANITIZER_NETBSD
+
+#include "lsan_allocator.h"
+
+namespace __lsan {
+
+static THREADLOCAL u32 current_thread_tid = kInvalidTid;
+u32 GetCurrentThread() { return current_thread_tid; }
+void SetCurrentThread(u32 tid) { current_thread_tid = tid; }
+
+static THREADLOCAL AllocatorCache allocator_cache;
+AllocatorCache *GetAllocatorCache() { return &allocator_cache; }
+
+void ReplaceSystemMalloc() {}
+
+} // namespace __lsan
+
+#endif // SANITIZER_LINUX || SANITIZER_NETBSD
+++ /dev/null
-//===-- lsan_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 LeakSanitizer, a memory leak checker.
-//
-// Mac-specific details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "interception/interception.h"
-#include "lsan.h"
-#include "lsan_allocator.h"
-#include "lsan_thread.h"
-
-#include <pthread.h>
-
-namespace __lsan {
-// Support for the following functions from libdispatch on Mac OS:
-// dispatch_async_f()
-// dispatch_async()
-// dispatch_sync_f()
-// dispatch_sync()
-// dispatch_after_f()
-// dispatch_after()
-// dispatch_group_async_f()
-// dispatch_group_async()
-// TODO(glider): libdispatch API contains other functions that we don't support
-// yet.
-//
-// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
-// they can cause jobs to run on a thread different from the current one.
-// TODO(glider): if so, we need a test for this (otherwise we should remove
-// them).
-//
-// The following functions use dispatch_barrier_async_f() (which isn't a library
-// function but is exported) and are thus supported:
-// dispatch_source_set_cancel_handler_f()
-// dispatch_source_set_cancel_handler()
-// dispatch_source_set_event_handler_f()
-// dispatch_source_set_event_handler()
-//
-// The reference manual for Grand Central Dispatch is available at
-// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
-// The implementation details are at
-// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
-
-typedef void *dispatch_group_t;
-typedef void *dispatch_queue_t;
-typedef void *dispatch_source_t;
-typedef u64 dispatch_time_t;
-typedef void (*dispatch_function_t)(void *block);
-typedef void *(*worker_t)(void *block);
-
-// A wrapper for the ObjC blocks used to support libdispatch.
-typedef struct {
- void *block;
- dispatch_function_t func;
- u32 parent_tid;
-} lsan_block_context_t;
-
-ALWAYS_INLINE
-void lsan_register_worker_thread(int parent_tid) {
- if (GetCurrentThread() == kInvalidTid) {
- u32 tid = ThreadCreate(parent_tid, 0, true);
- ThreadStart(tid, GetTid());
- SetCurrentThread(tid);
- }
-}
-
-// For use by only those functions that allocated the context via
-// alloc_lsan_context().
-extern "C" void lsan_dispatch_call_block_and_release(void *block) {
- lsan_block_context_t *context = (lsan_block_context_t *)block;
- VReport(2,
- "lsan_dispatch_call_block_and_release(): "
- "context: %p, pthread_self: %p\n",
- block, pthread_self());
- lsan_register_worker_thread(context->parent_tid);
- // Call the original dispatcher for the block.
- context->func(context->block);
- lsan_free(context);
-}
-
-} // namespace __lsan
-
-using namespace __lsan; // NOLINT
-
-// Wrap |ctxt| and |func| into an lsan_block_context_t.
-// The caller retains control of the allocated context.
-extern "C" lsan_block_context_t *alloc_lsan_context(void *ctxt,
- dispatch_function_t func) {
- GET_STACK_TRACE_THREAD;
- lsan_block_context_t *lsan_ctxt =
- (lsan_block_context_t *)lsan_malloc(sizeof(lsan_block_context_t), stack);
- lsan_ctxt->block = ctxt;
- lsan_ctxt->func = func;
- lsan_ctxt->parent_tid = GetCurrentThread();
- return lsan_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) { \
- lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); \
- return REAL(dispatch_x_f)(dq, (void *)lsan_ctxt, \
- lsan_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) {
- lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
- return REAL(dispatch_after_f)(when, dq, (void *)lsan_ctxt,
- lsan_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) {
- lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
- REAL(dispatch_group_async_f)
- (group, dq, (void *)lsan_ctxt, lsan_dispatch_call_block_and_release);
-}
-
-#if !defined(MISSING_BLOCKS_SUPPORT)
-extern "C" {
-void dispatch_async(dispatch_queue_t dq, void (^work)(void));
-void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
- void (^work)(void));
-void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
- void (^work)(void));
-void dispatch_source_set_cancel_handler(dispatch_source_t ds,
- void (^work)(void));
-void dispatch_source_set_event_handler(dispatch_source_t ds,
- void (^work)(void));
-}
-
-#define GET_LSAN_BLOCK(work) \
- void (^lsan_block)(void); \
- int parent_tid = GetCurrentThread(); \
- lsan_block = ^(void) { \
- lsan_register_worker_thread(parent_tid); \
- work(); \
- }
-
-INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void (^work)(void)) {
- GET_LSAN_BLOCK(work);
- REAL(dispatch_async)(dq, lsan_block);
-}
-
-INTERCEPTOR(void, dispatch_group_async, dispatch_group_t dg,
- dispatch_queue_t dq, void (^work)(void)) {
- GET_LSAN_BLOCK(work);
- REAL(dispatch_group_async)(dg, dq, lsan_block);
-}
-
-INTERCEPTOR(void, dispatch_after, dispatch_time_t when, dispatch_queue_t queue,
- void (^work)(void)) {
- GET_LSAN_BLOCK(work);
- REAL(dispatch_after)(when, queue, lsan_block);
-}
-
-INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds,
- void (^work)(void)) {
- if (!work) {
- REAL(dispatch_source_set_cancel_handler)(ds, work);
- return;
- }
- GET_LSAN_BLOCK(work);
- REAL(dispatch_source_set_cancel_handler)(ds, lsan_block);
-}
-
-INTERCEPTOR(void, dispatch_source_set_event_handler, dispatch_source_t ds,
- void (^work)(void)) {
- GET_LSAN_BLOCK(work);
- REAL(dispatch_source_set_event_handler)(ds, lsan_block);
-}
-#endif
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- lsan_mac.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer, a memory leak checker.
+//
+// Mac-specific details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "interception/interception.h"
+#include "lsan.h"
+#include "lsan_allocator.h"
+#include "lsan_thread.h"
+
+#include <pthread.h>
+
+namespace __lsan {
+// Support for the following functions from libdispatch on Mac OS:
+// dispatch_async_f()
+// dispatch_async()
+// dispatch_sync_f()
+// dispatch_sync()
+// dispatch_after_f()
+// dispatch_after()
+// dispatch_group_async_f()
+// dispatch_group_async()
+// TODO(glider): libdispatch API contains other functions that we don't support
+// yet.
+//
+// dispatch_sync() and dispatch_sync_f() are synchronous, although chances are
+// they can cause jobs to run on a thread different from the current one.
+// TODO(glider): if so, we need a test for this (otherwise we should remove
+// them).
+//
+// The following functions use dispatch_barrier_async_f() (which isn't a library
+// function but is exported) and are thus supported:
+// dispatch_source_set_cancel_handler_f()
+// dispatch_source_set_cancel_handler()
+// dispatch_source_set_event_handler_f()
+// dispatch_source_set_event_handler()
+//
+// The reference manual for Grand Central Dispatch is available at
+// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
+// The implementation details are at
+// http://libdispatch.macosforge.org/trac/browser/trunk/src/queue.c
+
+typedef void *dispatch_group_t;
+typedef void *dispatch_queue_t;
+typedef void *dispatch_source_t;
+typedef u64 dispatch_time_t;
+typedef void (*dispatch_function_t)(void *block);
+typedef void *(*worker_t)(void *block);
+
+// A wrapper for the ObjC blocks used to support libdispatch.
+typedef struct {
+ void *block;
+ dispatch_function_t func;
+ u32 parent_tid;
+} lsan_block_context_t;
+
+ALWAYS_INLINE
+void lsan_register_worker_thread(int parent_tid) {
+ if (GetCurrentThread() == kInvalidTid) {
+ u32 tid = ThreadCreate(parent_tid, 0, true);
+ ThreadStart(tid, GetTid());
+ SetCurrentThread(tid);
+ }
+}
+
+// For use by only those functions that allocated the context via
+// alloc_lsan_context().
+extern "C" void lsan_dispatch_call_block_and_release(void *block) {
+ lsan_block_context_t *context = (lsan_block_context_t *)block;
+ VReport(2,
+ "lsan_dispatch_call_block_and_release(): "
+ "context: %p, pthread_self: %p\n",
+ block, pthread_self());
+ lsan_register_worker_thread(context->parent_tid);
+ // Call the original dispatcher for the block.
+ context->func(context->block);
+ lsan_free(context);
+}
+
+} // namespace __lsan
+
+using namespace __lsan; // NOLINT
+
+// Wrap |ctxt| and |func| into an lsan_block_context_t.
+// The caller retains control of the allocated context.
+extern "C" lsan_block_context_t *alloc_lsan_context(void *ctxt,
+ dispatch_function_t func) {
+ GET_STACK_TRACE_THREAD;
+ lsan_block_context_t *lsan_ctxt =
+ (lsan_block_context_t *)lsan_malloc(sizeof(lsan_block_context_t), stack);
+ lsan_ctxt->block = ctxt;
+ lsan_ctxt->func = func;
+ lsan_ctxt->parent_tid = GetCurrentThread();
+ return lsan_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) { \
+ lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func); \
+ return REAL(dispatch_x_f)(dq, (void *)lsan_ctxt, \
+ lsan_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) {
+ lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
+ return REAL(dispatch_after_f)(when, dq, (void *)lsan_ctxt,
+ lsan_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) {
+ lsan_block_context_t *lsan_ctxt = alloc_lsan_context(ctxt, func);
+ REAL(dispatch_group_async_f)
+ (group, dq, (void *)lsan_ctxt, lsan_dispatch_call_block_and_release);
+}
+
+#if !defined(MISSING_BLOCKS_SUPPORT)
+extern "C" {
+void dispatch_async(dispatch_queue_t dq, void (^work)(void));
+void dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq,
+ void (^work)(void));
+void dispatch_after(dispatch_time_t when, dispatch_queue_t queue,
+ void (^work)(void));
+void dispatch_source_set_cancel_handler(dispatch_source_t ds,
+ void (^work)(void));
+void dispatch_source_set_event_handler(dispatch_source_t ds,
+ void (^work)(void));
+}
+
+#define GET_LSAN_BLOCK(work) \
+ void (^lsan_block)(void); \
+ int parent_tid = GetCurrentThread(); \
+ lsan_block = ^(void) { \
+ lsan_register_worker_thread(parent_tid); \
+ work(); \
+ }
+
+INTERCEPTOR(void, dispatch_async, dispatch_queue_t dq, void (^work)(void)) {
+ GET_LSAN_BLOCK(work);
+ REAL(dispatch_async)(dq, lsan_block);
+}
+
+INTERCEPTOR(void, dispatch_group_async, dispatch_group_t dg,
+ dispatch_queue_t dq, void (^work)(void)) {
+ GET_LSAN_BLOCK(work);
+ REAL(dispatch_group_async)(dg, dq, lsan_block);
+}
+
+INTERCEPTOR(void, dispatch_after, dispatch_time_t when, dispatch_queue_t queue,
+ void (^work)(void)) {
+ GET_LSAN_BLOCK(work);
+ REAL(dispatch_after)(when, queue, lsan_block);
+}
+
+INTERCEPTOR(void, dispatch_source_set_cancel_handler, dispatch_source_t ds,
+ void (^work)(void)) {
+ if (!work) {
+ REAL(dispatch_source_set_cancel_handler)(ds, work);
+ return;
+ }
+ GET_LSAN_BLOCK(work);
+ REAL(dispatch_source_set_cancel_handler)(ds, lsan_block);
+}
+
+INTERCEPTOR(void, dispatch_source_set_event_handler, dispatch_source_t ds,
+ void (^work)(void)) {
+ GET_LSAN_BLOCK(work);
+ REAL(dispatch_source_set_event_handler)(ds, lsan_block);
+}
+#endif
+
+#endif // SANITIZER_MAC
+++ /dev/null
-//===-- lsan_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 LeakSanitizer (LSan), a memory leak detector.
-//
-// Mac-specific malloc interception.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "lsan.h"
-#include "lsan_allocator.h"
-#include "lsan_thread.h"
-
-using namespace __lsan;
-#define COMMON_MALLOC_ZONE_NAME "lsan"
-#define COMMON_MALLOC_ENTER() ENSURE_LSAN_INITED
-#define COMMON_MALLOC_SANITIZER_INITIALIZED lsan_inited
-#define COMMON_MALLOC_FORCE_LOCK()
-#define COMMON_MALLOC_FORCE_UNLOCK()
-#define COMMON_MALLOC_MEMALIGN(alignment, size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = lsan_memalign(alignment, size, stack)
-#define COMMON_MALLOC_MALLOC(size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = lsan_malloc(size, stack)
-#define COMMON_MALLOC_REALLOC(ptr, size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = lsan_realloc(ptr, size, stack)
-#define COMMON_MALLOC_CALLOC(count, size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = lsan_calloc(count, size, stack)
-#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \
- GET_STACK_TRACE_MALLOC; \
- int res = lsan_posix_memalign(memptr, alignment, size, stack)
-#define COMMON_MALLOC_VALLOC(size) \
- GET_STACK_TRACE_MALLOC; \
- void *p = lsan_valloc(size, stack)
-#define COMMON_MALLOC_FREE(ptr) \
- lsan_free(ptr)
-#define COMMON_MALLOC_SIZE(ptr) \
- uptr size = lsan_mz_size(ptr)
-#define COMMON_MALLOC_FILL_STATS(zone, stats)
-#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
- (void)zone_name; \
- Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
-#define COMMON_MALLOC_NAMESPACE __lsan
-
-#include "sanitizer_common/sanitizer_malloc_mac.inc"
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- lsan_malloc_mac.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer (LSan), a memory leak detector.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "lsan.h"
+#include "lsan_allocator.h"
+#include "lsan_thread.h"
+
+using namespace __lsan;
+#define COMMON_MALLOC_ZONE_NAME "lsan"
+#define COMMON_MALLOC_ENTER() ENSURE_LSAN_INITED
+#define COMMON_MALLOC_SANITIZER_INITIALIZED lsan_inited
+#define COMMON_MALLOC_FORCE_LOCK()
+#define COMMON_MALLOC_FORCE_UNLOCK()
+#define COMMON_MALLOC_MEMALIGN(alignment, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = lsan_memalign(alignment, size, stack)
+#define COMMON_MALLOC_MALLOC(size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = lsan_malloc(size, stack)
+#define COMMON_MALLOC_REALLOC(ptr, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = lsan_realloc(ptr, size, stack)
+#define COMMON_MALLOC_CALLOC(count, size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = lsan_calloc(count, size, stack)
+#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \
+ GET_STACK_TRACE_MALLOC; \
+ int res = lsan_posix_memalign(memptr, alignment, size, stack)
+#define COMMON_MALLOC_VALLOC(size) \
+ GET_STACK_TRACE_MALLOC; \
+ void *p = lsan_valloc(size, stack)
+#define COMMON_MALLOC_FREE(ptr) \
+ lsan_free(ptr)
+#define COMMON_MALLOC_SIZE(ptr) \
+ uptr size = lsan_mz_size(ptr)
+#define COMMON_MALLOC_FILL_STATS(zone, stats)
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
+ (void)zone_name; \
+ Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
+#define COMMON_MALLOC_NAMESPACE __lsan
+#define COMMON_MALLOC_HAS_ZONE_ENUMERATOR 0
+#define COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT 0
+
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
+
+#endif // SANITIZER_MAC
+++ /dev/null
-//===-- lsan_preinit.cc ---------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-//
-// Call __lsan_init at the very early stage of process startup.
-//===----------------------------------------------------------------------===//
-
-#include "lsan.h"
-
-#if SANITIZER_CAN_USE_PREINIT_ARRAY
- // We force __lsan_init to be called before anyone else by placing it into
- // .preinit_array section.
- __attribute__((section(".preinit_array"), used))
- void (*__local_lsan_preinit)(void) = __lsan_init;
-#endif
--- /dev/null
+//===-- lsan_preinit.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+//
+// Call __lsan_init at the very early stage of process startup.
+//===----------------------------------------------------------------------===//
+
+#include "lsan.h"
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+ // We force __lsan_init to be called before anyone else by placing it into
+ // .preinit_array section.
+ __attribute__((section(".preinit_array"), used))
+ void (*__local_lsan_preinit)(void) = __lsan_init;
+#endif
+++ /dev/null
-//=-- lsan_thread.cc ------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of LeakSanitizer.
-// See lsan_thread.h for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "lsan_thread.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_thread_registry.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
-#include "lsan_allocator.h"
-#include "lsan_common.h"
-
-namespace __lsan {
-
-static ThreadRegistry *thread_registry;
-
-static ThreadContextBase *CreateThreadContext(u32 tid) {
- void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext");
- return new(mem) ThreadContext(tid);
-}
-
-static const uptr kMaxThreads = 1 << 13;
-static const uptr kThreadQuarantineSize = 64;
-
-void InitializeThreadRegistry() {
- static ALIGNED(64) char thread_registry_placeholder[sizeof(ThreadRegistry)];
- thread_registry = new(thread_registry_placeholder)
- ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize);
-}
-
-ThreadContext::ThreadContext(int tid)
- : ThreadContextBase(tid),
- stack_begin_(0),
- stack_end_(0),
- cache_begin_(0),
- cache_end_(0),
- tls_begin_(0),
- tls_end_(0),
- dtls_(nullptr) {}
-
-struct OnStartedArgs {
- uptr stack_begin, stack_end,
- cache_begin, cache_end,
- tls_begin, tls_end;
- DTLS *dtls;
-};
-
-void ThreadContext::OnStarted(void *arg) {
- OnStartedArgs *args = reinterpret_cast<OnStartedArgs *>(arg);
- stack_begin_ = args->stack_begin;
- stack_end_ = args->stack_end;
- tls_begin_ = args->tls_begin;
- tls_end_ = args->tls_end;
- cache_begin_ = args->cache_begin;
- cache_end_ = args->cache_end;
- dtls_ = args->dtls;
-}
-
-void ThreadContext::OnFinished() {
- AllocatorThreadFinish();
- DTLS_Destroy();
-}
-
-u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) {
- return thread_registry->CreateThread(user_id, detached, parent_tid,
- /* arg */ nullptr);
-}
-
-void ThreadStart(u32 tid, tid_t os_id, bool workerthread) {
- OnStartedArgs args;
- uptr stack_size = 0;
- uptr tls_size = 0;
- GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size,
- &args.tls_begin, &tls_size);
- args.stack_end = args.stack_begin + stack_size;
- args.tls_end = args.tls_begin + tls_size;
- GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
- args.dtls = DTLS_Get();
- thread_registry->StartThread(tid, os_id, workerthread, &args);
-}
-
-void ThreadFinish() {
- thread_registry->FinishThread(GetCurrentThread());
- SetCurrentThread(kInvalidTid);
-}
-
-ThreadContext *CurrentThreadContext() {
- if (!thread_registry) return nullptr;
- if (GetCurrentThread() == kInvalidTid)
- return nullptr;
- // No lock needed when getting current thread.
- return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread());
-}
-
-static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
- uptr uid = (uptr)arg;
- if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
- return true;
- }
- return false;
-}
-
-u32 ThreadTid(uptr uid) {
- return thread_registry->FindThread(FindThreadByUid, (void*)uid);
-}
-
-void ThreadJoin(u32 tid) {
- CHECK_NE(tid, kInvalidTid);
- thread_registry->JoinThread(tid, /* arg */nullptr);
-}
-
-void EnsureMainThreadIDIsCorrect() {
- if (GetCurrentThread() == 0)
- CurrentThreadContext()->os_id = GetTid();
-}
-
-///// Interface to the common LSan module. /////
-
-bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
- uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
- uptr *cache_end, DTLS **dtls) {
- ThreadContext *context = static_cast<ThreadContext *>(
- thread_registry->FindThreadContextByOsIDLocked(os_id));
- if (!context) return false;
- *stack_begin = context->stack_begin();
- *stack_end = context->stack_end();
- *tls_begin = context->tls_begin();
- *tls_end = context->tls_end();
- *cache_begin = context->cache_begin();
- *cache_end = context->cache_end();
- *dtls = context->dtls();
- return true;
-}
-
-void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
- void *arg) {
-}
-
-void LockThreadRegistry() {
- thread_registry->Lock();
-}
-
-void UnlockThreadRegistry() {
- thread_registry->Unlock();
-}
-
-ThreadRegistry *GetThreadRegistryLocked() {
- thread_registry->CheckLocked();
- return thread_registry;
-}
-
-} // namespace __lsan
--- /dev/null
+//=-- lsan_thread.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of LeakSanitizer.
+// See lsan_thread.h for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lsan_thread.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_thread_registry.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+#include "lsan_allocator.h"
+#include "lsan_common.h"
+
+namespace __lsan {
+
+static ThreadRegistry *thread_registry;
+
+static ThreadContextBase *CreateThreadContext(u32 tid) {
+ void *mem = MmapOrDie(sizeof(ThreadContext), "ThreadContext");
+ return new(mem) ThreadContext(tid);
+}
+
+static const uptr kMaxThreads = 1 << 13;
+static const uptr kThreadQuarantineSize = 64;
+
+void InitializeThreadRegistry() {
+ static ALIGNED(64) char thread_registry_placeholder[sizeof(ThreadRegistry)];
+ thread_registry = new(thread_registry_placeholder)
+ ThreadRegistry(CreateThreadContext, kMaxThreads, kThreadQuarantineSize);
+}
+
+ThreadContext::ThreadContext(int tid)
+ : ThreadContextBase(tid),
+ stack_begin_(0),
+ stack_end_(0),
+ cache_begin_(0),
+ cache_end_(0),
+ tls_begin_(0),
+ tls_end_(0),
+ dtls_(nullptr) {}
+
+struct OnStartedArgs {
+ uptr stack_begin, stack_end,
+ cache_begin, cache_end,
+ tls_begin, tls_end;
+ DTLS *dtls;
+};
+
+void ThreadContext::OnStarted(void *arg) {
+ OnStartedArgs *args = reinterpret_cast<OnStartedArgs *>(arg);
+ stack_begin_ = args->stack_begin;
+ stack_end_ = args->stack_end;
+ tls_begin_ = args->tls_begin;
+ tls_end_ = args->tls_end;
+ cache_begin_ = args->cache_begin;
+ cache_end_ = args->cache_end;
+ dtls_ = args->dtls;
+}
+
+void ThreadContext::OnFinished() {
+ AllocatorThreadFinish();
+ DTLS_Destroy();
+}
+
+u32 ThreadCreate(u32 parent_tid, uptr user_id, bool detached) {
+ return thread_registry->CreateThread(user_id, detached, parent_tid,
+ /* arg */ nullptr);
+}
+
+void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) {
+ OnStartedArgs args;
+ uptr stack_size = 0;
+ uptr tls_size = 0;
+ GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size,
+ &args.tls_begin, &tls_size);
+ args.stack_end = args.stack_begin + stack_size;
+ args.tls_end = args.tls_begin + tls_size;
+ GetAllocatorCacheRange(&args.cache_begin, &args.cache_end);
+ args.dtls = DTLS_Get();
+ thread_registry->StartThread(tid, os_id, thread_type, &args);
+}
+
+void ThreadFinish() {
+ thread_registry->FinishThread(GetCurrentThread());
+ SetCurrentThread(kInvalidTid);
+}
+
+ThreadContext *CurrentThreadContext() {
+ if (!thread_registry) return nullptr;
+ if (GetCurrentThread() == kInvalidTid)
+ return nullptr;
+ // No lock needed when getting current thread.
+ return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread());
+}
+
+static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
+ uptr uid = (uptr)arg;
+ if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
+ return true;
+ }
+ return false;
+}
+
+u32 ThreadTid(uptr uid) {
+ return thread_registry->FindThread(FindThreadByUid, (void*)uid);
+}
+
+void ThreadJoin(u32 tid) {
+ CHECK_NE(tid, kInvalidTid);
+ thread_registry->JoinThread(tid, /* arg */nullptr);
+}
+
+void EnsureMainThreadIDIsCorrect() {
+ if (GetCurrentThread() == 0)
+ CurrentThreadContext()->os_id = GetTid();
+}
+
+///// Interface to the common LSan module. /////
+
+bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end,
+ uptr *tls_begin, uptr *tls_end, uptr *cache_begin,
+ uptr *cache_end, DTLS **dtls) {
+ ThreadContext *context = static_cast<ThreadContext *>(
+ thread_registry->FindThreadContextByOsIDLocked(os_id));
+ if (!context) return false;
+ *stack_begin = context->stack_begin();
+ *stack_end = context->stack_end();
+ *tls_begin = context->tls_begin();
+ *tls_end = context->tls_end();
+ *cache_begin = context->cache_begin();
+ *cache_end = context->cache_end();
+ *dtls = context->dtls();
+ return true;
+}
+
+void ForEachExtraStackRange(tid_t os_id, RangeIteratorCallback callback,
+ void *arg) {
+}
+
+void LockThreadRegistry() {
+ thread_registry->Lock();
+}
+
+void UnlockThreadRegistry() {
+ thread_registry->Unlock();
+}
+
+ThreadRegistry *GetThreadRegistryLocked() {
+ thread_registry->CheckLocked();
+ return thread_registry;
+}
+
+} // namespace __lsan
//=-- lsan_thread.h -------------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void InitializeThreadRegistry();
-void ThreadStart(u32 tid, tid_t os_id, bool workerthread = false);
+void ThreadStart(u32 tid, tid_t os_id,
+ ThreadType thread_type = ThreadType::Regular);
void ThreadFinish();
u32 ThreadCreate(u32 tid, uptr uid, bool detached);
void ThreadJoin(u32 tid);
}
list_files() {
- (cd $1; ls *.{cc,h,inc,S} 2> /dev/null)
+ (cd $1; ls *.{cc,cpp,h,inc,S} 2> /dev/null)
}
merge lib/sanitizer_common sanitizer_common
merge lib/interception interception
merge lib/ubsan ubsan
+merge lib/BlocksRuntime/ BlocksRuntime
# Need to merge lib/builtins/assembly.h file:
mkdir -p builtins
AM_CXXFLAGS += $(EXTRA_CXXFLAGS)
if LIBBACKTRACE_SUPPORTED
AM_CXXFLAGS += -DSANITIZER_LIBBACKTRACE -DSANITIZER_CP_DEMANGLE \
+ -I $(top_srcdir)/ \
-I $(top_srcdir)/../libbacktrace \
-I $(top_builddir)/libbacktrace \
-I $(top_srcdir)/../include \
noinst_LTLIBRARIES = libsanitizer_common.la
sanitizer_common_files = \
- sancov_flags.cc \
- sanitizer_allocator.cc \
- sanitizer_allocator_checks.cc \
- sanitizer_allocator_report.cc \
- sanitizer_common.cc \
- sanitizer_common_libcdep.cc \
- sanitizer_coverage_libcdep_new.cc \
- sanitizer_deadlock_detector1.cc \
- sanitizer_deadlock_detector2.cc \
- sanitizer_errno.cc \
- sanitizer_file.cc \
- sanitizer_flags.cc \
- sanitizer_flag_parser.cc \
- sanitizer_libc.cc \
- sanitizer_libignore.cc \
- sanitizer_linux.cc \
- sanitizer_linux_libcdep.cc \
- sanitizer_linux_s390.cc \
- sanitizer_mac.cc \
- sanitizer_mac_libcdep.cc \
- sanitizer_netbsd.cc \
- sanitizer_openbsd.cc \
- sanitizer_persistent_allocator.cc \
- sanitizer_platform_limits_linux.cc \
- sanitizer_platform_limits_openbsd.cc \
- sanitizer_platform_limits_posix.cc \
- sanitizer_platform_limits_solaris.cc \
- sanitizer_posix.cc \
- sanitizer_posix_libcdep.cc \
- sanitizer_printf.cc \
- sanitizer_procmaps_bsd.cc \
- sanitizer_procmaps_common.cc \
- sanitizer_procmaps_linux.cc \
- sanitizer_procmaps_mac.cc \
- sanitizer_procmaps_solaris.cc \
- sanitizer_rtems.cc \
- sanitizer_solaris.cc \
- sanitizer_stackdepot.cc \
- sanitizer_stacktrace.cc \
- sanitizer_stacktrace_libcdep.cc \
- sanitizer_stacktrace_sparc.cc \
- sanitizer_symbolizer_mac.cc \
- sanitizer_symbolizer_report.cc \
- sanitizer_stacktrace_printer.cc \
- sanitizer_stoptheworld_linux_libcdep.cc \
- sanitizer_stoptheworld_mac.cc \
- sanitizer_suppressions.cc \
- sanitizer_symbolizer.cc \
- sanitizer_symbolizer_libbacktrace.cc \
- sanitizer_symbolizer_libcdep.cc \
- sanitizer_symbolizer_posix_libcdep.cc \
- sanitizer_symbolizer_win.cc \
- sanitizer_termination.cc \
- sanitizer_thread_registry.cc \
- sanitizer_tls_get_addr.cc \
- sanitizer_unwind_linux_libcdep.cc \
- sanitizer_unwind_win.cc \
- sanitizer_win.cc
+ sancov_flags.cpp \
+ sanitizer_allocator.cpp \
+ sanitizer_allocator_checks.cpp \
+ sanitizer_allocator_report.cpp \
+ sanitizer_common.cpp \
+ sanitizer_common_libcdep.cpp \
+ sanitizer_coverage_libcdep_new.cpp \
+ sanitizer_deadlock_detector1.cpp \
+ sanitizer_deadlock_detector2.cpp \
+ sanitizer_errno.cpp \
+ sanitizer_file.cpp \
+ sanitizer_flags.cpp \
+ sanitizer_flag_parser.cpp \
+ sanitizer_libc.cpp \
+ sanitizer_libignore.cpp \
+ sanitizer_linux.cpp \
+ sanitizer_linux_libcdep.cpp \
+ sanitizer_linux_s390.cpp \
+ sanitizer_mac.cpp \
+ sanitizer_mac_libcdep.cpp \
+ sanitizer_netbsd.cpp \
+ sanitizer_openbsd.cpp \
+ sanitizer_persistent_allocator.cpp \
+ sanitizer_platform_limits_linux.cpp \
+ sanitizer_platform_limits_openbsd.cpp \
+ sanitizer_platform_limits_posix.cpp \
+ sanitizer_platform_limits_solaris.cpp \
+ sanitizer_posix.cpp \
+ sanitizer_posix_libcdep.cpp \
+ sanitizer_printf.cpp \
+ sanitizer_procmaps_bsd.cpp \
+ sanitizer_procmaps_common.cpp \
+ sanitizer_procmaps_linux.cpp \
+ sanitizer_procmaps_mac.cpp \
+ sanitizer_procmaps_solaris.cpp \
+ sanitizer_rtems.cpp \
+ sanitizer_solaris.cpp \
+ sanitizer_stackdepot.cpp \
+ sanitizer_stacktrace.cpp \
+ sanitizer_stacktrace_libcdep.cpp \
+ sanitizer_stacktrace_sparc.cpp \
+ sanitizer_symbolizer_mac.cpp \
+ sanitizer_symbolizer_report.cpp \
+ sanitizer_stacktrace_printer.cpp \
+ sanitizer_stoptheworld_linux_libcdep.cpp \
+ sanitizer_stoptheworld_mac.cpp \
+ sanitizer_suppressions.cpp \
+ sanitizer_symbolizer.cpp \
+ sanitizer_symbolizer_libbacktrace.cpp \
+ sanitizer_symbolizer_libcdep.cpp \
+ sanitizer_symbolizer_posix_libcdep.cpp \
+ sanitizer_symbolizer_win.cpp \
+ sanitizer_termination.cpp \
+ sanitizer_thread_registry.cpp \
+ sanitizer_tls_get_addr.cpp \
+ sanitizer_unwind_linux_libcdep.cpp \
+ sanitizer_unwind_win.cpp \
+ sanitizer_win.cpp
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
-EXTRA_libsanitizer_common_la_SOURCES = sanitizer_linux_mips64.S sanitizer_linux_x86_64.S
libsanitizer_common_la_LIBADD = $(SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS)
libsanitizer_common_la_DEPENDENCIES = $(SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS)
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
host_triplet = @host@
target_triplet = @target@
@LIBBACKTRACE_SUPPORTED_TRUE@am__append_1 = -DSANITIZER_LIBBACKTRACE -DSANITIZER_CP_DEMANGLE \
+@LIBBACKTRACE_SUPPORTED_TRUE@ -I $(top_srcdir)/ \
@LIBBACKTRACE_SUPPORTED_TRUE@ -I $(top_srcdir)/../libbacktrace \
@LIBBACKTRACE_SUPPORTED_TRUE@ -I $(top_builddir)/libbacktrace \
@LIBBACKTRACE_SUPPORTED_TRUE@ -I $(top_srcdir)/../include \
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/sancov_flags.Plo \
+ ./$(DEPDIR)/sanitizer_allocator.Plo \
+ ./$(DEPDIR)/sanitizer_allocator_checks.Plo \
+ ./$(DEPDIR)/sanitizer_allocator_report.Plo \
+ ./$(DEPDIR)/sanitizer_common.Plo \
+ ./$(DEPDIR)/sanitizer_common_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_coverage_libcdep_new.Plo \
+ ./$(DEPDIR)/sanitizer_deadlock_detector1.Plo \
+ ./$(DEPDIR)/sanitizer_deadlock_detector2.Plo \
+ ./$(DEPDIR)/sanitizer_errno.Plo ./$(DEPDIR)/sanitizer_file.Plo \
+ ./$(DEPDIR)/sanitizer_flag_parser.Plo \
+ ./$(DEPDIR)/sanitizer_flags.Plo ./$(DEPDIR)/sanitizer_libc.Plo \
+ ./$(DEPDIR)/sanitizer_libignore.Plo \
+ ./$(DEPDIR)/sanitizer_linux.Plo \
+ ./$(DEPDIR)/sanitizer_linux_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_linux_s390.Plo \
+ ./$(DEPDIR)/sanitizer_mac.Plo \
+ ./$(DEPDIR)/sanitizer_mac_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_netbsd.Plo \
+ ./$(DEPDIR)/sanitizer_openbsd.Plo \
+ ./$(DEPDIR)/sanitizer_persistent_allocator.Plo \
+ ./$(DEPDIR)/sanitizer_platform_limits_linux.Plo \
+ ./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo \
+ ./$(DEPDIR)/sanitizer_platform_limits_posix.Plo \
+ ./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo \
+ ./$(DEPDIR)/sanitizer_posix.Plo \
+ ./$(DEPDIR)/sanitizer_posix_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_printf.Plo \
+ ./$(DEPDIR)/sanitizer_procmaps_bsd.Plo \
+ ./$(DEPDIR)/sanitizer_procmaps_common.Plo \
+ ./$(DEPDIR)/sanitizer_procmaps_linux.Plo \
+ ./$(DEPDIR)/sanitizer_procmaps_mac.Plo \
+ ./$(DEPDIR)/sanitizer_procmaps_solaris.Plo \
+ ./$(DEPDIR)/sanitizer_rtems.Plo \
+ ./$(DEPDIR)/sanitizer_solaris.Plo \
+ ./$(DEPDIR)/sanitizer_stackdepot.Plo \
+ ./$(DEPDIR)/sanitizer_stacktrace.Plo \
+ ./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_stacktrace_printer.Plo \
+ ./$(DEPDIR)/sanitizer_stacktrace_sparc.Plo \
+ ./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_stoptheworld_mac.Plo \
+ ./$(DEPDIR)/sanitizer_suppressions.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer_mac.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer_report.Plo \
+ ./$(DEPDIR)/sanitizer_symbolizer_win.Plo \
+ ./$(DEPDIR)/sanitizer_termination.Plo \
+ ./$(DEPDIR)/sanitizer_thread_registry.Plo \
+ ./$(DEPDIR)/sanitizer_tls_get_addr.Plo \
+ ./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo \
+ ./$(DEPDIR)/sanitizer_unwind_win.Plo \
+ ./$(DEPDIR)/sanitizer_win.Plo
am__mv = mv -f
-CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
-LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
- $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
- $(AM_CCASFLAGS) $(CCASFLAGS)
-AM_V_CPPAS = $(am__v_CPPAS_@AM_V@)
-am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@)
-am__v_CPPAS_0 = @echo " CPPAS " $@;
-am__v_CPPAS_1 =
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
am__v_CXXLD_0 = @echo " CXXLD " $@;
am__v_CXXLD_1 =
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
- $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
- $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
- $(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_@AM_V@)
-am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
-am__v_CC_0 = @echo " CC " $@;
-am__v_CC_1 =
-CCLD = $(CC)
-LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_@AM_V@)
-am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
-am__v_CCLD_0 = @echo " CCLD " $@;
-am__v_CCLD_1 =
-SOURCES = $(libsanitizer_common_la_SOURCES) \
- $(EXTRA_libsanitizer_common_la_SOURCES)
+SOURCES = $(libsanitizer_common_la_SOURCES)
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_common.la
sanitizer_common_files = \
- sancov_flags.cc \
- sanitizer_allocator.cc \
- sanitizer_allocator_checks.cc \
- sanitizer_allocator_report.cc \
- sanitizer_common.cc \
- sanitizer_common_libcdep.cc \
- sanitizer_coverage_libcdep_new.cc \
- sanitizer_deadlock_detector1.cc \
- sanitizer_deadlock_detector2.cc \
- sanitizer_errno.cc \
- sanitizer_file.cc \
- sanitizer_flags.cc \
- sanitizer_flag_parser.cc \
- sanitizer_libc.cc \
- sanitizer_libignore.cc \
- sanitizer_linux.cc \
- sanitizer_linux_libcdep.cc \
- sanitizer_linux_s390.cc \
- sanitizer_mac.cc \
- sanitizer_mac_libcdep.cc \
- sanitizer_netbsd.cc \
- sanitizer_openbsd.cc \
- sanitizer_persistent_allocator.cc \
- sanitizer_platform_limits_linux.cc \
- sanitizer_platform_limits_openbsd.cc \
- sanitizer_platform_limits_posix.cc \
- sanitizer_platform_limits_solaris.cc \
- sanitizer_posix.cc \
- sanitizer_posix_libcdep.cc \
- sanitizer_printf.cc \
- sanitizer_procmaps_bsd.cc \
- sanitizer_procmaps_common.cc \
- sanitizer_procmaps_linux.cc \
- sanitizer_procmaps_mac.cc \
- sanitizer_procmaps_solaris.cc \
- sanitizer_rtems.cc \
- sanitizer_solaris.cc \
- sanitizer_stackdepot.cc \
- sanitizer_stacktrace.cc \
- sanitizer_stacktrace_libcdep.cc \
- sanitizer_stacktrace_sparc.cc \
- sanitizer_symbolizer_mac.cc \
- sanitizer_symbolizer_report.cc \
- sanitizer_stacktrace_printer.cc \
- sanitizer_stoptheworld_linux_libcdep.cc \
- sanitizer_stoptheworld_mac.cc \
- sanitizer_suppressions.cc \
- sanitizer_symbolizer.cc \
- sanitizer_symbolizer_libbacktrace.cc \
- sanitizer_symbolizer_libcdep.cc \
- sanitizer_symbolizer_posix_libcdep.cc \
- sanitizer_symbolizer_win.cc \
- sanitizer_termination.cc \
- sanitizer_thread_registry.cc \
- sanitizer_tls_get_addr.cc \
- sanitizer_unwind_linux_libcdep.cc \
- sanitizer_unwind_win.cc \
- sanitizer_win.cc
+ sancov_flags.cpp \
+ sanitizer_allocator.cpp \
+ sanitizer_allocator_checks.cpp \
+ sanitizer_allocator_report.cpp \
+ sanitizer_common.cpp \
+ sanitizer_common_libcdep.cpp \
+ sanitizer_coverage_libcdep_new.cpp \
+ sanitizer_deadlock_detector1.cpp \
+ sanitizer_deadlock_detector2.cpp \
+ sanitizer_errno.cpp \
+ sanitizer_file.cpp \
+ sanitizer_flags.cpp \
+ sanitizer_flag_parser.cpp \
+ sanitizer_libc.cpp \
+ sanitizer_libignore.cpp \
+ sanitizer_linux.cpp \
+ sanitizer_linux_libcdep.cpp \
+ sanitizer_linux_s390.cpp \
+ sanitizer_mac.cpp \
+ sanitizer_mac_libcdep.cpp \
+ sanitizer_netbsd.cpp \
+ sanitizer_openbsd.cpp \
+ sanitizer_persistent_allocator.cpp \
+ sanitizer_platform_limits_linux.cpp \
+ sanitizer_platform_limits_openbsd.cpp \
+ sanitizer_platform_limits_posix.cpp \
+ sanitizer_platform_limits_solaris.cpp \
+ sanitizer_posix.cpp \
+ sanitizer_posix_libcdep.cpp \
+ sanitizer_printf.cpp \
+ sanitizer_procmaps_bsd.cpp \
+ sanitizer_procmaps_common.cpp \
+ sanitizer_procmaps_linux.cpp \
+ sanitizer_procmaps_mac.cpp \
+ sanitizer_procmaps_solaris.cpp \
+ sanitizer_rtems.cpp \
+ sanitizer_solaris.cpp \
+ sanitizer_stackdepot.cpp \
+ sanitizer_stacktrace.cpp \
+ sanitizer_stacktrace_libcdep.cpp \
+ sanitizer_stacktrace_sparc.cpp \
+ sanitizer_symbolizer_mac.cpp \
+ sanitizer_symbolizer_report.cpp \
+ sanitizer_stacktrace_printer.cpp \
+ sanitizer_stoptheworld_linux_libcdep.cpp \
+ sanitizer_stoptheworld_mac.cpp \
+ sanitizer_suppressions.cpp \
+ sanitizer_symbolizer.cpp \
+ sanitizer_symbolizer_libbacktrace.cpp \
+ sanitizer_symbolizer_libcdep.cpp \
+ sanitizer_symbolizer_posix_libcdep.cpp \
+ sanitizer_symbolizer_win.cpp \
+ sanitizer_termination.cpp \
+ sanitizer_thread_registry.cpp \
+ sanitizer_tls_get_addr.cpp \
+ sanitizer_unwind_linux_libcdep.cpp \
+ sanitizer_unwind_win.cpp \
+ sanitizer_win.cpp
libsanitizer_common_la_SOURCES = $(sanitizer_common_files)
-EXTRA_libsanitizer_common_la_SOURCES = sanitizer_linux_mips64.S sanitizer_linux_x86_64.S
libsanitizer_common_la_LIBADD = $(SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS)
libsanitizer_common_la_DEPENDENCIES = $(SANITIZER_COMMON_TARGET_DEPENDENT_OBJECTS)
all: all-am
.SUFFIXES:
-.SUFFIXES: .S .cc .lo .o .obj
+.SUFFIXES: .cpp .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sancov_flags.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator_checks.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator_report.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_libcdep_new.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_errno.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_file.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flag_parser.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libignore.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_mips64.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_s390.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_x86_64.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_netbsd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_openbsd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_persistent_allocator.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_bsd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_common.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_solaris.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_rtems.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_solaris.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_printer.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_sparc.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stoptheworld_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_suppressions.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_report.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_termination.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_win.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@
-
-.S.o:
-@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $<
-
-.S.obj:
-@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-
-.S.lo:
-@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(LTCPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
-
-.cc.o:
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sancov_flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator_checks.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator_report.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage_libcdep_new.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector1.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_deadlock_detector2.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_errno.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_file.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flag_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libignore.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_s390.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_netbsd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_openbsd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_persistent_allocator.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_posix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_posix_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_bsd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_common.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_procmaps_solaris.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_rtems.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_solaris.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_printer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_sparc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stoptheworld_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_suppressions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_report.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_termination.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_tls_get_addr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_unwind_win.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_win.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
-.cc.obj:
+.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-.cc.lo:
+.cpp.lo:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/sancov_flags.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_allocator.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_allocator_checks.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_allocator_report.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_common.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_common_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_coverage_libcdep_new.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_deadlock_detector1.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_deadlock_detector2.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_errno.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_file.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_flag_parser.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_flags.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_libc.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_libignore.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_linux.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_linux_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_linux_s390.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_mac_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_netbsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_openbsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_persistent_allocator.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_linux.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_posix.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_posix.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_posix_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_printf.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_bsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_common.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_linux.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_solaris.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_rtems.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_solaris.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stackdepot.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace_printer.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace_sparc.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stoptheworld_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_suppressions.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_report.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_win.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_termination.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_thread_registry.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_tls_get_addr.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_unwind_win.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_win.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/sancov_flags.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_allocator.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_allocator_checks.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_allocator_report.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_common.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_common_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_coverage_libcdep_new.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_deadlock_detector1.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_deadlock_detector2.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_errno.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_file.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_flag_parser.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_flags.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_libc.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_libignore.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_linux.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_linux_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_linux_s390.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_mac_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_netbsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_openbsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_persistent_allocator.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_linux.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_openbsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_posix.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_platform_limits_solaris.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_posix.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_posix_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_printf.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_bsd.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_common.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_linux.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_procmaps_solaris.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_rtems.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_solaris.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stackdepot.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace_printer.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stacktrace_sparc.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_stoptheworld_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_suppressions.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_mac.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_report.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_symbolizer_win.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_termination.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_thread_registry.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_tls_get_addr.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_unwind_linux_libcdep.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_unwind_win.Plo
+ -rm -f ./$(DEPDIR)/sanitizer_win.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-noinstLTLIBRARIES cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip installcheck installcheck-am \
- installdirs maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags dvi dvi-am \
+ html html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
.PRECIOUS: Makefile
+++ /dev/null
- .type __start___sancov_guards,@object
- .globl __start___sancov_guards
- .section __sancov_guards,"aw",@progbits
- .p2align 2
-__start___sancov_guards:
+++ /dev/null
- .type __stop___sancov_guards,@object
- .globl __stop___sancov_guards
- .section __sancov_guards,"aw",@progbits
- .p2align 2
-__stop___sancov_guards:
+++ /dev/null
-//===-- sancov_flags.cc -----------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Sanitizer Coverage runtime flags.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sancov_flags.h"
-#include "sanitizer_flag_parser.h"
-#include "sanitizer_platform.h"
-
-SANITIZER_INTERFACE_WEAK_DEF(const char*, __sancov_default_options, void) {
- return "";
-}
-
-using namespace __sanitizer;
-
-namespace __sancov {
-
-SancovFlags sancov_flags_dont_use_directly; // use via flags();
-
-void SancovFlags::SetDefaults() {
-#define SANCOV_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
-#include "sancov_flags.inc"
-#undef SANCOV_FLAG
-}
-
-static void RegisterSancovFlags(FlagParser *parser, SancovFlags *f) {
-#define SANCOV_FLAG(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &f->Name);
-#include "sancov_flags.inc"
-#undef SANCOV_FLAG
-}
-
-static const char *MaybeCallSancovDefaultOptions() {
- return (&__sancov_default_options) ? __sancov_default_options() : "";
-}
-
-void InitializeSancovFlags() {
- SancovFlags *f = sancov_flags();
- f->SetDefaults();
-
- FlagParser parser;
- RegisterSancovFlags(&parser, f);
-
- parser.ParseString(MaybeCallSancovDefaultOptions());
- parser.ParseString(GetEnv("SANCOV_OPTIONS"));
-
- ReportUnrecognizedFlags();
- if (f->help) parser.PrintFlagDescriptions();
-}
-
-} // namespace __sancov
--- /dev/null
+//===-- sancov_flags.cpp ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Sanitizer Coverage runtime flags.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sancov_flags.h"
+#include "sanitizer_flag_parser.h"
+#include "sanitizer_platform.h"
+
+SANITIZER_INTERFACE_WEAK_DEF(const char*, __sancov_default_options, void) {
+ return "";
+}
+
+using namespace __sanitizer;
+
+namespace __sancov {
+
+SancovFlags sancov_flags_dont_use_directly; // use via flags();
+
+void SancovFlags::SetDefaults() {
+#define SANCOV_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "sancov_flags.inc"
+#undef SANCOV_FLAG
+}
+
+static void RegisterSancovFlags(FlagParser *parser, SancovFlags *f) {
+#define SANCOV_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "sancov_flags.inc"
+#undef SANCOV_FLAG
+}
+
+static const char *MaybeCallSancovDefaultOptions() {
+ return (&__sancov_default_options) ? __sancov_default_options() : "";
+}
+
+void InitializeSancovFlags() {
+ SancovFlags *f = sancov_flags();
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterSancovFlags(&parser, f);
+
+ parser.ParseString(MaybeCallSancovDefaultOptions());
+ parser.ParseStringFromEnv("SANCOV_OPTIONS");
+
+ ReportUnrecognizedFlags();
+ if (f->help) parser.PrintFlagDescriptions();
+}
+
+} // namespace __sancov
//===-- sancov_flags.h ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sancov_flags.inc ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_addrhashmap.h ---------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_allocator.cc --------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-// This allocator is used inside run-times.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator.h"
-
-#include "sanitizer_allocator_checks.h"
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_common.h"
-
-namespace __sanitizer {
-
-// Default allocator names.
-const char *PrimaryAllocatorName = "SizeClassAllocator";
-const char *SecondaryAllocatorName = "LargeMmapAllocator";
-
-// ThreadSanitizer for Go uses libc malloc/free.
-#if SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
-# if SANITIZER_LINUX && !SANITIZER_ANDROID
-extern "C" void *__libc_malloc(uptr size);
-# if !SANITIZER_GO
-extern "C" void *__libc_memalign(uptr alignment, uptr size);
-# endif
-extern "C" void *__libc_realloc(void *ptr, uptr size);
-extern "C" void __libc_free(void *ptr);
-# else
-# include <stdlib.h>
-# define __libc_malloc malloc
-# if !SANITIZER_GO
-static void *__libc_memalign(uptr alignment, uptr size) {
- void *p;
- uptr error = posix_memalign(&p, alignment, size);
- if (error) return nullptr;
- return p;
-}
-# endif
-# define __libc_realloc realloc
-# define __libc_free free
-# endif
-
-static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
- uptr alignment) {
- (void)cache;
-#if !SANITIZER_GO
- if (alignment == 0)
- return __libc_malloc(size);
- else
- return __libc_memalign(alignment, size);
-#else
- // Windows does not provide __libc_memalign/posix_memalign. It provides
- // __aligned_malloc, but the allocated blocks can't be passed to free,
- // they need to be passed to __aligned_free. InternalAlloc interface does
- // not account for such requirement. Alignemnt does not seem to be used
- // anywhere in runtime, so just call __libc_malloc for now.
- DCHECK_EQ(alignment, 0);
- return __libc_malloc(size);
-#endif
-}
-
-static void *RawInternalRealloc(void *ptr, uptr size,
- InternalAllocatorCache *cache) {
- (void)cache;
- return __libc_realloc(ptr, size);
-}
-
-static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
- (void)cache;
- __libc_free(ptr);
-}
-
-InternalAllocator *internal_allocator() {
- return 0;
-}
-
-#else // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
-
-static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
-static atomic_uint8_t internal_allocator_initialized;
-static StaticSpinMutex internal_alloc_init_mu;
-
-static InternalAllocatorCache internal_allocator_cache;
-static StaticSpinMutex internal_allocator_cache_mu;
-
-InternalAllocator *internal_allocator() {
- InternalAllocator *internal_allocator_instance =
- reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder);
- if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) {
- SpinMutexLock l(&internal_alloc_init_mu);
- if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
- 0) {
- internal_allocator_instance->Init(kReleaseToOSIntervalNever);
- atomic_store(&internal_allocator_initialized, 1, memory_order_release);
- }
- }
- return internal_allocator_instance;
-}
-
-static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
- uptr alignment) {
- if (alignment == 0) alignment = 8;
- if (cache == 0) {
- SpinMutexLock l(&internal_allocator_cache_mu);
- return internal_allocator()->Allocate(&internal_allocator_cache, size,
- alignment);
- }
- return internal_allocator()->Allocate(cache, size, alignment);
-}
-
-static void *RawInternalRealloc(void *ptr, uptr size,
- InternalAllocatorCache *cache) {
- uptr alignment = 8;
- if (cache == 0) {
- SpinMutexLock l(&internal_allocator_cache_mu);
- return internal_allocator()->Reallocate(&internal_allocator_cache, ptr,
- size, alignment);
- }
- return internal_allocator()->Reallocate(cache, ptr, size, alignment);
-}
-
-static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
- if (!cache) {
- SpinMutexLock l(&internal_allocator_cache_mu);
- return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
- }
- internal_allocator()->Deallocate(cache, ptr);
-}
-
-#endif // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
-
-const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
-
-static void NORETURN ReportInternalAllocatorOutOfMemory(uptr requested_size) {
- SetAllocatorOutOfMemory();
- Report("FATAL: %s: internal allocator is out of memory trying to allocate "
- "0x%zx bytes\n", SanitizerToolName, requested_size);
- Die();
-}
-
-void *InternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) {
- if (size + sizeof(u64) < size)
- return nullptr;
- void *p = RawInternalAlloc(size + sizeof(u64), cache, alignment);
- if (UNLIKELY(!p))
- ReportInternalAllocatorOutOfMemory(size + sizeof(u64));
- ((u64*)p)[0] = kBlockMagic;
- return (char*)p + sizeof(u64);
-}
-
-void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) {
- if (!addr)
- return InternalAlloc(size, cache);
- if (size + sizeof(u64) < size)
- return nullptr;
- addr = (char*)addr - sizeof(u64);
- size = size + sizeof(u64);
- CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
- void *p = RawInternalRealloc(addr, size, cache);
- if (UNLIKELY(!p))
- ReportInternalAllocatorOutOfMemory(size);
- return (char*)p + sizeof(u64);
-}
-
-void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) {
- if (UNLIKELY(CheckForCallocOverflow(count, size))) {
- Report("FATAL: %s: calloc parameters overflow: count * size (%zd * %zd) "
- "cannot be represented in type size_t\n", SanitizerToolName, count,
- size);
- Die();
- }
- void *p = InternalAlloc(count * size, cache);
- if (LIKELY(p))
- internal_memset(p, 0, count * size);
- return p;
-}
-
-void InternalFree(void *addr, InternalAllocatorCache *cache) {
- if (!addr)
- return;
- addr = (char*)addr - sizeof(u64);
- CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
- ((u64*)addr)[0] = 0;
- RawInternalFree(addr, cache);
-}
-
-// LowLevelAllocator
-constexpr uptr kLowLevelAllocatorDefaultAlignment = 8;
-static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment;
-static LowLevelAllocateCallback low_level_alloc_callback;
-
-void *LowLevelAllocator::Allocate(uptr size) {
- // Align allocation size.
- size = RoundUpTo(size, low_level_alloc_min_alignment);
- if (allocated_end_ - allocated_current_ < (sptr)size) {
- uptr size_to_allocate = Max(size, GetPageSizeCached());
- allocated_current_ =
- (char*)MmapOrDie(size_to_allocate, __func__);
- allocated_end_ = allocated_current_ + size_to_allocate;
- if (low_level_alloc_callback) {
- low_level_alloc_callback((uptr)allocated_current_,
- size_to_allocate);
- }
- }
- CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
- void *res = allocated_current_;
- allocated_current_ += size;
- return res;
-}
-
-void SetLowLevelAllocateMinAlignment(uptr alignment) {
- CHECK(IsPowerOfTwo(alignment));
- low_level_alloc_min_alignment = Max(alignment, low_level_alloc_min_alignment);
-}
-
-void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {
- low_level_alloc_callback = callback;
-}
-
-// Allocator's OOM and other errors handling support.
-
-static atomic_uint8_t allocator_out_of_memory = {0};
-static atomic_uint8_t allocator_may_return_null = {0};
-
-bool IsAllocatorOutOfMemory() {
- return atomic_load_relaxed(&allocator_out_of_memory);
-}
-
-void SetAllocatorOutOfMemory() {
- atomic_store_relaxed(&allocator_out_of_memory, 1);
-}
-
-bool AllocatorMayReturnNull() {
- return atomic_load(&allocator_may_return_null, memory_order_relaxed);
-}
-
-void SetAllocatorMayReturnNull(bool may_return_null) {
- atomic_store(&allocator_may_return_null, may_return_null,
- memory_order_relaxed);
-}
-
-void PrintHintAllocatorCannotReturnNull() {
- Report("HINT: if you don't care about these errors you may set "
- "allocator_may_return_null=1\n");
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_allocator.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// This allocator is used inside run-times.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator.h"
+
+#include "sanitizer_allocator_checks.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+
+namespace __sanitizer {
+
+// Default allocator names.
+const char *PrimaryAllocatorName = "SizeClassAllocator";
+const char *SecondaryAllocatorName = "LargeMmapAllocator";
+
+// ThreadSanitizer for Go uses libc malloc/free.
+#if SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
+# if SANITIZER_LINUX && !SANITIZER_ANDROID
+extern "C" void *__libc_malloc(uptr size);
+# if !SANITIZER_GO
+extern "C" void *__libc_memalign(uptr alignment, uptr size);
+# endif
+extern "C" void *__libc_realloc(void *ptr, uptr size);
+extern "C" void __libc_free(void *ptr);
+# else
+# include <stdlib.h>
+# define __libc_malloc malloc
+# if !SANITIZER_GO
+static void *__libc_memalign(uptr alignment, uptr size) {
+ void *p;
+ uptr error = posix_memalign(&p, alignment, size);
+ if (error) return nullptr;
+ return p;
+}
+# endif
+# define __libc_realloc realloc
+# define __libc_free free
+# endif
+
+static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
+ uptr alignment) {
+ (void)cache;
+#if !SANITIZER_GO
+ if (alignment == 0)
+ return __libc_malloc(size);
+ else
+ return __libc_memalign(alignment, size);
+#else
+ // Windows does not provide __libc_memalign/posix_memalign. It provides
+ // __aligned_malloc, but the allocated blocks can't be passed to free,
+ // they need to be passed to __aligned_free. InternalAlloc interface does
+ // not account for such requirement. Alignemnt does not seem to be used
+ // anywhere in runtime, so just call __libc_malloc for now.
+ DCHECK_EQ(alignment, 0);
+ return __libc_malloc(size);
+#endif
+}
+
+static void *RawInternalRealloc(void *ptr, uptr size,
+ InternalAllocatorCache *cache) {
+ (void)cache;
+ return __libc_realloc(ptr, size);
+}
+
+static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
+ (void)cache;
+ __libc_free(ptr);
+}
+
+InternalAllocator *internal_allocator() {
+ return 0;
+}
+
+#else // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
+
+static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)];
+static atomic_uint8_t internal_allocator_initialized;
+static StaticSpinMutex internal_alloc_init_mu;
+
+static InternalAllocatorCache internal_allocator_cache;
+static StaticSpinMutex internal_allocator_cache_mu;
+
+InternalAllocator *internal_allocator() {
+ InternalAllocator *internal_allocator_instance =
+ reinterpret_cast<InternalAllocator *>(&internal_alloc_placeholder);
+ if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) {
+ SpinMutexLock l(&internal_alloc_init_mu);
+ if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) ==
+ 0) {
+ internal_allocator_instance->Init(kReleaseToOSIntervalNever);
+ atomic_store(&internal_allocator_initialized, 1, memory_order_release);
+ }
+ }
+ return internal_allocator_instance;
+}
+
+static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache,
+ uptr alignment) {
+ if (alignment == 0) alignment = 8;
+ if (cache == 0) {
+ SpinMutexLock l(&internal_allocator_cache_mu);
+ return internal_allocator()->Allocate(&internal_allocator_cache, size,
+ alignment);
+ }
+ return internal_allocator()->Allocate(cache, size, alignment);
+}
+
+static void *RawInternalRealloc(void *ptr, uptr size,
+ InternalAllocatorCache *cache) {
+ uptr alignment = 8;
+ if (cache == 0) {
+ SpinMutexLock l(&internal_allocator_cache_mu);
+ return internal_allocator()->Reallocate(&internal_allocator_cache, ptr,
+ size, alignment);
+ }
+ return internal_allocator()->Reallocate(cache, ptr, size, alignment);
+}
+
+static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) {
+ if (!cache) {
+ SpinMutexLock l(&internal_allocator_cache_mu);
+ return internal_allocator()->Deallocate(&internal_allocator_cache, ptr);
+ }
+ internal_allocator()->Deallocate(cache, ptr);
+}
+
+#endif // SANITIZER_GO || defined(SANITIZER_USE_MALLOC)
+
+const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull;
+
+static void NORETURN ReportInternalAllocatorOutOfMemory(uptr requested_size) {
+ SetAllocatorOutOfMemory();
+ Report("FATAL: %s: internal allocator is out of memory trying to allocate "
+ "0x%zx bytes\n", SanitizerToolName, requested_size);
+ Die();
+}
+
+void *InternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) {
+ if (size + sizeof(u64) < size)
+ return nullptr;
+ void *p = RawInternalAlloc(size + sizeof(u64), cache, alignment);
+ if (UNLIKELY(!p))
+ ReportInternalAllocatorOutOfMemory(size + sizeof(u64));
+ ((u64*)p)[0] = kBlockMagic;
+ return (char*)p + sizeof(u64);
+}
+
+void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) {
+ if (!addr)
+ return InternalAlloc(size, cache);
+ if (size + sizeof(u64) < size)
+ return nullptr;
+ addr = (char*)addr - sizeof(u64);
+ size = size + sizeof(u64);
+ CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
+ void *p = RawInternalRealloc(addr, size, cache);
+ if (UNLIKELY(!p))
+ ReportInternalAllocatorOutOfMemory(size);
+ return (char*)p + sizeof(u64);
+}
+
+void *InternalReallocArray(void *addr, uptr count, uptr size,
+ InternalAllocatorCache *cache) {
+ if (UNLIKELY(CheckForCallocOverflow(count, size))) {
+ Report(
+ "FATAL: %s: reallocarray parameters overflow: count * size (%zd * %zd) "
+ "cannot be represented in type size_t\n",
+ SanitizerToolName, count, size);
+ Die();
+ }
+ return InternalRealloc(addr, count * size, cache);
+}
+
+void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) {
+ if (UNLIKELY(CheckForCallocOverflow(count, size))) {
+ Report("FATAL: %s: calloc parameters overflow: count * size (%zd * %zd) "
+ "cannot be represented in type size_t\n", SanitizerToolName, count,
+ size);
+ Die();
+ }
+ void *p = InternalAlloc(count * size, cache);
+ if (LIKELY(p))
+ internal_memset(p, 0, count * size);
+ return p;
+}
+
+void InternalFree(void *addr, InternalAllocatorCache *cache) {
+ if (!addr)
+ return;
+ addr = (char*)addr - sizeof(u64);
+ CHECK_EQ(kBlockMagic, ((u64*)addr)[0]);
+ ((u64*)addr)[0] = 0;
+ RawInternalFree(addr, cache);
+}
+
+// LowLevelAllocator
+constexpr uptr kLowLevelAllocatorDefaultAlignment = 8;
+static uptr low_level_alloc_min_alignment = kLowLevelAllocatorDefaultAlignment;
+static LowLevelAllocateCallback low_level_alloc_callback;
+
+void *LowLevelAllocator::Allocate(uptr size) {
+ // Align allocation size.
+ size = RoundUpTo(size, low_level_alloc_min_alignment);
+ if (allocated_end_ - allocated_current_ < (sptr)size) {
+ uptr size_to_allocate = Max(size, GetPageSizeCached());
+ allocated_current_ =
+ (char*)MmapOrDie(size_to_allocate, __func__);
+ allocated_end_ = allocated_current_ + size_to_allocate;
+ if (low_level_alloc_callback) {
+ low_level_alloc_callback((uptr)allocated_current_,
+ size_to_allocate);
+ }
+ }
+ CHECK(allocated_end_ - allocated_current_ >= (sptr)size);
+ void *res = allocated_current_;
+ allocated_current_ += size;
+ return res;
+}
+
+void SetLowLevelAllocateMinAlignment(uptr alignment) {
+ CHECK(IsPowerOfTwo(alignment));
+ low_level_alloc_min_alignment = Max(alignment, low_level_alloc_min_alignment);
+}
+
+void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) {
+ low_level_alloc_callback = callback;
+}
+
+// Allocator's OOM and other errors handling support.
+
+static atomic_uint8_t allocator_out_of_memory = {0};
+static atomic_uint8_t allocator_may_return_null = {0};
+
+bool IsAllocatorOutOfMemory() {
+ return atomic_load_relaxed(&allocator_out_of_memory);
+}
+
+void SetAllocatorOutOfMemory() {
+ atomic_store_relaxed(&allocator_out_of_memory, 1);
+}
+
+bool AllocatorMayReturnNull() {
+ return atomic_load(&allocator_may_return_null, memory_order_relaxed);
+}
+
+void SetAllocatorMayReturnNull(bool may_return_null) {
+ atomic_store(&allocator_may_return_null, may_return_null,
+ memory_order_relaxed);
+}
+
+void PrintHintAllocatorCannotReturnNull() {
+ Report("HINT: if you don't care about these errors you may set "
+ "allocator_may_return_null=1\n");
+}
+
+} // namespace __sanitizer
//===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#ifndef SANITIZER_ALLOCATOR_H
#define SANITIZER_ALLOCATOR_H
-#include "sanitizer_internal_defs.h"
#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_lfstack.h"
#include "sanitizer_libc.h"
#include "sanitizer_list.h"
+#include "sanitizer_local_address_space_view.h"
#include "sanitizer_mutex.h"
-#include "sanitizer_lfstack.h"
#include "sanitizer_procmaps.h"
+#include "sanitizer_type_traits.h"
namespace __sanitizer {
//===-- sanitizer_allocator_bytemap.h ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#endif
// Maps integers in rage [0, kSize) to u8 values.
-template<u64 kSize>
+template <u64 kSize, typename AddressSpaceViewTy = LocalAddressSpaceView>
class FlatByteMap {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
void Init() {
internal_memset(map_, 0, sizeof(map_));
}
// to kSize2-byte arrays. The secondary arrays are mmaped on demand.
// Each value is initially zero and can be set to something else only once.
// Setting and getting values from multiple threads is safe w/o extra locking.
-template <u64 kSize1, u64 kSize2, class MapUnmapCallback = NoOpMapUnmapCallback>
+template <u64 kSize1, u64 kSize2,
+ typename AddressSpaceViewTy = LocalAddressSpaceView,
+ class MapUnmapCallback = NoOpMapUnmapCallback>
class TwoLevelByteMap {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
void Init() {
internal_memset(map1_, 0, sizeof(map1_));
mu_.Init();
CHECK_LT(idx, kSize1 * kSize2);
u8 *map2 = Get(idx / kSize2);
if (!map2) return 0;
- return map2[idx % kSize2];
+ auto value_ptr = AddressSpaceView::Load(&map2[idx % kSize2]);
+ return *value_ptr;
}
private:
atomic_uintptr_t map1_[kSize1];
StaticSpinMutex mu_;
};
+
+++ /dev/null
-//===-- sanitizer_allocator_checks.cc ---------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Various checks shared between ThreadSanitizer, MemorySanitizer, etc. memory
-// allocators.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_errno.h"
-
-namespace __sanitizer {
-
-void SetErrnoToENOMEM() {
- errno = errno_ENOMEM;
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_allocator_checks.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Various checks shared between ThreadSanitizer, MemorySanitizer, etc. memory
+// allocators.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_errno.h"
+
+namespace __sanitizer {
+
+void SetErrnoToENOMEM() {
+ errno = errno_ENOMEM;
+}
+
+} // namespace __sanitizer
//===-- sanitizer_allocator_checks.h ----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// When allocating 2^x bytes it should return 2^x aligned chunk.
// PrimaryAllocator is used via a local AllocatorCache.
// SecondaryAllocator can allocate anything, but is not efficient.
-template <class PrimaryAllocator, class AllocatorCache,
- class SecondaryAllocator> // NOLINT
+template <class PrimaryAllocator,
+ class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
class CombinedAllocator {
public:
+ using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
+ using SecondaryAllocator =
+ LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
+ LargeMmapAllocatorPtrArray,
+ typename PrimaryAllocator::AddressSpaceView>;
+
void InitLinkerInitialized(s32 release_to_os_interval_ms) {
+ stats_.InitLinkerInitialized();
primary_.Init(release_to_os_interval_ms);
secondary_.InitLinkerInitialized();
- stats_.InitLinkerInitialized();
}
void Init(s32 release_to_os_interval_ms) {
+ stats_.Init();
primary_.Init(release_to_os_interval_ms);
secondary_.Init();
- stats_.Init();
}
void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
//===-- sanitizer_allocator_interface.h ------------------------- C++ -----===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_allocator_internal.h --------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// purposes.
typedef CompactSizeClassMap InternalSizeClassMap;
-static const uptr kInternalAllocatorRegionSizeLog = 20;
-static const uptr kInternalAllocatorNumRegions =
- SANITIZER_MMAP_RANGE_SIZE >> kInternalAllocatorRegionSizeLog;
-#if SANITIZER_WORDSIZE == 32
-typedef FlatByteMap<kInternalAllocatorNumRegions> ByteMap;
-#else
-typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap;
-#endif
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = 0;
typedef InternalSizeClassMap SizeClassMap;
- static const uptr kRegionSizeLog = kInternalAllocatorRegionSizeLog;
- typedef __sanitizer::ByteMap ByteMap;
+ static const uptr kRegionSizeLog = 20;
+ using AddressSpaceView = LocalAddressSpaceView;
typedef NoOpMapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
typedef SizeClassAllocator32<AP32> PrimaryInternalAllocator;
-typedef SizeClassAllocatorLocalCache<PrimaryInternalAllocator>
- InternalAllocatorCache;
-
-typedef LargeMmapAllocator<NoOpMapUnmapCallback,
- LargeMmapAllocatorPtrArrayStatic>
- SecondaryInternalAllocator;
-
-typedef CombinedAllocator<PrimaryInternalAllocator, InternalAllocatorCache,
- SecondaryInternalAllocator> InternalAllocator;
+typedef CombinedAllocator<PrimaryInternalAllocator,
+ LargeMmapAllocatorPtrArrayStatic>
+ InternalAllocator;
+typedef InternalAllocator::AllocatorCache InternalAllocatorCache;
void *InternalAlloc(uptr size, InternalAllocatorCache *cache = nullptr,
uptr alignment = 0);
void *InternalRealloc(void *p, uptr size,
InternalAllocatorCache *cache = nullptr);
-void *InternalCalloc(uptr countr, uptr size,
+void *InternalReallocArray(void *p, uptr count, uptr size,
+ InternalAllocatorCache *cache = nullptr);
+void *InternalCalloc(uptr count, uptr size,
InternalAllocatorCache *cache = nullptr);
void InternalFree(void *p, InternalAllocatorCache *cache = nullptr);
InternalAllocator *internal_allocator();
//===-- sanitizer_allocator_local_cache.h -----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#error This file must be included inside sanitizer_allocator.h
#endif
-// Objects of this type should be used as local caches for SizeClassAllocator64
-// or SizeClassAllocator32. Since the typical use of this class is to have one
-// object per thread in TLS, is has to be POD.
-template<class SizeClassAllocator>
-struct SizeClassAllocatorLocalCache
- : SizeClassAllocator::AllocatorCache {};
-
// Cache used by SizeClassAllocator64.
template <class SizeClassAllocator>
struct SizeClassAllocator64LocalCache {
//===-- sanitizer_allocator_primary32.h -------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
template <class Params>
class SizeClassAllocator32 {
+ private:
+ static const u64 kTwoLevelByteMapSize1 =
+ (Params::kSpaceSize >> Params::kRegionSizeLog) >> 12;
+ static const u64 kMinFirstMapSizeTwoLevelByteMap = 4;
+
public:
+ using AddressSpaceView = typename Params::AddressSpaceView;
static const uptr kSpaceBeg = Params::kSpaceBeg;
static const u64 kSpaceSize = Params::kSpaceSize;
static const uptr kMetadataSize = Params::kMetadataSize;
typedef typename Params::SizeClassMap SizeClassMap;
static const uptr kRegionSizeLog = Params::kRegionSizeLog;
- typedef typename Params::ByteMap ByteMap;
typedef typename Params::MapUnmapCallback MapUnmapCallback;
+ using ByteMap = typename conditional<
+ (kTwoLevelByteMapSize1 < kMinFirstMapSizeTwoLevelByteMap),
+ FlatByteMap<(Params::kSpaceSize >> Params::kRegionSizeLog),
+ AddressSpaceView>,
+ TwoLevelByteMap<kTwoLevelByteMapSize1, 1 << 12, AddressSpaceView>>::type;
COMPILER_CHECK(!SANITIZER_SIGN_EXTENDED_ADDRESSES ||
(kSpaceSize & (kSpaceSize - 1)) == 0);
return ClassIdToSize(GetSizeClass(p));
}
- uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+ static uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
uptr TotalMemoryUsed() {
// No need to lock here.
};
COMPILER_CHECK(sizeof(SizeClassInfo) % kCacheLineSize == 0);
- uptr ComputeRegionId(uptr mem) {
+ uptr ComputeRegionId(uptr mem) const {
if (SANITIZER_SIGN_EXTENDED_ADDRESSES)
mem &= (kSpaceSize - 1);
const uptr res = mem >> kRegionSizeLog;
//===-- sanitizer_allocator_primary64.h -------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
template <class Params>
class SizeClassAllocator64 {
public:
+ using AddressSpaceView = typename Params::AddressSpaceView;
static const uptr kSpaceBeg = Params::kSpaceBeg;
static const uptr kSpaceSize = Params::kSpaceSize;
static const uptr kMetadataSize = Params::kMetadataSize;
CHECK_NE(NonConstSpaceBeg, ~(uptr)0);
}
SetReleaseToOSIntervalMs(release_to_os_interval_ms);
- MapWithCallbackOrDie(SpaceEnd(), AdditionalSize());
+ MapWithCallbackOrDie(SpaceEnd(), AdditionalSize(),
+ "SizeClassAllocator: region info");
// Check that the RegionInfo array is aligned on the CacheLine size.
DCHECK_EQ(SpaceEnd() % kCacheLineSize, 0);
}
return true;
}
- bool PointerIsMine(const void *p) {
+ bool PointerIsMine(const void *p) const {
uptr P = reinterpret_cast<uptr>(p);
if (kUsingConstantSpaceBeg && (kSpaceBeg % kSpaceSize) == 0)
return P / kSpaceSize == kSpaceBeg / kSpaceSize;
uptr beg = chunk_idx * size;
uptr next_beg = beg + size;
if (class_id >= kNumClasses) return nullptr;
- RegionInfo *region = GetRegionInfo(class_id);
+ const RegionInfo *region = AddressSpaceView::Load(GetRegionInfo(class_id));
if (region->mapped_user >= next_beg)
return reinterpret_cast<void*>(reg_beg + beg);
return nullptr;
return ClassIdToSize(GetSizeClass(p));
}
- uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
+ static uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); }
void *GetMetaData(const void *p) {
uptr class_id = GetSizeClass(p);
RegionInfo *region = GetRegionInfo(class_id);
uptr chunk_size = ClassIdToSize(class_id);
uptr region_beg = SpaceBeg() + class_id * kRegionSize;
+ uptr region_allocated_user_size =
+ AddressSpaceView::Load(region)->allocated_user;
for (uptr chunk = region_beg;
- chunk < region_beg + region->allocated_user;
+ chunk < region_beg + region_allocated_user_size;
chunk += chunk_size) {
// Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk));
callback(chunk, arg);
return reinterpret_cast<CompactPtrT *>(GetMetadataEnd(region_beg));
}
- bool MapWithCallback(uptr beg, uptr size) {
- uptr mapped = address_range.Map(beg, size);
+ bool MapWithCallback(uptr beg, uptr size, const char *name) {
+ uptr mapped = address_range.Map(beg, size, name);
if (UNLIKELY(!mapped))
return false;
CHECK_EQ(beg, mapped);
return true;
}
- void MapWithCallbackOrDie(uptr beg, uptr size) {
- CHECK_EQ(beg, address_range.MapOrDie(beg, size));
+ void MapWithCallbackOrDie(uptr beg, uptr size, const char *name) {
+ CHECK_EQ(beg, address_range.MapOrDie(beg, size, name));
MapUnmapCallback().OnMap(beg, size);
}
uptr current_map_end = reinterpret_cast<uptr>(GetFreeArray(region_beg)) +
region->mapped_free_array;
uptr new_map_size = new_mapped_free_array - region->mapped_free_array;
- if (UNLIKELY(!MapWithCallback(current_map_end, new_map_size)))
+ if (UNLIKELY(!MapWithCallback(current_map_end, new_map_size,
+ "SizeClassAllocator: freearray")))
return false;
region->mapped_free_array = new_mapped_free_array;
}
if (UNLIKELY(IsRegionExhausted(region, class_id, user_map_size)))
return false;
if (UNLIKELY(!MapWithCallback(region_beg + region->mapped_user,
- user_map_size)))
+ user_map_size,
+ "SizeClassAllocator: region data")))
return false;
stat->Add(AllocatorStatMapped, user_map_size);
region->mapped_user += user_map_size;
return false;
if (UNLIKELY(!MapWithCallback(
GetMetadataEnd(region_beg) - region->mapped_meta - meta_map_size,
- meta_map_size)))
+ meta_map_size, "SizeClassAllocator: region metadata")))
return false;
region->mapped_meta += meta_map_size;
}
+++ /dev/null
-//===-- sanitizer_allocator_report.cc ---------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Shared allocator error reporting for ThreadSanitizer, MemorySanitizer, etc.
-///
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator.h"
-#include "sanitizer_allocator_report.h"
-#include "sanitizer_common.h"
-#include "sanitizer_report_decorator.h"
-
-namespace __sanitizer {
-
-class ScopedAllocatorErrorReport {
- public:
- ScopedAllocatorErrorReport(const char *error_summary_,
- const StackTrace *stack_)
- : error_summary(error_summary_),
- stack(stack_) {
- Printf("%s", d.Error());
- }
- ~ScopedAllocatorErrorReport() {
- Printf("%s", d.Default());
- stack->Print();
- PrintHintAllocatorCannotReturnNull();
- ReportErrorSummary(error_summary, stack);
- }
-
- private:
- ScopedErrorReportLock lock;
- const char *error_summary;
- const StackTrace* const stack;
- const SanitizerCommonDecorator d;
-};
-
-void NORETURN ReportCallocOverflow(uptr count, uptr size,
- const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("calloc-overflow", stack);
- Report("ERROR: %s: calloc parameters overflow: count * size (%zd * %zd) "
- "cannot be represented in type size_t\n", SanitizerToolName, count,
- size);
- }
- Die();
-}
-
-void NORETURN ReportPvallocOverflow(uptr size, const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("pvalloc-overflow", stack);
- Report("ERROR: %s: pvalloc parameters overflow: size 0x%zx rounded up to "
- "system page size 0x%zx cannot be represented in type size_t\n",
- SanitizerToolName, size, GetPageSizeCached());
- }
- Die();
-}
-
-void NORETURN ReportInvalidAllocationAlignment(uptr alignment,
- const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("invalid-allocation-alignment", stack);
- Report("ERROR: %s: invalid allocation alignment: %zd, alignment must be a "
- "power of two\n", SanitizerToolName, alignment);
- }
- Die();
-}
-
-void NORETURN ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
- const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("invalid-aligned-alloc-alignment", stack);
-#if SANITIZER_POSIX
- Report("ERROR: %s: invalid alignment requested in "
- "aligned_alloc: %zd, alignment must be a power of two and the "
- "requested size 0x%zx must be a multiple of alignment\n",
- SanitizerToolName, alignment, size);
-#else
- Report("ERROR: %s: invalid alignment requested in aligned_alloc: %zd, "
- "the requested size 0x%zx must be a multiple of alignment\n",
- SanitizerToolName, alignment, size);
-#endif
- }
- Die();
-}
-
-void NORETURN ReportInvalidPosixMemalignAlignment(uptr alignment,
- const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("invalid-posix-memalign-alignment",
- stack);
- Report("ERROR: %s: invalid alignment requested in "
- "posix_memalign: %zd, alignment must be a power of two and a "
- "multiple of sizeof(void*) == %zd\n", SanitizerToolName, alignment,
- sizeof(void*)); // NOLINT
- }
- Die();
-}
-
-void NORETURN ReportAllocationSizeTooBig(uptr user_size, uptr max_size,
- const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("allocation-size-too-big", stack);
- Report("ERROR: %s: requested allocation size 0x%zx exceeds maximum "
- "supported size of 0x%zx\n", SanitizerToolName, user_size, max_size);
- }
- Die();
-}
-
-void NORETURN ReportOutOfMemory(uptr requested_size, const StackTrace *stack) {
- {
- ScopedAllocatorErrorReport report("out-of-memory", stack);
- Report("ERROR: %s: allocator is out of memory trying to allocate 0x%zx "
- "bytes\n", SanitizerToolName, requested_size);
- }
- Die();
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_allocator_report.cpp --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Shared allocator error reporting for ThreadSanitizer, MemorySanitizer, etc.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator.h"
+#include "sanitizer_allocator_report.h"
+#include "sanitizer_common.h"
+#include "sanitizer_report_decorator.h"
+
+namespace __sanitizer {
+
+class ScopedAllocatorErrorReport {
+ public:
+ ScopedAllocatorErrorReport(const char *error_summary_,
+ const StackTrace *stack_)
+ : error_summary(error_summary_),
+ stack(stack_) {
+ Printf("%s", d.Error());
+ }
+ ~ScopedAllocatorErrorReport() {
+ Printf("%s", d.Default());
+ stack->Print();
+ PrintHintAllocatorCannotReturnNull();
+ ReportErrorSummary(error_summary, stack);
+ }
+
+ private:
+ ScopedErrorReportLock lock;
+ const char *error_summary;
+ const StackTrace* const stack;
+ const SanitizerCommonDecorator d;
+};
+
+void NORETURN ReportCallocOverflow(uptr count, uptr size,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("calloc-overflow", stack);
+ Report("ERROR: %s: calloc parameters overflow: count * size (%zd * %zd) "
+ "cannot be represented in type size_t\n", SanitizerToolName, count,
+ size);
+ }
+ Die();
+}
+
+void NORETURN ReportReallocArrayOverflow(uptr count, uptr size,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("reallocarray-overflow", stack);
+ Report(
+ "ERROR: %s: reallocarray parameters overflow: count * size (%zd * %zd) "
+ "cannot be represented in type size_t\n",
+ SanitizerToolName, count, size);
+ }
+ Die();
+}
+
+void NORETURN ReportPvallocOverflow(uptr size, const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("pvalloc-overflow", stack);
+ Report("ERROR: %s: pvalloc parameters overflow: size 0x%zx rounded up to "
+ "system page size 0x%zx cannot be represented in type size_t\n",
+ SanitizerToolName, size, GetPageSizeCached());
+ }
+ Die();
+}
+
+void NORETURN ReportInvalidAllocationAlignment(uptr alignment,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("invalid-allocation-alignment", stack);
+ Report("ERROR: %s: invalid allocation alignment: %zd, alignment must be a "
+ "power of two\n", SanitizerToolName, alignment);
+ }
+ Die();
+}
+
+void NORETURN ReportInvalidAlignedAllocAlignment(uptr size, uptr alignment,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("invalid-aligned-alloc-alignment", stack);
+#if SANITIZER_POSIX
+ Report("ERROR: %s: invalid alignment requested in "
+ "aligned_alloc: %zd, alignment must be a power of two and the "
+ "requested size 0x%zx must be a multiple of alignment\n",
+ SanitizerToolName, alignment, size);
+#else
+ Report("ERROR: %s: invalid alignment requested in aligned_alloc: %zd, "
+ "the requested size 0x%zx must be a multiple of alignment\n",
+ SanitizerToolName, alignment, size);
+#endif
+ }
+ Die();
+}
+
+void NORETURN ReportInvalidPosixMemalignAlignment(uptr alignment,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("invalid-posix-memalign-alignment",
+ stack);
+ Report("ERROR: %s: invalid alignment requested in "
+ "posix_memalign: %zd, alignment must be a power of two and a "
+ "multiple of sizeof(void*) == %zd\n", SanitizerToolName, alignment,
+ sizeof(void*)); // NOLINT
+ }
+ Die();
+}
+
+void NORETURN ReportAllocationSizeTooBig(uptr user_size, uptr max_size,
+ const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("allocation-size-too-big", stack);
+ Report("ERROR: %s: requested allocation size 0x%zx exceeds maximum "
+ "supported size of 0x%zx\n", SanitizerToolName, user_size, max_size);
+ }
+ Die();
+}
+
+void NORETURN ReportOutOfMemory(uptr requested_size, const StackTrace *stack) {
+ {
+ ScopedAllocatorErrorReport report("out-of-memory", stack);
+ Report("ERROR: %s: allocator is out of memory trying to allocate 0x%zx "
+ "bytes\n", SanitizerToolName, requested_size);
+ }
+ Die();
+}
+
+} // namespace __sanitizer
//===-- sanitizer_allocator_report.h ----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
void NORETURN ReportCallocOverflow(uptr count, uptr size,
const StackTrace *stack);
+void NORETURN ReportReallocArrayOverflow(uptr count, uptr size,
+ const StackTrace *stack);
void NORETURN ReportPvallocOverflow(uptr size, const StackTrace *stack);
void NORETURN ReportInvalidAllocationAlignment(uptr alignment,
const StackTrace *stack);
//===-- sanitizer_allocator_secondary.h -------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// The main purpose of this allocator is to cover large and rare allocation
// sizes not covered by more efficient allocators (e.g. SizeClassAllocator64).
template <class MapUnmapCallback = NoOpMapUnmapCallback,
- class PtrArrayT = DefaultLargeMmapAllocatorPtrArray>
+ class PtrArrayT = DefaultLargeMmapAllocatorPtrArray,
+ class AddressSpaceViewTy = LocalAddressSpaceView>
class LargeMmapAllocator {
public:
+ using AddressSpaceView = AddressSpaceViewTy;
void InitLinkerInitialized() {
page_size_ = GetPageSizeCached();
chunks_ = reinterpret_cast<Header**>(ptr_array_.Init());
uptr p = reinterpret_cast<uptr>(ptr);
SpinMutexLock l(&mutex_);
uptr nearest_chunk = 0;
+ Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
// Cache-friendly linear search.
for (uptr i = 0; i < n_chunks_; i++) {
- uptr ch = reinterpret_cast<uptr>(chunks_[i]);
+ uptr ch = reinterpret_cast<uptr>(chunks[i]);
if (p < ch) continue; // p is at left to this chunk, skip it.
if (p - ch < p - nearest_chunk)
nearest_chunk = ch;
}
if (!nearest_chunk)
return nullptr;
- Header *h = reinterpret_cast<Header *>(nearest_chunk);
+ const Header *h =
+ AddressSpaceView::Load(reinterpret_cast<Header *>(nearest_chunk));
+ Header *h_ptr = reinterpret_cast<Header *>(nearest_chunk);
CHECK_GE(nearest_chunk, h->map_beg);
CHECK_LT(nearest_chunk, h->map_beg + h->map_size);
CHECK_LE(nearest_chunk, p);
if (h->map_beg + h->map_size <= p)
return nullptr;
- return GetUser(h);
+ return GetUser(h_ptr);
}
void EnsureSortedChunks() {
if (chunks_sorted_) return;
- Sort(reinterpret_cast<uptr *>(chunks_), n_chunks_);
+ Header **chunks = AddressSpaceView::LoadWritable(chunks_, n_chunks_);
+ Sort(reinterpret_cast<uptr *>(chunks), n_chunks_);
for (uptr i = 0; i < n_chunks_; i++)
- chunks_[i]->chunk_idx = i;
+ AddressSpaceView::LoadWritable(chunks[i])->chunk_idx = i;
chunks_sorted_ = true;
}
uptr n = n_chunks_;
if (!n) return nullptr;
EnsureSortedChunks();
- auto min_mmap_ = reinterpret_cast<uptr>(chunks_[0]);
- auto max_mmap_ =
- reinterpret_cast<uptr>(chunks_[n - 1]) + chunks_[n - 1]->map_size;
+ Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
+ auto min_mmap_ = reinterpret_cast<uptr>(chunks[0]);
+ auto max_mmap_ = reinterpret_cast<uptr>(chunks[n - 1]) +
+ AddressSpaceView::Load(chunks[n - 1])->map_size;
if (p < min_mmap_ || p >= max_mmap_)
return nullptr;
uptr beg = 0, end = n - 1;
// to avoid expensive cache-thrashing loads.
while (end - beg >= 2) {
uptr mid = (beg + end) / 2; // Invariant: mid >= beg + 1
- if (p < reinterpret_cast<uptr>(chunks_[mid]))
- end = mid - 1; // We are not interested in chunks_[mid].
+ if (p < reinterpret_cast<uptr>(chunks[mid]))
+ end = mid - 1; // We are not interested in chunks[mid].
else
- beg = mid; // chunks_[mid] may still be what we want.
+ beg = mid; // chunks[mid] may still be what we want.
}
if (beg < end) {
CHECK_EQ(beg + 1, end);
// There are 2 chunks left, choose one.
- if (p >= reinterpret_cast<uptr>(chunks_[end]))
+ if (p >= reinterpret_cast<uptr>(chunks[end]))
beg = end;
}
- Header *h = chunks_[beg];
+ const Header *h = AddressSpaceView::Load(chunks[beg]);
+ Header *h_ptr = chunks[beg];
if (h->map_beg + h->map_size <= p || p < h->map_beg)
return nullptr;
- return GetUser(h);
+ return GetUser(h_ptr);
}
void PrintStats() {
// The allocator must be locked when calling this function.
void ForEachChunk(ForEachChunkCallback callback, void *arg) {
EnsureSortedChunks(); // Avoid doing the sort while iterating.
+ const Header *const *chunks = AddressSpaceView::Load(chunks_, n_chunks_);
for (uptr i = 0; i < n_chunks_; i++) {
- auto t = chunks_[i];
+ const Header *t = chunks[i];
callback(reinterpret_cast<uptr>(GetUser(t)), arg);
// Consistency check: verify that the array did not change.
- CHECK_EQ(chunks_[i], t);
- CHECK_EQ(chunks_[i]->chunk_idx, i);
+ CHECK_EQ(chunks[i], t);
+ CHECK_EQ(AddressSpaceView::Load(chunks[i])->chunk_idx, i);
}
}
return GetHeader(reinterpret_cast<uptr>(p));
}
- void *GetUser(Header *h) {
+ void *GetUser(const Header *h) {
CHECK(IsAligned((uptr)h, page_size_));
return reinterpret_cast<void*>(reinterpret_cast<uptr>(h) + page_size_);
}
//===-- sanitizer_allocator_size_class_map.h --------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// allowing for denser per-class arrays, smaller memory footprint and usually
// better performances in threaded environments.
typedef SizeClassMap<3, 4, 8, 17, 8, 10> DenseSizeClassMap;
+// Similar to VeryCompact map above, this one has a small number of different
+// size classes, and also reduced thread-local caches.
+typedef SizeClassMap<2, 5, 9, 16, 8, 10> VeryDenseSizeClassMap;
//===-- sanitizer_allocator_stats.h -----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
private:
mutable StaticSpinMutex mu_;
};
+
+
//===-- sanitizer_asm.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#if !defined(__APPLE__)
# define ASM_HIDDEN(symbol) .hidden symbol
-# define ASM_TYPE_FUNCTION(symbol) .type symbol, @function
+# define ASM_TYPE_FUNCTION(symbol) .type symbol, %function
# define ASM_SIZE(symbol) .size symbol, .-symbol
# define ASM_SYMBOL(symbol) symbol
# define ASM_SYMBOL_INTERCEPTOR(symbol) symbol
+# define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
#else
# define ASM_HIDDEN(symbol)
# define ASM_TYPE_FUNCTION(symbol)
# define ASM_SIZE(symbol)
# define ASM_SYMBOL(symbol) _##symbol
# define ASM_SYMBOL_INTERCEPTOR(symbol) _wrap_##symbol
+# define ASM_WRAPPER_NAME(symbol) __interceptor_##symbol
+#endif
+
+#if defined(__ELF__) && (defined(__GNU__) || defined(__FreeBSD__) || \
+ defined(__Fuchsia__) || defined(__linux__))
+#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits // NOLINT
+#else
+#define NO_EXEC_STACK_DIRECTIVE
#endif
//===-- sanitizer_atomic.h --------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_atomic_clang.h --------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_atomic_clang_mips.h ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
} // namespace __sanitizer
#endif // SANITIZER_ATOMIC_CLANG_MIPS_H
+
//===-- sanitizer_atomic_clang_other.h --------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_atomic_clang_x86.h ----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_atomic_msvc.h ---------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_bitvector.h -----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_bvgraph.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_common.cc -----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common.h"
-#include "sanitizer_allocator_interface.h"
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_placement_new.h"
-
-namespace __sanitizer {
-
-const char *SanitizerToolName = "SanitizerTool";
-
-atomic_uint32_t current_verbosity;
-uptr PageSizeCached;
-u32 NumberOfCPUsCached;
-
-// PID of the tracer task in StopTheWorld. It shares the address space with the
-// main process, but has a different PID and thus requires special handling.
-uptr stoptheworld_tracer_pid = 0;
-// Cached pid of parent process - if the parent process dies, we want to keep
-// writing to the same log file.
-uptr stoptheworld_tracer_ppid = 0;
-
-void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
- const char *mmap_type, error_t err,
- bool raw_report) {
- static int recursion_count;
- if (SANITIZER_RTEMS || raw_report || recursion_count) {
- // If we are on RTEMS or raw report is requested or we went into recursion,
- // just die. The Report() and CHECK calls below may call mmap recursively
- // and fail.
- RawWrite("ERROR: Failed to mmap\n");
- Die();
- }
- recursion_count++;
- Report("ERROR: %s failed to "
- "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
- SanitizerToolName, mmap_type, size, size, mem_type, err);
-#if !SANITIZER_GO
- DumpProcessMap();
-#endif
- UNREACHABLE("unable to mmap");
-}
-
-typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
-typedef bool U32ComparisonFunction(const u32 &a, const u32 &b);
-
-const char *StripPathPrefix(const char *filepath,
- const char *strip_path_prefix) {
- if (!filepath) return nullptr;
- if (!strip_path_prefix) return filepath;
- const char *res = filepath;
- if (const char *pos = internal_strstr(filepath, strip_path_prefix))
- res = pos + internal_strlen(strip_path_prefix);
- if (res[0] == '.' && res[1] == '/')
- res += 2;
- return res;
-}
-
-const char *StripModuleName(const char *module) {
- if (!module)
- return nullptr;
- if (SANITIZER_WINDOWS) {
- // On Windows, both slash and backslash are possible.
- // Pick the one that goes last.
- if (const char *bslash_pos = internal_strrchr(module, '\\'))
- return StripModuleName(bslash_pos + 1);
- }
- if (const char *slash_pos = internal_strrchr(module, '/')) {
- return slash_pos + 1;
- }
- return module;
-}
-
-void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
- if (!common_flags()->print_summary)
- return;
- InternalScopedString buff(kMaxSummaryLength);
- buff.append("SUMMARY: %s: %s",
- alt_tool_name ? alt_tool_name : SanitizerToolName, error_message);
- __sanitizer_report_error_summary(buff.data());
-}
-
-// Removes the ANSI escape sequences from the input string (in-place).
-void RemoveANSIEscapeSequencesFromString(char *str) {
- if (!str)
- return;
-
- // We are going to remove the escape sequences in place.
- char *s = str;
- char *z = str;
- while (*s != '\0') {
- CHECK_GE(s, z);
- // Skip over ANSI escape sequences with pointer 's'.
- if (*s == '\033' && *(s + 1) == '[') {
- s = internal_strchrnul(s, 'm');
- if (*s == '\0') {
- break;
- }
- s++;
- continue;
- }
- // 's' now points at a character we want to keep. Copy over the buffer
- // content if the escape sequence has been perviously skipped andadvance
- // both pointers.
- if (s != z)
- *z = *s;
-
- // If we have not seen an escape sequence, just advance both pointers.
- z++;
- s++;
- }
-
- // Null terminate the string.
- *z = '\0';
-}
-
-void LoadedModule::set(const char *module_name, uptr base_address) {
- clear();
- full_name_ = internal_strdup(module_name);
- base_address_ = base_address;
-}
-
-void LoadedModule::set(const char *module_name, uptr base_address,
- ModuleArch arch, u8 uuid[kModuleUUIDSize],
- bool instrumented) {
- set(module_name, base_address);
- arch_ = arch;
- internal_memcpy(uuid_, uuid, sizeof(uuid_));
- instrumented_ = instrumented;
-}
-
-void LoadedModule::clear() {
- InternalFree(full_name_);
- base_address_ = 0;
- max_executable_address_ = 0;
- full_name_ = nullptr;
- arch_ = kModuleArchUnknown;
- internal_memset(uuid_, 0, kModuleUUIDSize);
- instrumented_ = false;
- while (!ranges_.empty()) {
- AddressRange *r = ranges_.front();
- ranges_.pop_front();
- InternalFree(r);
- }
-}
-
-void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable,
- bool writable, const char *name) {
- void *mem = InternalAlloc(sizeof(AddressRange));
- AddressRange *r =
- new(mem) AddressRange(beg, end, executable, writable, name);
- ranges_.push_back(r);
- if (executable && end > max_executable_address_)
- max_executable_address_ = end;
-}
-
-bool LoadedModule::containsAddress(uptr address) const {
- for (const AddressRange &r : ranges()) {
- if (r.beg <= address && address < r.end)
- return true;
- }
- return false;
-}
-
-static atomic_uintptr_t g_total_mmaped;
-
-void IncreaseTotalMmap(uptr size) {
- if (!common_flags()->mmap_limit_mb) return;
- uptr total_mmaped =
- atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
- // Since for now mmap_limit_mb is not a user-facing flag, just kill
- // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
- RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
-}
-
-void DecreaseTotalMmap(uptr size) {
- if (!common_flags()->mmap_limit_mb) return;
- atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
-}
-
-bool TemplateMatch(const char *templ, const char *str) {
- if ((!str) || str[0] == 0)
- return false;
- bool start = false;
- if (templ && templ[0] == '^') {
- start = true;
- templ++;
- }
- bool asterisk = false;
- while (templ && templ[0]) {
- if (templ[0] == '*') {
- templ++;
- start = false;
- asterisk = true;
- continue;
- }
- if (templ[0] == '$')
- return str[0] == 0 || asterisk;
- if (str[0] == 0)
- return false;
- char *tpos = (char*)internal_strchr(templ, '*');
- char *tpos1 = (char*)internal_strchr(templ, '$');
- if ((!tpos) || (tpos1 && tpos1 < tpos))
- tpos = tpos1;
- if (tpos)
- tpos[0] = 0;
- const char *str0 = str;
- const char *spos = internal_strstr(str, templ);
- str = spos + internal_strlen(templ);
- templ = tpos;
- if (tpos)
- tpos[0] = tpos == tpos1 ? '$' : '*';
- if (!spos)
- return false;
- if (start && spos != str0)
- return false;
- start = false;
- asterisk = false;
- }
- return true;
-}
-
-static char binary_name_cache_str[kMaxPathLength];
-static char process_name_cache_str[kMaxPathLength];
-
-const char *GetProcessName() {
- return process_name_cache_str;
-}
-
-static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
- ReadLongProcessName(buf, buf_len);
- char *s = const_cast<char *>(StripModuleName(buf));
- uptr len = internal_strlen(s);
- if (s != buf) {
- internal_memmove(buf, s, len);
- buf[len] = '\0';
- }
- return len;
-}
-
-void UpdateProcessName() {
- ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
-}
-
-// Call once to make sure that binary_name_cache_str is initialized
-void CacheBinaryName() {
- if (binary_name_cache_str[0] != '\0')
- return;
- ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
- ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
-}
-
-uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
- CacheBinaryName();
- uptr name_len = internal_strlen(binary_name_cache_str);
- name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
- if (buf_len == 0)
- return 0;
- internal_memcpy(buf, binary_name_cache_str, name_len);
- buf[name_len] = '\0';
- return name_len;
-}
-
-void PrintCmdline() {
- char **argv = GetArgv();
- if (!argv) return;
- Printf("\nCommand: ");
- for (uptr i = 0; argv[i]; ++i)
- Printf("%s ", argv[i]);
- Printf("\n\n");
-}
-
-// Malloc hooks.
-static const int kMaxMallocFreeHooks = 5;
-struct MallocFreeHook {
- void (*malloc_hook)(const void *, uptr);
- void (*free_hook)(const void *);
-};
-
-static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
-
-void RunMallocHooks(const void *ptr, uptr size) {
- for (int i = 0; i < kMaxMallocFreeHooks; i++) {
- auto hook = MFHooks[i].malloc_hook;
- if (!hook) return;
- hook(ptr, size);
- }
-}
-
-void RunFreeHooks(const void *ptr) {
- for (int i = 0; i < kMaxMallocFreeHooks; i++) {
- auto hook = MFHooks[i].free_hook;
- if (!hook) return;
- hook(ptr);
- }
-}
-
-static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
- void (*free_hook)(const void *)) {
- if (!malloc_hook || !free_hook) return 0;
- for (int i = 0; i < kMaxMallocFreeHooks; i++) {
- if (MFHooks[i].malloc_hook == nullptr) {
- MFHooks[i].malloc_hook = malloc_hook;
- MFHooks[i].free_hook = free_hook;
- return i + 1;
- }
- }
- return 0;
-}
-
-} // namespace __sanitizer
-
-using namespace __sanitizer; // NOLINT
-
-extern "C" {
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
- const char *error_summary) {
- Printf("%s\n", error_summary);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __sanitizer_acquire_crash_state() {
- static atomic_uint8_t in_crash_state = {};
- return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
- uptr),
- void (*free_hook)(const void *)) {
- return InstallMallocFreeHooks(malloc_hook, free_hook);
-}
-} // extern "C"
--- /dev/null
+//===-- sanitizer_common.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_allocator_interface.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+
+namespace __sanitizer {
+
+const char *SanitizerToolName = "SanitizerTool";
+
+atomic_uint32_t current_verbosity;
+uptr PageSizeCached;
+u32 NumberOfCPUsCached;
+
+// PID of the tracer task in StopTheWorld. It shares the address space with the
+// main process, but has a different PID and thus requires special handling.
+uptr stoptheworld_tracer_pid = 0;
+// Cached pid of parent process - if the parent process dies, we want to keep
+// writing to the same log file.
+uptr stoptheworld_tracer_ppid = 0;
+
+void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type,
+ const char *mmap_type, error_t err,
+ bool raw_report) {
+ static int recursion_count;
+ if (SANITIZER_RTEMS || raw_report || recursion_count) {
+ // If we are on RTEMS or raw report is requested or we went into recursion,
+ // just die. The Report() and CHECK calls below may call mmap recursively
+ // and fail.
+ RawWrite("ERROR: Failed to mmap\n");
+ Die();
+ }
+ recursion_count++;
+ Report("ERROR: %s failed to "
+ "%s 0x%zx (%zd) bytes of %s (error code: %d)\n",
+ SanitizerToolName, mmap_type, size, size, mem_type, err);
+#if !SANITIZER_GO
+ DumpProcessMap();
+#endif
+ UNREACHABLE("unable to mmap");
+}
+
+typedef bool UptrComparisonFunction(const uptr &a, const uptr &b);
+typedef bool U32ComparisonFunction(const u32 &a, const u32 &b);
+
+const char *StripPathPrefix(const char *filepath,
+ const char *strip_path_prefix) {
+ if (!filepath) return nullptr;
+ if (!strip_path_prefix) return filepath;
+ const char *res = filepath;
+ if (const char *pos = internal_strstr(filepath, strip_path_prefix))
+ res = pos + internal_strlen(strip_path_prefix);
+ if (res[0] == '.' && res[1] == '/')
+ res += 2;
+ return res;
+}
+
+const char *StripModuleName(const char *module) {
+ if (!module)
+ return nullptr;
+ if (SANITIZER_WINDOWS) {
+ // On Windows, both slash and backslash are possible.
+ // Pick the one that goes last.
+ if (const char *bslash_pos = internal_strrchr(module, '\\'))
+ return StripModuleName(bslash_pos + 1);
+ }
+ if (const char *slash_pos = internal_strrchr(module, '/')) {
+ return slash_pos + 1;
+ }
+ return module;
+}
+
+void ReportErrorSummary(const char *error_message, const char *alt_tool_name) {
+ if (!common_flags()->print_summary)
+ return;
+ InternalScopedString buff(kMaxSummaryLength);
+ buff.append("SUMMARY: %s: %s",
+ alt_tool_name ? alt_tool_name : SanitizerToolName, error_message);
+ __sanitizer_report_error_summary(buff.data());
+}
+
+// Removes the ANSI escape sequences from the input string (in-place).
+void RemoveANSIEscapeSequencesFromString(char *str) {
+ if (!str)
+ return;
+
+ // We are going to remove the escape sequences in place.
+ char *s = str;
+ char *z = str;
+ while (*s != '\0') {
+ CHECK_GE(s, z);
+ // Skip over ANSI escape sequences with pointer 's'.
+ if (*s == '\033' && *(s + 1) == '[') {
+ s = internal_strchrnul(s, 'm');
+ if (*s == '\0') {
+ break;
+ }
+ s++;
+ continue;
+ }
+ // 's' now points at a character we want to keep. Copy over the buffer
+ // content if the escape sequence has been perviously skipped andadvance
+ // both pointers.
+ if (s != z)
+ *z = *s;
+
+ // If we have not seen an escape sequence, just advance both pointers.
+ z++;
+ s++;
+ }
+
+ // Null terminate the string.
+ *z = '\0';
+}
+
+void LoadedModule::set(const char *module_name, uptr base_address) {
+ clear();
+ full_name_ = internal_strdup(module_name);
+ base_address_ = base_address;
+}
+
+void LoadedModule::set(const char *module_name, uptr base_address,
+ ModuleArch arch, u8 uuid[kModuleUUIDSize],
+ bool instrumented) {
+ set(module_name, base_address);
+ arch_ = arch;
+ internal_memcpy(uuid_, uuid, sizeof(uuid_));
+ instrumented_ = instrumented;
+}
+
+void LoadedModule::clear() {
+ InternalFree(full_name_);
+ base_address_ = 0;
+ max_executable_address_ = 0;
+ full_name_ = nullptr;
+ arch_ = kModuleArchUnknown;
+ internal_memset(uuid_, 0, kModuleUUIDSize);
+ instrumented_ = false;
+ while (!ranges_.empty()) {
+ AddressRange *r = ranges_.front();
+ ranges_.pop_front();
+ InternalFree(r);
+ }
+}
+
+void LoadedModule::addAddressRange(uptr beg, uptr end, bool executable,
+ bool writable, const char *name) {
+ void *mem = InternalAlloc(sizeof(AddressRange));
+ AddressRange *r =
+ new(mem) AddressRange(beg, end, executable, writable, name);
+ ranges_.push_back(r);
+ if (executable && end > max_executable_address_)
+ max_executable_address_ = end;
+}
+
+bool LoadedModule::containsAddress(uptr address) const {
+ for (const AddressRange &r : ranges()) {
+ if (r.beg <= address && address < r.end)
+ return true;
+ }
+ return false;
+}
+
+static atomic_uintptr_t g_total_mmaped;
+
+void IncreaseTotalMmap(uptr size) {
+ if (!common_flags()->mmap_limit_mb) return;
+ uptr total_mmaped =
+ atomic_fetch_add(&g_total_mmaped, size, memory_order_relaxed) + size;
+ // Since for now mmap_limit_mb is not a user-facing flag, just kill
+ // a program. Use RAW_CHECK to avoid extra mmaps in reporting.
+ RAW_CHECK((total_mmaped >> 20) < common_flags()->mmap_limit_mb);
+}
+
+void DecreaseTotalMmap(uptr size) {
+ if (!common_flags()->mmap_limit_mb) return;
+ atomic_fetch_sub(&g_total_mmaped, size, memory_order_relaxed);
+}
+
+bool TemplateMatch(const char *templ, const char *str) {
+ if ((!str) || str[0] == 0)
+ return false;
+ bool start = false;
+ if (templ && templ[0] == '^') {
+ start = true;
+ templ++;
+ }
+ bool asterisk = false;
+ while (templ && templ[0]) {
+ if (templ[0] == '*') {
+ templ++;
+ start = false;
+ asterisk = true;
+ continue;
+ }
+ if (templ[0] == '$')
+ return str[0] == 0 || asterisk;
+ if (str[0] == 0)
+ return false;
+ char *tpos = (char*)internal_strchr(templ, '*');
+ char *tpos1 = (char*)internal_strchr(templ, '$');
+ if ((!tpos) || (tpos1 && tpos1 < tpos))
+ tpos = tpos1;
+ if (tpos)
+ tpos[0] = 0;
+ const char *str0 = str;
+ const char *spos = internal_strstr(str, templ);
+ str = spos + internal_strlen(templ);
+ templ = tpos;
+ if (tpos)
+ tpos[0] = tpos == tpos1 ? '$' : '*';
+ if (!spos)
+ return false;
+ if (start && spos != str0)
+ return false;
+ start = false;
+ asterisk = false;
+ }
+ return true;
+}
+
+static char binary_name_cache_str[kMaxPathLength];
+static char process_name_cache_str[kMaxPathLength];
+
+const char *GetProcessName() {
+ return process_name_cache_str;
+}
+
+static uptr ReadProcessName(/*out*/ char *buf, uptr buf_len) {
+ ReadLongProcessName(buf, buf_len);
+ char *s = const_cast<char *>(StripModuleName(buf));
+ uptr len = internal_strlen(s);
+ if (s != buf) {
+ internal_memmove(buf, s, len);
+ buf[len] = '\0';
+ }
+ return len;
+}
+
+void UpdateProcessName() {
+ ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+// Call once to make sure that binary_name_cache_str is initialized
+void CacheBinaryName() {
+ if (binary_name_cache_str[0] != '\0')
+ return;
+ ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str));
+ ReadProcessName(process_name_cache_str, sizeof(process_name_cache_str));
+}
+
+uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) {
+ CacheBinaryName();
+ uptr name_len = internal_strlen(binary_name_cache_str);
+ name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1;
+ if (buf_len == 0)
+ return 0;
+ internal_memcpy(buf, binary_name_cache_str, name_len);
+ buf[name_len] = '\0';
+ return name_len;
+}
+
+void PrintCmdline() {
+ char **argv = GetArgv();
+ if (!argv) return;
+ Printf("\nCommand: ");
+ for (uptr i = 0; argv[i]; ++i)
+ Printf("%s ", argv[i]);
+ Printf("\n\n");
+}
+
+// Malloc hooks.
+static const int kMaxMallocFreeHooks = 5;
+struct MallocFreeHook {
+ void (*malloc_hook)(const void *, uptr);
+ void (*free_hook)(const void *);
+};
+
+static MallocFreeHook MFHooks[kMaxMallocFreeHooks];
+
+void RunMallocHooks(const void *ptr, uptr size) {
+ for (int i = 0; i < kMaxMallocFreeHooks; i++) {
+ auto hook = MFHooks[i].malloc_hook;
+ if (!hook) return;
+ hook(ptr, size);
+ }
+}
+
+void RunFreeHooks(const void *ptr) {
+ for (int i = 0; i < kMaxMallocFreeHooks; i++) {
+ auto hook = MFHooks[i].free_hook;
+ if (!hook) return;
+ hook(ptr);
+ }
+}
+
+static int InstallMallocFreeHooks(void (*malloc_hook)(const void *, uptr),
+ void (*free_hook)(const void *)) {
+ if (!malloc_hook || !free_hook) return 0;
+ for (int i = 0; i < kMaxMallocFreeHooks; i++) {
+ if (MFHooks[i].malloc_hook == nullptr) {
+ MFHooks[i].malloc_hook = malloc_hook;
+ MFHooks[i].free_hook = free_hook;
+ return i + 1;
+ }
+ }
+ return 0;
+}
+
+} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_report_error_summary,
+ const char *error_summary) {
+ Printf("%s\n", error_summary);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_acquire_crash_state() {
+ static atomic_uint8_t in_crash_state = {};
+ return !atomic_exchange(&in_crash_state, 1, memory_order_relaxed);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_install_malloc_and_free_hooks(void (*malloc_hook)(const void *,
+ uptr),
+ void (*free_hook)(const void *)) {
+ return InstallMallocFreeHooks(malloc_hook, free_hook);
+}
+} // extern "C"
//===-- sanitizer_common.h --------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
return atomic_load(¤t_verbosity, memory_order_relaxed);
}
+#if SANITIZER_ANDROID
+INLINE uptr GetPageSize() {
+// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array.
+ return 4096;
+}
+INLINE uptr GetPageSizeCached() {
+ return 4096;
+}
+#else
uptr GetPageSize();
extern uptr PageSizeCached;
INLINE uptr GetPageSizeCached() {
PageSizeCached = GetPageSize();
return PageSizeCached;
}
+#endif
uptr GetMmapGranularity();
uptr GetMaxVirtualAddress();
uptr GetMaxUserVirtualAddress();
bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name = nullptr)
WARN_UNUSED_RESULT;
void *MmapNoReserveOrDie(uptr size, const char *mem_type);
-void *MmapFixedOrDie(uptr fixed_addr, uptr size);
+void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name = nullptr);
// Behaves just like MmapFixedOrDie, but tolerates out of memory condition, in
// that case returns nullptr.
-void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size);
+void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size,
+ const char *name = nullptr);
void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr);
void *MmapNoAccess(uptr size);
// Map aligned chunk of address space; size and alignment are powers of two.
void IncreaseTotalMmap(uptr size);
void DecreaseTotalMmap(uptr size);
uptr GetRSS();
-bool NoHugePagesInRegion(uptr addr, uptr length);
+void SetShadowRegionHugePageMode(uptr addr, uptr length);
bool DontDumpShadowMemory(uptr addr, uptr length);
// Check if the built VMA size matches the runtime one.
void CheckVMASize();
class ReservedAddressRange {
public:
uptr Init(uptr size, const char *name = nullptr, uptr fixed_addr = 0);
- uptr Map(uptr fixed_addr, uptr size);
- uptr MapOrDie(uptr fixed_addr, uptr size);
+ uptr Map(uptr fixed_addr, uptr size, const char *name = nullptr);
+ uptr MapOrDie(uptr fixed_addr, uptr size, const char *name = nullptr);
void Unmap(uptr addr, uptr size);
void *base() const { return base_; }
uptr size() const { return size_; }
u32 GetUid();
void ReExec();
void CheckASLR();
+void CheckMPROTECT();
char **GetArgv();
+char **GetEnviron();
void PrintCmdline();
bool StackSizeIsUnlimited();
-uptr GetStackSizeLimitInBytes();
void SetStackSizeLimitInBytes(uptr limit);
bool AddressSpaceIsUnlimited();
void SetAddressSpaceUnlimited();
error_t *errno_p = nullptr);
// When adding a new architecture, don't forget to also update
-// script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cc.
+// script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cpp.
inline const char *ModuleArchToString(ModuleArch arch) {
switch (arch) {
case kModuleArchUnknown:
void WriteToSyslog(const char *buffer);
-#if SANITIZER_MAC
+#if defined(SANITIZER_WINDOWS) && defined(_MSC_VER) && !defined(__clang__)
+#define SANITIZER_WIN_TRACE 1
+#else
+#define SANITIZER_WIN_TRACE 0
+#endif
+
+#if SANITIZER_MAC || SANITIZER_WIN_TRACE
void LogFullErrorReport(const char *buffer);
#else
INLINE void LogFullErrorReport(const char *buffer) {}
INLINE void LogMessageOnPrintf(const char *str) {}
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX || SANITIZER_WIN_TRACE
// Initialize Android logging. Any writes before this are silently lost.
void AndroidLogInit();
void SetAbortMessage(const char *);
bool IsMemoryAccess() const;
};
+void InitializePlatformEarly();
void MaybeReexec();
template <typename Fn>
//===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#define ctime __ctime50
#define ctime_r __ctime_r50
#define devname __devname50
+#define fgetpos __fgetpos50
+#define fsetpos __fsetpos50
+#define fts_children __fts_children60
+#define fts_close __fts_close60
+#define fts_open __fts_open60
+#define fts_read __fts_read60
+#define fts_set __fts_set60
#define getitimer __getitimer50
+#define getmntinfo __getmntinfo13
#define getpwent __getpwent50
#define getpwnam __getpwnam50
#define getpwnam_r __getpwnam_r50
#define getutxent __getutxent50
#define getutxid __getutxid50
#define getutxline __getutxline50
+#define pututxline __pututxline50
#define glob __glob30
#define gmtime __gmtime50
#define gmtime_r __gmtime_r50
#define stat __stat50
#define time __time50
#define times __times13
+#define unvis __unvis50
#define wait3 __wait350
#define wait4 __wait450
extern const unsigned short *_ctype_tab_;
};
};
+#if SI_POSIX
typedef AddrHashMap<CommonInterceptorMetadata, 31051> MetadataHashMap;
static MetadataHashMap *interceptor_metadata_map;
-#if SI_POSIX
UNUSED static void SetInterceptorMetadata(__sanitizer_FILE *addr,
const FileMetadata &file) {
MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr);
#endif
#if SANITIZER_INTERCEPT_MEMCMP
-
DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc,
const void *s1, const void *s2, uptr n,
int result)
-INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
- if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
- return internal_memcmp(a1, a2, size);
- void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
+// Common code for `memcmp` and `bcmp`.
+int MemcmpInterceptorCommon(void *ctx,
+ int (*real_fn)(const void *, const void *, uptr),
+ const void *a1, const void *a2, uptr size) {
if (common_flags()->intercept_memcmp) {
if (common_flags()->strict_memcmp) {
// Check the entire regions even if the first bytes of the buffers are
return r;
}
}
- int result = REAL(memcmp(a1, a2, size));
+ int result = real_fn(a1, a2, size);
CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1,
a2, size, result);
return result;
}
+INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_memcmp(a1, a2, size);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size);
+ return MemcmpInterceptorCommon(ctx, REAL(memcmp), a1, a2, size);
+}
+
#define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp)
#else
#define INIT_MEMCMP
#endif
+#if SANITIZER_INTERCEPT_BCMP
+INTERCEPTOR(int, bcmp, const void *a1, const void *a2, uptr size) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_memcmp(a1, a2, size);
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, bcmp, a1, a2, size);
+ return MemcmpInterceptorCommon(ctx, REAL(bcmp), a1, a2, size);
+}
+
+#define INIT_BCMP COMMON_INTERCEPT_FUNCTION(bcmp)
+#else
+#define INIT_BCMP
+#endif
+
#if SANITIZER_INTERCEPT_MEMCHR
INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) {
if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
// libc file streams can call user-supplied functions, see fopencookie.
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fputs, s, file);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
+ if (!SANITIZER_MAC || s) { // `fputs(NULL, file)` is supported on Darwin.
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
+ }
return REAL(fputs)(s, file);
}
#define INIT_FPUTS COMMON_INTERCEPT_FUNCTION(fputs)
// libc file streams can call user-supplied functions, see fopencookie.
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, puts, s);
- COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
+ if (!SANITIZER_MAC || s) { // `puts(NULL)` is supported on Darwin.
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1);
+ }
return REAL(puts)(s);
}
#define INIT_PUTS COMMON_INTERCEPT_FUNCTION(puts)
#define INIT_IOCTL
#endif
-#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \
- SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \
- SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
-static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) {
+#if SANITIZER_POSIX
+UNUSED static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) {
if (pwd) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd));
if (pwd->pw_name)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_name,
- REAL(strlen)(pwd->pw_name) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd->pw_name,
+ REAL(strlen)(pwd->pw_name) + 1);
if (pwd->pw_passwd)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_passwd,
- REAL(strlen)(pwd->pw_passwd) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd->pw_passwd,
+ REAL(strlen)(pwd->pw_passwd) + 1);
#if !SANITIZER_ANDROID
if (pwd->pw_gecos)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_gecos,
- REAL(strlen)(pwd->pw_gecos) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd->pw_gecos,
+ REAL(strlen)(pwd->pw_gecos) + 1);
#endif
-#if SANITIZER_MAC
+#if SANITIZER_MAC || SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
if (pwd->pw_class)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_class,
- REAL(strlen)(pwd->pw_class) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd->pw_class,
+ REAL(strlen)(pwd->pw_class) + 1);
#endif
if (pwd->pw_dir)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_dir,
- REAL(strlen)(pwd->pw_dir) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd->pw_dir,
+ REAL(strlen)(pwd->pw_dir) + 1);
if (pwd->pw_shell)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_shell,
- REAL(strlen)(pwd->pw_shell) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd->pw_shell,
+ REAL(strlen)(pwd->pw_shell) + 1);
}
}
-static void unpoison_group(void *ctx, __sanitizer_group *grp) {
+UNUSED static void unpoison_group(void *ctx, __sanitizer_group *grp) {
if (grp) {
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, sizeof(*grp));
if (grp->gr_name)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_name,
- REAL(strlen)(grp->gr_name) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp->gr_name,
+ REAL(strlen)(grp->gr_name) + 1);
if (grp->gr_passwd)
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_passwd,
- REAL(strlen)(grp->gr_passwd) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp->gr_passwd,
+ REAL(strlen)(grp->gr_passwd) + 1);
char **p = grp->gr_mem;
for (; *p; ++p) {
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(*p, REAL(strlen)(*p) + 1);
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1);
}
- COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_mem,
- (p - grp->gr_mem + 1) * sizeof(*p));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp->gr_mem,
+ (p - grp->gr_mem + 1) * sizeof(*p));
}
}
-#endif // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS ||
- // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT ||
- // SANITIZER_INTERCEPT_GETPWENT_R ||
- // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
+#endif // SANITIZER_POSIX
#if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) {
if (name)
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
__sanitizer_passwd *res = REAL(getpwnam)(name);
- if (res) unpoison_passwd(ctx, res);
+ unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid);
__sanitizer_passwd *res = REAL(getpwuid)(uid);
- if (res) unpoison_passwd(ctx, res);
+ unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) {
COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name);
COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
__sanitizer_group *res = REAL(getgrnam)(name);
- if (res) unpoison_group(ctx, res);
+ unpoison_group(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid);
__sanitizer_group *res = REAL(getgrgid)(gid);
- if (res) unpoison_group(ctx, res);
+ unpoison_group(ctx, res);
return res;
}
#define INIT_GETPWNAM_AND_FRIENDS \
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result);
- if (!res) {
- if (result && *result) unpoison_passwd(ctx, *result);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ if (!res && result)
+ unpoison_passwd(ctx, *result);
if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result);
- if (!res) {
- if (result && *result) unpoison_passwd(ctx, *result);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ if (!res && result)
+ unpoison_passwd(ctx, *result);
if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(getgrnam_r)(name, grp, buf, buflen, result);
- if (!res) {
- if (result && *result) unpoison_group(ctx, *result);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ if (!res && result)
+ unpoison_group(ctx, *result);
if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result);
- if (!res) {
- if (result && *result) unpoison_group(ctx, *result);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ if (!res && result)
+ unpoison_group(ctx, *result);
if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result));
return res;
}
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy);
__sanitizer_passwd *res = REAL(getpwent)(dummy);
- if (res) unpoison_passwd(ctx, res);
+ unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy);
__sanitizer_group *res = REAL(getgrent)(dummy);
- if (res) unpoison_group(ctx, res);;
+ unpoison_group(ctx, res);
return res;
}
#define INIT_GETPWENT \
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp);
__sanitizer_passwd *res = REAL(fgetpwent)(fp);
- if (res) unpoison_passwd(ctx, res);
+ unpoison_passwd(ctx, res);
return res;
}
INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp);
__sanitizer_group *res = REAL(fgetgrent)(fp);
- if (res) unpoison_group(ctx, res);
+ unpoison_group(ctx, res);
return res;
}
#define INIT_FGETPWENT \
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp);
- if (!res) {
- if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ if (!res && pwbufp)
+ unpoison_passwd(ctx, *pwbufp);
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
-INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
- SIZE_T buflen, __sanitizer_passwd **pwbufp) {
+INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
+ __sanitizer_group **pwbufp) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
+ COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
- if (!res) {
- if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
+ if (!res && pwbufp)
+ unpoison_group(ctx, *pwbufp);
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
-INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen,
- __sanitizer_group **pwbufp) {
+#define INIT_GETPWENT_R \
+ COMMON_INTERCEPT_FUNCTION(getpwent_r); \
+ COMMON_INTERCEPT_FUNCTION(getgrent_r);
+#else
+#define INIT_GETPWENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_FGETPWENT_R
+INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf,
+ SIZE_T buflen, __sanitizer_passwd **pwbufp) {
void *ctx;
- COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp);
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
- int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp);
- if (!res) {
- if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp);
+ if (!res && pwbufp)
+ unpoison_passwd(ctx, *pwbufp);
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
+#define INIT_FGETPWENT_R \
+ COMMON_INTERCEPT_FUNCTION(fgetpwent_r);
+#else
+#define INIT_FGETPWENT_R
+#endif
+
+#if SANITIZER_INTERCEPT_FGETGRENT_R
INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf,
SIZE_T buflen, __sanitizer_group **pwbufp) {
void *ctx;
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp);
- if (!res) {
- if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp);
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen);
- }
+ if (!res && pwbufp)
+ unpoison_group(ctx, *pwbufp);
if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp));
return res;
}
-#define INIT_GETPWENT_R \
- COMMON_INTERCEPT_FUNCTION(getpwent_r); \
- COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \
- COMMON_INTERCEPT_FUNCTION(getgrent_r); \
+#define INIT_FGETGRENT_R \
COMMON_INTERCEPT_FUNCTION(fgetgrent_r);
#else
-#define INIT_GETPWENT_R
+#define INIT_FGETGRENT_R
#endif
#if SANITIZER_INTERCEPT_SETPWENT
namespace __sanitizer {
extern "C" {
int real_clock_gettime(u32 clk_id, void *tp) {
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_clock_gettime(clk_id, tp);
return REAL(clock_gettime)(clk_id, tp);
}
} // extern "C"
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps);
if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz);
- // FIXME: under ASan the call below may write to freed memory and corrupt
- // its metadata. See
- // https://github.com/google/sanitizers/issues/321.
- SIZE_T res = REAL(wcrtomb)(dest, src, ps);
- if (res != ((SIZE_T)-1) && dest) {
- SIZE_T write_cnt = res;
- COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt);
+
+ if (!dest)
+ return REAL(wcrtomb)(dest, src, ps);
+
+ char local_dest[32];
+ SIZE_T res = REAL(wcrtomb)(local_dest, src, ps);
+ if (res != ((SIZE_T)-1)) {
+ CHECK_LE(res, sizeof(local_dest));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, res);
+ REAL(memcpy)(dest, local_dest, res);
}
return res;
}
#define INIT_WCRTOMB
#endif
+#if SANITIZER_INTERCEPT_WCTOMB
+INTERCEPTOR(int, wctomb, char *dest, wchar_t src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, wctomb, dest, src);
+ if (!dest)
+ return REAL(wctomb)(dest, src);
+
+ char local_dest[32];
+ int res = REAL(wctomb)(local_dest, src);
+ if (res != -1) {
+ CHECK_LE(res, sizeof(local_dest));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, res);
+ REAL(memcpy)(dest, local_dest, res);
+ }
+ return res;
+}
+
+#define INIT_WCTOMB COMMON_INTERCEPT_FUNCTION(wctomb);
+#else
+#define INIT_WCTOMB
+#endif
+
#if SANITIZER_INTERCEPT_TCGETATTR
INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) {
void *ctx;
#define INIT_SIGPROCMASK
#endif
+#if SANITIZER_INTERCEPT_PTHREAD_SIGMASK
+INTERCEPTOR(int, pthread_sigmask, int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pthread_sigmask, how, set, oldset);
+ if (set) COMMON_INTERCEPTOR_READ_RANGE(ctx, set, sizeof(*set));
+ // FIXME: under ASan the call below may write to freed memory and corrupt
+ // its metadata. See
+ // https://github.com/google/sanitizers/issues/321.
+ int res = REAL(pthread_sigmask)(how, set, oldset);
+ if (!res && oldset)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset));
+ return res;
+}
+#define INIT_PTHREAD_SIGMASK COMMON_INTERCEPT_FUNCTION(pthread_sigmask);
+#else
+#define INIT_PTHREAD_SIGMASK
+#endif
+
#if SANITIZER_INTERCEPT_BACKTRACE
INTERCEPTOR(int, backtrace, void **buffer, int size) {
void *ctx;
INTERCEPTOR(int, fstatvfs, int fd, void *buf) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
int res = REAL(fstatvfs)(fd, buf);
- if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ if (!res) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
return res;
}
#define INIT_STATVFS \
#define INIT_TMPNAM_R
#endif
+#if SANITIZER_INTERCEPT_TTYNAME
+INTERCEPTOR(char *, ttyname, int fd) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, ttyname, fd);
+ char *res = REAL(ttyname)(fd);
+ if (res != nullptr)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1);
+ return res;
+}
+#define INIT_TTYNAME COMMON_INTERCEPT_FUNCTION(ttyname);
+#else
+#define INIT_TTYNAME
+#endif
+
#if SANITIZER_INTERCEPT_TTYNAME_R
INTERCEPTOR(int, ttyname_r, int fd, char *name, SIZE_T namesize) {
void *ctx;
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
}
+#define INIT_REMQUO \
+ COMMON_INTERCEPT_FUNCTION(remquo); \
+ COMMON_INTERCEPT_FUNCTION(remquof);
+#else
+#define INIT_REMQUO
+#endif
+
+#if SANITIZER_INTERCEPT_REMQUOL
INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo);
if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo));
return res;
}
-#define INIT_REMQUO \
- COMMON_INTERCEPT_FUNCTION(remquo); \
- COMMON_INTERCEPT_FUNCTION(remquof); \
+#define INIT_REMQUOL \
COMMON_INTERCEPT_FUNCTION_LDBL(remquol);
#else
-#define INIT_REMQUO
+#define INIT_REMQUOL
#endif
#if SANITIZER_INTERCEPT_LGAMMA
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
return res;
}
+#define INIT_LGAMMA \
+ COMMON_INTERCEPT_FUNCTION(lgamma); \
+ COMMON_INTERCEPT_FUNCTION(lgammaf);
+#else
+#define INIT_LGAMMA
+#endif
+
+#if SANITIZER_INTERCEPT_LGAMMAL
INTERCEPTOR(long double, lgammal, long double x) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x);
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam));
return res;
}
-#define INIT_LGAMMA \
- COMMON_INTERCEPT_FUNCTION(lgamma); \
- COMMON_INTERCEPT_FUNCTION(lgammaf); \
+#define INIT_LGAMMAL \
COMMON_INTERCEPT_FUNCTION_LDBL(lgammal);
#else
-#define INIT_LGAMMA
+#define INIT_LGAMMAL
#endif
#if SANITIZER_INTERCEPT_LGAMMA_R
void *ctx;
COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
}
-
#define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero);
#else
#define INIT___BZERO
#endif // SANITIZER_INTERCEPT___BZERO
+#if SANITIZER_INTERCEPT_BZERO
+INTERCEPTOR(void *, bzero, void *block, uptr size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size);
+}
+#define INIT_BZERO COMMON_INTERCEPT_FUNCTION(bzero);
+#else
+#define INIT_BZERO
+#endif // SANITIZER_INTERCEPT_BZERO
+
#if SANITIZER_INTERCEPT_FTIME
INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) {
void *ctx;
void unpoison_file(__sanitizer_FILE *fp) {
#if SANITIZER_HAS_STRUCT_FILE
COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp));
+#if SANITIZER_NETBSD
+ if (fp->_bf._base && fp->_bf._size > 0)
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_bf._base,
+ fp->_bf._size);
+#else
if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end)
COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base,
fp->_IO_read_end - fp->_IO_read_base);
+#endif
#endif // SANITIZER_HAS_STRUCT_FILE
}
#endif
COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz);
return res;
}
+INTERCEPTOR(void *, pututxline, const void *ut) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pututxline, ut);
+ if (ut)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ut, __sanitizer::struct_utmpx_sz);
+ void *res = REAL(pututxline)(ut);
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, __sanitizer::struct_utmpx_sz);
+ return res;
+}
#define INIT_UTMPX \
COMMON_INTERCEPT_FUNCTION(getutxent); \
COMMON_INTERCEPT_FUNCTION(getutxid); \
- COMMON_INTERCEPT_FUNCTION(getutxline);
+ COMMON_INTERCEPT_FUNCTION(getutxline); \
+ COMMON_INTERCEPT_FUNCTION(pututxline);
#else
#define INIT_UTMPX
#endif
#define INIT_WCSCAT
#endif
+#if SANITIZER_INTERCEPT_WCSDUP
+INTERCEPTOR(wchar_t *, wcsdup, wchar_t *s) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, wcsdup, s);
+ SIZE_T len = REAL(wcslen)(s);
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, s, sizeof(wchar_t) * (len + 1));
+ wchar_t *result = REAL(wcsdup)(s);
+ if (result)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(wchar_t) * (len + 1));
+ return result;
+}
+
+#define INIT_WCSDUP COMMON_INTERCEPT_FUNCTION(wcsdup);
+#else
+#define INIT_WCSDUP
+#endif
+
#if SANITIZER_INTERCEPT_STRXFRM
static SIZE_T RealStrLen(const char *str) { return REAL(strlen)(str); }
#endif
#if SANITIZER_INTERCEPT_DEVNAME_R
-INTERCEPTOR(int, devname_r, u64 dev, u32 type, char *path, uptr len) {
+#if SANITIZER_NETBSD
+#define DEVNAME_R_RETTYPE int
+#define DEVNAME_R_SUCCESS(x) (!(x))
+#else
+#define DEVNAME_R_RETTYPE char*
+#define DEVNAME_R_SUCCESS(x) (x)
+#endif
+INTERCEPTOR(DEVNAME_R_RETTYPE, devname_r, u64 dev, u32 type, char *path,
+ uptr len) {
void *ctx;
- int res;
COMMON_INTERCEPTOR_ENTER(ctx, devname_r, dev, type, path, len);
- res = REAL(devname_r)(dev, type, path, len);
- if (!res)
+ DEVNAME_R_RETTYPE res = REAL(devname_r)(dev, type, path, len);
+ if (DEVNAME_R_SUCCESS(res))
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, path, REAL(strlen)(path) + 1);
return res;
}
#define INIT_NETENT
#endif
-static void InitializeCommonInterceptors() {
- static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
- interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap();
+#if SANITIZER_INTERCEPT_GETMNTINFO
+INTERCEPTOR(int, getmntinfo, void **mntbufp, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getmntinfo, mntbufp, flags);
+ int cnt = REAL(getmntinfo)(mntbufp, flags);
+ if (cnt > 0 && mntbufp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mntbufp, sizeof(void *));
+ if (*mntbufp)
+#if SANITIZER_NETBSD
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *mntbufp, cnt * struct_statvfs_sz);
+#else
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *mntbufp, cnt * struct_statfs_sz);
+#endif
+ }
+ return cnt;
+}
+#define INIT_GETMNTINFO COMMON_INTERCEPT_FUNCTION(getmntinfo)
+#else
+#define INIT_GETMNTINFO
+#endif
+
+#if SANITIZER_INTERCEPT_MI_VECTOR_HASH
+INTERCEPTOR(void, mi_vector_hash, const void *key, SIZE_T len, u32 seed,
+ u32 hashes[3]) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, mi_vector_hash, key, len, seed, hashes);
+ if (key)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, len);
+ REAL(mi_vector_hash)(key, len, seed, hashes);
+ if (hashes)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hashes, sizeof(hashes[0]) * 3);
+}
+#define INIT_MI_VECTOR_HASH COMMON_INTERCEPT_FUNCTION(mi_vector_hash)
+#else
+#define INIT_MI_VECTOR_HASH
+#endif
+
+#if SANITIZER_INTERCEPT_SETVBUF
+INTERCEPTOR(int, setvbuf, __sanitizer_FILE *stream, char *buf, int mode,
+ SIZE_T size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setvbuf, stream, buf, mode, size);
+ int ret = REAL(setvbuf)(stream, buf, mode, size);
+ if (buf)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, size);
+ if (stream)
+ unpoison_file(stream);
+ return ret;
+}
+
+INTERCEPTOR(void, setbuf, __sanitizer_FILE *stream, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setbuf, stream, buf);
+ REAL(setbuf)(stream, buf);
+ if (buf) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer_bufsiz);
+ }
+ if (stream)
+ unpoison_file(stream);
+}
+
+INTERCEPTOR(void, setbuffer, __sanitizer_FILE *stream, char *buf, int mode) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setbuffer, stream, buf, mode);
+ REAL(setbuffer)(stream, buf, mode);
+ if (buf) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer_bufsiz);
+ }
+ if (stream)
+ unpoison_file(stream);
+}
+
+INTERCEPTOR(void, setlinebuf, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, setlinebuf, stream);
+ REAL(setlinebuf)(stream);
+ if (stream)
+ unpoison_file(stream);
+}
+#define INIT_SETVBUF COMMON_INTERCEPT_FUNCTION(setvbuf); \
+ COMMON_INTERCEPT_FUNCTION(setbuf); \
+ COMMON_INTERCEPT_FUNCTION(setbuffer); \
+ COMMON_INTERCEPT_FUNCTION(setlinebuf)
+#else
+#define INIT_SETVBUF
+#endif
+
+#if SANITIZER_INTERCEPT_GETVFSSTAT
+INTERCEPTOR(int, getvfsstat, void *buf, SIZE_T bufsize, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getvfsstat, buf, bufsize, flags);
+ int ret = REAL(getvfsstat)(buf, bufsize, flags);
+ if (buf && ret > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, ret * struct_statvfs_sz);
+ return ret;
+}
+#define INIT_GETVFSSTAT COMMON_INTERCEPT_FUNCTION(getvfsstat)
+#else
+#define INIT_GETVFSSTAT
+#endif
+
+#if SANITIZER_INTERCEPT_REGEX
+INTERCEPTOR(int, regcomp, void *preg, const char *pattern, int cflags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regcomp, preg, pattern, cflags);
+ if (pattern)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pattern, REAL(strlen)(pattern) + 1);
+ int res = REAL(regcomp)(preg, pattern, cflags);
+ if (!res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, preg, struct_regex_sz);
+ return res;
+}
+INTERCEPTOR(int, regexec, const void *preg, const char *string, SIZE_T nmatch,
+ struct __sanitizer_regmatch *pmatch[], int eflags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regexec, preg, string, nmatch, pmatch, eflags);
+ if (preg)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, preg, struct_regex_sz);
+ if (string)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, string, REAL(strlen)(string) + 1);
+ int res = REAL(regexec)(preg, string, nmatch, pmatch, eflags);
+ if (!res && pmatch)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pmatch, nmatch * struct_regmatch_sz);
+ return res;
+}
+INTERCEPTOR(SIZE_T, regerror, int errcode, const void *preg, char *errbuf,
+ SIZE_T errbuf_size) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regerror, errcode, preg, errbuf, errbuf_size);
+ if (preg)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, preg, struct_regex_sz);
+ SIZE_T res = REAL(regerror)(errcode, preg, errbuf, errbuf_size);
+ if (errbuf)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, errbuf, REAL(strlen)(errbuf) + 1);
+ return res;
+}
+INTERCEPTOR(void, regfree, const void *preg) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regfree, preg);
+ if (preg)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, preg, struct_regex_sz);
+ REAL(regfree)(preg);
+}
+#define INIT_REGEX \
+ COMMON_INTERCEPT_FUNCTION(regcomp); \
+ COMMON_INTERCEPT_FUNCTION(regexec); \
+ COMMON_INTERCEPT_FUNCTION(regerror); \
+ COMMON_INTERCEPT_FUNCTION(regfree);
+#else
+#define INIT_REGEX
+#endif
+
+#if SANITIZER_INTERCEPT_REGEXSUB
+INTERCEPTOR(SSIZE_T, regnsub, char *buf, SIZE_T bufsiz, const char *sub,
+ const struct __sanitizer_regmatch *rm, const char *str) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regnsub, buf, bufsiz, sub, rm, str);
+ if (sub)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sub, REAL(strlen)(sub) + 1);
+ // The implementation demands and hardcodes 10 elements
+ if (rm)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rm, 10 * struct_regmatch_sz);
+ if (str)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1);
+ SSIZE_T res = REAL(regnsub)(buf, bufsiz, sub, rm, str);
+ if (res > 0 && buf)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+ return res;
+}
+INTERCEPTOR(SSIZE_T, regasub, char **buf, const char *sub,
+ const struct __sanitizer_regmatch *rm, const char *sstr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, regasub, buf, sub, rm, sstr);
+ if (sub)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sub, REAL(strlen)(sub) + 1);
+ // Hardcode 10 elements as this is hardcoded size
+ if (rm)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rm, 10 * struct_regmatch_sz);
+ if (sstr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sstr, REAL(strlen)(sstr) + 1);
+ SSIZE_T res = REAL(regasub)(buf, sub, rm, sstr);
+ if (res > 0 && buf) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sizeof(char *));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *buf, REAL(strlen)(*buf) + 1);
+ }
+ return res;
+}
+
+#define INIT_REGEXSUB \
+ COMMON_INTERCEPT_FUNCTION(regnsub); \
+ COMMON_INTERCEPT_FUNCTION(regasub);
+#else
+#define INIT_REGEXSUB
+#endif
+
+#if SANITIZER_INTERCEPT_FTS
+INTERCEPTOR(void *, fts_open, char *const *path_argv, int options,
+ int (*compar)(void **, void **)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_open, path_argv, options, compar);
+ if (path_argv) {
+ for (char *const *pa = path_argv; ; ++pa) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **));
+ if (!*pa)
+ break;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1);
+ }
+ }
+ // TODO(kamil): handle compar callback
+ void *fts = REAL(fts_open)(path_argv, options, compar);
+ if (fts)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, fts, struct_FTS_sz);
+ return fts;
+}
+
+INTERCEPTOR(void *, fts_read, void *ftsp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_read, ftsp);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ void *ftsent = REAL(fts_read)(ftsp);
+ if (ftsent)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ftsent, struct_FTSENT_sz);
+ return ftsent;
+}
+
+INTERCEPTOR(void *, fts_children, void *ftsp, int options) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_children, ftsp, options);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ void *ftsent = REAL(fts_children)(ftsp, options);
+ if (ftsent)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ftsent, struct_FTSENT_sz);
+ return ftsent;
+}
+
+INTERCEPTOR(int, fts_set, void *ftsp, void *f, int options) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_set, ftsp, f, options);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ if (f)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, f, struct_FTSENT_sz);
+ return REAL(fts_set)(ftsp, f, options);
+}
+
+INTERCEPTOR(int, fts_close, void *ftsp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fts_close, ftsp);
+ if (ftsp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ftsp, struct_FTS_sz);
+ return REAL(fts_close)(ftsp);
+}
+#define INIT_FTS \
+ COMMON_INTERCEPT_FUNCTION(fts_open); \
+ COMMON_INTERCEPT_FUNCTION(fts_read); \
+ COMMON_INTERCEPT_FUNCTION(fts_children); \
+ COMMON_INTERCEPT_FUNCTION(fts_set); \
+ COMMON_INTERCEPT_FUNCTION(fts_close);
+#else
+#define INIT_FTS
+#endif
+
+#if SANITIZER_INTERCEPT_SYSCTL
+INTERCEPTOR(int, sysctl, int *name, unsigned int namelen, void *oldp,
+ SIZE_T *oldlenp, void *newp, SIZE_T newlen) {
+ void *ctx;
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_sysctl(name, namelen, oldp, oldlenp, newp, newlen);
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctl, name, namelen, oldp, oldlenp, newp,
+ newlen);
+ if (name)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, name, namelen * sizeof(*name));
+ if (oldlenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (newp && newlen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, newp, newlen);
+ int res = REAL(sysctl)(name, namelen, oldp, oldlenp, newp, newlen);
+ if (!res) {
+ if (oldlenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (oldp)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldp, *oldlenp);
+ }
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sysctlbyname, char *sname, void *oldp, SIZE_T *oldlenp,
+ void *newp, SIZE_T newlen) {
+ void *ctx;
+ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)
+ return internal_sysctlbyname(sname, oldp, oldlenp, newp, newlen);
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctlbyname, sname, oldp, oldlenp, newp,
+ newlen);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ if (oldlenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (newp && newlen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, newp, newlen);
+ int res = REAL(sysctlbyname)(sname, oldp, oldlenp, newp, newlen);
+ if (!res) {
+ if (oldlenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldlenp, sizeof(*oldlenp));
+ if (oldp)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldp, *oldlenp);
+ }
+ }
+ return res;
+}
+
+INTERCEPTOR(int, sysctlnametomib, const char *sname, int *name,
+ SIZE_T *namelenp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctlnametomib, sname, name, namelenp);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ if (namelenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, namelenp, sizeof(*namelenp));
+ int res = REAL(sysctlnametomib)(sname, name, namelenp);
+ if (!res) {
+ if (namelenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelenp, sizeof(*namelenp));
+ if (name)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, *namelenp * sizeof(*name));
+ }
+ }
+ return res;
+}
+
+#define INIT_SYSCTL \
+ COMMON_INTERCEPT_FUNCTION(sysctl); \
+ COMMON_INTERCEPT_FUNCTION(sysctlbyname); \
+ COMMON_INTERCEPT_FUNCTION(sysctlnametomib);
+#else
+#define INIT_SYSCTL
+#endif
+
+#if SANITIZER_INTERCEPT_ASYSCTL
+INTERCEPTOR(void *, asysctl, const int *name, SIZE_T namelen, SIZE_T *len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, asysctl, name, namelen, len);
+ if (name)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, name, sizeof(*name) * namelen);
+ void *res = REAL(asysctl)(name, namelen, len);
+ if (res && len) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, len, sizeof(*len));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, *len);
+ }
+ return res;
+}
+
+INTERCEPTOR(void *, asysctlbyname, const char *sname, SIZE_T *len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, asysctlbyname, sname, len);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ void *res = REAL(asysctlbyname)(sname, len);
+ if (res && len) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, len, sizeof(*len));
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, *len);
+ }
+ return res;
+}
+#define INIT_ASYSCTL \
+ COMMON_INTERCEPT_FUNCTION(asysctl); \
+ COMMON_INTERCEPT_FUNCTION(asysctlbyname);
+#else
+#define INIT_ASYSCTL
+#endif
+
+#if SANITIZER_INTERCEPT_SYSCTLGETMIBINFO
+INTERCEPTOR(int, sysctlgetmibinfo, char *sname, int *name,
+ unsigned int *namelenp, char *cname, SIZE_T *csz, void **rnode,
+ int v) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sysctlgetmibinfo, sname, name, namelenp, cname,
+ csz, rnode, v);
+ if (sname)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sname, REAL(strlen)(sname) + 1);
+ if (namelenp)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, namelenp, sizeof(*namelenp));
+ if (csz)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, csz, sizeof(*csz));
+ // Skip rnode, it's rarely used and not trivial to sanitize
+ // It's also used mostly internally
+ int res = REAL(sysctlgetmibinfo)(sname, name, namelenp, cname, csz, rnode, v);
+ if (!res) {
+ if (namelenp) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelenp, sizeof(*namelenp));
+ if (name)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, *namelenp * sizeof(*name));
+ }
+ if (csz) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, csz, sizeof(*csz));
+ if (cname)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cname, *csz);
+ }
+ }
+ return res;
+}
+#define INIT_SYSCTLGETMIBINFO \
+ COMMON_INTERCEPT_FUNCTION(sysctlgetmibinfo);
+#else
+#define INIT_SYSCTLGETMIBINFO
+#endif
+
+#if SANITIZER_INTERCEPT_NL_LANGINFO
+INTERCEPTOR(char *, nl_langinfo, long item) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, nl_langinfo, item);
+ char *ret = REAL(nl_langinfo)(item);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, REAL(strlen)(ret) + 1);
+ return ret;
+}
+#define INIT_NL_LANGINFO COMMON_INTERCEPT_FUNCTION(nl_langinfo)
+#else
+#define INIT_NL_LANGINFO
+#endif
+
+#if SANITIZER_INTERCEPT_MODCTL
+INTERCEPTOR(int, modctl, int operation, void *argp) {
+ void *ctx;
+ int ret;
+ COMMON_INTERCEPTOR_ENTER(ctx, modctl, operation, argp);
+
+ if (operation == modctl_load) {
+ if (argp) {
+ __sanitizer_modctl_load_t *ml = (__sanitizer_modctl_load_t *)argp;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ml, sizeof(*ml));
+ if (ml->ml_filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ml->ml_filename,
+ REAL(strlen)(ml->ml_filename) + 1);
+ if (ml->ml_props)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, ml->ml_props, ml->ml_propslen);
+ }
+ ret = REAL(modctl)(operation, argp);
+ } else if (operation == modctl_unload) {
+ if (argp) {
+ const char *name = (const char *)argp;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ }
+ ret = REAL(modctl)(operation, argp);
+ } else if (operation == modctl_stat) {
+ uptr iov_len;
+ struct __sanitizer_iovec *iov = (struct __sanitizer_iovec *)argp;
+ if (iov) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, iov, sizeof(*iov));
+ iov_len = iov->iov_len;
+ }
+ ret = REAL(modctl)(operation, argp);
+ if (iov)
+ COMMON_INTERCEPTOR_WRITE_RANGE(
+ ctx, iov->iov_base, Min(iov_len, iov->iov_len));
+ } else if (operation == modctl_exists)
+ ret = REAL(modctl)(operation, argp);
+ else
+ ret = REAL(modctl)(operation, argp);
+
+ return ret;
+}
+#define INIT_MODCTL COMMON_INTERCEPT_FUNCTION(modctl)
+#else
+#define INIT_MODCTL
+#endif
+
+#if SANITIZER_INTERCEPT_STRTONUM
+INTERCEPTOR(long long, strtonum, const char *nptr, long long minval,
+ long long maxval, const char **errstr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strtonum, nptr, minval, maxval, errstr);
+
+ // TODO(kamil): Implement strtoll as a common inteceptor
+ char *real_endptr;
+ long long ret = (long long)REAL(strtoimax)(nptr, &real_endptr, 10);
+ StrtolFixAndCheck(ctx, nptr, nullptr, real_endptr, 10);
+
+ ret = REAL(strtonum)(nptr, minval, maxval, errstr);
+ if (errstr) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, errstr, sizeof(const char *));
+ if (*errstr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *errstr, REAL(strlen)(*errstr) + 1);
+ }
+ return ret;
+}
+#define INIT_STRTONUM COMMON_INTERCEPT_FUNCTION(strtonum)
+#else
+#define INIT_STRTONUM
+#endif
+
+#if SANITIZER_INTERCEPT_FPARSELN
+INTERCEPTOR(char *, fparseln, __sanitizer_FILE *stream, SIZE_T *len,
+ SIZE_T *lineno, const char delim[3], int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fparseln, stream, len, lineno, delim, flags);
+ if (lineno)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, lineno, sizeof(*lineno));
+ if (delim)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, delim, sizeof(delim[0]) * 3);
+ char *ret = REAL(fparseln)(stream, len, lineno, delim, flags);
+ if (ret) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, REAL(strlen)(ret) + 1);
+ if (len)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, len, sizeof(*len));
+ if (lineno)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineno, sizeof(*lineno));
+ }
+ return ret;
+}
+#define INIT_FPARSELN COMMON_INTERCEPT_FUNCTION(fparseln)
+#else
+#define INIT_FPARSELN
+#endif
+
+#if SANITIZER_INTERCEPT_STATVFS1
+INTERCEPTOR(int, statvfs1, const char *path, void *buf, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, statvfs1, path, buf, flags);
+ if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ int res = REAL(statvfs1)(path, buf, flags);
+ if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ return res;
+}
+INTERCEPTOR(int, fstatvfs1, int fd, void *buf, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs1, fd, buf, flags);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ int res = REAL(fstatvfs1)(fd, buf, flags);
+ if (!res) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz);
+ if (fd >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
+ return res;
+}
+#define INIT_STATVFS1 \
+ COMMON_INTERCEPT_FUNCTION(statvfs1); \
+ COMMON_INTERCEPT_FUNCTION(fstatvfs1);
+#else
+#define INIT_STATVFS1
+#endif
+
+#if SANITIZER_INTERCEPT_STRTOI
+INTERCEPTOR(INTMAX_T, strtoi, const char *nptr, char **endptr, int base,
+ INTMAX_T low, INTMAX_T high, int *rstatus) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strtoi, nptr, endptr, base, low, high, rstatus);
+ char *real_endptr;
+ INTMAX_T ret = REAL(strtoi)(nptr, &real_endptr, base, low, high, rstatus);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+ if (rstatus)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rstatus, sizeof(*rstatus));
+ return ret;
+}
+
+INTERCEPTOR(UINTMAX_T, strtou, const char *nptr, char **endptr, int base,
+ UINTMAX_T low, UINTMAX_T high, int *rstatus) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strtou, nptr, endptr, base, low, high, rstatus);
+ char *real_endptr;
+ UINTMAX_T ret = REAL(strtou)(nptr, &real_endptr, base, low, high, rstatus);
+ StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base);
+ if (rstatus)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rstatus, sizeof(*rstatus));
+ return ret;
+}
+#define INIT_STRTOI \
+ COMMON_INTERCEPT_FUNCTION(strtoi); \
+ COMMON_INTERCEPT_FUNCTION(strtou)
+#else
+#define INIT_STRTOI
+#endif
+
+#if SANITIZER_INTERCEPT_CAPSICUM
+#define CAP_RIGHTS_INIT_INTERCEPTOR(cap_rights_init, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_init, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ __sanitizer_cap_rights_t *ret = \
+ REAL(cap_rights_init)(rights, ##__VA_ARGS__); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret)); \
+ return ret; \
+ }
+
+#define CAP_RIGHTS_SET_INTERCEPTOR(cap_rights_set, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_set, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ __sanitizer_cap_rights_t *ret = \
+ REAL(cap_rights_set)(rights, ##__VA_ARGS__); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret)); \
+ return ret; \
+ }
+
+#define CAP_RIGHTS_CLEAR_INTERCEPTOR(cap_rights_clear, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_clear, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ __sanitizer_cap_rights_t *ret = \
+ REAL(cap_rights_clear)(rights, ##__VA_ARGS__); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret)); \
+ return ret; \
+ }
+
+#define CAP_RIGHTS_IS_SET_INTERCEPTOR(cap_rights_is_set, rights, ...) \
+ { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_is_set, rights, ##__VA_ARGS__); \
+ if (rights) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights)); \
+ return REAL(cap_rights_is_set)(rights, ##__VA_ARGS__); \
+ }
+
+INTERCEPTOR(__sanitizer_cap_rights_t *, cap_rights_init,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_INIT_INTERCEPTOR(cap_rights_init, rights);
+}
+
+INTERCEPTOR(__sanitizer_cap_rights_t *, cap_rights_set,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_SET_INTERCEPTOR(cap_rights_set, rights);
+}
+
+INTERCEPTOR(__sanitizer_cap_rights_t *, cap_rights_clear,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_CLEAR_INTERCEPTOR(cap_rights_clear, rights);
+}
+
+INTERCEPTOR(bool, cap_rights_is_set,
+ __sanitizer_cap_rights_t *rights) {
+ CAP_RIGHTS_IS_SET_INTERCEPTOR(cap_rights_is_set, rights);
+}
+
+INTERCEPTOR(int, cap_rights_limit, int fd,
+ const __sanitizer_cap_rights_t *rights) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_limit, fd, rights);
+ if (rights)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights));
+
+ return REAL(cap_rights_limit)(fd, rights);
+}
+
+INTERCEPTOR(int, cap_rights_get, int fd, __sanitizer_cap_rights_t *rights) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_get, fd, rights);
+ int ret = REAL(cap_rights_get)(fd, rights);
+ if (!ret && rights)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rights, sizeof(*rights));
+
+ return ret;
+}
+
+INTERCEPTOR(bool, cap_rights_is_valid, const __sanitizer_cap_rights_t *rights) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_is_valid, rights);
+ if (rights)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, rights, sizeof(*rights));
+
+ return REAL(cap_rights_is_valid(rights));
+}
+
+INTERCEPTOR(__sanitizer_cap_rights *, cap_rights_merge,
+ __sanitizer_cap_rights *dst, const __sanitizer_cap_rights *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_merge, dst, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+
+ __sanitizer_cap_rights *ret = REAL(cap_rights_merge)(dst, src);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(*dst));
+
+ return ret;
+}
+
+INTERCEPTOR(__sanitizer_cap_rights *, cap_rights_remove,
+ __sanitizer_cap_rights *dst, const __sanitizer_cap_rights *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_remove, dst, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src));
+
+ __sanitizer_cap_rights *ret = REAL(cap_rights_remove)(dst, src);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(*dst));
+
+ return ret;
+}
+
+INTERCEPTOR(bool, cap_rights_contains, const __sanitizer_cap_rights *big,
+ const __sanitizer_cap_rights *little) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_rights_contains, big, little);
+ if (little)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, little, sizeof(*little));
+ if (big)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, big, sizeof(*big));
+
+ return REAL(cap_rights_contains)(big, little);
+}
+
+INTERCEPTOR(int, cap_ioctls_limit, int fd, const uptr *cmds, SIZE_T ncmds) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_ioctls_limit, fd, cmds, ncmds);
+ if (cmds)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cmds, sizeof(*cmds) * ncmds);
+
+ return REAL(cap_ioctls_limit)(fd, cmds, ncmds);
+}
+
+INTERCEPTOR(int, cap_ioctls_get, int fd, uptr *cmds, SIZE_T maxcmds) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cap_ioctls_get, fd, cmds, maxcmds);
+ int ret = REAL(cap_ioctls_get)(fd, cmds, maxcmds);
+ if (!ret && cmds)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cmds, sizeof(*cmds) * maxcmds);
+
+ return ret;
+}
+#define INIT_CAPSICUM \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_init); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_set); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_clear); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_is_set); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_get); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_limit); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_contains); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_remove); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_merge); \
+ COMMON_INTERCEPT_FUNCTION(cap_rights_is_valid); \
+ COMMON_INTERCEPT_FUNCTION(cap_ioctls_get); \
+ COMMON_INTERCEPT_FUNCTION(cap_ioctls_limit)
+#else
+#define INIT_CAPSICUM
+#endif
+
+#if SANITIZER_INTERCEPT_SHA1
+INTERCEPTOR(void, SHA1Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Init, context);
+ REAL(SHA1Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA1_CTX_sz);
+}
+INTERCEPTOR(void, SHA1Update, void *context, const u8 *data, unsigned len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA1_CTX_sz);
+ REAL(SHA1Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA1_CTX_sz);
+}
+INTERCEPTOR(void, SHA1Final, u8 digest[20], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA1_CTX_sz);
+ REAL(SHA1Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(u8) * 20);
+}
+INTERCEPTOR(void, SHA1Transform, u32 state[5], u8 buffer[64]) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Transform, state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, state, sizeof(u32) * 5);
+ if (buffer)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, sizeof(u8) * 64);
+ REAL(SHA1Transform)(state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, state, sizeof(u32) * 5);
+}
+INTERCEPTOR(char *, SHA1End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA1_CTX_sz);
+ char *ret = REAL(SHA1End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, SHA1File, char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(SHA1File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, SHA1FileChunk, char *filename, char *buf, OFF_T offset,
+ OFF_T length) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1FileChunk, filename, buf, offset, length);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(SHA1FileChunk)(filename, buf, offset, length);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, SHA1Data, u8 *data, SIZE_T len, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA1Data, data, len, buf);
+ if (data)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(SHA1Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA1_return_length);
+ return ret;
+}
+#define INIT_SHA1 \
+ COMMON_INTERCEPT_FUNCTION(SHA1Init); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Update); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Final); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Transform); \
+ COMMON_INTERCEPT_FUNCTION(SHA1End); \
+ COMMON_INTERCEPT_FUNCTION(SHA1File); \
+ COMMON_INTERCEPT_FUNCTION(SHA1FileChunk); \
+ COMMON_INTERCEPT_FUNCTION(SHA1Data)
+#else
+#define INIT_SHA1
+#endif
+
+#if SANITIZER_INTERCEPT_MD4
+INTERCEPTOR(void, MD4Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Init, context);
+ REAL(MD4Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD4_CTX_sz);
+}
+
+INTERCEPTOR(void, MD4Update, void *context, const unsigned char *data,
+ unsigned int len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD4_CTX_sz);
+ REAL(MD4Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD4_CTX_sz);
+}
+
+INTERCEPTOR(void, MD4Final, unsigned char digest[16], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD4_CTX_sz);
+ REAL(MD4Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
+}
+
+INTERCEPTOR(char *, MD4End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD4_CTX_sz);
+ char *ret = REAL(MD4End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD4_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD4File, const char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(MD4File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD4_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD4Data, const unsigned char *data, unsigned int len,
+ char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD4Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(MD4Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD4_return_length);
+ return ret;
+}
+
+#define INIT_MD4 \
+ COMMON_INTERCEPT_FUNCTION(MD4Init); \
+ COMMON_INTERCEPT_FUNCTION(MD4Update); \
+ COMMON_INTERCEPT_FUNCTION(MD4Final); \
+ COMMON_INTERCEPT_FUNCTION(MD4End); \
+ COMMON_INTERCEPT_FUNCTION(MD4File); \
+ COMMON_INTERCEPT_FUNCTION(MD4Data)
+#else
+#define INIT_MD4
+#endif
+
+#if SANITIZER_INTERCEPT_RMD160
+INTERCEPTOR(void, RMD160Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Init, context);
+ REAL(RMD160Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, RMD160_CTX_sz);
+}
+INTERCEPTOR(void, RMD160Update, void *context, const u8 *data, unsigned len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, RMD160_CTX_sz);
+ REAL(RMD160Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, RMD160_CTX_sz);
+}
+INTERCEPTOR(void, RMD160Final, u8 digest[20], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, RMD160_CTX_sz);
+ REAL(RMD160Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(u8) * 20);
+}
+INTERCEPTOR(void, RMD160Transform, u32 state[5], u16 buffer[16]) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Transform, state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, state, sizeof(u32) * 5);
+ if (buffer)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, sizeof(u32) * 16);
+ REAL(RMD160Transform)(state, buffer);
+ if (state)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, state, sizeof(u32) * 5);
+}
+INTERCEPTOR(char *, RMD160End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, RMD160_CTX_sz);
+ char *ret = REAL(RMD160End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, RMD160File, char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(RMD160File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, RMD160FileChunk, char *filename, char *buf, OFF_T offset,
+ OFF_T length) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160FileChunk, filename, buf, offset, length);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(RMD160FileChunk)(filename, buf, offset, length);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+INTERCEPTOR(char *, RMD160Data, u8 *data, SIZE_T len, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, RMD160Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(RMD160Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, RMD160_return_length);
+ return ret;
+}
+#define INIT_RMD160 \
+ COMMON_INTERCEPT_FUNCTION(RMD160Init); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Update); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Final); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Transform); \
+ COMMON_INTERCEPT_FUNCTION(RMD160End); \
+ COMMON_INTERCEPT_FUNCTION(RMD160File); \
+ COMMON_INTERCEPT_FUNCTION(RMD160FileChunk); \
+ COMMON_INTERCEPT_FUNCTION(RMD160Data)
+#else
+#define INIT_RMD160
+#endif
+
+#if SANITIZER_INTERCEPT_MD5
+INTERCEPTOR(void, MD5Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Init, context);
+ REAL(MD5Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
+}
+
+INTERCEPTOR(void, MD5Update, void *context, const unsigned char *data,
+ unsigned int len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
+ REAL(MD5Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD5_CTX_sz);
+}
+
+INTERCEPTOR(void, MD5Final, unsigned char digest[16], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
+ REAL(MD5Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
+}
+
+INTERCEPTOR(char *, MD5End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD5_CTX_sz);
+ char *ret = REAL(MD5End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD5File, const char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(MD5File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD5Data, const unsigned char *data, unsigned int len,
+ char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD5Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(MD5Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD5_return_length);
+ return ret;
+}
+
+#define INIT_MD5 \
+ COMMON_INTERCEPT_FUNCTION(MD5Init); \
+ COMMON_INTERCEPT_FUNCTION(MD5Update); \
+ COMMON_INTERCEPT_FUNCTION(MD5Final); \
+ COMMON_INTERCEPT_FUNCTION(MD5End); \
+ COMMON_INTERCEPT_FUNCTION(MD5File); \
+ COMMON_INTERCEPT_FUNCTION(MD5Data)
+#else
+#define INIT_MD5
+#endif
+
+#if SANITIZER_INTERCEPT_FSEEK
+INTERCEPTOR(int, fseek, __sanitizer_FILE *stream, long int offset, int whence) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fseek, stream, offset, whence);
+ return REAL(fseek)(stream, offset, whence);
+}
+INTERCEPTOR(int, fseeko, __sanitizer_FILE *stream, OFF_T offset, int whence) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fseeko, stream, offset, whence);
+ return REAL(fseeko)(stream, offset, whence);
+}
+INTERCEPTOR(long int, ftell, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, ftell, stream);
+ return REAL(ftell)(stream);
+}
+INTERCEPTOR(OFF_T, ftello, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, ftello, stream);
+ return REAL(ftello)(stream);
+}
+INTERCEPTOR(void, rewind, __sanitizer_FILE *stream) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, rewind, stream);
+ return REAL(rewind)(stream);
+}
+INTERCEPTOR(int, fgetpos, __sanitizer_FILE *stream, void *pos) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fgetpos, stream, pos);
+ int ret = REAL(fgetpos)(stream, pos);
+ if (pos && !ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pos, fpos_t_sz);
+ return ret;
+}
+INTERCEPTOR(int, fsetpos, __sanitizer_FILE *stream, const void *pos) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fsetpos, stream, pos);
+ if (pos)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pos, fpos_t_sz);
+ return REAL(fsetpos)(stream, pos);
+}
+#define INIT_FSEEK \
+ COMMON_INTERCEPT_FUNCTION(fseek); \
+ COMMON_INTERCEPT_FUNCTION(fseeko); \
+ COMMON_INTERCEPT_FUNCTION(ftell); \
+ COMMON_INTERCEPT_FUNCTION(ftello); \
+ COMMON_INTERCEPT_FUNCTION(rewind); \
+ COMMON_INTERCEPT_FUNCTION(fgetpos); \
+ COMMON_INTERCEPT_FUNCTION(fsetpos)
+#else
+#define INIT_FSEEK
+#endif
+
+#if SANITIZER_INTERCEPT_MD2
+INTERCEPTOR(void, MD2Init, void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Init, context);
+ REAL(MD2Init)(context);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD2_CTX_sz);
+}
+
+INTERCEPTOR(void, MD2Update, void *context, const unsigned char *data,
+ unsigned int len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Update, context, data, len);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD2_CTX_sz);
+ REAL(MD2Update)(context, data, len);
+ if (context)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, MD2_CTX_sz);
+}
+
+INTERCEPTOR(void, MD2Final, unsigned char digest[16], void *context) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Final, digest, context);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD2_CTX_sz);
+ REAL(MD2Final)(digest, context);
+ if (digest)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, sizeof(unsigned char) * 16);
+}
+
+INTERCEPTOR(char *, MD2End, void *context, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2End, context, buf);
+ if (context)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, MD2_CTX_sz);
+ char *ret = REAL(MD2End)(context, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD2_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD2File, const char *filename, char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2File, filename, buf);
+ if (filename)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);
+ char *ret = REAL(MD2File)(filename, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD2_return_length);
+ return ret;
+}
+
+INTERCEPTOR(char *, MD2Data, const unsigned char *data, unsigned int len,
+ char *buf) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, MD2Data, data, len, buf);
+ if (data && len > 0)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len);
+ char *ret = REAL(MD2Data)(data, len, buf);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, MD2_return_length);
+ return ret;
+}
+
+#define INIT_MD2 \
+ COMMON_INTERCEPT_FUNCTION(MD2Init); \
+ COMMON_INTERCEPT_FUNCTION(MD2Update); \
+ COMMON_INTERCEPT_FUNCTION(MD2Final); \
+ COMMON_INTERCEPT_FUNCTION(MD2End); \
+ COMMON_INTERCEPT_FUNCTION(MD2File); \
+ COMMON_INTERCEPT_FUNCTION(MD2Data)
+#else
+#define INIT_MD2
+#endif
+
+#if SANITIZER_INTERCEPT_SHA2
+#define SHA2_INTERCEPTORS(LEN, SHA2_STATE_T) \
+ INTERCEPTOR(void, SHA##LEN##_Init, void *context) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Init, context); \
+ REAL(SHA##LEN##_Init)(context); \
+ if (context) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ } \
+ INTERCEPTOR(void, SHA##LEN##_Update, void *context, \
+ const u8 *data, SIZE_T len) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Update, context, data, len); \
+ if (data && len > 0) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
+ if (context) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ REAL(SHA##LEN##_Update)(context, data, len); \
+ if (context) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ } \
+ INTERCEPTOR(void, SHA##LEN##_Final, u8 digest[LEN/8], \
+ void *context) { \
+ void *ctx; \
+ CHECK_EQ(SHA##LEN##_digest_length, LEN/8); \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Final, digest, context); \
+ if (context) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ REAL(SHA##LEN##_Final)(digest, context); \
+ if (digest) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, digest, \
+ sizeof(digest[0]) * \
+ SHA##LEN##_digest_length); \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_End, void *context, char *buf) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_End, context, buf); \
+ if (context) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, context, SHA##LEN##_CTX_sz); \
+ char *ret = REAL(SHA##LEN##_End)(context, buf); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_File, const char *filename, char *buf) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_File, filename, buf); \
+ if (filename) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);\
+ char *ret = REAL(SHA##LEN##_File)(filename, buf); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_FileChunk, const char *filename, char *buf, \
+ OFF_T offset, OFF_T length) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_FileChunk, filename, buf, offset, \
+ length); \
+ if (filename) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, filename, REAL(strlen)(filename) + 1);\
+ char *ret = REAL(SHA##LEN##_FileChunk)(filename, buf, offset, length); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ } \
+ INTERCEPTOR(char *, SHA##LEN##_Data, u8 *data, SIZE_T len, char *buf) { \
+ void *ctx; \
+ COMMON_INTERCEPTOR_ENTER(ctx, SHA##LEN##_Data, data, len, buf); \
+ if (data && len > 0) \
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, len); \
+ char *ret = REAL(SHA##LEN##_Data)(data, len, buf); \
+ if (ret) \
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, SHA##LEN##_return_length); \
+ return ret; \
+ }
+
+SHA2_INTERCEPTORS(224, u32);
+SHA2_INTERCEPTORS(256, u32);
+SHA2_INTERCEPTORS(384, u64);
+SHA2_INTERCEPTORS(512, u64);
+
+#define INIT_SHA2_INTECEPTORS(LEN) \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Init); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Update); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Final); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_End); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_File); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_FileChunk); \
+ COMMON_INTERCEPT_FUNCTION(SHA##LEN##_Data)
+
+#define INIT_SHA2 \
+ INIT_SHA2_INTECEPTORS(224); \
+ INIT_SHA2_INTECEPTORS(256); \
+ INIT_SHA2_INTECEPTORS(384); \
+ INIT_SHA2_INTECEPTORS(512)
+#undef SHA2_INTERCEPTORS
+#else
+#define INIT_SHA2
+#endif
+
+#if SANITIZER_INTERCEPT_VIS
+INTERCEPTOR(char *, vis, char *dst, int c, int flag, int nextc) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, vis, dst, c, flag, nextc);
+ char *end = REAL(vis)(dst, c, flag, nextc);
+ // dst is NULL terminated and end points to the NULL char
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, end - dst + 1);
+ return end;
+}
+INTERCEPTOR(char *, nvis, char *dst, SIZE_T dlen, int c, int flag, int nextc) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, nvis, dst, dlen, c, flag, nextc);
+ char *end = REAL(nvis)(dst, dlen, c, flag, nextc);
+ // nvis cannot make sure the dst is NULL terminated
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, end - dst + 1);
+ return end;
+}
+INTERCEPTOR(int, strvis, char *dst, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strvis, dst, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int len = REAL(strvis)(dst, src, flag);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, stravis, char **dst, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, stravis, dst, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int len = REAL(stravis)(dst, src, flag);
+ if (dst) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sizeof(char *));
+ if (*dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *dst, len + 1);
+ }
+ return len;
+}
+INTERCEPTOR(int, strnvis, char *dst, SIZE_T dlen, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnvis, dst, dlen, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int len = REAL(strnvis)(dst, dlen, src, flag);
+ // The interface will be valid even if there is no space for NULL char
+ if (dst && len > 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, strvisx, char *dst, const char *src, SIZE_T len, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strvisx, dst, src, len, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ int ret = REAL(strvisx)(dst, src, len, flag);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strnvisx, char *dst, SIZE_T dlen, const char *src, SIZE_T len,
+ int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnvisx, dst, dlen, src, len, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ int ret = REAL(strnvisx)(dst, dlen, src, len, flag);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strenvisx, char *dst, SIZE_T dlen, const char *src, SIZE_T len,
+ int flag, int *cerr_ptr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strenvisx, dst, dlen, src, len, flag, cerr_ptr);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ // FIXME: only need to be checked when "flag | VIS_NOLOCALE" doesn't hold
+ // according to the implementation
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cerr_ptr, sizeof(int));
+ int ret = REAL(strenvisx)(dst, dlen, src, len, flag, cerr_ptr);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cerr_ptr, sizeof(int));
+ return ret;
+}
+INTERCEPTOR(char *, svis, char *dst, int c, int flag, int nextc,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, svis, dst, c, flag, nextc, extra);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ char *end = REAL(svis)(dst, c, flag, nextc, extra);
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, end - dst + 1);
+ return end;
+}
+INTERCEPTOR(char *, snvis, char *dst, SIZE_T dlen, int c, int flag, int nextc,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, snvis, dst, dlen, c, flag, nextc, extra);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ char *end = REAL(snvis)(dst, dlen, c, flag, nextc, extra);
+ if (dst && end)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst,
+ Min((SIZE_T)(end - dst + 1), dlen));
+ return end;
+}
+INTERCEPTOR(int, strsvis, char *dst, const char *src, int flag,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsvis, dst, src, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int len = REAL(strsvis)(dst, src, flag, extra);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, strsnvis, char *dst, SIZE_T dlen, const char *src, int flag,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsnvis, dst, dlen, src, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int len = REAL(strsnvis)(dst, dlen, src, flag, extra);
+ // The interface will be valid even if there is no space for NULL char
+ if (dst && len >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, len + 1);
+ return len;
+}
+INTERCEPTOR(int, strsvisx, char *dst, const char *src, SIZE_T len, int flag,
+ const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsvisx, dst, src, len, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int ret = REAL(strsvisx)(dst, src, len, flag, extra);
+ if (dst)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strsnvisx, char *dst, SIZE_T dlen, const char *src, SIZE_T len,
+ int flag, const char *extra) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsnvisx, dst, dlen, src, len, flag, extra);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ int ret = REAL(strsnvisx)(dst, dlen, src, len, flag, extra);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strsenvisx, char *dst, SIZE_T dlen, const char *src,
+ SIZE_T len, int flag, const char *extra, int *cerr_ptr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strsenvisx, dst, dlen, src, len, flag, extra,
+ cerr_ptr);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, len);
+ if (extra)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, extra, REAL(strlen)(extra) + 1);
+ // FIXME: only need to be checked when "flag | VIS_NOLOCALE" doesn't hold
+ // according to the implementation
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cerr_ptr, sizeof(int));
+ int ret = REAL(strsenvisx)(dst, dlen, src, len, flag, extra, cerr_ptr);
+ if (dst && ret >= 0)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ if (cerr_ptr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cerr_ptr, sizeof(int));
+ return ret;
+}
+INTERCEPTOR(int, unvis, char *cp, int c, int *astate, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, unvis, cp, c, astate, flag);
+ if (astate)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, astate, sizeof(*astate));
+ int ret = REAL(unvis)(cp, c, astate, flag);
+ if (ret == unvis_valid || ret == unvis_validpush) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cp, sizeof(*cp));
+ }
+ return ret;
+}
+INTERCEPTOR(int, strunvis, char *dst, const char *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strunvis, dst, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strunvis)(dst, src);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strnunvis, char *dst, SIZE_T dlen, const char *src) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnunvis, dst, dlen, src);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strnunvis)(dst, dlen, src);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strunvisx, char *dst, const char *src, int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strunvisx, dst, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strunvisx)(dst, src, flag);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+INTERCEPTOR(int, strnunvisx, char *dst, SIZE_T dlen, const char *src,
+ int flag) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, strnunvisx, dst, dlen, src, flag);
+ if (src)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, REAL(strlen)(src) + 1);
+ int ret = REAL(strnunvisx)(dst, dlen, src, flag);
+ if (ret != -1)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, ret + 1);
+ return ret;
+}
+#define INIT_VIS \
+ COMMON_INTERCEPT_FUNCTION(vis); \
+ COMMON_INTERCEPT_FUNCTION(nvis); \
+ COMMON_INTERCEPT_FUNCTION(strvis); \
+ COMMON_INTERCEPT_FUNCTION(stravis); \
+ COMMON_INTERCEPT_FUNCTION(strnvis); \
+ COMMON_INTERCEPT_FUNCTION(strvisx); \
+ COMMON_INTERCEPT_FUNCTION(strnvisx); \
+ COMMON_INTERCEPT_FUNCTION(strenvisx); \
+ COMMON_INTERCEPT_FUNCTION(svis); \
+ COMMON_INTERCEPT_FUNCTION(snvis); \
+ COMMON_INTERCEPT_FUNCTION(strsvis); \
+ COMMON_INTERCEPT_FUNCTION(strsnvis); \
+ COMMON_INTERCEPT_FUNCTION(strsvisx); \
+ COMMON_INTERCEPT_FUNCTION(strsnvisx); \
+ COMMON_INTERCEPT_FUNCTION(strsenvisx); \
+ COMMON_INTERCEPT_FUNCTION(unvis); \
+ COMMON_INTERCEPT_FUNCTION(strunvis); \
+ COMMON_INTERCEPT_FUNCTION(strnunvis); \
+ COMMON_INTERCEPT_FUNCTION(strunvisx); \
+ COMMON_INTERCEPT_FUNCTION(strnunvisx)
+#else
+#define INIT_VIS
+#endif
+
+#if SANITIZER_INTERCEPT_CDB
+INTERCEPTOR(struct __sanitizer_cdbr *, cdbr_open, const char *path, int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_open, path, flags);
+ if (path)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ struct __sanitizer_cdbr *cdbr = REAL(cdbr_open)(path, flags);
+ if (cdbr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbr, sizeof(*cdbr));
+ return cdbr;
+}
+
+INTERCEPTOR(struct __sanitizer_cdbr *, cdbr_open_mem, void *base, SIZE_T size,
+ int flags, void (*unmap)(void *, void *, SIZE_T), void *cookie) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_open_mem, base, size, flags, unmap,
+ cookie);
+ if (base && size)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, base, size);
+ struct __sanitizer_cdbr *cdbr =
+ REAL(cdbr_open_mem)(base, size, flags, unmap, cookie);
+ if (cdbr)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbr, sizeof(*cdbr));
+ return cdbr;
+}
+
+INTERCEPTOR(u32, cdbr_entries, struct __sanitizer_cdbr *cdbr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_entries, cdbr);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ return REAL(cdbr_entries)(cdbr);
+}
+
+INTERCEPTOR(int, cdbr_get, struct __sanitizer_cdbr *cdbr, u32 index,
+ const void **data, SIZE_T *datalen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_get, cdbr, index, data, datalen);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ int ret = REAL(cdbr_get)(cdbr, index, data, datalen);
+ if (!ret) {
+ if (data)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(*data));
+ if (datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datalen, sizeof(*datalen));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *data, *datalen);
+ }
+ return ret;
+}
+
+INTERCEPTOR(int, cdbr_find, struct __sanitizer_cdbr *cdbr, const void *key,
+ SIZE_T keylen, const void **data, SIZE_T *datalen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_find, cdbr, key, keylen, data, datalen);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ if (key)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, keylen);
+ int ret = REAL(cdbr_find)(cdbr, key, keylen, data, datalen);
+ if (!ret) {
+ if (data)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(*data));
+ if (datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datalen, sizeof(*datalen));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *data, *datalen);
+ }
+ return ret;
+}
+
+INTERCEPTOR(void, cdbr_close, struct __sanitizer_cdbr *cdbr) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbr_close, cdbr);
+ if (cdbr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbr, sizeof(*cdbr));
+ REAL(cdbr_close)(cdbr);
+}
+
+INTERCEPTOR(struct __sanitizer_cdbw *, cdbw_open) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_open);
+ struct __sanitizer_cdbw *ret = REAL(cdbw_open)();
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, sizeof(*ret));
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_put, struct __sanitizer_cdbw *cdbw, const void *key,
+ SIZE_T keylen, const void *data, SIZE_T datalen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_put, cdbw, key, keylen, data, datalen);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, datalen);
+ if (key && keylen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, keylen);
+ int ret = REAL(cdbw_put)(cdbw, key, keylen, data, datalen);
+ if (!ret && cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_put_data, struct __sanitizer_cdbw *cdbw, const void *data,
+ SIZE_T datalen, u32 *index) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_put_data, cdbw, data, datalen, index);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (data && datalen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, data, datalen);
+ int ret = REAL(cdbw_put_data)(cdbw, data, datalen, index);
+ if (!ret) {
+ if (index)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, index, sizeof(*index));
+ if (cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ }
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_put_key, struct __sanitizer_cdbw *cdbw, const void *key,
+ SIZE_T keylen, u32 index) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_put_key, cdbw, key, keylen, index);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (key && keylen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, key, keylen);
+ int ret = REAL(cdbw_put_key)(cdbw, key, keylen, index);
+ if (!ret && cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ return ret;
+}
+
+INTERCEPTOR(int, cdbw_output, struct __sanitizer_cdbw *cdbw, int output,
+ const char descr[16], u32 (*seedgen)(void)) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_output, cdbw, output, descr, seedgen);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, output);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (descr)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, descr, internal_strnlen(descr, 16));
+ if (seedgen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, (void *)seedgen, sizeof(seedgen));
+ int ret = REAL(cdbw_output)(cdbw, output, descr, seedgen);
+ if (!ret) {
+ if (cdbw)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cdbw, sizeof(*cdbw));
+ if (output >= 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, output);
+ }
+ return ret;
+}
+
+INTERCEPTOR(void, cdbw_close, struct __sanitizer_cdbw *cdbw) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, cdbw_close, cdbw);
+ if (cdbw)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, cdbw, sizeof(*cdbw));
+ REAL(cdbw_close)(cdbw);
+}
+
+#define INIT_CDB \
+ COMMON_INTERCEPT_FUNCTION(cdbr_open); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_open_mem); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_entries); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_get); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_find); \
+ COMMON_INTERCEPT_FUNCTION(cdbr_close); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_open); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_put); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_put_data); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_put_key); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_output); \
+ COMMON_INTERCEPT_FUNCTION(cdbw_close)
+#else
+#define INIT_CDB
+#endif
+
+#if SANITIZER_INTERCEPT_GETFSENT
+INTERCEPTOR(void *, getfsent) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getfsent);
+ void *ret = REAL(getfsent)();
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, struct_fstab_sz);
+ return ret;
+}
+
+INTERCEPTOR(void *, getfsspec, const char *spec) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getfsspec, spec);
+ if (spec)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, spec, REAL(strlen)(spec) + 1);
+ void *ret = REAL(getfsspec)(spec);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, struct_fstab_sz);
+ return ret;
+}
+
+INTERCEPTOR(void *, getfsfile, const char *file) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getfsfile, file);
+ if (file)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, file, REAL(strlen)(file) + 1);
+ void *ret = REAL(getfsfile)(file);
+ if (ret)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ret, struct_fstab_sz);
+ return ret;
+}
+
+#define INIT_GETFSENT \
+ COMMON_INTERCEPT_FUNCTION(getfsent); \
+ COMMON_INTERCEPT_FUNCTION(getfsspec); \
+ COMMON_INTERCEPT_FUNCTION(getfsfile);
+#else
+#define INIT_GETFSENT
+#endif
+
+#if SANITIZER_INTERCEPT_ARC4RANDOM
+INTERCEPTOR(void, arc4random_buf, void *buf, SIZE_T len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, arc4random_buf, buf, len);
+ REAL(arc4random_buf)(buf, len);
+ if (buf && len)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, len);
+}
+
+INTERCEPTOR(void, arc4random_addrandom, u8 *dat, int datlen) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, arc4random_addrandom, dat, datlen);
+ if (dat && datlen)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, dat, datlen);
+ REAL(arc4random_addrandom)(dat, datlen);
+}
+
+#define INIT_ARC4RANDOM \
+ COMMON_INTERCEPT_FUNCTION(arc4random_buf); \
+ COMMON_INTERCEPT_FUNCTION(arc4random_addrandom);
+#else
+#define INIT_ARC4RANDOM
+#endif
+
+#if SANITIZER_INTERCEPT_POPEN
+INTERCEPTOR(__sanitizer_FILE *, popen, const char *command, const char *type) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, popen, command, type);
+ if (command)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, command, REAL(strlen)(command) + 1);
+ if (type)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, type, REAL(strlen)(type) + 1);
+ __sanitizer_FILE *res = REAL(popen)(command, type);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, nullptr);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_POPEN COMMON_INTERCEPT_FUNCTION(popen)
+#else
+#define INIT_POPEN
+#endif
+
+#if SANITIZER_INTERCEPT_POPENVE
+INTERCEPTOR(__sanitizer_FILE *, popenve, const char *path,
+ char *const *argv, char *const *envp, const char *type) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, popenve, path, argv, envp, type);
+ if (path)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1);
+ if (argv) {
+ for (char *const *pa = argv; ; ++pa) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **));
+ if (!*pa)
+ break;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1);
+ }
+ }
+ if (envp) {
+ for (char *const *pa = envp; ; ++pa) {
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, pa, sizeof(char **));
+ if (!*pa)
+ break;
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, *pa, REAL(strlen)(*pa) + 1);
+ }
+ }
+ if (type)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, type, REAL(strlen)(type) + 1);
+ __sanitizer_FILE *res = REAL(popenve)(path, argv, envp, type);
+ COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, nullptr);
+ if (res) unpoison_file(res);
+ return res;
+}
+#define INIT_POPENVE COMMON_INTERCEPT_FUNCTION(popenve)
+#else
+#define INIT_POPENVE
+#endif
+
+#if SANITIZER_INTERCEPT_PCLOSE
+INTERCEPTOR(int, pclose, __sanitizer_FILE *fp) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, pclose, fp);
+ COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp);
+ const FileMetadata *m = GetInterceptorMetadata(fp);
+ int res = REAL(pclose)(fp);
+ if (m) {
+ COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size);
+ DeleteInterceptorMetadata(fp);
+ }
+ return res;
+}
+#define INIT_PCLOSE COMMON_INTERCEPT_FUNCTION(pclose);
+#else
+#define INIT_PCLOSE
+#endif
+
+#if SANITIZER_INTERCEPT_FUNOPEN
+typedef int (*funopen_readfn)(void *cookie, char *buf, int len);
+typedef int (*funopen_writefn)(void *cookie, const char *buf, int len);
+typedef OFF_T (*funopen_seekfn)(void *cookie, OFF_T offset, int whence);
+typedef int (*funopen_closefn)(void *cookie);
+
+struct WrappedFunopenCookie {
+ void *real_cookie;
+ funopen_readfn real_read;
+ funopen_writefn real_write;
+ funopen_seekfn real_seek;
+ funopen_closefn real_close;
+};
+
+static int wrapped_funopen_read(void *cookie, char *buf, int len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_readfn real_read = wrapped_cookie->real_read;
+ return real_read(wrapped_cookie->real_cookie, buf, len);
+}
+
+static int wrapped_funopen_write(void *cookie, const char *buf, int len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_writefn real_write = wrapped_cookie->real_write;
+ return real_write(wrapped_cookie->real_cookie, buf, len);
+}
+
+static OFF_T wrapped_funopen_seek(void *cookie, OFF_T offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_seekfn real_seek = wrapped_cookie->real_seek;
+ return real_seek(wrapped_cookie->real_cookie, offset, whence);
+}
+
+static int wrapped_funopen_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedFunopenCookie *wrapped_cookie = (WrappedFunopenCookie *)cookie;
+ funopen_closefn real_close = wrapped_cookie->real_close;
+ int res = real_close(wrapped_cookie->real_cookie);
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, funopen, void *cookie, funopen_readfn readfn,
+ funopen_writefn writefn, funopen_seekfn seekfn,
+ funopen_closefn closefn) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, funopen, cookie, readfn, writefn, seekfn,
+ closefn);
+
+ WrappedFunopenCookie *wrapped_cookie =
+ (WrappedFunopenCookie *)InternalAlloc(sizeof(WrappedFunopenCookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_read = readfn;
+ wrapped_cookie->real_write = writefn;
+ wrapped_cookie->real_seek = seekfn;
+ wrapped_cookie->real_close = closefn;
+
+ __sanitizer_FILE *res =
+ REAL(funopen)(wrapped_cookie,
+ readfn ? wrapped_funopen_read : nullptr,
+ writefn ? wrapped_funopen_write : nullptr,
+ seekfn ? wrapped_funopen_seek : nullptr,
+ closefn ? wrapped_funopen_close : nullptr);
+ if (res)
+ unpoison_file(res);
+ return res;
+}
+#define INIT_FUNOPEN COMMON_INTERCEPT_FUNCTION(funopen)
+#else
+#define INIT_FUNOPEN
+#endif
+
+#if SANITIZER_INTERCEPT_FUNOPEN2
+typedef SSIZE_T (*funopen2_readfn)(void *cookie, void *buf, SIZE_T len);
+typedef SSIZE_T (*funopen2_writefn)(void *cookie, const void *buf, SIZE_T len);
+typedef OFF_T (*funopen2_seekfn)(void *cookie, OFF_T offset, int whence);
+typedef int (*funopen2_flushfn)(void *cookie);
+typedef int (*funopen2_closefn)(void *cookie);
+
+struct WrappedFunopen2Cookie {
+ void *real_cookie;
+ funopen2_readfn real_read;
+ funopen2_writefn real_write;
+ funopen2_seekfn real_seek;
+ funopen2_flushfn real_flush;
+ funopen2_closefn real_close;
+};
+
+static SSIZE_T wrapped_funopen2_read(void *cookie, void *buf, SIZE_T len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_readfn real_read = wrapped_cookie->real_read;
+ return real_read(wrapped_cookie->real_cookie, buf, len);
+}
+
+static SSIZE_T wrapped_funopen2_write(void *cookie, const void *buf,
+ SIZE_T len) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_writefn real_write = wrapped_cookie->real_write;
+ return real_write(wrapped_cookie->real_cookie, buf, len);
+}
+
+static OFF_T wrapped_funopen2_seek(void *cookie, OFF_T offset, int whence) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(3);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_seekfn real_seek = wrapped_cookie->real_seek;
+ return real_seek(wrapped_cookie->real_cookie, offset, whence);
+}
+
+static int wrapped_funopen2_flush(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_flushfn real_flush = wrapped_cookie->real_flush;
+ return real_flush(wrapped_cookie->real_cookie);
+}
+
+static int wrapped_funopen2_close(void *cookie) {
+ COMMON_INTERCEPTOR_UNPOISON_PARAM(1);
+ WrappedFunopen2Cookie *wrapped_cookie = (WrappedFunopen2Cookie *)cookie;
+ funopen2_closefn real_close = wrapped_cookie->real_close;
+ int res = real_close(wrapped_cookie->real_cookie);
+ InternalFree(wrapped_cookie);
+ return res;
+}
+
+INTERCEPTOR(__sanitizer_FILE *, funopen2, void *cookie, funopen2_readfn readfn,
+ funopen2_writefn writefn, funopen2_seekfn seekfn,
+ funopen2_flushfn flushfn, funopen2_closefn closefn) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, funopen2, cookie, readfn, writefn, seekfn,
+ flushfn, closefn);
+
+ WrappedFunopen2Cookie *wrapped_cookie =
+ (WrappedFunopen2Cookie *)InternalAlloc(sizeof(WrappedFunopen2Cookie));
+ wrapped_cookie->real_cookie = cookie;
+ wrapped_cookie->real_read = readfn;
+ wrapped_cookie->real_write = writefn;
+ wrapped_cookie->real_seek = seekfn;
+ wrapped_cookie->real_flush = flushfn;
+ wrapped_cookie->real_close = closefn;
+
+ __sanitizer_FILE *res =
+ REAL(funopen2)(wrapped_cookie,
+ readfn ? wrapped_funopen2_read : nullptr,
+ writefn ? wrapped_funopen2_write : nullptr,
+ seekfn ? wrapped_funopen2_seek : nullptr,
+ flushfn ? wrapped_funopen2_flush : nullptr,
+ closefn ? wrapped_funopen2_close : nullptr);
+ if (res)
+ unpoison_file(res);
+ return res;
+}
+#define INIT_FUNOPEN2 COMMON_INTERCEPT_FUNCTION(funopen2)
+#else
+#define INIT_FUNOPEN2
+#endif
+
+#if SANITIZER_INTERCEPT_FDEVNAME
+INTERCEPTOR(char *, fdevname, int fd) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fdevname, fd);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ char *name = REAL(fdevname)(fd);
+ if (name) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, REAL(strlen)(name) + 1);
+ if (fd > 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
+ return name;
+}
+
+INTERCEPTOR(char *, fdevname_r, int fd, char *buf, SIZE_T len) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, fdevname_r, fd, buf, len);
+ COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd);
+ char *name = REAL(fdevname_r)(fd, buf, len);
+ if (name && buf && len > 0) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1);
+ if (fd > 0)
+ COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd);
+ }
+ return name;
+}
+
+#define INIT_FDEVNAME \
+ COMMON_INTERCEPT_FUNCTION(fdevname); \
+ COMMON_INTERCEPT_FUNCTION(fdevname_r);
+#else
+#define INIT_FDEVNAME
+#endif
+
+#if SANITIZER_INTERCEPT_GETUSERSHELL
+INTERCEPTOR(char *, getusershell) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getusershell);
+ char *res = REAL(getusershell)();
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ return res;
+}
+
+#define INIT_GETUSERSHELL COMMON_INTERCEPT_FUNCTION(getusershell);
+#else
+#define INIT_GETUSERSHELL
+#endif
+
+#if SANITIZER_INTERCEPT_SL_INIT
+INTERCEPTOR(void *, sl_init) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_init);
+ void *res = REAL(sl_init)();
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, __sanitizer::struct_StringList_sz);
+ return res;
+}
+
+INTERCEPTOR(int, sl_add, void *sl, char *item) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_add, sl, item);
+ if (sl)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ if (item)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, item, REAL(strlen)(item) + 1);
+ int res = REAL(sl_add)(sl, item);
+ if (!res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ return res;
+}
+
+INTERCEPTOR(char *, sl_find, void *sl, const char *item) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_find, sl, item);
+ if (sl)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ if (item)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, item, REAL(strlen)(item) + 1);
+ char *res = REAL(sl_find)(sl, item);
+ if (res)
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1);
+ return res;
+}
+
+INTERCEPTOR(void, sl_free, void *sl, int freeall) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, sl_free, sl, freeall);
+ if (sl)
+ COMMON_INTERCEPTOR_READ_RANGE(ctx, sl, __sanitizer::struct_StringList_sz);
+ REAL(sl_free)(sl, freeall);
+}
+
+#define INIT_SL_INIT \
+ COMMON_INTERCEPT_FUNCTION(sl_init); \
+ COMMON_INTERCEPT_FUNCTION(sl_add); \
+ COMMON_INTERCEPT_FUNCTION(sl_find); \
+ COMMON_INTERCEPT_FUNCTION(sl_free);
+#else
+#define INIT_SL_INIT
+#endif
+
+#if SANITIZER_INTERCEPT_GETRANDOM
+INTERCEPTOR(SSIZE_T, getrandom, void *buf, SIZE_T buflen, unsigned int flags) {
+ void *ctx;
+ COMMON_INTERCEPTOR_ENTER(ctx, getrandom, buf, buflen, flags);
+ SSIZE_T n = REAL(getrandom)(buf, buflen, flags);
+ if (n > 0) {
+ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, n);
+ }
+ return n;
+}
+#define INIT_GETRANDOM COMMON_INTERCEPT_FUNCTION(getrandom)
+#else
+#define INIT_GETRANDOM
+#endif
+
+static void InitializeCommonInterceptors() {
+#if SI_POSIX
+ static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1];
+ interceptor_metadata_map =
+ new ((void *)&metadata_mem) MetadataHashMap(); // NOLINT
+#endif
INIT_MMAP;
INIT_MMAP64;
INIT_MEMCPY;
INIT_MEMCHR;
INIT_MEMCMP;
+ INIT_BCMP;
INIT_MEMRCHR;
INIT_MEMMEM;
INIT_READ;
INIT_GETPWENT;
INIT_FGETPWENT;
INIT_GETPWENT_R;
+ INIT_FGETPWENT_R;
+ INIT_FGETGRENT_R;
INIT_SETPWENT;
INIT_CLOCK_GETTIME;
INIT_GETITIMER;
INIT_WCSTOMBS;
INIT_WCSNRTOMBS;
INIT_WCRTOMB;
+ INIT_WCTOMB;
INIT_TCGETATTR;
INIT_REALPATH;
INIT_CANONICALIZE_FILE_NAME;
INIT_SIGSETOPS;
INIT_SIGPENDING;
INIT_SIGPROCMASK;
+ INIT_PTHREAD_SIGMASK;
INIT_BACKTRACE;
INIT__EXIT;
INIT_PTHREAD_MUTEX_LOCK;
INIT_PTHREAD_BARRIERATTR_GETPSHARED;
INIT_TMPNAM;
INIT_TMPNAM_R;
+ INIT_TTYNAME;
INIT_TTYNAME_R;
INIT_TEMPNAM;
INIT_PTHREAD_SETNAME_NP;
INIT_PTHREAD_GETNAME_NP;
INIT_SINCOS;
INIT_REMQUO;
+ INIT_REMQUOL;
INIT_LGAMMA;
+ INIT_LGAMMAL;
INIT_LGAMMA_R;
INIT_LGAMMAL_R;
INIT_DRAND48_R;
INIT_CAPGET;
INIT_AEABI_MEM;
INIT___BZERO;
+ INIT_BZERO;
INIT_FTIME;
INIT_XDR;
INIT_TSEARCH;
INIT_GETLOADAVG;
INIT_WCSLEN;
INIT_WCSCAT;
+ INIT_WCSDUP;
INIT_WCSXFRM;
INIT___WCSXFRM_L;
INIT_ACCT;
INIT_TTYENT;
INIT_PROTOENT;
INIT_NETENT;
+ INIT_GETMNTINFO;
+ INIT_MI_VECTOR_HASH;
+ INIT_SETVBUF;
+ INIT_GETVFSSTAT;
+ INIT_REGEX;
+ INIT_REGEXSUB;
+ INIT_FTS;
+ INIT_SYSCTL;
+ INIT_ASYSCTL;
+ INIT_SYSCTLGETMIBINFO;
+ INIT_NL_LANGINFO;
+ INIT_MODCTL;
+ INIT_STRTONUM;
+ INIT_FPARSELN;
+ INIT_STATVFS1;
+ INIT_STRTOI;
+ INIT_CAPSICUM;
+ INIT_SHA1;
+ INIT_MD4;
+ INIT_RMD160;
+ INIT_MD5;
+ INIT_FSEEK;
+ INIT_MD2;
+ INIT_SHA2;
+ INIT_VIS;
+ INIT_CDB;
+ INIT_GETFSENT;
+ INIT_ARC4RANDOM;
+ INIT_POPEN;
+ INIT_POPENVE;
+ INIT_PCLOSE;
+ INIT_FUNOPEN;
+ INIT_FUNOPEN2;
+ INIT_FDEVNAME;
+ INIT_GETUSERSHELL;
+ INIT_SL_INIT;
+ INIT_GETRANDOM;
INIT___PRINTF_CHK;
}
//===-- sanitizer_common_interceptors_format.inc ----------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_common_interceptors_ioctl.inc -----------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
--- /dev/null
+#if defined(__aarch64__) && defined(__linux__)
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA)
+
+.comm _ZN14__interception10real_vforkE,8,8
+.globl ASM_WRAPPER_NAME(vfork)
+ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
+ASM_WRAPPER_NAME(vfork):
+ // Save x30 in the off-stack spill area.
+ stp xzr, x30, [sp, #-16]!
+ bl COMMON_INTERCEPTOR_SPILL_AREA
+ ldp xzr, x30, [sp], 16
+ str x30, [x0]
+
+ // Call real vfork. This may return twice. User code that runs between the first and the second return
+ // may clobber the stack frame of the interceptor; that's why it does not have a frame.
+ adrp x0, _ZN14__interception10real_vforkE
+ ldr x0, [x0, :lo12:_ZN14__interception10real_vforkE]
+ blr x0
+
+ stp x0, xzr, [sp, #-16]!
+ cmp x0, #0
+ b.eq .L_exit
+
+ // x0 != 0 => parent process. Clear stack shadow.
+ add x0, sp, #16
+ bl COMMON_INTERCEPTOR_HANDLE_VFORK
+
+.L_exit:
+ // Restore x30.
+ bl COMMON_INTERCEPTOR_SPILL_AREA
+ ldr x30, [x0]
+ ldp x0, xzr, [sp], 16
+
+ ret
+ASM_SIZE(vfork)
+
+.weak vfork
+.set vfork, ASM_WRAPPER_NAME(vfork)
+
+#endif
--- /dev/null
+#if defined(__arm__) && defined(__linux__)
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA)
+
+.comm _ZN14__interception10real_vforkE,4,4
+.globl ASM_WRAPPER_NAME(vfork)
+ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
+ASM_WRAPPER_NAME(vfork):
+ // Save LR in the off-stack spill area.
+ push {r4, lr}
+ bl COMMON_INTERCEPTOR_SPILL_AREA
+ pop {r4, lr}
+ str lr, [r0]
+
+ // Call real vfork. This may return twice. User code that runs between the first and the second return
+ // may clobber the stack frame of the interceptor; that's why it does not have a frame.
+ ldr r0, .LCPI0_0
+.LPC0_0:
+ ldr r0, [pc, r0]
+ mov lr, pc
+ bx r0
+
+ push {r0, r4}
+ cmp r0, #0
+ beq .L_exit
+
+ // r0 != 0 => parent process. Clear stack shadow.
+ add r0, sp, #8
+ bl COMMON_INTERCEPTOR_HANDLE_VFORK
+
+.L_exit:
+ // Restore LR.
+ bl COMMON_INTERCEPTOR_SPILL_AREA
+ ldr lr, [r0]
+ pop {r0, r4}
+
+ mov pc, lr
+
+.LCPI0_0:
+ .long _ZN14__interception10real_vforkE - (.LPC0_0+8)
+
+ASM_SIZE(vfork)
+
+.weak vfork
+.set vfork, ASM_WRAPPER_NAME(vfork)
+
+#endif
--- /dev/null
+#if defined(__i386__) && defined(__linux__)
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+.comm _ZN14__interception10real_vforkE,4,4
+.globl ASM_WRAPPER_NAME(vfork)
+ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
+ASM_WRAPPER_NAME(vfork):
+ // Store return address in the spill area and tear down the stack frame.
+ sub $12, %esp
+ call COMMON_INTERCEPTOR_SPILL_AREA
+ mov 12(%esp), %ecx
+ mov %ecx, (%eax)
+ add $16, %esp
+
+ call .L0$pb
+.L0$pb:
+ pop %eax
+.Ltmp0:
+ add $_GLOBAL_OFFSET_TABLE_+(.Ltmp0-.L0$pb), %eax
+ call *_ZN14__interception10real_vforkE@GOTOFF(%eax)
+
+ // Restore the stack frame.
+ // 12(%esp) return address
+ // 8(%esp) spill %ebx
+ // 4(%esp) spill REAL(vfork) return value
+ // (%esp) call frame (arg0) for __*_handle_vfork
+ sub $16, %esp
+ mov %ebx, 8(%esp)
+ mov %eax, 4(%esp)
+
+ // Form GOT address in %ebx.
+ call .L1$pb
+.L1$pb:
+ pop %ebx
+.Ltmp1:
+ add $_GLOBAL_OFFSET_TABLE_+(.Ltmp1-.L1$pb), %ebx
+
+ // Restore original return address.
+ call COMMON_INTERCEPTOR_SPILL_AREA
+ mov (%eax), %ecx
+ mov %ecx, 12(%esp)
+ mov 4(%esp), %eax
+
+ // Call handle_vfork in the parent process (%rax != 0).
+ test %eax, %eax
+ je .L_exit
+
+ lea 16(%esp), %ecx
+ mov %ecx, (%esp)
+ call COMMON_INTERCEPTOR_HANDLE_VFORK@PLT
+
+.L_exit:
+ mov 4(%esp), %eax
+ mov 8(%esp), %ebx
+ add $12, %esp
+ ret
+ASM_SIZE(vfork)
+
+.weak vfork
+.set vfork, ASM_WRAPPER_NAME(vfork)
+
+#endif
--- /dev/null
+#if defined(__x86_64__) && defined(__linux__)
+
+#include "sanitizer_common/sanitizer_asm.h"
+
+.comm _ZN14__interception10real_vforkE,8,8
+.globl ASM_WRAPPER_NAME(vfork)
+ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork))
+ASM_WRAPPER_NAME(vfork):
+ // Store return address in the spill area and tear down the stack frame.
+ push %rcx
+ call COMMON_INTERCEPTOR_SPILL_AREA
+ pop %rcx
+ pop %rdi
+ mov %rdi, (%rax)
+
+ call *_ZN14__interception10real_vforkE(%rip)
+
+ // Restore return address from the spill area.
+ push %rcx
+ push %rax
+ call COMMON_INTERCEPTOR_SPILL_AREA
+ mov (%rax), %rdx
+ mov %rdx, 8(%rsp)
+ mov (%rsp), %rax
+
+ // Call handle_vfork in the parent process (%rax != 0).
+ test %rax, %rax
+ je .L_exit
+
+ lea 16(%rsp), %rdi
+ call COMMON_INTERCEPTOR_HANDLE_VFORK@PLT
+
+.L_exit:
+ pop %rax
+ ret
+ASM_SIZE(vfork)
+
+.weak vfork
+.set vfork, ASM_WRAPPER_NAME(vfork)
+
+#endif
//===-- sanitizer_common_interface.inc ------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Sanitizer Common interface list.
//===-- sanitizer_common_interface_posix.inc ------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Sanitizer Common interface list only available for Posix systems.
+++ /dev/null
-//===-- sanitizer_common_libcdep.cc ---------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator_interface.h"
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_procmaps.h"
-
-
-namespace __sanitizer {
-
-static void (*SoftRssLimitExceededCallback)(bool exceeded);
-void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
- CHECK_EQ(SoftRssLimitExceededCallback, nullptr);
- SoftRssLimitExceededCallback = Callback;
-}
-
-#if SANITIZER_LINUX && !SANITIZER_GO
-// Weak default implementation for when sanitizer_stackdepot is not linked in.
-SANITIZER_WEAK_ATTRIBUTE StackDepotStats *StackDepotGetStats() {
- return nullptr;
-}
-
-void BackgroundThread(void *arg) {
- const uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
- const uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
- const bool heap_profile = common_flags()->heap_profile;
- uptr prev_reported_rss = 0;
- uptr prev_reported_stack_depot_size = 0;
- bool reached_soft_rss_limit = false;
- uptr rss_during_last_reported_profile = 0;
- while (true) {
- SleepForMillis(100);
- const uptr current_rss_mb = GetRSS() >> 20;
- if (Verbosity()) {
- // If RSS has grown 10% since last time, print some information.
- if (prev_reported_rss * 11 / 10 < current_rss_mb) {
- Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
- prev_reported_rss = current_rss_mb;
- }
- // If stack depot has grown 10% since last time, print it too.
- StackDepotStats *stack_depot_stats = StackDepotGetStats();
- if (stack_depot_stats) {
- if (prev_reported_stack_depot_size * 11 / 10 <
- stack_depot_stats->allocated) {
- Printf("%s: StackDepot: %zd ids; %zdM allocated\n",
- SanitizerToolName,
- stack_depot_stats->n_uniq_ids,
- stack_depot_stats->allocated >> 20);
- prev_reported_stack_depot_size = stack_depot_stats->allocated;
- }
- }
- }
- // Check RSS against the limit.
- if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
- Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
- SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
- DumpProcessMap();
- Die();
- }
- if (soft_rss_limit_mb) {
- if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
- reached_soft_rss_limit = true;
- Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
- SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
- if (SoftRssLimitExceededCallback)
- SoftRssLimitExceededCallback(true);
- } else if (soft_rss_limit_mb >= current_rss_mb &&
- reached_soft_rss_limit) {
- reached_soft_rss_limit = false;
- if (SoftRssLimitExceededCallback)
- SoftRssLimitExceededCallback(false);
- }
- }
- if (heap_profile &&
- current_rss_mb > rss_during_last_reported_profile * 1.1) {
- Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb);
- __sanitizer_print_memory_profile(90, 20);
- rss_during_last_reported_profile = current_rss_mb;
- }
- }
-}
-#endif
-
-void WriteToSyslog(const char *msg) {
- InternalScopedString msg_copy(kErrorMessageBufferSize);
- msg_copy.append("%s", msg);
- char *p = msg_copy.data();
- char *q;
-
- // Print one line at a time.
- // syslog, at least on Android, has an implicit message length limit.
- while ((q = internal_strchr(p, '\n'))) {
- *q = '\0';
- WriteOneLineToSyslog(p);
- p = q + 1;
- }
- // Print remaining characters, if there are any.
- // Note that this will add an extra newline at the end.
- // FIXME: buffer extra output. This would need a thread-local buffer, which
- // on Android requires plugging into the tools (ex. ASan's) Thread class.
- if (*p)
- WriteOneLineToSyslog(p);
-}
-
-void MaybeStartBackgroudThread() {
-#if SANITIZER_LINUX && \
- !SANITIZER_GO // Need to implement/test on other platforms.
- // Start the background thread if one of the rss limits is given.
- if (!common_flags()->hard_rss_limit_mb &&
- !common_flags()->soft_rss_limit_mb &&
- !common_flags()->heap_profile) return;
- if (!&real_pthread_create) return; // Can't spawn the thread anyway.
- internal_start_thread(BackgroundThread, nullptr);
-#endif
-}
-
-static void (*sandboxing_callback)();
-void SetSandboxingCallback(void (*f)()) {
- sandboxing_callback = f;
-}
-
-} // namespace __sanitizer
-
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
- __sanitizer_sandbox_arguments *args) {
- __sanitizer::PlatformPrepareForSandboxing(args);
- if (__sanitizer::sandboxing_callback)
- __sanitizer::sandboxing_callback();
-}
--- /dev/null
+//===-- sanitizer_common_libcdep.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_interface.h"
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_procmaps.h"
+
+
+namespace __sanitizer {
+
+static void (*SoftRssLimitExceededCallback)(bool exceeded);
+void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) {
+ CHECK_EQ(SoftRssLimitExceededCallback, nullptr);
+ SoftRssLimitExceededCallback = Callback;
+}
+
+#if (SANITIZER_LINUX || SANITIZER_NETBSD) && !SANITIZER_GO
+// Weak default implementation for when sanitizer_stackdepot is not linked in.
+SANITIZER_WEAK_ATTRIBUTE StackDepotStats *StackDepotGetStats() {
+ return nullptr;
+}
+
+void BackgroundThread(void *arg) {
+ const uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb;
+ const uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb;
+ const bool heap_profile = common_flags()->heap_profile;
+ uptr prev_reported_rss = 0;
+ uptr prev_reported_stack_depot_size = 0;
+ bool reached_soft_rss_limit = false;
+ uptr rss_during_last_reported_profile = 0;
+ while (true) {
+ SleepForMillis(100);
+ const uptr current_rss_mb = GetRSS() >> 20;
+ if (Verbosity()) {
+ // If RSS has grown 10% since last time, print some information.
+ if (prev_reported_rss * 11 / 10 < current_rss_mb) {
+ Printf("%s: RSS: %zdMb\n", SanitizerToolName, current_rss_mb);
+ prev_reported_rss = current_rss_mb;
+ }
+ // If stack depot has grown 10% since last time, print it too.
+ StackDepotStats *stack_depot_stats = StackDepotGetStats();
+ if (stack_depot_stats) {
+ if (prev_reported_stack_depot_size * 11 / 10 <
+ stack_depot_stats->allocated) {
+ Printf("%s: StackDepot: %zd ids; %zdM allocated\n",
+ SanitizerToolName,
+ stack_depot_stats->n_uniq_ids,
+ stack_depot_stats->allocated >> 20);
+ prev_reported_stack_depot_size = stack_depot_stats->allocated;
+ }
+ }
+ }
+ // Check RSS against the limit.
+ if (hard_rss_limit_mb && hard_rss_limit_mb < current_rss_mb) {
+ Report("%s: hard rss limit exhausted (%zdMb vs %zdMb)\n",
+ SanitizerToolName, hard_rss_limit_mb, current_rss_mb);
+ DumpProcessMap();
+ Die();
+ }
+ if (soft_rss_limit_mb) {
+ if (soft_rss_limit_mb < current_rss_mb && !reached_soft_rss_limit) {
+ reached_soft_rss_limit = true;
+ Report("%s: soft rss limit exhausted (%zdMb vs %zdMb)\n",
+ SanitizerToolName, soft_rss_limit_mb, current_rss_mb);
+ if (SoftRssLimitExceededCallback)
+ SoftRssLimitExceededCallback(true);
+ } else if (soft_rss_limit_mb >= current_rss_mb &&
+ reached_soft_rss_limit) {
+ reached_soft_rss_limit = false;
+ if (SoftRssLimitExceededCallback)
+ SoftRssLimitExceededCallback(false);
+ }
+ }
+ if (heap_profile &&
+ current_rss_mb > rss_during_last_reported_profile * 1.1) {
+ Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb);
+ __sanitizer_print_memory_profile(90, 20);
+ rss_during_last_reported_profile = current_rss_mb;
+ }
+ }
+}
+#endif
+
+void WriteToSyslog(const char *msg) {
+ InternalScopedString msg_copy(kErrorMessageBufferSize);
+ msg_copy.append("%s", msg);
+ char *p = msg_copy.data();
+ char *q;
+
+ // Print one line at a time.
+ // syslog, at least on Android, has an implicit message length limit.
+ while ((q = internal_strchr(p, '\n'))) {
+ *q = '\0';
+ WriteOneLineToSyslog(p);
+ p = q + 1;
+ }
+ // Print remaining characters, if there are any.
+ // Note that this will add an extra newline at the end.
+ // FIXME: buffer extra output. This would need a thread-local buffer, which
+ // on Android requires plugging into the tools (ex. ASan's) Thread class.
+ if (*p)
+ WriteOneLineToSyslog(p);
+}
+
+void MaybeStartBackgroudThread() {
+#if (SANITIZER_LINUX || SANITIZER_NETBSD) && \
+ !SANITIZER_GO // Need to implement/test on other platforms.
+ // Start the background thread if one of the rss limits is given.
+ if (!common_flags()->hard_rss_limit_mb &&
+ !common_flags()->soft_rss_limit_mb &&
+ !common_flags()->heap_profile) return;
+ if (!&real_pthread_create) return; // Can't spawn the thread anyway.
+ internal_start_thread(BackgroundThread, nullptr);
+#endif
+}
+
+static void (*sandboxing_callback)();
+void SetSandboxingCallback(void (*f)()) {
+ sandboxing_callback = f;
+}
+
+} // namespace __sanitizer
+
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_sandbox_on_notify,
+ __sanitizer_sandbox_arguments *args) {
+ __sanitizer::PlatformPrepareForSandboxing(args);
+ if (__sanitizer::sandboxing_callback)
+ __sanitizer::sandboxing_callback();
+}
+++ /dev/null
-//===-- sanitizer_common_nolibc.cc ----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains stubs for libc function to facilitate optional use of
-// libc in no-libcdep sources.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-
-namespace __sanitizer {
-
-// The Windows implementations of these functions use the win32 API directly,
-// bypassing libc.
-#if !SANITIZER_WINDOWS
-#if SANITIZER_LINUX
-void LogMessageOnPrintf(const char *str) {}
-#endif
-void WriteToSyslog(const char *buffer) {}
-void Abort() { internal__exit(1); }
-void SleepForSeconds(int seconds) { internal_sleep(seconds); }
-#endif // !SANITIZER_WINDOWS
-
-#if !SANITIZER_WINDOWS && !SANITIZER_MAC
-void ListOfModules::init() {}
-#endif
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_common_nolibc.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains stubs for libc function to facilitate optional use of
+// libc in no-libcdep sources.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+
+namespace __sanitizer {
+
+// The Windows implementations of these functions use the win32 API directly,
+// bypassing libc.
+#if !SANITIZER_WINDOWS
+#if SANITIZER_LINUX
+void LogMessageOnPrintf(const char *str) {}
+#endif
+void WriteToSyslog(const char *buffer) {}
+void Abort() { internal__exit(1); }
+void SleepForSeconds(int seconds) { internal_sleep(seconds); }
+#endif // !SANITIZER_WINDOWS
+
+#if !SANITIZER_WINDOWS && !SANITIZER_MAC
+void ListOfModules::init() {}
+#endif
+
+} // namespace __sanitizer
//===-- sanitizer_common_syscalls.inc ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
POST_WRITE(oldact, oldact_sz);
}
}
+
+PRE_SYSCALL(getrandom)(void *buf, uptr count, long flags) {
+ if (buf) {
+ PRE_WRITE(buf, count);
+ }
+}
+
+POST_SYSCALL(getrandom)(long res, void *buf, uptr count, long flags) {
+ if (res > 0 && buf) {
+ POST_WRITE(buf, res);
+ }
+}
} // extern "C"
#undef PRE_SYSCALL
+++ /dev/null
-//===-- sanitizer_coverage_fuchsia.cc -------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Sanitizer Coverage Controller for Trace PC Guard, Fuchsia-specific version.
-//
-// This Fuchsia-specific implementation uses the same basic scheme and the
-// same simple '.sancov' file format as the generic implementation. The
-// difference is that we just produce a single blob of output for the whole
-// program, not a separate one per DSO. We do not sort the PC table and do
-// not prune the zeros, so the resulting file is always as large as it
-// would be to report 100% coverage. Implicit tracing information about
-// the address ranges of DSOs allows offline tools to split the one big
-// blob into separate files that the 'sancov' tool can understand.
-//
-// Unlike the traditional implementation that uses an atexit hook to write
-// out data files at the end, the results on Fuchsia do not go into a file
-// per se. The 'coverage_dir' option is ignored. Instead, they are stored
-// directly into a shared memory object (a Zircon VMO). At exit, that VMO
-// is handed over to a system service that's responsible for getting the
-// data out to somewhere that it can be fed into the sancov tool (where and
-// how is not our problem).
-
-#include "sanitizer_platform.h"
-#if SANITIZER_FUCHSIA
-#include "sanitizer_atomic.h"
-#include "sanitizer_common.h"
-#include "sanitizer_internal_defs.h"
-
-#include <zircon/process.h>
-#include <zircon/sanitizer.h>
-#include <zircon/syscalls.h>
-
-using namespace __sanitizer; // NOLINT
-
-namespace __sancov {
-namespace {
-
-// TODO(mcgrathr): Move the constant into a header shared with other impls.
-constexpr u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL;
-static_assert(SANITIZER_WORDSIZE == 64, "Fuchsia is always LP64");
-
-constexpr const char kSancovSinkName[] = "sancov";
-
-// Collects trace-pc guard coverage.
-// This class relies on zero-initialization.
-class TracePcGuardController final {
- public:
- // For each PC location being tracked, there is a u32 reserved in global
- // data called the "guard". At startup, we assign each guard slot a
- // unique index into the big results array. Later during runtime, the
- // first call to TracePcGuard (below) will store the corresponding PC at
- // that index in the array. (Each later call with the same guard slot is
- // presumed to be from the same PC.) Then it clears the guard slot back
- // to zero, which tells the compiler not to bother calling in again. At
- // the end of the run, we have a big array where each element is either
- // zero or is a tracked PC location that was hit in the trace.
-
- // This is called from global constructors. Each translation unit has a
- // contiguous array of guard slots, and a constructor that calls here
- // with the bounds of its array. Those constructors are allowed to call
- // here more than once for the same array. Usually all of these
- // constructors run in the initial thread, but it's possible that a
- // dlopen call on a secondary thread will run constructors that get here.
- void InitTracePcGuard(u32 *start, u32 *end) {
- if (end > start && *start == 0 && common_flags()->coverage) {
- // Complete the setup before filling in any guards with indices.
- // This avoids the possibility of code called from Setup reentering
- // TracePcGuard.
- u32 idx = Setup(end - start);
- for (u32 *p = start; p < end; ++p) {
- *p = idx++;
- }
- }
- }
-
- void TracePcGuard(u32 *guard, uptr pc) {
- atomic_uint32_t *guard_ptr = reinterpret_cast<atomic_uint32_t *>(guard);
- u32 idx = atomic_exchange(guard_ptr, 0, memory_order_relaxed);
- if (idx > 0) array_[idx] = pc;
- }
-
- void Dump() {
- BlockingMutexLock locked(&setup_lock_);
- if (array_) {
- CHECK_NE(vmo_, ZX_HANDLE_INVALID);
-
- // Publish the VMO to the system, where it can be collected and
- // analyzed after this process exits. This always consumes the VMO
- // handle. Any failure is just logged and not indicated to us.
- __sanitizer_publish_data(kSancovSinkName, vmo_);
- vmo_ = ZX_HANDLE_INVALID;
-
- // This will route to __sanitizer_log_write, which will ensure that
- // information about shared libraries is written out. This message
- // uses the `dumpfile` symbolizer markup element to highlight the
- // dump. See the explanation for this in:
- // https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
- Printf("SanitizerCoverage: {{{dumpfile:%s:%s}}} with up to %u PCs\n",
- kSancovSinkName, vmo_name_, next_index_ - 1);
- }
- }
-
- private:
- // We map in the largest possible view into the VMO: one word
- // for every possible 32-bit index value. This avoids the need
- // to change the mapping when increasing the size of the VMO.
- // We can always spare the 32G of address space.
- static constexpr size_t MappingSize = sizeof(uptr) << 32;
-
- BlockingMutex setup_lock_ = BlockingMutex(LINKER_INITIALIZED);
- uptr *array_ = nullptr;
- u32 next_index_ = 0;
- zx_handle_t vmo_ = {};
- char vmo_name_[ZX_MAX_NAME_LEN] = {};
-
- size_t DataSize() const { return next_index_ * sizeof(uintptr_t); }
-
- u32 Setup(u32 num_guards) {
- BlockingMutexLock locked(&setup_lock_);
- DCHECK(common_flags()->coverage);
-
- if (next_index_ == 0) {
- CHECK_EQ(vmo_, ZX_HANDLE_INVALID);
- CHECK_EQ(array_, nullptr);
-
- // The first sample goes at [1] to reserve [0] for the magic number.
- next_index_ = 1 + num_guards;
-
- zx_status_t status = _zx_vmo_create(DataSize(), 0, &vmo_);
- CHECK_EQ(status, ZX_OK);
-
- // Give the VMO a name including our process KOID so it's easy to spot.
- internal_snprintf(vmo_name_, sizeof(vmo_name_), "%s.%zu", kSancovSinkName,
- internal_getpid());
- _zx_object_set_property(vmo_, ZX_PROP_NAME, vmo_name_,
- internal_strlen(vmo_name_));
-
- // Map the largest possible view we might need into the VMO. Later
- // we might need to increase the VMO's size before we can use larger
- // indices, but we'll never move the mapping address so we don't have
- // any multi-thread synchronization issues with that.
- uintptr_t mapping;
- status =
- _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
- 0, vmo_, 0, MappingSize, &mapping);
- CHECK_EQ(status, ZX_OK);
-
- // Hereafter other threads are free to start storing into
- // elements [1, next_index_) of the big array.
- array_ = reinterpret_cast<uptr *>(mapping);
-
- // Store the magic number.
- // Hereafter, the VMO serves as the contents of the '.sancov' file.
- array_[0] = Magic64;
-
- return 1;
- } else {
- // The VMO is already mapped in, but it's not big enough to use the
- // new indices. So increase the size to cover the new maximum index.
-
- CHECK_NE(vmo_, ZX_HANDLE_INVALID);
- CHECK_NE(array_, nullptr);
-
- uint32_t first_index = next_index_;
- next_index_ += num_guards;
-
- zx_status_t status = _zx_vmo_set_size(vmo_, DataSize());
- CHECK_EQ(status, ZX_OK);
-
- return first_index;
- }
- }
-};
-
-static TracePcGuardController pc_guard_controller;
-
-} // namespace
-} // namespace __sancov
-
-namespace __sanitizer {
-void InitializeCoverage(bool enabled, const char *dir) {
- CHECK_EQ(enabled, common_flags()->coverage);
- CHECK_EQ(dir, common_flags()->coverage_dir);
-
- static bool coverage_enabled = false;
- if (!coverage_enabled) {
- coverage_enabled = enabled;
- Atexit(__sanitizer_cov_dump);
- AddDieCallback(__sanitizer_cov_dump);
- }
-}
-} // namespace __sanitizer
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT
- const uptr *pcs, uptr len) {
- UNIMPLEMENTED();
-}
-
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32 *guard) {
- if (!*guard) return;
- __sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1);
-}
-
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init,
- u32 *start, u32 *end) {
- if (start == end || *start) return;
- __sancov::pc_guard_controller.InitTracePcGuard(start, end);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() {
- __sancov::pc_guard_controller.Dump();
-}
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
- __sanitizer_dump_trace_pc_guard_coverage();
-}
-// Default empty implementations (weak). Users should redefine them.
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp1, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp2, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp4, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp8, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {}
-} // extern "C"
-
-#endif // !SANITIZER_FUCHSIA
--- /dev/null
+//===-- sanitizer_coverage_fuchsia.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Sanitizer Coverage Controller for Trace PC Guard, Fuchsia-specific version.
+//
+// This Fuchsia-specific implementation uses the same basic scheme and the
+// same simple '.sancov' file format as the generic implementation. The
+// difference is that we just produce a single blob of output for the whole
+// program, not a separate one per DSO. We do not sort the PC table and do
+// not prune the zeros, so the resulting file is always as large as it
+// would be to report 100% coverage. Implicit tracing information about
+// the address ranges of DSOs allows offline tools to split the one big
+// blob into separate files that the 'sancov' tool can understand.
+//
+// Unlike the traditional implementation that uses an atexit hook to write
+// out data files at the end, the results on Fuchsia do not go into a file
+// per se. The 'coverage_dir' option is ignored. Instead, they are stored
+// directly into a shared memory object (a Zircon VMO). At exit, that VMO
+// is handed over to a system service that's responsible for getting the
+// data out to somewhere that it can be fed into the sancov tool (where and
+// how is not our problem).
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FUCHSIA
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer_fuchsia.h"
+
+#include <zircon/process.h>
+#include <zircon/sanitizer.h>
+#include <zircon/syscalls.h>
+
+using namespace __sanitizer; // NOLINT
+
+namespace __sancov {
+namespace {
+
+// TODO(mcgrathr): Move the constant into a header shared with other impls.
+constexpr u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL;
+static_assert(SANITIZER_WORDSIZE == 64, "Fuchsia is always LP64");
+
+constexpr const char kSancovSinkName[] = "sancov";
+
+// Collects trace-pc guard coverage.
+// This class relies on zero-initialization.
+class TracePcGuardController final {
+ public:
+ // For each PC location being tracked, there is a u32 reserved in global
+ // data called the "guard". At startup, we assign each guard slot a
+ // unique index into the big results array. Later during runtime, the
+ // first call to TracePcGuard (below) will store the corresponding PC at
+ // that index in the array. (Each later call with the same guard slot is
+ // presumed to be from the same PC.) Then it clears the guard slot back
+ // to zero, which tells the compiler not to bother calling in again. At
+ // the end of the run, we have a big array where each element is either
+ // zero or is a tracked PC location that was hit in the trace.
+
+ // This is called from global constructors. Each translation unit has a
+ // contiguous array of guard slots, and a constructor that calls here
+ // with the bounds of its array. Those constructors are allowed to call
+ // here more than once for the same array. Usually all of these
+ // constructors run in the initial thread, but it's possible that a
+ // dlopen call on a secondary thread will run constructors that get here.
+ void InitTracePcGuard(u32 *start, u32 *end) {
+ if (end > start && *start == 0 && common_flags()->coverage) {
+ // Complete the setup before filling in any guards with indices.
+ // This avoids the possibility of code called from Setup reentering
+ // TracePcGuard.
+ u32 idx = Setup(end - start);
+ for (u32 *p = start; p < end; ++p) {
+ *p = idx++;
+ }
+ }
+ }
+
+ void TracePcGuard(u32 *guard, uptr pc) {
+ atomic_uint32_t *guard_ptr = reinterpret_cast<atomic_uint32_t *>(guard);
+ u32 idx = atomic_exchange(guard_ptr, 0, memory_order_relaxed);
+ if (idx > 0) array_[idx] = pc;
+ }
+
+ void Dump() {
+ BlockingMutexLock locked(&setup_lock_);
+ if (array_) {
+ CHECK_NE(vmo_, ZX_HANDLE_INVALID);
+
+ // Publish the VMO to the system, where it can be collected and
+ // analyzed after this process exits. This always consumes the VMO
+ // handle. Any failure is just logged and not indicated to us.
+ __sanitizer_publish_data(kSancovSinkName, vmo_);
+ vmo_ = ZX_HANDLE_INVALID;
+
+ // This will route to __sanitizer_log_write, which will ensure that
+ // information about shared libraries is written out. This message
+ // uses the `dumpfile` symbolizer markup element to highlight the
+ // dump. See the explanation for this in:
+ // https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
+ Printf("SanitizerCoverage: " FORMAT_DUMPFILE " with up to %u PCs\n",
+ kSancovSinkName, vmo_name_, next_index_ - 1);
+ }
+ }
+
+ private:
+ // We map in the largest possible view into the VMO: one word
+ // for every possible 32-bit index value. This avoids the need
+ // to change the mapping when increasing the size of the VMO.
+ // We can always spare the 32G of address space.
+ static constexpr size_t MappingSize = sizeof(uptr) << 32;
+
+ BlockingMutex setup_lock_ = BlockingMutex(LINKER_INITIALIZED);
+ uptr *array_ = nullptr;
+ u32 next_index_ = 0;
+ zx_handle_t vmo_ = {};
+ char vmo_name_[ZX_MAX_NAME_LEN] = {};
+
+ size_t DataSize() const { return next_index_ * sizeof(uintptr_t); }
+
+ u32 Setup(u32 num_guards) {
+ BlockingMutexLock locked(&setup_lock_);
+ DCHECK(common_flags()->coverage);
+
+ if (next_index_ == 0) {
+ CHECK_EQ(vmo_, ZX_HANDLE_INVALID);
+ CHECK_EQ(array_, nullptr);
+
+ // The first sample goes at [1] to reserve [0] for the magic number.
+ next_index_ = 1 + num_guards;
+
+ zx_status_t status = _zx_vmo_create(DataSize(), ZX_VMO_RESIZABLE, &vmo_);
+ CHECK_EQ(status, ZX_OK);
+
+ // Give the VMO a name including our process KOID so it's easy to spot.
+ internal_snprintf(vmo_name_, sizeof(vmo_name_), "%s.%zu", kSancovSinkName,
+ internal_getpid());
+ _zx_object_set_property(vmo_, ZX_PROP_NAME, vmo_name_,
+ internal_strlen(vmo_name_));
+
+ // Map the largest possible view we might need into the VMO. Later
+ // we might need to increase the VMO's size before we can use larger
+ // indices, but we'll never move the mapping address so we don't have
+ // any multi-thread synchronization issues with that.
+ uintptr_t mapping;
+ status =
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
+ 0, vmo_, 0, MappingSize, &mapping);
+ CHECK_EQ(status, ZX_OK);
+
+ // Hereafter other threads are free to start storing into
+ // elements [1, next_index_) of the big array.
+ array_ = reinterpret_cast<uptr *>(mapping);
+
+ // Store the magic number.
+ // Hereafter, the VMO serves as the contents of the '.sancov' file.
+ array_[0] = Magic64;
+
+ return 1;
+ } else {
+ // The VMO is already mapped in, but it's not big enough to use the
+ // new indices. So increase the size to cover the new maximum index.
+
+ CHECK_NE(vmo_, ZX_HANDLE_INVALID);
+ CHECK_NE(array_, nullptr);
+
+ uint32_t first_index = next_index_;
+ next_index_ += num_guards;
+
+ zx_status_t status = _zx_vmo_set_size(vmo_, DataSize());
+ CHECK_EQ(status, ZX_OK);
+
+ return first_index;
+ }
+ }
+};
+
+static TracePcGuardController pc_guard_controller;
+
+} // namespace
+} // namespace __sancov
+
+namespace __sanitizer {
+void InitializeCoverage(bool enabled, const char *dir) {
+ CHECK_EQ(enabled, common_flags()->coverage);
+ CHECK_EQ(dir, common_flags()->coverage_dir);
+
+ static bool coverage_enabled = false;
+ if (!coverage_enabled) {
+ coverage_enabled = enabled;
+ Atexit(__sanitizer_cov_dump);
+ AddDieCallback(__sanitizer_cov_dump);
+ }
+}
+} // namespace __sanitizer
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT
+ const uptr *pcs, uptr len) {
+ UNIMPLEMENTED();
+}
+
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32 *guard) {
+ if (!*guard) return;
+ __sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1);
+}
+
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init,
+ u32 *start, u32 *end) {
+ if (start == end || *start) return;
+ __sancov::pc_guard_controller.InitTracePcGuard(start, end);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() {
+ __sancov::pc_guard_controller.Dump();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+ __sanitizer_dump_trace_pc_guard_coverage();
+}
+// Default empty implementations (weak). Users should redefine them.
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp1, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp2, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp4, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp8, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {}
+} // extern "C"
+
+#endif // !SANITIZER_FUCHSIA
//===-- sanitizer_coverage_interface.inc ----------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Sanitizer Coverage interface list.
+++ /dev/null
-//===-- sanitizer_coverage_libcdep_new.cc ---------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// Sanitizer Coverage Controller for Trace PC Guard.
-
-#include "sanitizer_platform.h"
-
-#if !SANITIZER_FUCHSIA
-#include "sancov_flags.h"
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-
-using namespace __sanitizer;
-
-using AddressRange = LoadedModule::AddressRange;
-
-namespace __sancov {
-namespace {
-
-static const u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL;
-static const u64 Magic32 = 0xC0BFFFFFFFFFFF32ULL;
-static const u64 Magic = SANITIZER_WORDSIZE == 64 ? Magic64 : Magic32;
-
-static fd_t OpenFile(const char* path) {
- error_t err;
- fd_t fd = OpenFile(path, WrOnly, &err);
- if (fd == kInvalidFd)
- Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n",
- path, err);
- return fd;
-}
-
-static void GetCoverageFilename(char* path, const char* name,
- const char* extension) {
- CHECK(name);
- internal_snprintf(path, kMaxPathLength, "%s/%s.%zd.%s",
- common_flags()->coverage_dir, name, internal_getpid(),
- extension);
-}
-
-static void WriteModuleCoverage(char* file_path, const char* module_name,
- const uptr* pcs, uptr len) {
- GetCoverageFilename(file_path, StripModuleName(module_name), "sancov");
- fd_t fd = OpenFile(file_path);
- WriteToFile(fd, &Magic, sizeof(Magic));
- WriteToFile(fd, pcs, len * sizeof(*pcs));
- CloseFile(fd);
- Printf("SanitizerCoverage: %s: %zd PCs written\n", file_path, len);
-}
-
-static void SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) {
- if (!len) return;
-
- char* file_path = static_cast<char*>(InternalAlloc(kMaxPathLength));
- char* module_name = static_cast<char*>(InternalAlloc(kMaxPathLength));
- uptr* pcs = static_cast<uptr*>(InternalAlloc(len * sizeof(uptr)));
-
- internal_memcpy(pcs, unsorted_pcs, len * sizeof(uptr));
- Sort(pcs, len);
-
- bool module_found = false;
- uptr last_base = 0;
- uptr module_start_idx = 0;
-
- for (uptr i = 0; i < len; ++i) {
- const uptr pc = pcs[i];
- if (!pc) continue;
-
- if (!__sanitizer_get_module_and_offset_for_pc(pc, nullptr, 0, &pcs[i])) {
- Printf("ERROR: unknown pc 0x%x (may happen if dlclose is used)\n", pc);
- continue;
- }
- uptr module_base = pc - pcs[i];
-
- if (module_base != last_base || !module_found) {
- if (module_found) {
- WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx],
- i - module_start_idx);
- }
-
- last_base = module_base;
- module_start_idx = i;
- module_found = true;
- __sanitizer_get_module_and_offset_for_pc(pc, module_name, kMaxPathLength,
- &pcs[i]);
- }
- }
-
- if (module_found) {
- WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx],
- len - module_start_idx);
- }
-
- InternalFree(file_path);
- InternalFree(module_name);
- InternalFree(pcs);
-}
-
-// Collects trace-pc guard coverage.
-// This class relies on zero-initialization.
-class TracePcGuardController {
- public:
- void Initialize() {
- CHECK(!initialized);
-
- initialized = true;
- InitializeSancovFlags();
-
- pc_vector.Initialize(0);
- }
-
- void InitTracePcGuard(u32* start, u32* end) {
- if (!initialized) Initialize();
- CHECK(!*start);
- CHECK_NE(start, end);
-
- u32 i = pc_vector.size();
- for (u32* p = start; p < end; p++) *p = ++i;
- pc_vector.resize(i);
- }
-
- void TracePcGuard(u32* guard, uptr pc) {
- u32 idx = *guard;
- if (!idx) return;
- // we start indices from 1.
- atomic_uintptr_t* pc_ptr =
- reinterpret_cast<atomic_uintptr_t*>(&pc_vector[idx - 1]);
- if (atomic_load(pc_ptr, memory_order_relaxed) == 0)
- atomic_store(pc_ptr, pc, memory_order_relaxed);
- }
-
- void Reset() {
- internal_memset(&pc_vector[0], 0, sizeof(pc_vector[0]) * pc_vector.size());
- }
-
- void Dump() {
- if (!initialized || !common_flags()->coverage) return;
- __sanitizer_dump_coverage(pc_vector.data(), pc_vector.size());
- }
-
- private:
- bool initialized;
- InternalMmapVectorNoCtor<uptr> pc_vector;
-};
-
-static TracePcGuardController pc_guard_controller;
-
-} // namespace
-} // namespace __sancov
-
-namespace __sanitizer {
-void InitializeCoverage(bool enabled, const char *dir) {
- static bool coverage_enabled = false;
- if (coverage_enabled)
- return; // May happen if two sanitizer enable coverage in the same process.
- coverage_enabled = enabled;
- Atexit(__sanitizer_cov_dump);
- AddDieCallback(__sanitizer_cov_dump);
-}
-} // namespace __sanitizer
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT
- const uptr* pcs, uptr len) {
- return __sancov::SanitizerDumpCoverage(pcs, len);
-}
-
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32* guard) {
- if (!*guard) return;
- __sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1);
-}
-
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init,
- u32* start, u32* end) {
- if (start == end || *start) return;
- __sancov::pc_guard_controller.InitTracePcGuard(start, end);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() {
- __sancov::pc_guard_controller.Dump();
-}
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
- __sanitizer_dump_trace_pc_guard_coverage();
-}
-SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_reset() {
- __sancov::pc_guard_controller.Reset();
-}
-// Default empty implementations (weak). Users should redefine them.
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp1, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp2, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp4, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp8, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_8bit_counters_init, void) {}
-SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_pcs_init, void) {}
-} // extern "C"
-// Weak definition for code instrumented with -fsanitize-coverage=stack-depth
-// and later linked with code containing a strong definition.
-// E.g., -fsanitize=fuzzer-no-link
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-SANITIZER_TLS_INITIAL_EXEC_ATTRIBUTE uptr __sancov_lowest_stack;
-
-#endif // !SANITIZER_FUCHSIA
--- /dev/null
+//===-- sanitizer_coverage_libcdep_new.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Sanitizer Coverage Controller for Trace PC Guard.
+
+#include "sanitizer_platform.h"
+
+#if !SANITIZER_FUCHSIA
+#include "sancov_flags.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+
+using namespace __sanitizer;
+
+using AddressRange = LoadedModule::AddressRange;
+
+namespace __sancov {
+namespace {
+
+static const u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL;
+static const u64 Magic32 = 0xC0BFFFFFFFFFFF32ULL;
+static const u64 Magic = SANITIZER_WORDSIZE == 64 ? Magic64 : Magic32;
+
+static fd_t OpenFile(const char* path) {
+ error_t err;
+ fd_t fd = OpenFile(path, WrOnly, &err);
+ if (fd == kInvalidFd)
+ Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n",
+ path, err);
+ return fd;
+}
+
+static void GetCoverageFilename(char* path, const char* name,
+ const char* extension) {
+ CHECK(name);
+ internal_snprintf(path, kMaxPathLength, "%s/%s.%zd.%s",
+ common_flags()->coverage_dir, name, internal_getpid(),
+ extension);
+}
+
+static void WriteModuleCoverage(char* file_path, const char* module_name,
+ const uptr* pcs, uptr len) {
+ GetCoverageFilename(file_path, StripModuleName(module_name), "sancov");
+ fd_t fd = OpenFile(file_path);
+ WriteToFile(fd, &Magic, sizeof(Magic));
+ WriteToFile(fd, pcs, len * sizeof(*pcs));
+ CloseFile(fd);
+ Printf("SanitizerCoverage: %s: %zd PCs written\n", file_path, len);
+}
+
+static void SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) {
+ if (!len) return;
+
+ char* file_path = static_cast<char*>(InternalAlloc(kMaxPathLength));
+ char* module_name = static_cast<char*>(InternalAlloc(kMaxPathLength));
+ uptr* pcs = static_cast<uptr*>(InternalAlloc(len * sizeof(uptr)));
+
+ internal_memcpy(pcs, unsorted_pcs, len * sizeof(uptr));
+ Sort(pcs, len);
+
+ bool module_found = false;
+ uptr last_base = 0;
+ uptr module_start_idx = 0;
+
+ for (uptr i = 0; i < len; ++i) {
+ const uptr pc = pcs[i];
+ if (!pc) continue;
+
+ if (!__sanitizer_get_module_and_offset_for_pc(pc, nullptr, 0, &pcs[i])) {
+ Printf("ERROR: unknown pc 0x%x (may happen if dlclose is used)\n", pc);
+ continue;
+ }
+ uptr module_base = pc - pcs[i];
+
+ if (module_base != last_base || !module_found) {
+ if (module_found) {
+ WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx],
+ i - module_start_idx);
+ }
+
+ last_base = module_base;
+ module_start_idx = i;
+ module_found = true;
+ __sanitizer_get_module_and_offset_for_pc(pc, module_name, kMaxPathLength,
+ &pcs[i]);
+ }
+ }
+
+ if (module_found) {
+ WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx],
+ len - module_start_idx);
+ }
+
+ InternalFree(file_path);
+ InternalFree(module_name);
+ InternalFree(pcs);
+}
+
+// Collects trace-pc guard coverage.
+// This class relies on zero-initialization.
+class TracePcGuardController {
+ public:
+ void Initialize() {
+ CHECK(!initialized);
+
+ initialized = true;
+ InitializeSancovFlags();
+
+ pc_vector.Initialize(0);
+ }
+
+ void InitTracePcGuard(u32* start, u32* end) {
+ if (!initialized) Initialize();
+ CHECK(!*start);
+ CHECK_NE(start, end);
+
+ u32 i = pc_vector.size();
+ for (u32* p = start; p < end; p++) *p = ++i;
+ pc_vector.resize(i);
+ }
+
+ void TracePcGuard(u32* guard, uptr pc) {
+ u32 idx = *guard;
+ if (!idx) return;
+ // we start indices from 1.
+ atomic_uintptr_t* pc_ptr =
+ reinterpret_cast<atomic_uintptr_t*>(&pc_vector[idx - 1]);
+ if (atomic_load(pc_ptr, memory_order_relaxed) == 0)
+ atomic_store(pc_ptr, pc, memory_order_relaxed);
+ }
+
+ void Reset() {
+ internal_memset(&pc_vector[0], 0, sizeof(pc_vector[0]) * pc_vector.size());
+ }
+
+ void Dump() {
+ if (!initialized || !common_flags()->coverage) return;
+ __sanitizer_dump_coverage(pc_vector.data(), pc_vector.size());
+ }
+
+ private:
+ bool initialized;
+ InternalMmapVectorNoCtor<uptr> pc_vector;
+};
+
+static TracePcGuardController pc_guard_controller;
+
+} // namespace
+} // namespace __sancov
+
+namespace __sanitizer {
+void InitializeCoverage(bool enabled, const char *dir) {
+ static bool coverage_enabled = false;
+ if (coverage_enabled)
+ return; // May happen if two sanitizer enable coverage in the same process.
+ coverage_enabled = enabled;
+ Atexit(__sanitizer_cov_dump);
+ AddDieCallback(__sanitizer_cov_dump);
+}
+} // namespace __sanitizer
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT
+ const uptr* pcs, uptr len) {
+ return __sancov::SanitizerDumpCoverage(pcs, len);
+}
+
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32* guard) {
+ if (!*guard) return;
+ __sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1);
+}
+
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init,
+ u32* start, u32* end) {
+ if (start == end || *start) return;
+ __sancov::pc_guard_controller.InitTracePcGuard(start, end);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() {
+ __sancov::pc_guard_controller.Dump();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() {
+ __sanitizer_dump_trace_pc_guard_coverage();
+}
+SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_reset() {
+ __sancov::pc_guard_controller.Reset();
+}
+// Default empty implementations (weak). Users should redefine them.
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp1, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp2, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp4, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_const_cmp8, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_8bit_counters_init, void) {}
+SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_pcs_init, void) {}
+} // extern "C"
+// Weak definition for code instrumented with -fsanitize-coverage=stack-depth
+// and later linked with code containing a strong definition.
+// E.g., -fsanitize=fuzzer-no-link
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+SANITIZER_TLS_INITIAL_EXEC_ATTRIBUTE uptr __sancov_lowest_stack;
+
+#endif // !SANITIZER_FUCHSIA
+++ /dev/null
-//===-- sanitizer_coverage_win_dll_thunk.cc -------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines a family of thunks that should be statically linked into
-// the DLLs that have instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DLL_THUNK
-#include "sanitizer_win_dll_thunk.h"
-// Sanitizer Coverage interface functions.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_coverage_interface.inc"
-#endif // SANITIZER_DLL_THUNK
--- /dev/null
+//===-- sanitizer_coverage_win_dll_thunk.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a family of thunks that should be statically linked into
+// the DLLs that have instrumentation in order to delegate the calls to the
+// shared runtime that lives in the main binary.
+// See https://github.com/google/sanitizers/issues/209 for the details.
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DLL_THUNK
+#include "sanitizer_win_dll_thunk.h"
+// Sanitizer Coverage interface functions.
+#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "sanitizer_coverage_interface.inc"
+#endif // SANITIZER_DLL_THUNK
+++ /dev/null
-//===-- sanitizer_coverage_win_dynamic_runtime_thunk.cc -------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines things that need to be present in the application modules
-// to interact with Sanitizer Coverage, when it is included in a dll.
-//
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_win_defs.h"
-// Define weak alias for all weak functions imported from sanitizer coverage.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "sanitizer_coverage_interface.inc"
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
--- /dev/null
+//===-- sanitizer_coverage_win_dynamic_runtime_thunk.cpp ------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines things that need to be present in the application modules
+// to interact with Sanitizer Coverage, when it is included in a dll.
+//
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
+#define SANITIZER_IMPORT_INTERFACE 1
+#include "sanitizer_win_defs.h"
+// Define weak alias for all weak functions imported from sanitizer coverage.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
+#include "sanitizer_coverage_interface.inc"
+#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+
+namespace __sanitizer {
+// Add one, otherwise unused, external symbol to this object file so that the
+// Visual C++ linker includes it and reads the .drective section.
+void ForceWholeArchiveIncludeForSanCov() {}
+}
+++ /dev/null
-//===-- sanitizer_coverage_win_sections.cc --------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines delimiters for Sanitizer Coverage's section. It contains
-// Windows specific tricks to coax the linker into giving us the start and stop
-// addresses of a section, as ELF linkers can do, to get the size of certain
-// arrays. According to https://msdn.microsoft.com/en-us/library/7977wcck.aspx
-// sections with the same name before "$" are sorted alphabetically by the
-// string that comes after "$" and merged into one section. We take advantage
-// of this by putting data we want the size of into the middle (M) of a section,
-// by using the letter "M" after "$". We get the start of this data (ie:
-// __start_section_name) by making the start variable come at the start of the
-// section (using the letter A after "$"). We do the same to get the end of the
-// data by using the letter "Z" after "$" to make the end variable come after
-// the data. Note that because of our technique the address of the start
-// variable is actually the address of data that comes before our middle
-// section. We also need to prevent the linker from adding any padding. Each
-// technique we use for this is explained in the comments below.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-#include <stdint.h>
-extern "C" {
-// The Guard array and counter array should both be merged into the .data
-// section to reduce the number of PE sections However, because PCTable is
-// constant it should be merged with the .rdata section.
-#pragma section(".SCOV$GA", read, write) // NOLINT
-// Use align(1) to avoid adding any padding that will mess up clients trying to
-// determine the start and end of the array.
-__declspec(allocate(".SCOV$GA")) __declspec(align(1)) uint64_t
- __start___sancov_guards = 0;
-#pragma section(".SCOV$GZ", read, write) // NOLINT
-__declspec(allocate(".SCOV$GZ")) __declspec(align(1)) uint64_t
- __stop___sancov_guards = 0;
-
-#pragma section(".SCOV$CA", read, write) // NOLINT
-__declspec(allocate(".SCOV$CA")) __declspec(align(1)) uint64_t
- __start___sancov_cntrs = 0;
-#pragma section(".SCOV$CZ", read, write) // NOLINT
-__declspec(allocate(".SCOV$CZ")) __declspec(align(1)) uint64_t
- __stop___sancov_cntrs = 0;
-
-#pragma comment(linker, "/MERGE:.SCOV=.data")
-
-// Use uint64_t so there won't be any issues if the linker tries to word align
-// the pc array.
-#pragma section(".SCOVP$A", read) // NOLINT
-__declspec(allocate(".SCOVP$A")) __declspec(align(1)) uint64_t
- __start___sancov_pcs = 0;
-#pragma section(".SCOVP$Z", read) // NOLINT
-__declspec(allocate(".SCOVP$Z")) __declspec(align(1)) uint64_t
- __stop___sancov_pcs = 0;
-
-#pragma comment(linker, "/MERGE:.SCOVP=.rdata")
-}
-#endif // SANITIZER_WINDOWS
--- /dev/null
+//===-- sanitizer_coverage_win_sections.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines delimiters for Sanitizer Coverage's section. It contains
+// Windows specific tricks to coax the linker into giving us the start and stop
+// addresses of a section, as ELF linkers can do, to get the size of certain
+// arrays. According to https://msdn.microsoft.com/en-us/library/7977wcck.aspx
+// sections with the same name before "$" are sorted alphabetically by the
+// string that comes after "$" and merged into one section. We take advantage
+// of this by putting data we want the size of into the middle (M) of a section,
+// by using the letter "M" after "$". We get the start of this data (ie:
+// __start_section_name) by making the start variable come at the start of the
+// section (using the letter A after "$"). We do the same to get the end of the
+// data by using the letter "Z" after "$" to make the end variable come after
+// the data. Note that because of our technique the address of the start
+// variable is actually the address of data that comes before our middle
+// section. We also need to prevent the linker from adding any padding. Each
+// technique we use for this is explained in the comments below.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+#include <stdint.h>
+
+extern "C" {
+// Use uint64_t so the linker won't need to add any padding if it tries to word
+// align the start of the 8-bit counters array. The array will always start 8
+// bytes after __start_sancov_cntrs.
+#pragma section(".SCOV$CA", read, write) // NOLINT
+__declspec(allocate(".SCOV$CA")) uint64_t __start___sancov_cntrs = 0;
+
+// Even though we said not to align __stop__sancov_cntrs (using the "align"
+// declspec), MSVC's linker may try to align the section, .SCOV$CZ, containing
+// it. This can cause a mismatch between the number of PCs and counters since
+// each PCTable element is 8 bytes (unlike counters which are 1 byte) so no
+// padding would be added to align .SCOVP$Z, However, if .SCOV$CZ section is 1
+// byte, the linker won't try to align it on an 8-byte boundary, so use a
+// uint8_t for __stop_sancov_cntrs.
+#pragma section(".SCOV$CZ", read, write) // NOLINT
+__declspec(allocate(".SCOV$CZ")) __declspec(align(1)) uint8_t
+ __stop___sancov_cntrs = 0;
+
+#pragma section(".SCOV$GA", read, write) // NOLINT
+__declspec(allocate(".SCOV$GA")) uint64_t __start___sancov_guards = 0;
+#pragma section(".SCOV$GZ", read, write) // NOLINT
+__declspec(allocate(".SCOV$GZ")) __declspec(align(1)) uint8_t
+ __stop___sancov_guards = 0;
+
+// The guard array and counter array should both be merged into the .data
+// section to reduce the number of PE sections. However, because PCTable is
+// constant it should be merged with the .rdata section.
+#pragma comment(linker, "/MERGE:.SCOV=.data")
+
+#pragma section(".SCOVP$A", read) // NOLINT
+__declspec(allocate(".SCOVP$A")) uint64_t __start___sancov_pcs = 0;
+#pragma section(".SCOVP$Z", read) // NOLINT
+__declspec(allocate(".SCOVP$Z")) __declspec(align(1)) uint8_t
+ __stop___sancov_pcs = 0;
+
+#pragma comment(linker, "/MERGE:.SCOVP=.rdata")
+}
+#endif // SANITIZER_WINDOWS
+++ /dev/null
-//===-- sanitizer_coverage_win_weak_interception.cc -----------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// This module should be included in Sanitizer Coverage when it implemented as a
-// shared library on Windows (dll), in order to delegate the calls of weak
-// functions to the implementation in the main executable when a strong
-// definition is provided.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC
-#include "sanitizer_win_weak_interception.h"
-#include "sanitizer_interface_internal.h"
-#include "sancov_flags.h"
-// Check if strong definitions for weak functions are present in the main
-// executable. If that is the case, override dll functions to point to strong
-// implementations.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_coverage_interface.inc"
-#endif // SANITIZER_DYNAMIC
--- /dev/null
+//===-- sanitizer_coverage_win_weak_interception.cpp ----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This module should be included in Sanitizer Coverage when it implemented as a
+// shared library on Windows (dll), in order to delegate the calls of weak
+// functions to the implementation in the main executable when a strong
+// definition is provided.
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DYNAMIC
+#include "sanitizer_win_weak_interception.h"
+#include "sanitizer_interface_internal.h"
+#include "sancov_flags.h"
+// Check if strong definitions for weak functions are present in the main
+// executable. If that is the case, override dll functions to point to strong
+// implementations.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "sanitizer_coverage_interface.inc"
+#endif // SANITIZER_DYNAMIC
//===-- sanitizer_dbghelp.h ------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_deadlock_detector.h ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#ifndef SANITIZER_DEADLOCK_DETECTOR_H
#define SANITIZER_DEADLOCK_DETECTOR_H
-#include "sanitizer_common.h"
#include "sanitizer_bvgraph.h"
+#include "sanitizer_common.h"
namespace __sanitizer {
// Returns true if this is the first (non-recursive) acquisition of this lock.
bool addLock(uptr lock_id, uptr current_epoch, u32 stk) {
- // Printf("addLock: %zx %zx stk %u\n", lock_id, current_epoch, stk);
CHECK_EQ(epoch_, current_epoch);
if (!bv_.setBit(lock_id)) {
// The lock is already held by this thread, it must be recursive.
}
}
}
- // Printf("remLock: %zx %zx\n", lock_id, epoch_);
if (!bv_.clearBit(lock_id))
return; // probably addLock happened before flush
if (n_all_locks_) {
if (!available_nodes_.empty())
return getAvailableNode(data);
if (!recycled_nodes_.empty()) {
- // Printf("recycling: n_edges_ %zd\n", n_edges_);
for (sptr i = n_edges_ - 1; i >= 0; i--) {
if (recycled_nodes_.getBit(edges_[i].from) ||
recycled_nodes_.getBit(edges_[i].to)) {
unique_tid};
edges_[n_edges_++] = e;
}
- // Printf("Edge%zd: %u %zd=>%zd in T%d\n",
- // n_edges_, stk, added_edges[i], cur_idx, unique_tid);
}
return n_added_edges;
}
+++ /dev/null
-//===-- sanitizer_deadlock_detector1.cc -----------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Deadlock detector implementation based on NxN adjacency bit matrix.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_deadlock_detector_interface.h"
-#include "sanitizer_deadlock_detector.h"
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_mutex.h"
-
-#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
-
-namespace __sanitizer {
-
-typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector.
-
-struct DDPhysicalThread {
-};
-
-struct DDLogicalThread {
- u64 ctx;
- DeadlockDetectorTLS<DDBV> dd;
- DDReport rep;
- bool report_pending;
-};
-
-struct DD : public DDetector {
- SpinMutex mtx;
- DeadlockDetector<DDBV> dd;
- DDFlags flags;
-
- explicit DD(const DDFlags *flags);
-
- DDPhysicalThread *CreatePhysicalThread() override;
- void DestroyPhysicalThread(DDPhysicalThread *pt) override;
-
- DDLogicalThread *CreateLogicalThread(u64 ctx) override;
- void DestroyLogicalThread(DDLogicalThread *lt) override;
-
- void MutexInit(DDCallback *cb, DDMutex *m) override;
- void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
- void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
- bool trylock) override;
- void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
- void MutexDestroy(DDCallback *cb, DDMutex *m) override;
-
- DDReport *GetReport(DDCallback *cb) override;
-
- void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
- void ReportDeadlock(DDCallback *cb, DDMutex *m);
-};
-
-DDetector *DDetector::Create(const DDFlags *flags) {
- (void)flags;
- void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
- return new(mem) DD(flags);
-}
-
-DD::DD(const DDFlags *flags)
- : flags(*flags) {
- dd.clear();
-}
-
-DDPhysicalThread* DD::CreatePhysicalThread() {
- return nullptr;
-}
-
-void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
-}
-
-DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
- DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));
- lt->ctx = ctx;
- lt->dd.clear();
- lt->report_pending = false;
- return lt;
-}
-
-void DD::DestroyLogicalThread(DDLogicalThread *lt) {
- lt->~DDLogicalThread();
- InternalFree(lt);
-}
-
-void DD::MutexInit(DDCallback *cb, DDMutex *m) {
- m->id = 0;
- m->stk = cb->Unwind();
-}
-
-void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {
- if (!dd.nodeBelongsToCurrentEpoch(m->id))
- m->id = dd.newNode(reinterpret_cast<uptr>(m));
- dd.ensureCurrentEpoch(<->dd);
-}
-
-void DD::MutexBeforeLock(DDCallback *cb,
- DDMutex *m, bool wlock) {
- DDLogicalThread *lt = cb->lt;
- if (lt->dd.empty()) return; // This will be the first lock held by lt.
- if (dd.hasAllEdges(<->dd, m->id)) return; // We already have all edges.
- SpinMutexLock lk(&mtx);
- MutexEnsureID(lt, m);
- if (dd.isHeld(<->dd, m->id))
- return; // FIXME: allow this only for recursive locks.
- if (dd.onLockBefore(<->dd, m->id)) {
- // Actually add this edge now so that we have all the stack traces.
- dd.addEdges(<->dd, m->id, cb->Unwind(), cb->UniqueTid());
- ReportDeadlock(cb, m);
- }
-}
-
-void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
- DDLogicalThread *lt = cb->lt;
- uptr path[20];
- uptr len = dd.findPathToLock(<->dd, m->id, path, ARRAY_SIZE(path));
- if (len == 0U) {
- // A cycle of 20+ locks? Well, that's a bit odd...
- Printf("WARNING: too long mutex cycle found\n");
- return;
- }
- CHECK_EQ(m->id, path[0]);
- lt->report_pending = true;
- len = Min<uptr>(len, DDReport::kMaxLoopSize);
- DDReport *rep = <->rep;
- rep->n = len;
- for (uptr i = 0; i < len; i++) {
- uptr from = path[i];
- uptr to = path[(i + 1) % len];
- DDMutex *m0 = (DDMutex*)dd.getData(from);
- DDMutex *m1 = (DDMutex*)dd.getData(to);
-
- u32 stk_from = -1U, stk_to = -1U;
- int unique_tid = 0;
- dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
- // Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
- // unique_tid);
- rep->loop[i].thr_ctx = unique_tid;
- rep->loop[i].mtx_ctx0 = m0->ctx;
- rep->loop[i].mtx_ctx1 = m1->ctx;
- rep->loop[i].stk[0] = stk_to;
- rep->loop[i].stk[1] = stk_from;
- }
-}
-
-void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
- DDLogicalThread *lt = cb->lt;
- u32 stk = 0;
- if (flags.second_deadlock_stack)
- stk = cb->Unwind();
- // Printf("T%p MutexLock: %zx stk %u\n", lt, m->id, stk);
- if (dd.onFirstLock(<->dd, m->id, stk))
- return;
- if (dd.onLockFast(<->dd, m->id, stk))
- return;
-
- SpinMutexLock lk(&mtx);
- MutexEnsureID(lt, m);
- if (wlock) // Only a recursive rlock may be held.
- CHECK(!dd.isHeld(<->dd, m->id));
- if (!trylock)
- dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
- dd.onLockAfter(<->dd, m->id, stk);
-}
-
-void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
- // Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id);
- dd.onUnlock(&cb->lt->dd, m->id);
-}
-
-void DD::MutexDestroy(DDCallback *cb,
- DDMutex *m) {
- if (!m->id) return;
- SpinMutexLock lk(&mtx);
- if (dd.nodeBelongsToCurrentEpoch(m->id))
- dd.removeNode(m->id);
- m->id = 0;
-}
-
-DDReport *DD::GetReport(DDCallback *cb) {
- if (!cb->lt->report_pending)
- return nullptr;
- cb->lt->report_pending = false;
- return &cb->lt->rep;
-}
-
-} // namespace __sanitizer
-#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
--- /dev/null
+//===-- sanitizer_deadlock_detector1.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Deadlock detector implementation based on NxN adjacency bit matrix.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_deadlock_detector.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
+
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+
+namespace __sanitizer {
+
+typedef TwoLevelBitVector<> DDBV; // DeadlockDetector's bit vector.
+
+struct DDPhysicalThread {
+};
+
+struct DDLogicalThread {
+ u64 ctx;
+ DeadlockDetectorTLS<DDBV> dd;
+ DDReport rep;
+ bool report_pending;
+};
+
+struct DD : public DDetector {
+ SpinMutex mtx;
+ DeadlockDetector<DDBV> dd;
+ DDFlags flags;
+
+ explicit DD(const DDFlags *flags);
+
+ DDPhysicalThread *CreatePhysicalThread() override;
+ void DestroyPhysicalThread(DDPhysicalThread *pt) override;
+
+ DDLogicalThread *CreateLogicalThread(u64 ctx) override;
+ void DestroyLogicalThread(DDLogicalThread *lt) override;
+
+ void MutexInit(DDCallback *cb, DDMutex *m) override;
+ void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) override;
+ void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override;
+ void MutexDestroy(DDCallback *cb, DDMutex *m) override;
+
+ DDReport *GetReport(DDCallback *cb) override;
+
+ void MutexEnsureID(DDLogicalThread *lt, DDMutex *m);
+ void ReportDeadlock(DDCallback *cb, DDMutex *m);
+};
+
+DDetector *DDetector::Create(const DDFlags *flags) {
+ (void)flags;
+ void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
+ return new(mem) DD(flags);
+}
+
+DD::DD(const DDFlags *flags)
+ : flags(*flags) {
+ dd.clear();
+}
+
+DDPhysicalThread* DD::CreatePhysicalThread() {
+ return nullptr;
+}
+
+void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
+}
+
+DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
+ DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(sizeof(*lt));
+ lt->ctx = ctx;
+ lt->dd.clear();
+ lt->report_pending = false;
+ return lt;
+}
+
+void DD::DestroyLogicalThread(DDLogicalThread *lt) {
+ lt->~DDLogicalThread();
+ InternalFree(lt);
+}
+
+void DD::MutexInit(DDCallback *cb, DDMutex *m) {
+ m->id = 0;
+ m->stk = cb->Unwind();
+}
+
+void DD::MutexEnsureID(DDLogicalThread *lt, DDMutex *m) {
+ if (!dd.nodeBelongsToCurrentEpoch(m->id))
+ m->id = dd.newNode(reinterpret_cast<uptr>(m));
+ dd.ensureCurrentEpoch(<->dd);
+}
+
+void DD::MutexBeforeLock(DDCallback *cb,
+ DDMutex *m, bool wlock) {
+ DDLogicalThread *lt = cb->lt;
+ if (lt->dd.empty()) return; // This will be the first lock held by lt.
+ if (dd.hasAllEdges(<->dd, m->id)) return; // We already have all edges.
+ SpinMutexLock lk(&mtx);
+ MutexEnsureID(lt, m);
+ if (dd.isHeld(<->dd, m->id))
+ return; // FIXME: allow this only for recursive locks.
+ if (dd.onLockBefore(<->dd, m->id)) {
+ // Actually add this edge now so that we have all the stack traces.
+ dd.addEdges(<->dd, m->id, cb->Unwind(), cb->UniqueTid());
+ ReportDeadlock(cb, m);
+ }
+}
+
+void DD::ReportDeadlock(DDCallback *cb, DDMutex *m) {
+ DDLogicalThread *lt = cb->lt;
+ uptr path[20];
+ uptr len = dd.findPathToLock(<->dd, m->id, path, ARRAY_SIZE(path));
+ if (len == 0U) {
+ // A cycle of 20+ locks? Well, that's a bit odd...
+ Printf("WARNING: too long mutex cycle found\n");
+ return;
+ }
+ CHECK_EQ(m->id, path[0]);
+ lt->report_pending = true;
+ len = Min<uptr>(len, DDReport::kMaxLoopSize);
+ DDReport *rep = <->rep;
+ rep->n = len;
+ for (uptr i = 0; i < len; i++) {
+ uptr from = path[i];
+ uptr to = path[(i + 1) % len];
+ DDMutex *m0 = (DDMutex*)dd.getData(from);
+ DDMutex *m1 = (DDMutex*)dd.getData(to);
+
+ u32 stk_from = -1U, stk_to = -1U;
+ int unique_tid = 0;
+ dd.findEdge(from, to, &stk_from, &stk_to, &unique_tid);
+ // Printf("Edge: %zd=>%zd: %u/%u T%d\n", from, to, stk_from, stk_to,
+ // unique_tid);
+ rep->loop[i].thr_ctx = unique_tid;
+ rep->loop[i].mtx_ctx0 = m0->ctx;
+ rep->loop[i].mtx_ctx1 = m1->ctx;
+ rep->loop[i].stk[0] = stk_to;
+ rep->loop[i].stk[1] = stk_from;
+ }
+}
+
+void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock) {
+ DDLogicalThread *lt = cb->lt;
+ u32 stk = 0;
+ if (flags.second_deadlock_stack)
+ stk = cb->Unwind();
+ // Printf("T%p MutexLock: %zx stk %u\n", lt, m->id, stk);
+ if (dd.onFirstLock(<->dd, m->id, stk))
+ return;
+ if (dd.onLockFast(<->dd, m->id, stk))
+ return;
+
+ SpinMutexLock lk(&mtx);
+ MutexEnsureID(lt, m);
+ if (wlock) // Only a recursive rlock may be held.
+ CHECK(!dd.isHeld(<->dd, m->id));
+ if (!trylock)
+ dd.addEdges(<->dd, m->id, stk ? stk : cb->Unwind(), cb->UniqueTid());
+ dd.onLockAfter(<->dd, m->id, stk);
+}
+
+void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
+ // Printf("T%p MutexUnLock: %zx\n", cb->lt, m->id);
+ dd.onUnlock(&cb->lt->dd, m->id);
+}
+
+void DD::MutexDestroy(DDCallback *cb,
+ DDMutex *m) {
+ if (!m->id) return;
+ SpinMutexLock lk(&mtx);
+ if (dd.nodeBelongsToCurrentEpoch(m->id))
+ dd.removeNode(m->id);
+ m->id = 0;
+}
+
+DDReport *DD::GetReport(DDCallback *cb) {
+ if (!cb->lt->report_pending)
+ return nullptr;
+ cb->lt->report_pending = false;
+ return &cb->lt->rep;
+}
+
+} // namespace __sanitizer
+#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 1
+++ /dev/null
-//===-- sanitizer_deadlock_detector2.cc -----------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Deadlock detector implementation based on adjacency lists.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_deadlock_detector_interface.h"
-#include "sanitizer_common.h"
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_mutex.h"
-
-#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
-
-namespace __sanitizer {
-
-const int kMaxNesting = 64;
-const u32 kNoId = -1;
-const u32 kEndId = -2;
-const int kMaxLink = 8;
-const int kL1Size = 1024;
-const int kL2Size = 1024;
-const int kMaxMutex = kL1Size * kL2Size;
-
-struct Id {
- u32 id;
- u32 seq;
-
- explicit Id(u32 id = 0, u32 seq = 0)
- : id(id)
- , seq(seq) {
- }
-};
-
-struct Link {
- u32 id;
- u32 seq;
- u32 tid;
- u32 stk0;
- u32 stk1;
-
- explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
- : id(id)
- , seq(seq)
- , tid(tid)
- , stk0(s0)
- , stk1(s1) {
- }
-};
-
-struct DDPhysicalThread {
- DDReport rep;
- bool report_pending;
- bool visited[kMaxMutex];
- Link pending[kMaxMutex];
- Link path[kMaxMutex];
-};
-
-struct ThreadMutex {
- u32 id;
- u32 stk;
-};
-
-struct DDLogicalThread {
- u64 ctx;
- ThreadMutex locked[kMaxNesting];
- int nlocked;
-};
-
-struct Mutex {
- StaticSpinMutex mtx;
- u32 seq;
- int nlink;
- Link link[kMaxLink];
-};
-
-struct DD : public DDetector {
- explicit DD(const DDFlags *flags);
-
- DDPhysicalThread* CreatePhysicalThread();
- void DestroyPhysicalThread(DDPhysicalThread *pt);
-
- DDLogicalThread* CreateLogicalThread(u64 ctx);
- void DestroyLogicalThread(DDLogicalThread *lt);
-
- void MutexInit(DDCallback *cb, DDMutex *m);
- void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
- void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
- bool trylock);
- void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
- void MutexDestroy(DDCallback *cb, DDMutex *m);
-
- DDReport *GetReport(DDCallback *cb);
-
- void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
- void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
- u32 allocateId(DDCallback *cb);
- Mutex *getMutex(u32 id);
- u32 getMutexId(Mutex *m);
-
- DDFlags flags;
-
- Mutex* mutex[kL1Size];
-
- SpinMutex mtx;
- InternalMmapVector<u32> free_id;
- int id_gen = 0;
-};
-
-DDetector *DDetector::Create(const DDFlags *flags) {
- (void)flags;
- void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
- return new(mem) DD(flags);
-}
-
-DD::DD(const DDFlags *flags) : flags(*flags) { free_id.reserve(1024); }
-
-DDPhysicalThread* DD::CreatePhysicalThread() {
- DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
- "deadlock detector (physical thread)");
- return pt;
-}
-
-void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
- pt->~DDPhysicalThread();
- UnmapOrDie(pt, sizeof(DDPhysicalThread));
-}
-
-DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
- DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
- sizeof(DDLogicalThread));
- lt->ctx = ctx;
- lt->nlocked = 0;
- return lt;
-}
-
-void DD::DestroyLogicalThread(DDLogicalThread *lt) {
- lt->~DDLogicalThread();
- InternalFree(lt);
-}
-
-void DD::MutexInit(DDCallback *cb, DDMutex *m) {
- VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
- m->id = kNoId;
- m->recursion = 0;
- atomic_store(&m->owner, 0, memory_order_relaxed);
-}
-
-Mutex *DD::getMutex(u32 id) {
- return &mutex[id / kL2Size][id % kL2Size];
-}
-
-u32 DD::getMutexId(Mutex *m) {
- for (int i = 0; i < kL1Size; i++) {
- Mutex *tab = mutex[i];
- if (tab == 0)
- break;
- if (m >= tab && m < tab + kL2Size)
- return i * kL2Size + (m - tab);
- }
- return -1;
-}
-
-u32 DD::allocateId(DDCallback *cb) {
- u32 id = -1;
- SpinMutexLock l(&mtx);
- if (free_id.size() > 0) {
- id = free_id.back();
- free_id.pop_back();
- } else {
- CHECK_LT(id_gen, kMaxMutex);
- if ((id_gen % kL2Size) == 0) {
- mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
- "deadlock detector (mutex table)");
- }
- id = id_gen++;
- }
- CHECK_LE(id, kMaxMutex);
- VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
- return id;
-}
-
-void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
- VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
- cb->lt->ctx, m, wlock, cb->lt->nlocked);
- DDPhysicalThread *pt = cb->pt;
- DDLogicalThread *lt = cb->lt;
-
- uptr owner = atomic_load(&m->owner, memory_order_relaxed);
- if (owner == (uptr)cb->lt) {
- VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
- cb->lt->ctx);
- return;
- }
-
- CHECK_LE(lt->nlocked, kMaxNesting);
-
- // FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
- if (m->id == kNoId)
- m->id = allocateId(cb);
-
- ThreadMutex *tm = <->locked[lt->nlocked++];
- tm->id = m->id;
- if (flags.second_deadlock_stack)
- tm->stk = cb->Unwind();
- if (lt->nlocked == 1) {
- VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
- cb->lt->ctx);
- return;
- }
-
- bool added = false;
- Mutex *mtx = getMutex(m->id);
- for (int i = 0; i < lt->nlocked - 1; i++) {
- u32 id1 = lt->locked[i].id;
- u32 stk1 = lt->locked[i].stk;
- Mutex *mtx1 = getMutex(id1);
- SpinMutexLock l(&mtx1->mtx);
- if (mtx1->nlink == kMaxLink) {
- // FIXME(dvyukov): check stale links
- continue;
- }
- int li = 0;
- for (; li < mtx1->nlink; li++) {
- Link *link = &mtx1->link[li];
- if (link->id == m->id) {
- if (link->seq != mtx->seq) {
- link->seq = mtx->seq;
- link->tid = lt->ctx;
- link->stk0 = stk1;
- link->stk1 = cb->Unwind();
- added = true;
- VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
- cb->lt->ctx, getMutexId(mtx1), m->id);
- }
- break;
- }
- }
- if (li == mtx1->nlink) {
- // FIXME(dvyukov): check stale links
- Link *link = &mtx1->link[mtx1->nlink++];
- link->id = m->id;
- link->seq = mtx->seq;
- link->tid = lt->ctx;
- link->stk0 = stk1;
- link->stk1 = cb->Unwind();
- added = true;
- VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
- cb->lt->ctx, getMutexId(mtx1), m->id);
- }
- }
-
- if (!added || mtx->nlink == 0) {
- VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
- cb->lt->ctx);
- return;
- }
-
- CycleCheck(pt, lt, m);
-}
-
-void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
- bool trylock) {
- VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
- cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
- DDLogicalThread *lt = cb->lt;
-
- uptr owner = atomic_load(&m->owner, memory_order_relaxed);
- if (owner == (uptr)cb->lt) {
- VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
- CHECK(wlock);
- m->recursion++;
- return;
- }
- CHECK_EQ(owner, 0);
- if (wlock) {
- VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
- CHECK_EQ(m->recursion, 0);
- m->recursion = 1;
- atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
- }
-
- if (!trylock)
- return;
-
- CHECK_LE(lt->nlocked, kMaxNesting);
- if (m->id == kNoId)
- m->id = allocateId(cb);
- ThreadMutex *tm = <->locked[lt->nlocked++];
- tm->id = m->id;
- if (flags.second_deadlock_stack)
- tm->stk = cb->Unwind();
-}
-
-void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
- VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
- cb->lt->ctx, m, wlock, cb->lt->nlocked);
- DDLogicalThread *lt = cb->lt;
-
- uptr owner = atomic_load(&m->owner, memory_order_relaxed);
- if (owner == (uptr)cb->lt) {
- VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
- if (--m->recursion > 0)
- return;
- VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
- atomic_store(&m->owner, 0, memory_order_relaxed);
- }
- CHECK_NE(m->id, kNoId);
- int last = lt->nlocked - 1;
- for (int i = last; i >= 0; i--) {
- if (cb->lt->locked[i].id == m->id) {
- lt->locked[i] = lt->locked[last];
- lt->nlocked--;
- break;
- }
- }
-}
-
-void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
- VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
- cb->lt->ctx, m);
- DDLogicalThread *lt = cb->lt;
-
- if (m->id == kNoId)
- return;
-
- // Remove the mutex from lt->locked if there.
- int last = lt->nlocked - 1;
- for (int i = last; i >= 0; i--) {
- if (lt->locked[i].id == m->id) {
- lt->locked[i] = lt->locked[last];
- lt->nlocked--;
- break;
- }
- }
-
- // Clear and invalidate the mutex descriptor.
- {
- Mutex *mtx = getMutex(m->id);
- SpinMutexLock l(&mtx->mtx);
- mtx->seq++;
- mtx->nlink = 0;
- }
-
- // Return id to cache.
- {
- SpinMutexLock l(&mtx);
- free_id.push_back(m->id);
- }
-}
-
-void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
- DDMutex *m) {
- internal_memset(pt->visited, 0, sizeof(pt->visited));
- int npath = 0;
- int npending = 0;
- {
- Mutex *mtx = getMutex(m->id);
- SpinMutexLock l(&mtx->mtx);
- for (int li = 0; li < mtx->nlink; li++)
- pt->pending[npending++] = mtx->link[li];
- }
- while (npending > 0) {
- Link link = pt->pending[--npending];
- if (link.id == kEndId) {
- npath--;
- continue;
- }
- if (pt->visited[link.id])
- continue;
- Mutex *mtx1 = getMutex(link.id);
- SpinMutexLock l(&mtx1->mtx);
- if (mtx1->seq != link.seq)
- continue;
- pt->visited[link.id] = true;
- if (mtx1->nlink == 0)
- continue;
- pt->path[npath++] = link;
- pt->pending[npending++] = Link(kEndId);
- if (link.id == m->id)
- return Report(pt, lt, npath); // Bingo!
- for (int li = 0; li < mtx1->nlink; li++) {
- Link *link1 = &mtx1->link[li];
- // Mutex *mtx2 = getMutex(link->id);
- // FIXME(dvyukov): fast seq check
- // FIXME(dvyukov): fast nlink != 0 check
- // FIXME(dvyukov): fast pending check?
- // FIXME(dvyukov): npending can be larger than kMaxMutex
- pt->pending[npending++] = *link1;
- }
- }
-}
-
-void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
- DDReport *rep = &pt->rep;
- rep->n = npath;
- for (int i = 0; i < npath; i++) {
- Link *link = &pt->path[i];
- Link *link0 = &pt->path[i ? i - 1 : npath - 1];
- rep->loop[i].thr_ctx = link->tid;
- rep->loop[i].mtx_ctx0 = link0->id;
- rep->loop[i].mtx_ctx1 = link->id;
- rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
- rep->loop[i].stk[1] = link->stk1;
- }
- pt->report_pending = true;
-}
-
-DDReport *DD::GetReport(DDCallback *cb) {
- if (!cb->pt->report_pending)
- return 0;
- cb->pt->report_pending = false;
- return &cb->pt->rep;
-}
-
-} // namespace __sanitizer
-#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
--- /dev/null
+//===-- sanitizer_deadlock_detector2.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Deadlock detector implementation based on adjacency lists.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_deadlock_detector_interface.h"
+#include "sanitizer_common.h"
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_mutex.h"
+
+#if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
+
+namespace __sanitizer {
+
+const int kMaxNesting = 64;
+const u32 kNoId = -1;
+const u32 kEndId = -2;
+const int kMaxLink = 8;
+const int kL1Size = 1024;
+const int kL2Size = 1024;
+const int kMaxMutex = kL1Size * kL2Size;
+
+struct Id {
+ u32 id;
+ u32 seq;
+
+ explicit Id(u32 id = 0, u32 seq = 0)
+ : id(id)
+ , seq(seq) {
+ }
+};
+
+struct Link {
+ u32 id;
+ u32 seq;
+ u32 tid;
+ u32 stk0;
+ u32 stk1;
+
+ explicit Link(u32 id = 0, u32 seq = 0, u32 tid = 0, u32 s0 = 0, u32 s1 = 0)
+ : id(id)
+ , seq(seq)
+ , tid(tid)
+ , stk0(s0)
+ , stk1(s1) {
+ }
+};
+
+struct DDPhysicalThread {
+ DDReport rep;
+ bool report_pending;
+ bool visited[kMaxMutex];
+ Link pending[kMaxMutex];
+ Link path[kMaxMutex];
+};
+
+struct ThreadMutex {
+ u32 id;
+ u32 stk;
+};
+
+struct DDLogicalThread {
+ u64 ctx;
+ ThreadMutex locked[kMaxNesting];
+ int nlocked;
+};
+
+struct Mutex {
+ StaticSpinMutex mtx;
+ u32 seq;
+ int nlink;
+ Link link[kMaxLink];
+};
+
+struct DD : public DDetector {
+ explicit DD(const DDFlags *flags);
+
+ DDPhysicalThread* CreatePhysicalThread();
+ void DestroyPhysicalThread(DDPhysicalThread *pt);
+
+ DDLogicalThread* CreateLogicalThread(u64 ctx);
+ void DestroyLogicalThread(DDLogicalThread *lt);
+
+ void MutexInit(DDCallback *cb, DDMutex *m);
+ void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock);
+ void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock);
+ void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock);
+ void MutexDestroy(DDCallback *cb, DDMutex *m);
+
+ DDReport *GetReport(DDCallback *cb);
+
+ void CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt, DDMutex *mtx);
+ void Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath);
+ u32 allocateId(DDCallback *cb);
+ Mutex *getMutex(u32 id);
+ u32 getMutexId(Mutex *m);
+
+ DDFlags flags;
+
+ Mutex* mutex[kL1Size];
+
+ SpinMutex mtx;
+ InternalMmapVector<u32> free_id;
+ int id_gen = 0;
+};
+
+DDetector *DDetector::Create(const DDFlags *flags) {
+ (void)flags;
+ void *mem = MmapOrDie(sizeof(DD), "deadlock detector");
+ return new(mem) DD(flags);
+}
+
+DD::DD(const DDFlags *flags) : flags(*flags) { free_id.reserve(1024); }
+
+DDPhysicalThread* DD::CreatePhysicalThread() {
+ DDPhysicalThread *pt = (DDPhysicalThread*)MmapOrDie(sizeof(DDPhysicalThread),
+ "deadlock detector (physical thread)");
+ return pt;
+}
+
+void DD::DestroyPhysicalThread(DDPhysicalThread *pt) {
+ pt->~DDPhysicalThread();
+ UnmapOrDie(pt, sizeof(DDPhysicalThread));
+}
+
+DDLogicalThread* DD::CreateLogicalThread(u64 ctx) {
+ DDLogicalThread *lt = (DDLogicalThread*)InternalAlloc(
+ sizeof(DDLogicalThread));
+ lt->ctx = ctx;
+ lt->nlocked = 0;
+ return lt;
+}
+
+void DD::DestroyLogicalThread(DDLogicalThread *lt) {
+ lt->~DDLogicalThread();
+ InternalFree(lt);
+}
+
+void DD::MutexInit(DDCallback *cb, DDMutex *m) {
+ VPrintf(2, "#%llu: DD::MutexInit(%p)\n", cb->lt->ctx, m);
+ m->id = kNoId;
+ m->recursion = 0;
+ atomic_store(&m->owner, 0, memory_order_relaxed);
+}
+
+Mutex *DD::getMutex(u32 id) {
+ return &mutex[id / kL2Size][id % kL2Size];
+}
+
+u32 DD::getMutexId(Mutex *m) {
+ for (int i = 0; i < kL1Size; i++) {
+ Mutex *tab = mutex[i];
+ if (tab == 0)
+ break;
+ if (m >= tab && m < tab + kL2Size)
+ return i * kL2Size + (m - tab);
+ }
+ return -1;
+}
+
+u32 DD::allocateId(DDCallback *cb) {
+ u32 id = -1;
+ SpinMutexLock l(&mtx);
+ if (free_id.size() > 0) {
+ id = free_id.back();
+ free_id.pop_back();
+ } else {
+ CHECK_LT(id_gen, kMaxMutex);
+ if ((id_gen % kL2Size) == 0) {
+ mutex[id_gen / kL2Size] = (Mutex*)MmapOrDie(kL2Size * sizeof(Mutex),
+ "deadlock detector (mutex table)");
+ }
+ id = id_gen++;
+ }
+ CHECK_LE(id, kMaxMutex);
+ VPrintf(3, "#%llu: DD::allocateId assign id %d\n", cb->lt->ctx, id);
+ return id;
+}
+
+void DD::MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) {
+ VPrintf(2, "#%llu: DD::MutexBeforeLock(%p, wlock=%d) nlocked=%d\n",
+ cb->lt->ctx, m, wlock, cb->lt->nlocked);
+ DDPhysicalThread *pt = cb->pt;
+ DDLogicalThread *lt = cb->lt;
+
+ uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+ if (owner == (uptr)cb->lt) {
+ VPrintf(3, "#%llu: DD::MutexBeforeLock recursive\n",
+ cb->lt->ctx);
+ return;
+ }
+
+ CHECK_LE(lt->nlocked, kMaxNesting);
+
+ // FIXME(dvyukov): don't allocate id if lt->nlocked == 0?
+ if (m->id == kNoId)
+ m->id = allocateId(cb);
+
+ ThreadMutex *tm = <->locked[lt->nlocked++];
+ tm->id = m->id;
+ if (flags.second_deadlock_stack)
+ tm->stk = cb->Unwind();
+ if (lt->nlocked == 1) {
+ VPrintf(3, "#%llu: DD::MutexBeforeLock first mutex\n",
+ cb->lt->ctx);
+ return;
+ }
+
+ bool added = false;
+ Mutex *mtx = getMutex(m->id);
+ for (int i = 0; i < lt->nlocked - 1; i++) {
+ u32 id1 = lt->locked[i].id;
+ u32 stk1 = lt->locked[i].stk;
+ Mutex *mtx1 = getMutex(id1);
+ SpinMutexLock l(&mtx1->mtx);
+ if (mtx1->nlink == kMaxLink) {
+ // FIXME(dvyukov): check stale links
+ continue;
+ }
+ int li = 0;
+ for (; li < mtx1->nlink; li++) {
+ Link *link = &mtx1->link[li];
+ if (link->id == m->id) {
+ if (link->seq != mtx->seq) {
+ link->seq = mtx->seq;
+ link->tid = lt->ctx;
+ link->stk0 = stk1;
+ link->stk1 = cb->Unwind();
+ added = true;
+ VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
+ cb->lt->ctx, getMutexId(mtx1), m->id);
+ }
+ break;
+ }
+ }
+ if (li == mtx1->nlink) {
+ // FIXME(dvyukov): check stale links
+ Link *link = &mtx1->link[mtx1->nlink++];
+ link->id = m->id;
+ link->seq = mtx->seq;
+ link->tid = lt->ctx;
+ link->stk0 = stk1;
+ link->stk1 = cb->Unwind();
+ added = true;
+ VPrintf(3, "#%llu: DD::MutexBeforeLock added %d->%d link\n",
+ cb->lt->ctx, getMutexId(mtx1), m->id);
+ }
+ }
+
+ if (!added || mtx->nlink == 0) {
+ VPrintf(3, "#%llu: DD::MutexBeforeLock don't check\n",
+ cb->lt->ctx);
+ return;
+ }
+
+ CycleCheck(pt, lt, m);
+}
+
+void DD::MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock,
+ bool trylock) {
+ VPrintf(2, "#%llu: DD::MutexAfterLock(%p, wlock=%d, try=%d) nlocked=%d\n",
+ cb->lt->ctx, m, wlock, trylock, cb->lt->nlocked);
+ DDLogicalThread *lt = cb->lt;
+
+ uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+ if (owner == (uptr)cb->lt) {
+ VPrintf(3, "#%llu: DD::MutexAfterLock recursive\n", cb->lt->ctx);
+ CHECK(wlock);
+ m->recursion++;
+ return;
+ }
+ CHECK_EQ(owner, 0);
+ if (wlock) {
+ VPrintf(3, "#%llu: DD::MutexAfterLock set owner\n", cb->lt->ctx);
+ CHECK_EQ(m->recursion, 0);
+ m->recursion = 1;
+ atomic_store(&m->owner, (uptr)cb->lt, memory_order_relaxed);
+ }
+
+ if (!trylock)
+ return;
+
+ CHECK_LE(lt->nlocked, kMaxNesting);
+ if (m->id == kNoId)
+ m->id = allocateId(cb);
+ ThreadMutex *tm = <->locked[lt->nlocked++];
+ tm->id = m->id;
+ if (flags.second_deadlock_stack)
+ tm->stk = cb->Unwind();
+}
+
+void DD::MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) {
+ VPrintf(2, "#%llu: DD::MutexBeforeUnlock(%p, wlock=%d) nlocked=%d\n",
+ cb->lt->ctx, m, wlock, cb->lt->nlocked);
+ DDLogicalThread *lt = cb->lt;
+
+ uptr owner = atomic_load(&m->owner, memory_order_relaxed);
+ if (owner == (uptr)cb->lt) {
+ VPrintf(3, "#%llu: DD::MutexBeforeUnlock recursive\n", cb->lt->ctx);
+ if (--m->recursion > 0)
+ return;
+ VPrintf(3, "#%llu: DD::MutexBeforeUnlock reset owner\n", cb->lt->ctx);
+ atomic_store(&m->owner, 0, memory_order_relaxed);
+ }
+ CHECK_NE(m->id, kNoId);
+ int last = lt->nlocked - 1;
+ for (int i = last; i >= 0; i--) {
+ if (cb->lt->locked[i].id == m->id) {
+ lt->locked[i] = lt->locked[last];
+ lt->nlocked--;
+ break;
+ }
+ }
+}
+
+void DD::MutexDestroy(DDCallback *cb, DDMutex *m) {
+ VPrintf(2, "#%llu: DD::MutexDestroy(%p)\n",
+ cb->lt->ctx, m);
+ DDLogicalThread *lt = cb->lt;
+
+ if (m->id == kNoId)
+ return;
+
+ // Remove the mutex from lt->locked if there.
+ int last = lt->nlocked - 1;
+ for (int i = last; i >= 0; i--) {
+ if (lt->locked[i].id == m->id) {
+ lt->locked[i] = lt->locked[last];
+ lt->nlocked--;
+ break;
+ }
+ }
+
+ // Clear and invalidate the mutex descriptor.
+ {
+ Mutex *mtx = getMutex(m->id);
+ SpinMutexLock l(&mtx->mtx);
+ mtx->seq++;
+ mtx->nlink = 0;
+ }
+
+ // Return id to cache.
+ {
+ SpinMutexLock l(&mtx);
+ free_id.push_back(m->id);
+ }
+}
+
+void DD::CycleCheck(DDPhysicalThread *pt, DDLogicalThread *lt,
+ DDMutex *m) {
+ internal_memset(pt->visited, 0, sizeof(pt->visited));
+ int npath = 0;
+ int npending = 0;
+ {
+ Mutex *mtx = getMutex(m->id);
+ SpinMutexLock l(&mtx->mtx);
+ for (int li = 0; li < mtx->nlink; li++)
+ pt->pending[npending++] = mtx->link[li];
+ }
+ while (npending > 0) {
+ Link link = pt->pending[--npending];
+ if (link.id == kEndId) {
+ npath--;
+ continue;
+ }
+ if (pt->visited[link.id])
+ continue;
+ Mutex *mtx1 = getMutex(link.id);
+ SpinMutexLock l(&mtx1->mtx);
+ if (mtx1->seq != link.seq)
+ continue;
+ pt->visited[link.id] = true;
+ if (mtx1->nlink == 0)
+ continue;
+ pt->path[npath++] = link;
+ pt->pending[npending++] = Link(kEndId);
+ if (link.id == m->id)
+ return Report(pt, lt, npath); // Bingo!
+ for (int li = 0; li < mtx1->nlink; li++) {
+ Link *link1 = &mtx1->link[li];
+ // Mutex *mtx2 = getMutex(link->id);
+ // FIXME(dvyukov): fast seq check
+ // FIXME(dvyukov): fast nlink != 0 check
+ // FIXME(dvyukov): fast pending check?
+ // FIXME(dvyukov): npending can be larger than kMaxMutex
+ pt->pending[npending++] = *link1;
+ }
+ }
+}
+
+void DD::Report(DDPhysicalThread *pt, DDLogicalThread *lt, int npath) {
+ DDReport *rep = &pt->rep;
+ rep->n = npath;
+ for (int i = 0; i < npath; i++) {
+ Link *link = &pt->path[i];
+ Link *link0 = &pt->path[i ? i - 1 : npath - 1];
+ rep->loop[i].thr_ctx = link->tid;
+ rep->loop[i].mtx_ctx0 = link0->id;
+ rep->loop[i].mtx_ctx1 = link->id;
+ rep->loop[i].stk[0] = flags.second_deadlock_stack ? link->stk0 : 0;
+ rep->loop[i].stk[1] = link->stk1;
+ }
+ pt->report_pending = true;
+}
+
+DDReport *DD::GetReport(DDCallback *cb) {
+ if (!cb->pt->report_pending)
+ return 0;
+ cb->pt->report_pending = false;
+ return &cb->pt->rep;
+}
+
+} // namespace __sanitizer
+#endif // #if SANITIZER_DEADLOCK_DETECTOR_VERSION == 2
//===-- sanitizer_deadlock_detector_interface.h -----------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_errno.cc --------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between sanitizers run-time libraries.
-//
-// Defines errno to avoid including errno.h and its dependencies into other
-// files (e.g. interceptors are not supposed to include any system headers).
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_errno_codes.h"
-#include "sanitizer_internal_defs.h"
-
-#include <errno.h>
-
-namespace __sanitizer {
-
-COMPILER_CHECK(errno_ENOMEM == ENOMEM);
-COMPILER_CHECK(errno_EBUSY == EBUSY);
-COMPILER_CHECK(errno_EINVAL == EINVAL);
-
-// EOWNERDEAD is not present in some older platforms.
-#if defined(EOWNERDEAD)
-extern const int errno_EOWNERDEAD = EOWNERDEAD;
-#else
-extern const int errno_EOWNERDEAD = -1;
-#endif
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_errno.cpp -------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizers run-time libraries.
+//
+// Defines errno to avoid including errno.h and its dependencies into other
+// files (e.g. interceptors are not supposed to include any system headers).
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_errno_codes.h"
+#include "sanitizer_internal_defs.h"
+
+#include <errno.h>
+
+namespace __sanitizer {
+
+COMPILER_CHECK(errno_ENOMEM == ENOMEM);
+COMPILER_CHECK(errno_EBUSY == EBUSY);
+COMPILER_CHECK(errno_EINVAL == EINVAL);
+
+// EOWNERDEAD is not present in some older platforms.
+#if defined(EOWNERDEAD)
+extern const int errno_EOWNERDEAD = EOWNERDEAD;
+#else
+extern const int errno_EOWNERDEAD = -1;
+#endif
+
+} // namespace __sanitizer
//===-- sanitizer_errno.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_errno_codes.h ---------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_file.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===---------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries. It defines filesystem-related interfaces. This
-// is separate from sanitizer_common.cc so that it's simpler to disable
-// all the filesystem support code for a port that doesn't use it.
-//
-//===---------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if !SANITIZER_FUCHSIA
-
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-
-namespace __sanitizer {
-
-void CatastrophicErrorWrite(const char *buffer, uptr length) {
- WriteToFile(kStderrFd, buffer, length);
-}
-
-StaticSpinMutex report_file_mu;
-ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
-
-void RawWrite(const char *buffer) {
- report_file.Write(buffer, internal_strlen(buffer));
-}
-
-void ReportFile::ReopenIfNecessary() {
- mu->CheckLocked();
- if (fd == kStdoutFd || fd == kStderrFd) return;
-
- uptr pid = internal_getpid();
- // If in tracer, use the parent's file.
- if (pid == stoptheworld_tracer_pid)
- pid = stoptheworld_tracer_ppid;
- if (fd != kInvalidFd) {
- // If the report file is already opened by the current process,
- // do nothing. Otherwise the report file was opened by the parent
- // process, close it now.
- if (fd_pid == pid)
- return;
- else
- CloseFile(fd);
- }
-
- const char *exe_name = GetProcessName();
- if (common_flags()->log_exe_name && exe_name) {
- internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
- exe_name, pid);
- } else {
- internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
- }
- fd = OpenFile(full_path, WrOnly);
- if (fd == kInvalidFd) {
- const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
- WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
- WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
- Die();
- }
- fd_pid = pid;
-}
-
-void ReportFile::SetReportPath(const char *path) {
- if (!path)
- return;
- uptr len = internal_strlen(path);
- if (len > sizeof(path_prefix) - 100) {
- Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
- path[0], path[1], path[2], path[3],
- path[4], path[5], path[6], path[7]);
- Die();
- }
-
- SpinMutexLock l(mu);
- if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
- CloseFile(fd);
- fd = kInvalidFd;
- if (internal_strcmp(path, "stdout") == 0) {
- fd = kStdoutFd;
- } else if (internal_strcmp(path, "stderr") == 0) {
- fd = kStderrFd;
- } else {
- internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
- }
-}
-
-bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
- uptr *read_len, uptr max_len, error_t *errno_p) {
- *buff = nullptr;
- *buff_size = 0;
- *read_len = 0;
- if (!max_len)
- return true;
- uptr PageSize = GetPageSizeCached();
- uptr kMinFileLen = Min(PageSize, max_len);
-
- // The files we usually open are not seekable, so try different buffer sizes.
- for (uptr size = kMinFileLen;; size = Min(size * 2, max_len)) {
- UnmapOrDie(*buff, *buff_size);
- *buff = (char*)MmapOrDie(size, __func__);
- *buff_size = size;
- fd_t fd = OpenFile(file_name, RdOnly, errno_p);
- if (fd == kInvalidFd) {
- UnmapOrDie(*buff, *buff_size);
- return false;
- }
- *read_len = 0;
- // Read up to one page at a time.
- bool reached_eof = false;
- while (*read_len < size) {
- uptr just_read;
- if (!ReadFromFile(fd, *buff + *read_len, size - *read_len, &just_read,
- errno_p)) {
- UnmapOrDie(*buff, *buff_size);
- CloseFile(fd);
- return false;
- }
- *read_len += just_read;
- if (just_read == 0 || *read_len == max_len) {
- reached_eof = true;
- break;
- }
- }
- CloseFile(fd);
- if (reached_eof) // We've read the whole file.
- break;
- }
- return true;
-}
-
-bool ReadFileToVector(const char *file_name,
- InternalMmapVectorNoCtor<char> *buff, uptr max_len,
- error_t *errno_p) {
- buff->clear();
- if (!max_len)
- return true;
- uptr PageSize = GetPageSizeCached();
- fd_t fd = OpenFile(file_name, RdOnly, errno_p);
- if (fd == kInvalidFd)
- return false;
- uptr read_len = 0;
- while (read_len < max_len) {
- if (read_len >= buff->size())
- buff->resize(Min(Max(PageSize, read_len * 2), max_len));
- CHECK_LT(read_len, buff->size());
- CHECK_LE(buff->size(), max_len);
- uptr just_read;
- if (!ReadFromFile(fd, buff->data() + read_len, buff->size() - read_len,
- &just_read, errno_p)) {
- CloseFile(fd);
- return false;
- }
- read_len += just_read;
- if (!just_read)
- break;
- }
- CloseFile(fd);
- buff->resize(read_len);
- return true;
-}
-
-static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
-
-char *FindPathToBinary(const char *name) {
- if (FileExists(name)) {
- return internal_strdup(name);
- }
-
- const char *path = GetEnv("PATH");
- if (!path)
- return nullptr;
- uptr name_len = internal_strlen(name);
- InternalMmapVector<char> buffer(kMaxPathLength);
- const char *beg = path;
- while (true) {
- const char *end = internal_strchrnul(beg, kPathSeparator);
- uptr prefix_len = end - beg;
- if (prefix_len + name_len + 2 <= kMaxPathLength) {
- internal_memcpy(buffer.data(), beg, prefix_len);
- buffer[prefix_len] = '/';
- internal_memcpy(&buffer[prefix_len + 1], name, name_len);
- buffer[prefix_len + 1 + name_len] = '\0';
- if (FileExists(buffer.data()))
- return internal_strdup(buffer.data());
- }
- if (*end == '\0') break;
- beg = end + 1;
- }
- return nullptr;
-}
-
-} // namespace __sanitizer
-
-using namespace __sanitizer; // NOLINT
-
-extern "C" {
-void __sanitizer_set_report_path(const char *path) {
- report_file.SetReportPath(path);
-}
-
-void __sanitizer_set_report_fd(void *fd) {
- report_file.fd = (fd_t)reinterpret_cast<uptr>(fd);
- report_file.fd_pid = internal_getpid();
-}
-} // extern "C"
-
-#endif // !SANITIZER_FUCHSIA
--- /dev/null
+//===-- sanitizer_file.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries. It defines filesystem-related interfaces. This
+// is separate from sanitizer_common.cpp so that it's simpler to disable
+// all the filesystem support code for a port that doesn't use it.
+//
+//===---------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if !SANITIZER_FUCHSIA
+
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+
+namespace __sanitizer {
+
+void CatastrophicErrorWrite(const char *buffer, uptr length) {
+ WriteToFile(kStderrFd, buffer, length);
+}
+
+StaticSpinMutex report_file_mu;
+ReportFile report_file = {&report_file_mu, kStderrFd, "", "", 0};
+
+void RawWrite(const char *buffer) {
+ report_file.Write(buffer, internal_strlen(buffer));
+}
+
+void ReportFile::ReopenIfNecessary() {
+ mu->CheckLocked();
+ if (fd == kStdoutFd || fd == kStderrFd) return;
+
+ uptr pid = internal_getpid();
+ // If in tracer, use the parent's file.
+ if (pid == stoptheworld_tracer_pid)
+ pid = stoptheworld_tracer_ppid;
+ if (fd != kInvalidFd) {
+ // If the report file is already opened by the current process,
+ // do nothing. Otherwise the report file was opened by the parent
+ // process, close it now.
+ if (fd_pid == pid)
+ return;
+ else
+ CloseFile(fd);
+ }
+
+ const char *exe_name = GetProcessName();
+ if (common_flags()->log_exe_name && exe_name) {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix,
+ exe_name, pid);
+ } else {
+ internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid);
+ }
+ fd = OpenFile(full_path, WrOnly);
+ if (fd == kInvalidFd) {
+ const char *ErrorMsgPrefix = "ERROR: Can't open file: ";
+ WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix));
+ WriteToFile(kStderrFd, full_path, internal_strlen(full_path));
+ Die();
+ }
+ fd_pid = pid;
+}
+
+void ReportFile::SetReportPath(const char *path) {
+ if (!path)
+ return;
+ uptr len = internal_strlen(path);
+ if (len > sizeof(path_prefix) - 100) {
+ Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n",
+ path[0], path[1], path[2], path[3],
+ path[4], path[5], path[6], path[7]);
+ Die();
+ }
+
+ SpinMutexLock l(mu);
+ if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd)
+ CloseFile(fd);
+ fd = kInvalidFd;
+ if (internal_strcmp(path, "stdout") == 0) {
+ fd = kStdoutFd;
+ } else if (internal_strcmp(path, "stderr") == 0) {
+ fd = kStderrFd;
+ } else {
+ internal_snprintf(path_prefix, kMaxPathLength, "%s", path);
+ }
+}
+
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+ uptr *read_len, uptr max_len, error_t *errno_p) {
+ *buff = nullptr;
+ *buff_size = 0;
+ *read_len = 0;
+ if (!max_len)
+ return true;
+ uptr PageSize = GetPageSizeCached();
+ uptr kMinFileLen = Min(PageSize, max_len);
+
+ // The files we usually open are not seekable, so try different buffer sizes.
+ for (uptr size = kMinFileLen;; size = Min(size * 2, max_len)) {
+ UnmapOrDie(*buff, *buff_size);
+ *buff = (char*)MmapOrDie(size, __func__);
+ *buff_size = size;
+ fd_t fd = OpenFile(file_name, RdOnly, errno_p);
+ if (fd == kInvalidFd) {
+ UnmapOrDie(*buff, *buff_size);
+ return false;
+ }
+ *read_len = 0;
+ // Read up to one page at a time.
+ bool reached_eof = false;
+ while (*read_len < size) {
+ uptr just_read;
+ if (!ReadFromFile(fd, *buff + *read_len, size - *read_len, &just_read,
+ errno_p)) {
+ UnmapOrDie(*buff, *buff_size);
+ CloseFile(fd);
+ return false;
+ }
+ *read_len += just_read;
+ if (just_read == 0 || *read_len == max_len) {
+ reached_eof = true;
+ break;
+ }
+ }
+ CloseFile(fd);
+ if (reached_eof) // We've read the whole file.
+ break;
+ }
+ return true;
+}
+
+bool ReadFileToVector(const char *file_name,
+ InternalMmapVectorNoCtor<char> *buff, uptr max_len,
+ error_t *errno_p) {
+ buff->clear();
+ if (!max_len)
+ return true;
+ uptr PageSize = GetPageSizeCached();
+ fd_t fd = OpenFile(file_name, RdOnly, errno_p);
+ if (fd == kInvalidFd)
+ return false;
+ uptr read_len = 0;
+ while (read_len < max_len) {
+ if (read_len >= buff->size())
+ buff->resize(Min(Max(PageSize, read_len * 2), max_len));
+ CHECK_LT(read_len, buff->size());
+ CHECK_LE(buff->size(), max_len);
+ uptr just_read;
+ if (!ReadFromFile(fd, buff->data() + read_len, buff->size() - read_len,
+ &just_read, errno_p)) {
+ CloseFile(fd);
+ return false;
+ }
+ read_len += just_read;
+ if (!just_read)
+ break;
+ }
+ CloseFile(fd);
+ buff->resize(read_len);
+ return true;
+}
+
+static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
+
+char *FindPathToBinary(const char *name) {
+ if (FileExists(name)) {
+ return internal_strdup(name);
+ }
+
+ const char *path = GetEnv("PATH");
+ if (!path)
+ return nullptr;
+ uptr name_len = internal_strlen(name);
+ InternalMmapVector<char> buffer(kMaxPathLength);
+ const char *beg = path;
+ while (true) {
+ const char *end = internal_strchrnul(beg, kPathSeparator);
+ uptr prefix_len = end - beg;
+ if (prefix_len + name_len + 2 <= kMaxPathLength) {
+ internal_memcpy(buffer.data(), beg, prefix_len);
+ buffer[prefix_len] = '/';
+ internal_memcpy(&buffer[prefix_len + 1], name, name_len);
+ buffer[prefix_len + 1 + name_len] = '\0';
+ if (FileExists(buffer.data()))
+ return internal_strdup(buffer.data());
+ }
+ if (*end == '\0') break;
+ beg = end + 1;
+ }
+ return nullptr;
+}
+
+} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+void __sanitizer_set_report_path(const char *path) {
+ report_file.SetReportPath(path);
+}
+
+void __sanitizer_set_report_fd(void *fd) {
+ report_file.fd = (fd_t)reinterpret_cast<uptr>(fd);
+ report_file.fd_pid = internal_getpid();
+}
+} // extern "C"
+
+#endif // !SANITIZER_FUCHSIA
//===-- sanitizer_file.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
bool WriteToFile(fd_t fd, const void *buff, uptr buff_size,
uptr *bytes_written = nullptr, error_t *error_p = nullptr);
-bool RenameFile(const char *oldpath, const char *newpath,
- error_t *error_p = nullptr);
-
// Scoped file handle closer.
struct FileCloser {
explicit FileCloser(fd_t fd) : fd(fd) {}
+++ /dev/null
-//===-- sanitizer_flag_parser.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_flag_parser.h"
-
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_flag_parser.h"
-
-namespace __sanitizer {
-
-LowLevelAllocator FlagParser::Alloc;
-
-class UnknownFlags {
- static const int kMaxUnknownFlags = 20;
- const char *unknown_flags_[kMaxUnknownFlags];
- int n_unknown_flags_;
-
- public:
- void Add(const char *name) {
- CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
- unknown_flags_[n_unknown_flags_++] = name;
- }
-
- void Report() {
- if (!n_unknown_flags_) return;
- Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
- for (int i = 0; i < n_unknown_flags_; ++i)
- Printf(" %s\n", unknown_flags_[i]);
- n_unknown_flags_ = 0;
- }
-};
-
-UnknownFlags unknown_flags;
-
-void ReportUnrecognizedFlags() {
- unknown_flags.Report();
-}
-
-char *FlagParser::ll_strndup(const char *s, uptr n) {
- uptr len = internal_strnlen(s, n);
- char *s2 = (char*)Alloc.Allocate(len + 1);
- internal_memcpy(s2, s, len);
- s2[len] = 0;
- return s2;
-}
-
-void FlagParser::PrintFlagDescriptions() {
- Printf("Available flags for %s:\n", SanitizerToolName);
- for (int i = 0; i < n_flags_; ++i)
- Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc);
-}
-
-void FlagParser::fatal_error(const char *err) {
- Printf("ERROR: %s\n", err);
- Die();
-}
-
-bool FlagParser::is_space(char c) {
- return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
- c == '\r';
-}
-
-void FlagParser::skip_whitespace() {
- while (is_space(buf_[pos_])) ++pos_;
-}
-
-void FlagParser::parse_flag() {
- uptr name_start = pos_;
- while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
- if (buf_[pos_] != '=') fatal_error("expected '='");
- char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
-
- uptr value_start = ++pos_;
- char *value;
- if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
- char quote = buf_[pos_++];
- while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
- if (buf_[pos_] == 0) fatal_error("unterminated string");
- value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
- ++pos_; // consume the closing quote
- } else {
- while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
- if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
- fatal_error("expected separator or eol");
- value = ll_strndup(buf_ + value_start, pos_ - value_start);
- }
-
- bool res = run_handler(name, value);
- if (!res) fatal_error("Flag parsing failed.");
-}
-
-void FlagParser::parse_flags() {
- while (true) {
- skip_whitespace();
- if (buf_[pos_] == 0) break;
- parse_flag();
- }
-
- // Do a sanity check for certain flags.
- if (common_flags_dont_use.malloc_context_size < 1)
- common_flags_dont_use.malloc_context_size = 1;
-}
-
-void FlagParser::ParseString(const char *s) {
- if (!s) return;
- // Backup current parser state to allow nested ParseString() calls.
- const char *old_buf_ = buf_;
- uptr old_pos_ = pos_;
- buf_ = s;
- pos_ = 0;
-
- parse_flags();
-
- buf_ = old_buf_;
- pos_ = old_pos_;
-}
-
-bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
- static const uptr kMaxIncludeSize = 1 << 15;
- char *data;
- uptr data_mapped_size;
- error_t err;
- uptr len;
- if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
- Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
- if (ignore_missing)
- return true;
- Printf("Failed to read options from '%s': error %d\n", path, err);
- return false;
- }
- ParseString(data);
- UnmapOrDie(data, data_mapped_size);
- return true;
-}
-
-bool FlagParser::run_handler(const char *name, const char *value) {
- for (int i = 0; i < n_flags_; ++i) {
- if (internal_strcmp(name, flags_[i].name) == 0)
- return flags_[i].handler->Parse(value);
- }
- // Unrecognized flag. This is not a fatal error, we may print a warning later.
- unknown_flags.Add(name);
- return true;
-}
-
-void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
- const char *desc) {
- CHECK_LT(n_flags_, kMaxFlags);
- flags_[n_flags_].name = name;
- flags_[n_flags_].desc = desc;
- flags_[n_flags_].handler = handler;
- ++n_flags_;
-}
-
-FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
- flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_flag_parser.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flag_parser.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_flag_parser.h"
+
+namespace __sanitizer {
+
+LowLevelAllocator FlagParser::Alloc;
+
+class UnknownFlags {
+ static const int kMaxUnknownFlags = 20;
+ const char *unknown_flags_[kMaxUnknownFlags];
+ int n_unknown_flags_;
+
+ public:
+ void Add(const char *name) {
+ CHECK_LT(n_unknown_flags_, kMaxUnknownFlags);
+ unknown_flags_[n_unknown_flags_++] = name;
+ }
+
+ void Report() {
+ if (!n_unknown_flags_) return;
+ Printf("WARNING: found %d unrecognized flag(s):\n", n_unknown_flags_);
+ for (int i = 0; i < n_unknown_flags_; ++i)
+ Printf(" %s\n", unknown_flags_[i]);
+ n_unknown_flags_ = 0;
+ }
+};
+
+UnknownFlags unknown_flags;
+
+void ReportUnrecognizedFlags() {
+ unknown_flags.Report();
+}
+
+char *FlagParser::ll_strndup(const char *s, uptr n) {
+ uptr len = internal_strnlen(s, n);
+ char *s2 = (char*)Alloc.Allocate(len + 1);
+ internal_memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+}
+
+void FlagParser::PrintFlagDescriptions() {
+ Printf("Available flags for %s:\n", SanitizerToolName);
+ for (int i = 0; i < n_flags_; ++i)
+ Printf("\t%s\n\t\t- %s\n", flags_[i].name, flags_[i].desc);
+}
+
+void FlagParser::fatal_error(const char *err) {
+ Printf("%s: ERROR: %s\n", SanitizerToolName, err);
+ Die();
+}
+
+bool FlagParser::is_space(char c) {
+ return c == ' ' || c == ',' || c == ':' || c == '\n' || c == '\t' ||
+ c == '\r';
+}
+
+void FlagParser::skip_whitespace() {
+ while (is_space(buf_[pos_])) ++pos_;
+}
+
+void FlagParser::parse_flag(const char *env_option_name) {
+ uptr name_start = pos_;
+ while (buf_[pos_] != 0 && buf_[pos_] != '=' && !is_space(buf_[pos_])) ++pos_;
+ if (buf_[pos_] != '=') {
+ if (env_option_name) {
+ Printf("%s: ERROR: expected '=' in %s\n", SanitizerToolName,
+ env_option_name);
+ Die();
+ } else
+ fatal_error("expected '='");
+ }
+ char *name = ll_strndup(buf_ + name_start, pos_ - name_start);
+
+ uptr value_start = ++pos_;
+ char *value;
+ if (buf_[pos_] == '\'' || buf_[pos_] == '"') {
+ char quote = buf_[pos_++];
+ while (buf_[pos_] != 0 && buf_[pos_] != quote) ++pos_;
+ if (buf_[pos_] == 0) fatal_error("unterminated string");
+ value = ll_strndup(buf_ + value_start + 1, pos_ - value_start - 1);
+ ++pos_; // consume the closing quote
+ } else {
+ while (buf_[pos_] != 0 && !is_space(buf_[pos_])) ++pos_;
+ if (buf_[pos_] != 0 && !is_space(buf_[pos_]))
+ fatal_error("expected separator or eol");
+ value = ll_strndup(buf_ + value_start, pos_ - value_start);
+ }
+
+ bool res = run_handler(name, value);
+ if (!res) fatal_error("Flag parsing failed.");
+}
+
+void FlagParser::parse_flags(const char *env_option_name) {
+ while (true) {
+ skip_whitespace();
+ if (buf_[pos_] == 0) break;
+ parse_flag(env_option_name);
+ }
+
+ // Do a sanity check for certain flags.
+ if (common_flags_dont_use.malloc_context_size < 1)
+ common_flags_dont_use.malloc_context_size = 1;
+}
+
+void FlagParser::ParseStringFromEnv(const char *env_name) {
+ const char *env = GetEnv(env_name);
+ VPrintf(1, "%s: %s\n", env_name, env ? env : "<empty>");
+ ParseString(env, env_name);
+}
+
+void FlagParser::ParseString(const char *s, const char *env_option_name) {
+ if (!s) return;
+ // Backup current parser state to allow nested ParseString() calls.
+ const char *old_buf_ = buf_;
+ uptr old_pos_ = pos_;
+ buf_ = s;
+ pos_ = 0;
+
+ parse_flags(env_option_name);
+
+ buf_ = old_buf_;
+ pos_ = old_pos_;
+}
+
+bool FlagParser::ParseFile(const char *path, bool ignore_missing) {
+ static const uptr kMaxIncludeSize = 1 << 15;
+ char *data;
+ uptr data_mapped_size;
+ error_t err;
+ uptr len;
+ if (!ReadFileToBuffer(path, &data, &data_mapped_size, &len,
+ Max(kMaxIncludeSize, GetPageSizeCached()), &err)) {
+ if (ignore_missing)
+ return true;
+ Printf("Failed to read options from '%s': error %d\n", path, err);
+ return false;
+ }
+ ParseString(data, path);
+ UnmapOrDie(data, data_mapped_size);
+ return true;
+}
+
+bool FlagParser::run_handler(const char *name, const char *value) {
+ for (int i = 0; i < n_flags_; ++i) {
+ if (internal_strcmp(name, flags_[i].name) == 0)
+ return flags_[i].handler->Parse(value);
+ }
+ // Unrecognized flag. This is not a fatal error, we may print a warning later.
+ unknown_flags.Add(name);
+ return true;
+}
+
+void FlagParser::RegisterHandler(const char *name, FlagHandlerBase *handler,
+ const char *desc) {
+ CHECK_LT(n_flags_, kMaxFlags);
+ flags_[n_flags_].name = name;
+ flags_[n_flags_].desc = desc;
+ flags_[n_flags_].handler = handler;
+ ++n_flags_;
+}
+
+FlagParser::FlagParser() : n_flags_(0), buf_(nullptr), pos_(0) {
+ flags_ = (Flag *)Alloc.Allocate(sizeof(Flag) * kMaxFlags);
+}
+
+} // namespace __sanitizer
//===-- sanitizer_flag_parser.h ---------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
class FlagHandlerBase {
public:
virtual bool Parse(const char *value) { return false; }
+
+ protected:
+ ~FlagHandlerBase() {};
};
template <typename T>
return ok;
}
+template <>
+inline bool FlagHandler<s64>::Parse(const char *value) {
+ const char *value_end;
+ *t_ = internal_simple_strtoll(value, &value_end, 10);
+ bool ok = *value_end == 0;
+ if (!ok) Printf("ERROR: Invalid value for s64 option: '%s'\n", value);
+ return ok;
+}
+
class FlagParser {
static const int kMaxFlags = 200;
struct Flag {
FlagParser();
void RegisterHandler(const char *name, FlagHandlerBase *handler,
const char *desc);
- void ParseString(const char *s);
+ void ParseString(const char *s, const char *env_name = 0);
+ void ParseStringFromEnv(const char *env_name);
bool ParseFile(const char *path, bool ignore_missing);
void PrintFlagDescriptions();
void fatal_error(const char *err);
bool is_space(char c);
void skip_whitespace();
- void parse_flags();
- void parse_flag();
+ void parse_flags(const char *env_option_name);
+ void parse_flag(const char *env_option_name);
bool run_handler(const char *name, const char *value);
char *ll_strndup(const char *s, uptr n);
};
+++ /dev/null
-//===-- sanitizer_flags.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_flags.h"
-
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_list.h"
-#include "sanitizer_flag_parser.h"
-
-namespace __sanitizer {
-
-CommonFlags common_flags_dont_use;
-
-void CommonFlags::SetDefaults() {
-#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
-#include "sanitizer_flags.inc"
-#undef COMMON_FLAG
-}
-
-void CommonFlags::CopyFrom(const CommonFlags &other) {
- internal_memcpy(this, &other, sizeof(*this));
-}
-
-// Copy the string from "s" to "out", making the following substitutions:
-// %b = binary basename
-// %p = pid
-void SubstituteForFlagValue(const char *s, char *out, uptr out_size) {
- char *out_end = out + out_size;
- while (*s && out < out_end - 1) {
- if (s[0] != '%') {
- *out++ = *s++;
- continue;
- }
- switch (s[1]) {
- case 'b': {
- const char *base = GetProcessName();
- CHECK(base);
- while (*base && out < out_end - 1)
- *out++ = *base++;
- s += 2; // skip "%b"
- break;
- }
- case 'p': {
- int pid = internal_getpid();
- char buf[32];
- char *buf_pos = buf + 32;
- do {
- *--buf_pos = (pid % 10) + '0';
- pid /= 10;
- } while (pid);
- while (buf_pos < buf + 32 && out < out_end - 1)
- *out++ = *buf_pos++;
- s += 2; // skip "%p"
- break;
- }
- default:
- *out++ = *s++;
- break;
- }
- }
- CHECK(out < out_end - 1);
- *out = '\0';
-}
-
-class FlagHandlerInclude : public FlagHandlerBase {
- FlagParser *parser_;
- bool ignore_missing_;
-
- public:
- explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing)
- : parser_(parser), ignore_missing_(ignore_missing) {}
- bool Parse(const char *value) final {
- if (internal_strchr(value, '%')) {
- char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
- SubstituteForFlagValue(value, buf, kMaxPathLength);
- bool res = parser_->ParseFile(buf, ignore_missing_);
- UnmapOrDie(buf, kMaxPathLength);
- return res;
- }
- return parser_->ParseFile(value, ignore_missing_);
- }
-};
-
-void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
- FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT
- FlagHandlerInclude(parser, /*ignore_missing*/ false);
- parser->RegisterHandler("include", fh_include,
- "read more options from the given file");
- FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT
- FlagHandlerInclude(parser, /*ignore_missing*/ true);
- parser->RegisterHandler(
- "include_if_exists", fh_include_if_exists,
- "read more options from the given file (if it exists)");
-}
-
-void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) {
-#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &cf->Name);
-#include "sanitizer_flags.inc"
-#undef COMMON_FLAG
-
- RegisterIncludeFlags(parser, cf);
-}
-
-void InitializeCommonFlags(CommonFlags *cf) {
- // need to record coverage to generate coverage report.
- cf->coverage |= cf->html_cov_report;
- SetVerbosity(cf->verbosity);
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_flags.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer/AddressSanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_flags.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_list.h"
+#include "sanitizer_flag_parser.h"
+
+namespace __sanitizer {
+
+CommonFlags common_flags_dont_use;
+
+void CommonFlags::SetDefaults() {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+}
+
+void CommonFlags::CopyFrom(const CommonFlags &other) {
+ internal_memcpy(this, &other, sizeof(*this));
+}
+
+// Copy the string from "s" to "out", making the following substitutions:
+// %b = binary basename
+// %p = pid
+void SubstituteForFlagValue(const char *s, char *out, uptr out_size) {
+ char *out_end = out + out_size;
+ while (*s && out < out_end - 1) {
+ if (s[0] != '%') {
+ *out++ = *s++;
+ continue;
+ }
+ switch (s[1]) {
+ case 'b': {
+ const char *base = GetProcessName();
+ CHECK(base);
+ while (*base && out < out_end - 1)
+ *out++ = *base++;
+ s += 2; // skip "%b"
+ break;
+ }
+ case 'p': {
+ int pid = internal_getpid();
+ char buf[32];
+ char *buf_pos = buf + 32;
+ do {
+ *--buf_pos = (pid % 10) + '0';
+ pid /= 10;
+ } while (pid);
+ while (buf_pos < buf + 32 && out < out_end - 1)
+ *out++ = *buf_pos++;
+ s += 2; // skip "%p"
+ break;
+ }
+ default:
+ *out++ = *s++;
+ break;
+ }
+ }
+ CHECK(out < out_end - 1);
+ *out = '\0';
+}
+
+class FlagHandlerInclude : public FlagHandlerBase {
+ FlagParser *parser_;
+ bool ignore_missing_;
+
+ public:
+ explicit FlagHandlerInclude(FlagParser *parser, bool ignore_missing)
+ : parser_(parser), ignore_missing_(ignore_missing) {}
+ bool Parse(const char *value) final {
+ if (internal_strchr(value, '%')) {
+ char *buf = (char *)MmapOrDie(kMaxPathLength, "FlagHandlerInclude");
+ SubstituteForFlagValue(value, buf, kMaxPathLength);
+ bool res = parser_->ParseFile(buf, ignore_missing_);
+ UnmapOrDie(buf, kMaxPathLength);
+ return res;
+ }
+ return parser_->ParseFile(value, ignore_missing_);
+ }
+};
+
+void RegisterIncludeFlags(FlagParser *parser, CommonFlags *cf) {
+ FlagHandlerInclude *fh_include = new (FlagParser::Alloc) // NOLINT
+ FlagHandlerInclude(parser, /*ignore_missing*/ false);
+ parser->RegisterHandler("include", fh_include,
+ "read more options from the given file");
+ FlagHandlerInclude *fh_include_if_exists = new (FlagParser::Alloc) // NOLINT
+ FlagHandlerInclude(parser, /*ignore_missing*/ true);
+ parser->RegisterHandler(
+ "include_if_exists", fh_include_if_exists,
+ "read more options from the given file (if it exists)");
+}
+
+void RegisterCommonFlags(FlagParser *parser, CommonFlags *cf) {
+#define COMMON_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &cf->Name);
+#include "sanitizer_flags.inc"
+#undef COMMON_FLAG
+
+ RegisterIncludeFlags(parser, cf);
+}
+
+void InitializeCommonFlags(CommonFlags *cf) {
+ // need to record coverage to generate coverage report.
+ cf->coverage |= cf->html_cov_report;
+ SetVerbosity(cf->verbosity);
+}
+
+} // namespace __sanitizer
//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_flags.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
"handle_*=1 will be upgraded to handle_*=2.")
COMMON_FLAG(bool, use_sigaltstack, true,
"If set, uses alternate stack for signal handling.")
-COMMON_FLAG(bool, detect_deadlocks, false,
+COMMON_FLAG(bool, detect_deadlocks, true,
"If set, deadlock detection is enabled.")
COMMON_FLAG(
uptr, clear_shadow_mmap_threshold, 64 * 1024,
COMMON_FLAG(bool, intercept_send, true,
"If set, uses custom wrappers for send* functions "
"to find more errors.")
-COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer "
- "mappings in /proc/self/maps with "
- "user-readable names")
+COMMON_FLAG(bool, decorate_proc_maps, (bool)SANITIZER_ANDROID,
+ "If set, decorate sanitizer mappings in /proc/self/maps with "
+ "user-readable names")
COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool "
"found an error")
COMMON_FLAG(
COMMON_FLAG(bool, detect_write_exec, false,
"If true, triggers warning when writable-executable pages requests "
"are being made")
+COMMON_FLAG(bool, test_only_emulate_no_memorymap, false,
+ "TEST ONLY fail to read memory mappings to emulate sanitized "
+ "\"init\"")
//===-- sanitizer_freebsd.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_fuchsia.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and other sanitizer
-// run-time libraries and implements Fuchsia-specific functions from
-// sanitizer_common.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_fuchsia.h"
-#if SANITIZER_FUCHSIA
-
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_mutex.h"
-
-#include <limits.h>
-#include <pthread.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <zircon/errors.h>
-#include <zircon/process.h>
-#include <zircon/syscalls.h>
-
-namespace __sanitizer {
-
-void NORETURN internal__exit(int exitcode) { _zx_process_exit(exitcode); }
-
-uptr internal_sched_yield() {
- zx_status_t status = _zx_nanosleep(0);
- CHECK_EQ(status, ZX_OK);
- return 0; // Why doesn't this return void?
-}
-
-static void internal_nanosleep(zx_time_t ns) {
- zx_status_t status = _zx_nanosleep(_zx_deadline_after(ns));
- CHECK_EQ(status, ZX_OK);
-}
-
-unsigned int internal_sleep(unsigned int seconds) {
- internal_nanosleep(ZX_SEC(seconds));
- return 0;
-}
-
-u64 NanoTime() { return _zx_clock_get(ZX_CLOCK_UTC); }
-
-u64 MonotonicNanoTime() { return _zx_clock_get(ZX_CLOCK_MONOTONIC); }
-
-uptr internal_getpid() {
- zx_info_handle_basic_t info;
- zx_status_t status =
- _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &info,
- sizeof(info), NULL, NULL);
- CHECK_EQ(status, ZX_OK);
- uptr pid = static_cast<uptr>(info.koid);
- CHECK_EQ(pid, info.koid);
- return pid;
-}
-
-uptr GetThreadSelf() { return reinterpret_cast<uptr>(thrd_current()); }
-
-tid_t GetTid() { return GetThreadSelf(); }
-
-void Abort() { abort(); }
-
-int Atexit(void (*function)(void)) { return atexit(function); }
-
-void SleepForSeconds(int seconds) { internal_sleep(seconds); }
-
-void SleepForMillis(int millis) { internal_nanosleep(ZX_MSEC(millis)); }
-
-void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
- pthread_attr_t attr;
- CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
- void *base;
- size_t size;
- CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
- CHECK_EQ(pthread_attr_destroy(&attr), 0);
-
- *stack_bottom = reinterpret_cast<uptr>(base);
- *stack_top = *stack_bottom + size;
-}
-
-void MaybeReexec() {}
-void CheckASLR() {}
-void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
-void DisableCoreDumperIfNecessary() {}
-void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
-void SetAlternateSignalStack() {}
-void UnsetAlternateSignalStack() {}
-void InitTlsSize() {}
-
-void PrintModuleMap() {}
-
-bool SignalContext::IsStackOverflow() const { return false; }
-void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
-const char *SignalContext::Describe() const { UNIMPLEMENTED(); }
-
-enum MutexState : int { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
-
-BlockingMutex::BlockingMutex() {
- // NOTE! It's important that this use internal_memset, because plain
- // memset might be intercepted (e.g., actually be __asan_memset).
- // Defining this so the compiler initializes each field, e.g.:
- // BlockingMutex::BlockingMutex() : BlockingMutex(LINKER_INITIALIZED) {}
- // might result in the compiler generating a call to memset, which would
- // have the same problem.
- internal_memset(this, 0, sizeof(*this));
-}
-
-void BlockingMutex::Lock() {
- CHECK_EQ(owner_, 0);
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
- return;
- while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
- zx_status_t status = _zx_futex_wait(reinterpret_cast<zx_futex_t *>(m),
- MtxSleeping, ZX_TIME_INFINITE);
- if (status != ZX_ERR_BAD_STATE) // Normal race.
- CHECK_EQ(status, ZX_OK);
- }
-}
-
-void BlockingMutex::Unlock() {
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
- CHECK_NE(v, MtxUnlocked);
- if (v == MtxSleeping) {
- zx_status_t status = _zx_futex_wake(reinterpret_cast<zx_futex_t *>(m), 1);
- CHECK_EQ(status, ZX_OK);
- }
-}
-
-void BlockingMutex::CheckLocked() {
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
-}
-
-uptr GetPageSize() { return PAGE_SIZE; }
-
-uptr GetMmapGranularity() { return PAGE_SIZE; }
-
-sanitizer_shadow_bounds_t ShadowBounds;
-
-uptr GetMaxUserVirtualAddress() {
- ShadowBounds = __sanitizer_shadow_bounds();
- return ShadowBounds.memory_limit - 1;
-}
-
-uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }
-
-static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
- bool raw_report, bool die_for_nomem) {
- size = RoundUpTo(size, PAGE_SIZE);
-
- zx_handle_t vmo;
- zx_status_t status = _zx_vmo_create(size, 0, &vmo);
- if (status != ZX_OK) {
- if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
- ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status,
- raw_report);
- return nullptr;
- }
- _zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
- internal_strlen(mem_type));
-
- // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
- uintptr_t addr;
- status =
- _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
- vmo, 0, size, &addr);
- _zx_handle_close(vmo);
-
- if (status != ZX_OK) {
- if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
- ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status,
- raw_report);
- return nullptr;
- }
-
- IncreaseTotalMmap(size);
-
- return reinterpret_cast<void *>(addr);
-}
-
-void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
- return DoAnonymousMmapOrDie(size, mem_type, raw_report, true);
-}
-
-void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
- return MmapOrDie(size, mem_type);
-}
-
-void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
- return DoAnonymousMmapOrDie(size, mem_type, false, false);
-}
-
-uptr ReservedAddressRange::Init(uptr init_size, const char *name,
- uptr fixed_addr) {
- init_size = RoundUpTo(init_size, PAGE_SIZE);
- DCHECK_EQ(os_handle_, ZX_HANDLE_INVALID);
- uintptr_t base;
- zx_handle_t vmar;
- zx_status_t status =
- _zx_vmar_allocate_old(_zx_vmar_root_self(), 0, init_size,
- ZX_VM_FLAG_CAN_MAP_READ | ZX_VM_FLAG_CAN_MAP_WRITE |
- ZX_VM_FLAG_CAN_MAP_SPECIFIC,
- &vmar, &base);
- if (status != ZX_OK)
- ReportMmapFailureAndDie(init_size, name, "zx_vmar_allocate", status);
- base_ = reinterpret_cast<void *>(base);
- size_ = init_size;
- name_ = name;
- os_handle_ = vmar;
-
- return reinterpret_cast<uptr>(base_);
-}
-
-static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size,
- void *base, const char *name, bool die_for_nomem) {
- uptr offset = fixed_addr - reinterpret_cast<uptr>(base);
- map_size = RoundUpTo(map_size, PAGE_SIZE);
- zx_handle_t vmo;
- zx_status_t status = _zx_vmo_create(map_size, 0, &vmo);
- if (status != ZX_OK) {
- if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
- ReportMmapFailureAndDie(map_size, name, "zx_vmo_create", status);
- return 0;
- }
- _zx_object_set_property(vmo, ZX_PROP_NAME, name, internal_strlen(name));
- DCHECK_GE(base + size_, map_size + offset);
- uintptr_t addr;
-
- status =
- _zx_vmar_map(vmar, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
- offset, vmo, 0, map_size, &addr);
- _zx_handle_close(vmo);
- if (status != ZX_OK) {
- if (status != ZX_ERR_NO_MEMORY || die_for_nomem) {
- ReportMmapFailureAndDie(map_size, name, "zx_vmar_map", status);
- }
- return 0;
- }
- IncreaseTotalMmap(map_size);
- return addr;
-}
-
-uptr ReservedAddressRange::Map(uptr fixed_addr, uptr map_size) {
- return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
- name_, false);
-}
-
-uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size) {
- return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
- name_, true);
-}
-
-void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
- if (!addr || !size) return;
- size = RoundUpTo(size, PAGE_SIZE);
-
- zx_status_t status =
- _zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
- if (status != ZX_OK) {
- Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
- SanitizerToolName, size, size, addr);
- CHECK("unable to unmap" && 0);
- }
-
- DecreaseTotalMmap(size);
-}
-
-void ReservedAddressRange::Unmap(uptr addr, uptr size) {
- CHECK_LE(size, size_);
- const zx_handle_t vmar = static_cast<zx_handle_t>(os_handle_);
- if (addr == reinterpret_cast<uptr>(base_)) {
- if (size == size_) {
- // Destroying the vmar effectively unmaps the whole mapping.
- _zx_vmar_destroy(vmar);
- _zx_handle_close(vmar);
- os_handle_ = static_cast<uptr>(ZX_HANDLE_INVALID);
- DecreaseTotalMmap(size);
- return;
- }
- } else {
- CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
- }
- // Partial unmapping does not affect the fact that the initial range is still
- // reserved, and the resulting unmapped memory can't be reused.
- UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
-}
-
-// This should never be called.
-void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
- UNIMPLEMENTED();
-}
-
-void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
- const char *mem_type) {
- CHECK_GE(size, PAGE_SIZE);
- CHECK(IsPowerOfTwo(size));
- CHECK(IsPowerOfTwo(alignment));
-
- zx_handle_t vmo;
- zx_status_t status = _zx_vmo_create(size, 0, &vmo);
- if (status != ZX_OK) {
- if (status != ZX_ERR_NO_MEMORY)
- ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status, false);
- return nullptr;
- }
- _zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
- internal_strlen(mem_type));
-
- // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
-
- // Map a larger size to get a chunk of address space big enough that
- // it surely contains an aligned region of the requested size. Then
- // overwrite the aligned middle portion with a mapping from the
- // beginning of the VMO, and unmap the excess before and after.
- size_t map_size = size + alignment;
- uintptr_t addr;
- status =
- _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
- vmo, 0, map_size, &addr);
- if (status == ZX_OK) {
- uintptr_t map_addr = addr;
- uintptr_t map_end = map_addr + map_size;
- addr = RoundUpTo(map_addr, alignment);
- uintptr_t end = addr + size;
- if (addr != map_addr) {
- zx_info_vmar_t info;
- status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
- sizeof(info), NULL, NULL);
- if (status == ZX_OK) {
- uintptr_t new_addr;
- status = _zx_vmar_map(
- _zx_vmar_root_self(),
- ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
- addr - info.base, vmo, 0, size, &new_addr);
- if (status == ZX_OK) CHECK_EQ(new_addr, addr);
- }
- }
- if (status == ZX_OK && addr != map_addr)
- status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr);
- if (status == ZX_OK && end != map_end)
- status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end);
- }
- _zx_handle_close(vmo);
-
- if (status != ZX_OK) {
- if (status != ZX_ERR_NO_MEMORY)
- ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status, false);
- return nullptr;
- }
-
- IncreaseTotalMmap(size);
-
- return reinterpret_cast<void *>(addr);
-}
-
-void UnmapOrDie(void *addr, uptr size) {
- UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
-}
-
-// This is used on the shadow mapping, which cannot be changed.
-// Zircon doesn't have anything like MADV_DONTNEED.
-void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
-
-void DumpProcessMap() {
- // TODO(mcgrathr): write it
- return;
-}
-
-bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- // TODO(mcgrathr): Figure out a better way.
- zx_handle_t vmo;
- zx_status_t status = _zx_vmo_create(size, 0, &vmo);
- if (status == ZX_OK) {
- status = _zx_vmo_write(vmo, reinterpret_cast<const void *>(beg), 0, size);
- _zx_handle_close(vmo);
- }
- return status == ZX_OK;
-}
-
-// FIXME implement on this platform.
-void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {}
-
-bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
- uptr *read_len, uptr max_len, error_t *errno_p) {
- zx_handle_t vmo;
- zx_status_t status = __sanitizer_get_configuration(file_name, &vmo);
- if (status == ZX_OK) {
- uint64_t vmo_size;
- status = _zx_vmo_get_size(vmo, &vmo_size);
- if (status == ZX_OK) {
- if (vmo_size < max_len) max_len = vmo_size;
- size_t map_size = RoundUpTo(max_len, PAGE_SIZE);
- uintptr_t addr;
- status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0,
- map_size, &addr);
- if (status == ZX_OK) {
- *buff = reinterpret_cast<char *>(addr);
- *buff_size = map_size;
- *read_len = max_len;
- }
- }
- _zx_handle_close(vmo);
- }
- if (status != ZX_OK && errno_p) *errno_p = status;
- return status == ZX_OK;
-}
-
-void RawWrite(const char *buffer) {
- constexpr size_t size = 128;
- static _Thread_local char line[size];
- static _Thread_local size_t lastLineEnd = 0;
- static _Thread_local size_t cur = 0;
-
- while (*buffer) {
- if (cur >= size) {
- if (lastLineEnd == 0)
- lastLineEnd = size;
- __sanitizer_log_write(line, lastLineEnd);
- internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
- cur = cur - lastLineEnd;
- lastLineEnd = 0;
- }
- if (*buffer == '\n')
- lastLineEnd = cur + 1;
- line[cur++] = *buffer++;
- }
- // Flush all complete lines before returning.
- if (lastLineEnd != 0) {
- __sanitizer_log_write(line, lastLineEnd);
- internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
- cur = cur - lastLineEnd;
- lastLineEnd = 0;
- }
-}
-
-void CatastrophicErrorWrite(const char *buffer, uptr length) {
- __sanitizer_log_write(buffer, length);
-}
-
-char **StoredArgv;
-char **StoredEnviron;
-
-char **GetArgv() { return StoredArgv; }
-
-const char *GetEnv(const char *name) {
- if (StoredEnviron) {
- uptr NameLen = internal_strlen(name);
- for (char **Env = StoredEnviron; *Env != 0; Env++) {
- if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
- return (*Env) + NameLen + 1;
- }
- }
- return nullptr;
-}
-
-uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {
- const char *argv0 = "<UNKNOWN>";
- if (StoredArgv && StoredArgv[0]) {
- argv0 = StoredArgv[0];
- }
- internal_strncpy(buf, argv0, buf_len);
- return internal_strlen(buf);
-}
-
-uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
- return ReadBinaryName(buf, buf_len);
-}
-
-uptr MainThreadStackBase, MainThreadStackSize;
-
-bool GetRandom(void *buffer, uptr length, bool blocking) {
- CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN);
- _zx_cprng_draw(buffer, length);
- return true;
-}
-
-u32 GetNumberOfCPUs() {
- return zx_system_get_num_cpus();
-}
-
-uptr GetRSS() { UNIMPLEMENTED(); }
-
-} // namespace __sanitizer
-
-using namespace __sanitizer; // NOLINT
-
-extern "C" {
-void __sanitizer_startup_hook(int argc, char **argv, char **envp,
- void *stack_base, size_t stack_size) {
- __sanitizer::StoredArgv = argv;
- __sanitizer::StoredEnviron = envp;
- __sanitizer::MainThreadStackBase = reinterpret_cast<uintptr_t>(stack_base);
- __sanitizer::MainThreadStackSize = stack_size;
-}
-
-void __sanitizer_set_report_path(const char *path) {
- // Handle the initialization code in each sanitizer, but no other calls.
- // This setting is never consulted on Fuchsia.
- DCHECK_EQ(path, common_flags()->log_path);
-}
-
-void __sanitizer_set_report_fd(void *fd) {
- UNREACHABLE("not available on Fuchsia");
-}
-} // extern "C"
-
-#endif // SANITIZER_FUCHSIA
--- /dev/null
+//===-- sanitizer_fuchsia.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and other sanitizer
+// run-time libraries and implements Fuchsia-specific functions from
+// sanitizer_common.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_fuchsia.h"
+#if SANITIZER_FUCHSIA
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
+
+#include <limits.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <zircon/errors.h>
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+
+namespace __sanitizer {
+
+void NORETURN internal__exit(int exitcode) { _zx_process_exit(exitcode); }
+
+uptr internal_sched_yield() {
+ zx_status_t status = _zx_nanosleep(0);
+ CHECK_EQ(status, ZX_OK);
+ return 0; // Why doesn't this return void?
+}
+
+static void internal_nanosleep(zx_time_t ns) {
+ zx_status_t status = _zx_nanosleep(_zx_deadline_after(ns));
+ CHECK_EQ(status, ZX_OK);
+}
+
+unsigned int internal_sleep(unsigned int seconds) {
+ internal_nanosleep(ZX_SEC(seconds));
+ return 0;
+}
+
+u64 NanoTime() {
+ zx_time_t time;
+ zx_status_t status = _zx_clock_get(ZX_CLOCK_UTC, &time);
+ CHECK_EQ(status, ZX_OK);
+ return time;
+}
+
+u64 MonotonicNanoTime() { return _zx_clock_get_monotonic(); }
+
+uptr internal_getpid() {
+ zx_info_handle_basic_t info;
+ zx_status_t status =
+ _zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &info,
+ sizeof(info), NULL, NULL);
+ CHECK_EQ(status, ZX_OK);
+ uptr pid = static_cast<uptr>(info.koid);
+ CHECK_EQ(pid, info.koid);
+ return pid;
+}
+
+uptr GetThreadSelf() { return reinterpret_cast<uptr>(thrd_current()); }
+
+tid_t GetTid() { return GetThreadSelf(); }
+
+void Abort() { abort(); }
+
+int Atexit(void (*function)(void)) { return atexit(function); }
+
+void SleepForSeconds(int seconds) { internal_sleep(seconds); }
+
+void SleepForMillis(int millis) { internal_nanosleep(ZX_MSEC(millis)); }
+
+void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
+ pthread_attr_t attr;
+ CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
+ void *base;
+ size_t size;
+ CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
+ CHECK_EQ(pthread_attr_destroy(&attr), 0);
+
+ *stack_bottom = reinterpret_cast<uptr>(base);
+ *stack_top = *stack_bottom + size;
+}
+
+void InitializePlatformEarly() {}
+void MaybeReexec() {}
+void CheckASLR() {}
+void CheckMPROTECT() {}
+void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
+void DisableCoreDumperIfNecessary() {}
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
+void SetAlternateSignalStack() {}
+void UnsetAlternateSignalStack() {}
+void InitTlsSize() {}
+
+void PrintModuleMap() {}
+
+bool SignalContext::IsStackOverflow() const { return false; }
+void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
+const char *SignalContext::Describe() const { UNIMPLEMENTED(); }
+
+enum MutexState : int { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
+
+BlockingMutex::BlockingMutex() {
+ // NOTE! It's important that this use internal_memset, because plain
+ // memset might be intercepted (e.g., actually be __asan_memset).
+ // Defining this so the compiler initializes each field, e.g.:
+ // BlockingMutex::BlockingMutex() : BlockingMutex(LINKER_INITIALIZED) {}
+ // might result in the compiler generating a call to memset, which would
+ // have the same problem.
+ internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+ CHECK_EQ(owner_, 0);
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+ return;
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
+ zx_status_t status =
+ _zx_futex_wait(reinterpret_cast<zx_futex_t *>(m), MtxSleeping,
+ ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
+ if (status != ZX_ERR_BAD_STATE) // Normal race.
+ CHECK_EQ(status, ZX_OK);
+ }
+}
+
+void BlockingMutex::Unlock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
+ CHECK_NE(v, MtxUnlocked);
+ if (v == MtxSleeping) {
+ zx_status_t status = _zx_futex_wake(reinterpret_cast<zx_futex_t *>(m), 1);
+ CHECK_EQ(status, ZX_OK);
+ }
+}
+
+void BlockingMutex::CheckLocked() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
+}
+
+uptr GetPageSize() { return PAGE_SIZE; }
+
+uptr GetMmapGranularity() { return PAGE_SIZE; }
+
+sanitizer_shadow_bounds_t ShadowBounds;
+
+uptr GetMaxUserVirtualAddress() {
+ ShadowBounds = __sanitizer_shadow_bounds();
+ return ShadowBounds.memory_limit - 1;
+}
+
+uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }
+
+static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
+ bool raw_report, bool die_for_nomem) {
+ size = RoundUpTo(size, PAGE_SIZE);
+
+ zx_handle_t vmo;
+ zx_status_t status = _zx_vmo_create(size, 0, &vmo);
+ if (status != ZX_OK) {
+ if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
+ ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status,
+ raw_report);
+ return nullptr;
+ }
+ _zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
+ internal_strlen(mem_type));
+
+ // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
+ uintptr_t addr;
+ status =
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
+ vmo, 0, size, &addr);
+ _zx_handle_close(vmo);
+
+ if (status != ZX_OK) {
+ if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
+ ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status,
+ raw_report);
+ return nullptr;
+ }
+
+ IncreaseTotalMmap(size);
+
+ return reinterpret_cast<void *>(addr);
+}
+
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
+ return DoAnonymousMmapOrDie(size, mem_type, raw_report, true);
+}
+
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+ return MmapOrDie(size, mem_type);
+}
+
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
+ return DoAnonymousMmapOrDie(size, mem_type, false, false);
+}
+
+uptr ReservedAddressRange::Init(uptr init_size, const char *name,
+ uptr fixed_addr) {
+ init_size = RoundUpTo(init_size, PAGE_SIZE);
+ DCHECK_EQ(os_handle_, ZX_HANDLE_INVALID);
+ uintptr_t base;
+ zx_handle_t vmar;
+ zx_status_t status =
+ _zx_vmar_allocate(
+ _zx_vmar_root_self(),
+ ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC,
+ 0, init_size, &vmar, &base);
+ if (status != ZX_OK)
+ ReportMmapFailureAndDie(init_size, name, "zx_vmar_allocate", status);
+ base_ = reinterpret_cast<void *>(base);
+ size_ = init_size;
+ name_ = name;
+ os_handle_ = vmar;
+
+ return reinterpret_cast<uptr>(base_);
+}
+
+static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size,
+ void *base, const char *name, bool die_for_nomem) {
+ uptr offset = fixed_addr - reinterpret_cast<uptr>(base);
+ map_size = RoundUpTo(map_size, PAGE_SIZE);
+ zx_handle_t vmo;
+ zx_status_t status = _zx_vmo_create(map_size, 0, &vmo);
+ if (status != ZX_OK) {
+ if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
+ ReportMmapFailureAndDie(map_size, name, "zx_vmo_create", status);
+ return 0;
+ }
+ _zx_object_set_property(vmo, ZX_PROP_NAME, name, internal_strlen(name));
+ DCHECK_GE(base + size_, map_size + offset);
+ uintptr_t addr;
+
+ status =
+ _zx_vmar_map(vmar, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
+ offset, vmo, 0, map_size, &addr);
+ _zx_handle_close(vmo);
+ if (status != ZX_OK) {
+ if (status != ZX_ERR_NO_MEMORY || die_for_nomem) {
+ ReportMmapFailureAndDie(map_size, name, "zx_vmar_map", status);
+ }
+ return 0;
+ }
+ IncreaseTotalMmap(map_size);
+ return addr;
+}
+
+uptr ReservedAddressRange::Map(uptr fixed_addr, uptr map_size,
+ const char *name) {
+ return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
+ name_, false);
+}
+
+uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size,
+ const char *name) {
+ return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
+ name_, true);
+}
+
+void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
+ if (!addr || !size) return;
+ size = RoundUpTo(size, PAGE_SIZE);
+
+ zx_status_t status =
+ _zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
+ if (status != ZX_OK) {
+ Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
+ SanitizerToolName, size, size, addr);
+ CHECK("unable to unmap" && 0);
+ }
+
+ DecreaseTotalMmap(size);
+}
+
+void ReservedAddressRange::Unmap(uptr addr, uptr size) {
+ CHECK_LE(size, size_);
+ const zx_handle_t vmar = static_cast<zx_handle_t>(os_handle_);
+ if (addr == reinterpret_cast<uptr>(base_)) {
+ if (size == size_) {
+ // Destroying the vmar effectively unmaps the whole mapping.
+ _zx_vmar_destroy(vmar);
+ _zx_handle_close(vmar);
+ os_handle_ = static_cast<uptr>(ZX_HANDLE_INVALID);
+ DecreaseTotalMmap(size);
+ return;
+ }
+ } else {
+ CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
+ }
+ // Partial unmapping does not affect the fact that the initial range is still
+ // reserved, and the resulting unmapped memory can't be reused.
+ UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
+}
+
+// This should never be called.
+void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ UNIMPLEMENTED();
+}
+
+void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
+ const char *mem_type) {
+ CHECK_GE(size, PAGE_SIZE);
+ CHECK(IsPowerOfTwo(size));
+ CHECK(IsPowerOfTwo(alignment));
+
+ zx_handle_t vmo;
+ zx_status_t status = _zx_vmo_create(size, 0, &vmo);
+ if (status != ZX_OK) {
+ if (status != ZX_ERR_NO_MEMORY)
+ ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status, false);
+ return nullptr;
+ }
+ _zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
+ internal_strlen(mem_type));
+
+ // TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
+
+ // Map a larger size to get a chunk of address space big enough that
+ // it surely contains an aligned region of the requested size. Then
+ // overwrite the aligned middle portion with a mapping from the
+ // beginning of the VMO, and unmap the excess before and after.
+ size_t map_size = size + alignment;
+ uintptr_t addr;
+ status =
+ _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
+ vmo, 0, map_size, &addr);
+ if (status == ZX_OK) {
+ uintptr_t map_addr = addr;
+ uintptr_t map_end = map_addr + map_size;
+ addr = RoundUpTo(map_addr, alignment);
+ uintptr_t end = addr + size;
+ if (addr != map_addr) {
+ zx_info_vmar_t info;
+ status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
+ sizeof(info), NULL, NULL);
+ if (status == ZX_OK) {
+ uintptr_t new_addr;
+ status = _zx_vmar_map(
+ _zx_vmar_root_self(),
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
+ addr - info.base, vmo, 0, size, &new_addr);
+ if (status == ZX_OK) CHECK_EQ(new_addr, addr);
+ }
+ }
+ if (status == ZX_OK && addr != map_addr)
+ status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr);
+ if (status == ZX_OK && end != map_end)
+ status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end);
+ }
+ _zx_handle_close(vmo);
+
+ if (status != ZX_OK) {
+ if (status != ZX_ERR_NO_MEMORY)
+ ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status, false);
+ return nullptr;
+ }
+
+ IncreaseTotalMmap(size);
+
+ return reinterpret_cast<void *>(addr);
+}
+
+void UnmapOrDie(void *addr, uptr size) {
+ UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
+}
+
+// This is used on the shadow mapping, which cannot be changed.
+// Zircon doesn't have anything like MADV_DONTNEED.
+void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
+
+void DumpProcessMap() {
+ // TODO(mcgrathr): write it
+ return;
+}
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ // TODO(mcgrathr): Figure out a better way.
+ zx_handle_t vmo;
+ zx_status_t status = _zx_vmo_create(size, 0, &vmo);
+ if (status == ZX_OK) {
+ status = _zx_vmo_write(vmo, reinterpret_cast<const void *>(beg), 0, size);
+ _zx_handle_close(vmo);
+ }
+ return status == ZX_OK;
+}
+
+// FIXME implement on this platform.
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {}
+
+bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
+ uptr *read_len, uptr max_len, error_t *errno_p) {
+ zx_handle_t vmo;
+ zx_status_t status = __sanitizer_get_configuration(file_name, &vmo);
+ if (status == ZX_OK) {
+ uint64_t vmo_size;
+ status = _zx_vmo_get_size(vmo, &vmo_size);
+ if (status == ZX_OK) {
+ if (vmo_size < max_len) max_len = vmo_size;
+ size_t map_size = RoundUpTo(max_len, PAGE_SIZE);
+ uintptr_t addr;
+ status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0,
+ map_size, &addr);
+ if (status == ZX_OK) {
+ *buff = reinterpret_cast<char *>(addr);
+ *buff_size = map_size;
+ *read_len = max_len;
+ }
+ }
+ _zx_handle_close(vmo);
+ }
+ if (status != ZX_OK && errno_p) *errno_p = status;
+ return status == ZX_OK;
+}
+
+void RawWrite(const char *buffer) {
+ constexpr size_t size = 128;
+ static _Thread_local char line[size];
+ static _Thread_local size_t lastLineEnd = 0;
+ static _Thread_local size_t cur = 0;
+
+ while (*buffer) {
+ if (cur >= size) {
+ if (lastLineEnd == 0)
+ lastLineEnd = size;
+ __sanitizer_log_write(line, lastLineEnd);
+ internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
+ cur = cur - lastLineEnd;
+ lastLineEnd = 0;
+ }
+ if (*buffer == '\n')
+ lastLineEnd = cur + 1;
+ line[cur++] = *buffer++;
+ }
+ // Flush all complete lines before returning.
+ if (lastLineEnd != 0) {
+ __sanitizer_log_write(line, lastLineEnd);
+ internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
+ cur = cur - lastLineEnd;
+ lastLineEnd = 0;
+ }
+}
+
+void CatastrophicErrorWrite(const char *buffer, uptr length) {
+ __sanitizer_log_write(buffer, length);
+}
+
+char **StoredArgv;
+char **StoredEnviron;
+
+char **GetArgv() { return StoredArgv; }
+char **GetEnviron() { return StoredEnviron; }
+
+const char *GetEnv(const char *name) {
+ if (StoredEnviron) {
+ uptr NameLen = internal_strlen(name);
+ for (char **Env = StoredEnviron; *Env != 0; Env++) {
+ if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
+ return (*Env) + NameLen + 1;
+ }
+ }
+ return nullptr;
+}
+
+uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {
+ const char *argv0 = "<UNKNOWN>";
+ if (StoredArgv && StoredArgv[0]) {
+ argv0 = StoredArgv[0];
+ }
+ internal_strncpy(buf, argv0, buf_len);
+ return internal_strlen(buf);
+}
+
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
+}
+
+uptr MainThreadStackBase, MainThreadStackSize;
+
+bool GetRandom(void *buffer, uptr length, bool blocking) {
+ CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN);
+ _zx_cprng_draw(buffer, length);
+ return true;
+}
+
+u32 GetNumberOfCPUs() {
+ return zx_system_get_num_cpus();
+}
+
+uptr GetRSS() { UNIMPLEMENTED(); }
+
+} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+void __sanitizer_startup_hook(int argc, char **argv, char **envp,
+ void *stack_base, size_t stack_size) {
+ __sanitizer::StoredArgv = argv;
+ __sanitizer::StoredEnviron = envp;
+ __sanitizer::MainThreadStackBase = reinterpret_cast<uintptr_t>(stack_base);
+ __sanitizer::MainThreadStackSize = stack_size;
+}
+
+void __sanitizer_set_report_path(const char *path) {
+ // Handle the initialization code in each sanitizer, but no other calls.
+ // This setting is never consulted on Fuchsia.
+ DCHECK_EQ(path, common_flags()->log_path);
+}
+
+void __sanitizer_set_report_fd(void *fd) {
+ UNREACHABLE("not available on Fuchsia");
+}
+} // extern "C"
+
+#endif // SANITIZER_FUCHSIA
//===-- sanitizer_fuchsia.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===---------------------------------------------------------------------===//
//
//===-- sanitizer_getauxval.h -----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
--- /dev/null
+//===-- sanitizer_common.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements a simple hash function.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_HASH_H
+#define SANITIZER_HASH_H
+
+#include "sanitizer_internal_defs.h"
+
+namespace __sanitizer {
+class MurMur2HashBuilder {
+ static const u32 m = 0x5bd1e995;
+ static const u32 seed = 0x9747b28c;
+ static const u32 r = 24;
+ u32 h;
+
+ public:
+ explicit MurMur2HashBuilder(u32 init = 0) { h = seed ^ init; }
+ void add(u32 k) {
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+ h *= m;
+ h ^= k;
+ }
+ u32 get() {
+ u32 x = h;
+ x ^= x >> 13;
+ x *= m;
+ x ^= x >> 15;
+ return x;
+ }
+};
+} //namespace __sanitizer
+
+#endif // SANITIZER_HASH_H
//===-- sanitizer_interceptors_ioctl_netbsd.inc -----------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
const char *name;
};
-const unsigned ioctl_table_max = 1198;
+const unsigned ioctl_table_max = 1236;
static ioctl_desc ioctl_table[ioctl_table_max];
static unsigned ioctl_table_size = 0;
_(MLX_GET_CINFO, WRITE, struct_mlx_cinfo_sz);
/* Entries from file: dev/ic/nvmeio.h */
_(NVME_PASSTHROUGH_CMD, READWRITE, struct_nvme_pt_command_sz);
+ /* Entries from file: dev/ic/qemufwcfgio.h */
+ _(FWCFGIO_SET_INDEX, READ, sizeof(u16));
/* Entries from file: dev/ir/irdaio.h */
_(IRDA_RESET_PARAMS, NONE, 0);
_(IRDA_SET_PARAMS, READ, struct_irda_params_sz);
_(IRFRAMETTY_GET_DEVICE, WRITE, sizeof(unsigned int));
_(IRFRAMETTY_GET_DONGLE, WRITE, sizeof(unsigned int));
_(IRFRAMETTY_SET_DONGLE, READ, sizeof(unsigned int));
- /* Entries from file: dev/isa/satlinkio.h */
- _(SATIORESET, NONE, 0);
- _(SATIOGID, WRITE, struct_satlink_id_sz);
/* Entries from file: dev/isa/isvio.h */
_(ISV_CMD, READWRITE, struct_isv_cmd_sz);
/* Entries from file: dev/isa/wtreg.h */
_(SPKRTUNE, NONE, 0);
_(SPKRGETVOL, WRITE, sizeof(unsigned int));
_(SPKRSETVOL, READ, sizeof(unsigned int));
+#if defined(__x86_64__)
+ /* Entries from file: dev/nvmm/nvmm_ioctl.h */
+ _(NVMM_IOC_CAPABILITY, WRITE, struct_nvmm_ioc_capability_sz);
+ _(NVMM_IOC_MACHINE_CREATE, READWRITE, struct_nvmm_ioc_machine_create_sz);
+ _(NVMM_IOC_MACHINE_DESTROY, READ, struct_nvmm_ioc_machine_destroy_sz);
+ _(NVMM_IOC_MACHINE_CONFIGURE, READ, struct_nvmm_ioc_machine_configure_sz);
+ _(NVMM_IOC_VCPU_CREATE, READ, struct_nvmm_ioc_vcpu_create_sz);
+ _(NVMM_IOC_VCPU_DESTROY, READ, struct_nvmm_ioc_vcpu_destroy_sz);
+ _(NVMM_IOC_VCPU_SETSTATE, READ, struct_nvmm_ioc_vcpu_setstate_sz);
+ _(NVMM_IOC_VCPU_GETSTATE, READ, struct_nvmm_ioc_vcpu_getstate_sz);
+ _(NVMM_IOC_VCPU_INJECT, READ, struct_nvmm_ioc_vcpu_inject_sz);
+ _(NVMM_IOC_VCPU_RUN, READWRITE, struct_nvmm_ioc_vcpu_run_sz);
+ _(NVMM_IOC_GPA_MAP, READ, struct_nvmm_ioc_gpa_map_sz);
+ _(NVMM_IOC_GPA_UNMAP, READ, struct_nvmm_ioc_gpa_unmap_sz);
+ _(NVMM_IOC_HVA_MAP, READ, struct_nvmm_ioc_hva_map_sz);
+ _(NVMM_IOC_HVA_UNMAP, READ, struct_nvmm_ioc_hva_unmap_sz);
+ _(NVMM_IOC_CTL, READ, struct_nvmm_ioc_ctl_sz);
+#endif
+ /* Entries from file: dev/spi/spi_io.h */
+ _(SPI_IOCTL_CONFIGURE, READ, struct_spi_ioctl_configure_sz);
+ _(SPI_IOCTL_TRANSFER, READ, struct_spi_ioctl_transfer_sz);
+ /* Entries from file: fs/autofs/autofs_ioctl.h */
+ _(AUTOFSREQUEST, WRITE, struct_autofs_daemon_request_sz);
+ _(AUTOFSDONE, READ, struct_autofs_daemon_done_sz);
/* Entries from file: net/bpf.h */
_(BIOCGBLEN, WRITE, sizeof(unsigned int));
_(BIOCSBLEN, READWRITE, sizeof(unsigned int));
_(BIOCSHDRCMPLT, READ, sizeof(unsigned int));
_(BIOCSDLT, READ, sizeof(unsigned int));
_(BIOCGDLTLIST, READWRITE, struct_bpf_dltlist_sz);
- _(BIOCGSEESENT, WRITE, sizeof(unsigned int));
- _(BIOCSSEESENT, READ, sizeof(unsigned int));
+ _(BIOCGDIRECTION, WRITE, sizeof(unsigned int));
+ _(BIOCSDIRECTION, READ, sizeof(unsigned int));
_(BIOCSRTIMEOUT, READ, struct_timeval_sz);
_(BIOCGRTIMEOUT, WRITE, struct_timeval_sz);
_(BIOCGFEEDBACK, WRITE, sizeof(unsigned int));
_(BIOCSFEEDBACK, READ, sizeof(unsigned int));
- /* Entries from file: net/if_atm.h */
- _(SIOCRAWATM, READWRITE, sizeof(int));
- _(SIOCATMENA, READWRITE, struct_atm_pseudoioctl_sz);
- _(SIOCATMDIS, READWRITE, struct_atm_pseudoioctl_sz);
- _(SIOCSPVCTX, READWRITE, struct_pvctxreq_sz);
- _(SIOCGPVCTX, READWRITE, struct_pvctxreq_sz);
- _(SIOCSPVCSIF, READWRITE, struct_ifreq_sz);
- _(SIOCGPVCSIF, READWRITE, struct_ifreq_sz);
/* Entries from file: net/if_gre.h */
_(GRESADDRS, READ, struct_ifreq_sz);
_(GRESADDRD, READ, struct_ifreq_sz);
/* Entries from file: net/npf.h */
_(IOC_NPF_VERSION, WRITE, sizeof(int));
_(IOC_NPF_SWITCH, READ, sizeof(int));
- _(IOC_NPF_LOAD, READWRITE, struct_plistref_sz);
+ _(IOC_NPF_LOAD, READWRITE, struct_nvlist_ref_sz);
_(IOC_NPF_TABLE, READ, struct_npf_ioctl_table_sz);
_(IOC_NPF_STATS, READ, sizeof(uptr));
- _(IOC_NPF_SAVE, WRITE, struct_plistref_sz);
- _(IOC_NPF_RULE, READWRITE, struct_plistref_sz);
- _(IOC_NPF_CONN_LOOKUP, READWRITE, struct_plistref_sz);
+ _(IOC_NPF_SAVE, WRITE, struct_nvlist_ref_sz);
+ _(IOC_NPF_RULE, READWRITE, struct_nvlist_ref_sz);
+ _(IOC_NPF_CONN_LOOKUP, READWRITE, struct_nvlist_ref_sz);
/* Entries from file: net/if_pppoe.h */
_(PPPOESETPARMS, READ, struct_pppoediscparms_sz);
_(PPPOEGETPARMS, READWRITE, struct_pppoediscparms_sz);
_(SIOCGNATS, READWRITE, struct_ipfobj_sz);
_(SIOCGNATL, READWRITE, struct_ipfobj_sz);
_(SIOCPURGENAT, READWRITE, struct_ipfobj_sz);
+ /* Entries from file: netinet/sctp_uio.h */
+ _(SIOCCONNECTX, READWRITE, struct_sctp_connectx_addrs_sz);
+ _(SIOCCONNECTXDEL, READWRITE, struct_sctp_connectx_addrs_sz);
/* Entries from file: netinet6/in6_var.h */
_(SIOCSIFINFO_FLAGS, READWRITE, struct_in6_ndireq_sz);
_(SIOCAADDRCTL_POLICY, READ, struct_in6_addrpolicy_sz);
_(AUDIO_GETBUFINFO, WRITE, struct_audio_info_sz);
_(AUDIO_SETCHAN, READ, sizeof(int));
_(AUDIO_GETCHAN, WRITE, sizeof(int));
+ _(AUDIO_QUERYFORMAT, READWRITE, struct_audio_format_query_sz);
+ _(AUDIO_GETFORMAT, WRITE, struct_audio_info_sz);
+ _(AUDIO_SETFORMAT, READ, struct_audio_info_sz);
_(AUDIO_MIXER_READ, READWRITE, struct_mixer_ctrl_sz);
_(AUDIO_MIXER_WRITE, READWRITE, struct_mixer_ctrl_sz);
_(AUDIO_MIXER_DEVINFO, READWRITE, struct_mixer_devinfo_sz);
_(DIOCMWEDGES, WRITE, sizeof(int));
_(DIOCGSECTORSIZE, WRITE, sizeof(unsigned int));
_(DIOCGMEDIASIZE, WRITE, sizeof(uptr));
+ _(DIOCRMWEDGES, WRITE, sizeof(int));
/* Entries from file: sys/drvctlio.h */
_(DRVDETACHDEV, READ, struct_devdetachargs_sz);
_(DRVRESCANBUS, READ, struct_devrescanargs_sz);
/* Entries from file: sys/filio.h */
_(FIOCLEX, NONE, 0);
_(FIONCLEX, NONE, 0);
+ _(FIOSEEKDATA, READWRITE, sizeof(uptr));
+ _(FIOSEEKHOLE, READWRITE, sizeof(uptr));
_(FIONREAD, WRITE, sizeof(int));
_(FIONBIO, READ, sizeof(int));
_(FIOASYNC, READ, sizeof(int));
/* Entries from file: sys/power.h */
_(POWER_EVENT_RECVDICT, READWRITE, struct_plistref_sz);
_(POWER_IOC_GET_TYPE, WRITE, struct_power_type_sz);
- _(POWER_IOC_GET_TYPE_WITH_LOSSAGE, WRITE, sizeof(uptr));
/* Entries from file: sys/radioio.h */
_(RIOCGINFO, WRITE, struct_radio_info_sz);
_(RIOCSINFO, READWRITE, struct_radio_info_sz);
_(SIOCATMARK, WRITE, sizeof(int));
_(SIOCSPGRP, READ, sizeof(int));
_(SIOCGPGRP, WRITE, sizeof(int));
+ _(SIOCPEELOFF, READWRITE, sizeof(int));
_(SIOCADDRT, READ, struct_ortentry_sz);
_(SIOCDELRT, READ, struct_ortentry_sz);
_(SIOCSIFADDR, READ, struct_ifreq_sz);
_(SIOCSLINKSTR, READ, struct_ifdrv_sz);
_(SIOCGETHERCAP, READWRITE, struct_eccapreq_sz);
_(SIOCGIFINDEX, READWRITE, struct_ifreq_sz);
+ _(SIOCSETHERCAP, READ, struct_eccapreq_sz);
+ _(SIOCSIFDESCR, READ, struct_ifreq_sz);
+ _(SIOCGIFDESCR, READWRITE, struct_ifreq_sz);
+ _(SIOCGUMBINFO, READWRITE, struct_ifreq_sz);
+ _(SIOCSUMBPARAM, READ, struct_ifreq_sz);
+ _(SIOCGUMBPARAM, READWRITE, struct_ifreq_sz);
_(SIOCSETPFSYNC, READ, struct_ifreq_sz);
_(SIOCGETPFSYNC, READWRITE, struct_ifreq_sz);
/* Entries from file: sys/timepps.h */
_(WDOGIOC_TICKLE, NONE, 0);
_(WDOGIOC_GTICKLER, WRITE, sizeof(int));
_(WDOGIOC_GWDOGS, READWRITE, struct_wdog_conf_sz);
+ /* Entries from file: sys/kcov.h */
+ _(KCOV_IOC_SETBUFSIZE, READ, sizeof(u64));
+ _(KCOV_IOC_ENABLE, READ, sizeof(int));
+ _(KCOV_IOC_DISABLE, NONE, 0);
+ /* Entries from file: sys/ipmi.h */
+ _(IPMICTL_RECEIVE_MSG_TRUNC, READWRITE, struct_ipmi_recv_sz);
+ _(IPMICTL_RECEIVE_MSG, READWRITE, struct_ipmi_recv_sz);
+ _(IPMICTL_SEND_COMMAND, READ, struct_ipmi_req_sz);
+ _(IPMICTL_REGISTER_FOR_CMD, READ, struct_ipmi_cmdspec_sz);
+ _(IPMICTL_UNREGISTER_FOR_CMD, READ, struct_ipmi_cmdspec_sz);
+ _(IPMICTL_SET_GETS_EVENTS_CMD, READ, sizeof(int));
+ _(IPMICTL_SET_MY_ADDRESS_CMD, READ, sizeof(unsigned int));
+ _(IPMICTL_GET_MY_ADDRESS_CMD, WRITE, sizeof(unsigned int));
+ _(IPMICTL_SET_MY_LUN_CMD, READ, sizeof(unsigned int));
+ _(IPMICTL_GET_MY_LUN_CMD, WRITE, sizeof(unsigned int));
/* Entries from file: soundcard.h */
_(SNDCTL_DSP_RESET, NONE, 0);
_(SNDCTL_DSP_SYNC, NONE, 0);
//===-- sanitizer_interface_internal.h --------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_internal_defs.h -------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This header should NOT include any other headers to avoid portability issues.
// Common defs.
+#ifndef INLINE
#define INLINE inline
+#endif
#define INTERFACE_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
#define SANITIZER_WEAK_DEFAULT_IMPL \
extern "C" SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
namespace __asan { using namespace __sanitizer; } // NOLINT
namespace __dsan { using namespace __sanitizer; } // NOLINT
namespace __dfsan { using namespace __sanitizer; } // NOLINT
-namespace __esan { using namespace __sanitizer; } // NOLINT
namespace __lsan { using namespace __sanitizer; } // NOLINT
namespace __msan { using namespace __sanitizer; } // NOLINT
namespace __hwasan { using namespace __sanitizer; } // NOLINT
//===-- sanitizer_lfstack.h -=-----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_libc.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries. See sanitizer_libc.h for details.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-
-namespace __sanitizer {
-
-s64 internal_atoll(const char *nptr) {
- return internal_simple_strtoll(nptr, nullptr, 10);
-}
-
-void *internal_memchr(const void *s, int c, uptr n) {
- const char *t = (const char *)s;
- for (uptr i = 0; i < n; ++i, ++t)
- if (*t == c)
- return reinterpret_cast<void *>(const_cast<char *>(t));
- return nullptr;
-}
-
-void *internal_memrchr(const void *s, int c, uptr n) {
- const char *t = (const char *)s;
- void *res = nullptr;
- for (uptr i = 0; i < n; ++i, ++t) {
- if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
- }
- return res;
-}
-
-int internal_memcmp(const void* s1, const void* s2, uptr n) {
- const char *t1 = (const char *)s1;
- const char *t2 = (const char *)s2;
- for (uptr i = 0; i < n; ++i, ++t1, ++t2)
- if (*t1 != *t2)
- return *t1 < *t2 ? -1 : 1;
- return 0;
-}
-
-void *internal_memcpy(void *dest, const void *src, uptr n) {
- char *d = (char*)dest;
- const char *s = (const char *)src;
- for (uptr i = 0; i < n; ++i)
- d[i] = s[i];
- return dest;
-}
-
-void *internal_memmove(void *dest, const void *src, uptr n) {
- char *d = (char*)dest;
- const char *s = (const char *)src;
- sptr i, signed_n = (sptr)n;
- CHECK_GE(signed_n, 0);
- if (d < s) {
- for (i = 0; i < signed_n; ++i)
- d[i] = s[i];
- } else {
- if (d > s && signed_n > 0)
- for (i = signed_n - 1; i >= 0 ; --i) {
- d[i] = s[i];
- }
- }
- return dest;
-}
-
-void *internal_memset(void* s, int c, uptr n) {
- // Optimize for the most performance-critical case:
- if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
- u64 *p = reinterpret_cast<u64*>(s);
- u64 *e = p + n / 8;
- u64 v = c;
- v |= v << 8;
- v |= v << 16;
- v |= v << 32;
- for (; p < e; p += 2)
- p[0] = p[1] = v;
- return s;
- }
- // The next line prevents Clang from making a call to memset() instead of the
- // loop below.
- // FIXME: building the runtime with -ffreestanding is a better idea. However
- // there currently are linktime problems due to PR12396.
- char volatile *t = (char*)s;
- for (uptr i = 0; i < n; ++i, ++t) {
- *t = c;
- }
- return s;
-}
-
-uptr internal_strcspn(const char *s, const char *reject) {
- uptr i;
- for (i = 0; s[i]; i++) {
- if (internal_strchr(reject, s[i]))
- return i;
- }
- return i;
-}
-
-char* internal_strdup(const char *s) {
- uptr len = internal_strlen(s);
- char *s2 = (char*)InternalAlloc(len + 1);
- internal_memcpy(s2, s, len);
- s2[len] = 0;
- return s2;
-}
-
-int internal_strcmp(const char *s1, const char *s2) {
- while (true) {
- unsigned c1 = *s1;
- unsigned c2 = *s2;
- if (c1 != c2) return (c1 < c2) ? -1 : 1;
- if (c1 == 0) break;
- s1++;
- s2++;
- }
- return 0;
-}
-
-int internal_strncmp(const char *s1, const char *s2, uptr n) {
- for (uptr i = 0; i < n; i++) {
- unsigned c1 = *s1;
- unsigned c2 = *s2;
- if (c1 != c2) return (c1 < c2) ? -1 : 1;
- if (c1 == 0) break;
- s1++;
- s2++;
- }
- return 0;
-}
-
-char* internal_strchr(const char *s, int c) {
- while (true) {
- if (*s == (char)c)
- return const_cast<char *>(s);
- if (*s == 0)
- return nullptr;
- s++;
- }
-}
-
-char *internal_strchrnul(const char *s, int c) {
- char *res = internal_strchr(s, c);
- if (!res)
- res = const_cast<char *>(s) + internal_strlen(s);
- return res;
-}
-
-char *internal_strrchr(const char *s, int c) {
- const char *res = nullptr;
- for (uptr i = 0; s[i]; i++) {
- if (s[i] == c) res = s + i;
- }
- return const_cast<char *>(res);
-}
-
-uptr internal_strlen(const char *s) {
- uptr i = 0;
- while (s[i]) i++;
- return i;
-}
-
-uptr internal_strlcat(char *dst, const char *src, uptr maxlen) {
- const uptr srclen = internal_strlen(src);
- const uptr dstlen = internal_strnlen(dst, maxlen);
- if (dstlen == maxlen) return maxlen + srclen;
- if (srclen < maxlen - dstlen) {
- internal_memmove(dst + dstlen, src, srclen + 1);
- } else {
- internal_memmove(dst + dstlen, src, maxlen - dstlen - 1);
- dst[maxlen - 1] = '\0';
- }
- return dstlen + srclen;
-}
-
-char *internal_strncat(char *dst, const char *src, uptr n) {
- uptr len = internal_strlen(dst);
- uptr i;
- for (i = 0; i < n && src[i]; i++)
- dst[len + i] = src[i];
- dst[len + i] = 0;
- return dst;
-}
-
-uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) {
- const uptr srclen = internal_strlen(src);
- if (srclen < maxlen) {
- internal_memmove(dst, src, srclen + 1);
- } else if (maxlen != 0) {
- internal_memmove(dst, src, maxlen - 1);
- dst[maxlen - 1] = '\0';
- }
- return srclen;
-}
-
-char *internal_strncpy(char *dst, const char *src, uptr n) {
- uptr i;
- for (i = 0; i < n && src[i]; i++)
- dst[i] = src[i];
- internal_memset(dst + i, '\0', n - i);
- return dst;
-}
-
-uptr internal_strnlen(const char *s, uptr maxlen) {
- uptr i = 0;
- while (i < maxlen && s[i]) i++;
- return i;
-}
-
-char *internal_strstr(const char *haystack, const char *needle) {
- // This is O(N^2), but we are not using it in hot places.
- uptr len1 = internal_strlen(haystack);
- uptr len2 = internal_strlen(needle);
- if (len1 < len2) return nullptr;
- for (uptr pos = 0; pos <= len1 - len2; pos++) {
- if (internal_memcmp(haystack + pos, needle, len2) == 0)
- return const_cast<char *>(haystack) + pos;
- }
- return nullptr;
-}
-
-s64 internal_simple_strtoll(const char *nptr, const char **endptr, int base) {
- CHECK_EQ(base, 10);
- while (IsSpace(*nptr)) nptr++;
- int sgn = 1;
- u64 res = 0;
- bool have_digits = false;
- char *old_nptr = const_cast<char *>(nptr);
- if (*nptr == '+') {
- sgn = 1;
- nptr++;
- } else if (*nptr == '-') {
- sgn = -1;
- nptr++;
- }
- while (IsDigit(*nptr)) {
- res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX;
- int digit = ((*nptr) - '0');
- res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX;
- have_digits = true;
- nptr++;
- }
- if (endptr) {
- *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
- }
- if (sgn > 0) {
- return (s64)(Min((u64)INT64_MAX, res));
- } else {
- return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1);
- }
-}
-
-bool mem_is_zero(const char *beg, uptr size) {
- CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check.
- const char *end = beg + size;
- uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
- uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
- uptr all = 0;
- // Prologue.
- for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
- all |= *mem;
- // Aligned loop.
- for (; aligned_beg < aligned_end; aligned_beg++)
- all |= *aligned_beg;
- // Epilogue.
- if ((char*)aligned_end >= beg)
- for (const char *mem = (char*)aligned_end; mem < end; mem++)
- all |= *mem;
- return all == 0;
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_libc.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries. See sanitizer_libc.h for details.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+
+namespace __sanitizer {
+
+s64 internal_atoll(const char *nptr) {
+ return internal_simple_strtoll(nptr, nullptr, 10);
+}
+
+void *internal_memchr(const void *s, int c, uptr n) {
+ const char *t = (const char *)s;
+ for (uptr i = 0; i < n; ++i, ++t)
+ if (*t == c)
+ return reinterpret_cast<void *>(const_cast<char *>(t));
+ return nullptr;
+}
+
+void *internal_memrchr(const void *s, int c, uptr n) {
+ const char *t = (const char *)s;
+ void *res = nullptr;
+ for (uptr i = 0; i < n; ++i, ++t) {
+ if (*t == c) res = reinterpret_cast<void *>(const_cast<char *>(t));
+ }
+ return res;
+}
+
+int internal_memcmp(const void* s1, const void* s2, uptr n) {
+ const char *t1 = (const char *)s1;
+ const char *t2 = (const char *)s2;
+ for (uptr i = 0; i < n; ++i, ++t1, ++t2)
+ if (*t1 != *t2)
+ return *t1 < *t2 ? -1 : 1;
+ return 0;
+}
+
+void *internal_memcpy(void *dest, const void *src, uptr n) {
+ char *d = (char*)dest;
+ const char *s = (const char *)src;
+ for (uptr i = 0; i < n; ++i)
+ d[i] = s[i];
+ return dest;
+}
+
+void *internal_memmove(void *dest, const void *src, uptr n) {
+ char *d = (char*)dest;
+ const char *s = (const char *)src;
+ sptr i, signed_n = (sptr)n;
+ CHECK_GE(signed_n, 0);
+ if (d < s) {
+ for (i = 0; i < signed_n; ++i)
+ d[i] = s[i];
+ } else {
+ if (d > s && signed_n > 0)
+ for (i = signed_n - 1; i >= 0 ; --i) {
+ d[i] = s[i];
+ }
+ }
+ return dest;
+}
+
+void *internal_memset(void* s, int c, uptr n) {
+ // Optimize for the most performance-critical case:
+ if ((reinterpret_cast<uptr>(s) % 16) == 0 && (n % 16) == 0) {
+ u64 *p = reinterpret_cast<u64*>(s);
+ u64 *e = p + n / 8;
+ u64 v = c;
+ v |= v << 8;
+ v |= v << 16;
+ v |= v << 32;
+ for (; p < e; p += 2)
+ p[0] = p[1] = v;
+ return s;
+ }
+ // The next line prevents Clang from making a call to memset() instead of the
+ // loop below.
+ // FIXME: building the runtime with -ffreestanding is a better idea. However
+ // there currently are linktime problems due to PR12396.
+ char volatile *t = (char*)s;
+ for (uptr i = 0; i < n; ++i, ++t) {
+ *t = c;
+ }
+ return s;
+}
+
+uptr internal_strcspn(const char *s, const char *reject) {
+ uptr i;
+ for (i = 0; s[i]; i++) {
+ if (internal_strchr(reject, s[i]))
+ return i;
+ }
+ return i;
+}
+
+char* internal_strdup(const char *s) {
+ uptr len = internal_strlen(s);
+ char *s2 = (char*)InternalAlloc(len + 1);
+ internal_memcpy(s2, s, len);
+ s2[len] = 0;
+ return s2;
+}
+
+int internal_strcmp(const char *s1, const char *s2) {
+ while (true) {
+ unsigned c1 = *s1;
+ unsigned c2 = *s2;
+ if (c1 != c2) return (c1 < c2) ? -1 : 1;
+ if (c1 == 0) break;
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+
+int internal_strncmp(const char *s1, const char *s2, uptr n) {
+ for (uptr i = 0; i < n; i++) {
+ unsigned c1 = *s1;
+ unsigned c2 = *s2;
+ if (c1 != c2) return (c1 < c2) ? -1 : 1;
+ if (c1 == 0) break;
+ s1++;
+ s2++;
+ }
+ return 0;
+}
+
+char* internal_strchr(const char *s, int c) {
+ while (true) {
+ if (*s == (char)c)
+ return const_cast<char *>(s);
+ if (*s == 0)
+ return nullptr;
+ s++;
+ }
+}
+
+char *internal_strchrnul(const char *s, int c) {
+ char *res = internal_strchr(s, c);
+ if (!res)
+ res = const_cast<char *>(s) + internal_strlen(s);
+ return res;
+}
+
+char *internal_strrchr(const char *s, int c) {
+ const char *res = nullptr;
+ for (uptr i = 0; s[i]; i++) {
+ if (s[i] == c) res = s + i;
+ }
+ return const_cast<char *>(res);
+}
+
+uptr internal_strlen(const char *s) {
+ uptr i = 0;
+ while (s[i]) i++;
+ return i;
+}
+
+uptr internal_strlcat(char *dst, const char *src, uptr maxlen) {
+ const uptr srclen = internal_strlen(src);
+ const uptr dstlen = internal_strnlen(dst, maxlen);
+ if (dstlen == maxlen) return maxlen + srclen;
+ if (srclen < maxlen - dstlen) {
+ internal_memmove(dst + dstlen, src, srclen + 1);
+ } else {
+ internal_memmove(dst + dstlen, src, maxlen - dstlen - 1);
+ dst[maxlen - 1] = '\0';
+ }
+ return dstlen + srclen;
+}
+
+char *internal_strncat(char *dst, const char *src, uptr n) {
+ uptr len = internal_strlen(dst);
+ uptr i;
+ for (i = 0; i < n && src[i]; i++)
+ dst[len + i] = src[i];
+ dst[len + i] = 0;
+ return dst;
+}
+
+uptr internal_strlcpy(char *dst, const char *src, uptr maxlen) {
+ const uptr srclen = internal_strlen(src);
+ if (srclen < maxlen) {
+ internal_memmove(dst, src, srclen + 1);
+ } else if (maxlen != 0) {
+ internal_memmove(dst, src, maxlen - 1);
+ dst[maxlen - 1] = '\0';
+ }
+ return srclen;
+}
+
+char *internal_strncpy(char *dst, const char *src, uptr n) {
+ uptr i;
+ for (i = 0; i < n && src[i]; i++)
+ dst[i] = src[i];
+ internal_memset(dst + i, '\0', n - i);
+ return dst;
+}
+
+uptr internal_strnlen(const char *s, uptr maxlen) {
+ uptr i = 0;
+ while (i < maxlen && s[i]) i++;
+ return i;
+}
+
+char *internal_strstr(const char *haystack, const char *needle) {
+ // This is O(N^2), but we are not using it in hot places.
+ uptr len1 = internal_strlen(haystack);
+ uptr len2 = internal_strlen(needle);
+ if (len1 < len2) return nullptr;
+ for (uptr pos = 0; pos <= len1 - len2; pos++) {
+ if (internal_memcmp(haystack + pos, needle, len2) == 0)
+ return const_cast<char *>(haystack) + pos;
+ }
+ return nullptr;
+}
+
+s64 internal_simple_strtoll(const char *nptr, const char **endptr, int base) {
+ CHECK_EQ(base, 10);
+ while (IsSpace(*nptr)) nptr++;
+ int sgn = 1;
+ u64 res = 0;
+ bool have_digits = false;
+ char *old_nptr = const_cast<char *>(nptr);
+ if (*nptr == '+') {
+ sgn = 1;
+ nptr++;
+ } else if (*nptr == '-') {
+ sgn = -1;
+ nptr++;
+ }
+ while (IsDigit(*nptr)) {
+ res = (res <= UINT64_MAX / 10) ? res * 10 : UINT64_MAX;
+ int digit = ((*nptr) - '0');
+ res = (res <= UINT64_MAX - digit) ? res + digit : UINT64_MAX;
+ have_digits = true;
+ nptr++;
+ }
+ if (endptr) {
+ *endptr = (have_digits) ? const_cast<char *>(nptr) : old_nptr;
+ }
+ if (sgn > 0) {
+ return (s64)(Min((u64)INT64_MAX, res));
+ } else {
+ return (res > INT64_MAX) ? INT64_MIN : ((s64)res * -1);
+ }
+}
+
+bool mem_is_zero(const char *beg, uptr size) {
+ CHECK_LE(size, 1ULL << FIRST_32_SECOND_64(30, 40)); // Sanity check.
+ const char *end = beg + size;
+ uptr *aligned_beg = (uptr *)RoundUpTo((uptr)beg, sizeof(uptr));
+ uptr *aligned_end = (uptr *)RoundDownTo((uptr)end, sizeof(uptr));
+ uptr all = 0;
+ // Prologue.
+ for (const char *mem = beg; mem < (char*)aligned_beg && mem < end; mem++)
+ all |= *mem;
+ // Aligned loop.
+ for (; aligned_beg < aligned_end; aligned_beg++)
+ all |= *aligned_beg;
+ // Epilogue.
+ if ((char*)aligned_end >= beg)
+ for (const char *mem = (char*)aligned_end; mem < end; mem++)
+ all |= *mem;
+ return all == 0;
+}
+
+} // namespace __sanitizer
//===-- sanitizer_libc.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_libignore.cc --------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || \
- SANITIZER_NETBSD || SANITIZER_OPENBSD
-
-#include "sanitizer_libignore.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_posix.h"
-#include "sanitizer_procmaps.h"
-
-namespace __sanitizer {
-
-LibIgnore::LibIgnore(LinkerInitialized) {
-}
-
-void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
- BlockingMutexLock lock(&mutex_);
- if (count_ >= kMaxLibs) {
- Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
- kMaxLibs);
- Die();
- }
- Lib *lib = &libs_[count_++];
- lib->templ = internal_strdup(name_templ);
- lib->name = nullptr;
- lib->real_name = nullptr;
- lib->loaded = false;
-}
-
-void LibIgnore::OnLibraryLoaded(const char *name) {
- BlockingMutexLock lock(&mutex_);
- // Try to match suppressions with symlink target.
- InternalScopedString buf(kMaxPathLength);
- if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
- buf[0]) {
- for (uptr i = 0; i < count_; i++) {
- Lib *lib = &libs_[i];
- if (!lib->loaded && (!lib->real_name) &&
- TemplateMatch(lib->templ, name))
- lib->real_name = internal_strdup(buf.data());
- }
- }
-
- // Scan suppressions list and find newly loaded and unloaded libraries.
- ListOfModules modules;
- modules.init();
- for (uptr i = 0; i < count_; i++) {
- Lib *lib = &libs_[i];
- bool loaded = false;
- for (const auto &mod : modules) {
- for (const auto &range : mod.ranges()) {
- if (!range.executable)
- continue;
- if (!TemplateMatch(lib->templ, mod.full_name()) &&
- !(lib->real_name &&
- internal_strcmp(lib->real_name, mod.full_name()) == 0))
- continue;
- if (loaded) {
- Report("%s: called_from_lib suppression '%s' is matched against"
- " 2 libraries: '%s' and '%s'\n",
- SanitizerToolName, lib->templ, lib->name, mod.full_name());
- Die();
- }
- loaded = true;
- if (lib->loaded)
- continue;
- VReport(1,
- "Matched called_from_lib suppression '%s' against library"
- " '%s'\n",
- lib->templ, mod.full_name());
- lib->loaded = true;
- lib->name = internal_strdup(mod.full_name());
- const uptr idx =
- atomic_load(&ignored_ranges_count_, memory_order_relaxed);
- CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
- ignored_code_ranges_[idx].begin = range.beg;
- ignored_code_ranges_[idx].end = range.end;
- atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
- break;
- }
- }
- if (lib->loaded && !loaded) {
- Report("%s: library '%s' that was matched against called_from_lib"
- " suppression '%s' is unloaded\n",
- SanitizerToolName, lib->name, lib->templ);
- Die();
- }
- }
-
- // Track instrumented ranges.
- if (track_instrumented_libs_) {
- for (const auto &mod : modules) {
- if (!mod.instrumented())
- continue;
- for (const auto &range : mod.ranges()) {
- if (!range.executable)
- continue;
- if (IsPcInstrumented(range.beg) && IsPcInstrumented(range.end - 1))
- continue;
- VReport(1, "Adding instrumented range %p-%p from library '%s'\n",
- range.beg, range.end, mod.full_name());
- const uptr idx =
- atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
- CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
- instrumented_code_ranges_[idx].begin = range.beg;
- instrumented_code_ranges_[idx].end = range.end;
- atomic_store(&instrumented_ranges_count_, idx + 1,
- memory_order_release);
- }
- }
- }
-}
-
-void LibIgnore::OnLibraryUnloaded() {
- OnLibraryLoaded(nullptr);
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC ||
- // SANITIZER_NETBSD
--- /dev/null
+//===-- sanitizer_libignore.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || \
+ SANITIZER_NETBSD || SANITIZER_OPENBSD
+
+#include "sanitizer_libignore.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+LibIgnore::LibIgnore(LinkerInitialized) {
+}
+
+void LibIgnore::AddIgnoredLibrary(const char *name_templ) {
+ BlockingMutexLock lock(&mutex_);
+ if (count_ >= kMaxLibs) {
+ Report("%s: too many ignored libraries (max: %d)\n", SanitizerToolName,
+ kMaxLibs);
+ Die();
+ }
+ Lib *lib = &libs_[count_++];
+ lib->templ = internal_strdup(name_templ);
+ lib->name = nullptr;
+ lib->real_name = nullptr;
+ lib->loaded = false;
+}
+
+void LibIgnore::OnLibraryLoaded(const char *name) {
+ BlockingMutexLock lock(&mutex_);
+ // Try to match suppressions with symlink target.
+ InternalScopedString buf(kMaxPathLength);
+ if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 &&
+ buf[0]) {
+ for (uptr i = 0; i < count_; i++) {
+ Lib *lib = &libs_[i];
+ if (!lib->loaded && (!lib->real_name) &&
+ TemplateMatch(lib->templ, name))
+ lib->real_name = internal_strdup(buf.data());
+ }
+ }
+
+ // Scan suppressions list and find newly loaded and unloaded libraries.
+ ListOfModules modules;
+ modules.init();
+ for (uptr i = 0; i < count_; i++) {
+ Lib *lib = &libs_[i];
+ bool loaded = false;
+ for (const auto &mod : modules) {
+ for (const auto &range : mod.ranges()) {
+ if (!range.executable)
+ continue;
+ if (!TemplateMatch(lib->templ, mod.full_name()) &&
+ !(lib->real_name &&
+ internal_strcmp(lib->real_name, mod.full_name()) == 0))
+ continue;
+ if (loaded) {
+ Report("%s: called_from_lib suppression '%s' is matched against"
+ " 2 libraries: '%s' and '%s'\n",
+ SanitizerToolName, lib->templ, lib->name, mod.full_name());
+ Die();
+ }
+ loaded = true;
+ if (lib->loaded)
+ continue;
+ VReport(1,
+ "Matched called_from_lib suppression '%s' against library"
+ " '%s'\n",
+ lib->templ, mod.full_name());
+ lib->loaded = true;
+ lib->name = internal_strdup(mod.full_name());
+ const uptr idx =
+ atomic_load(&ignored_ranges_count_, memory_order_relaxed);
+ CHECK_LT(idx, ARRAY_SIZE(ignored_code_ranges_));
+ ignored_code_ranges_[idx].begin = range.beg;
+ ignored_code_ranges_[idx].end = range.end;
+ atomic_store(&ignored_ranges_count_, idx + 1, memory_order_release);
+ break;
+ }
+ }
+ if (lib->loaded && !loaded) {
+ Report("%s: library '%s' that was matched against called_from_lib"
+ " suppression '%s' is unloaded\n",
+ SanitizerToolName, lib->name, lib->templ);
+ Die();
+ }
+ }
+
+ // Track instrumented ranges.
+ if (track_instrumented_libs_) {
+ for (const auto &mod : modules) {
+ if (!mod.instrumented())
+ continue;
+ for (const auto &range : mod.ranges()) {
+ if (!range.executable)
+ continue;
+ if (IsPcInstrumented(range.beg) && IsPcInstrumented(range.end - 1))
+ continue;
+ VReport(1, "Adding instrumented range %p-%p from library '%s'\n",
+ range.beg, range.end, mod.full_name());
+ const uptr idx =
+ atomic_load(&instrumented_ranges_count_, memory_order_relaxed);
+ CHECK_LT(idx, ARRAY_SIZE(instrumented_code_ranges_));
+ instrumented_code_ranges_[idx].begin = range.beg;
+ instrumented_code_ranges_[idx].end = range.end;
+ atomic_store(&instrumented_ranges_count_, idx + 1,
+ memory_order_release);
+ }
+ }
+ }
+}
+
+void LibIgnore::OnLibraryUnloaded() {
+ OnLibraryLoaded(nullptr);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC ||
+ // SANITIZER_NETBSD
//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_linux.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements linux-specific functions from
-// sanitizer_libc.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_getauxval.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-
-#if SANITIZER_LINUX
-#include <asm/param.h>
-#endif
-
-// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
-// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
-// access stat from asm/stat.h, without conflicting with definition in
-// sys/stat.h, we use this trick.
-#if defined(__mips64)
-#include <asm/unistd.h>
-#include <sys/types.h>
-#define stat kernel_stat
-#include <asm/stat.h>
-#undef stat
-#endif
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <link.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-#if !SANITIZER_SOLARIS
-#include <sys/ptrace.h>
-#endif
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#if !SANITIZER_OPENBSD
-#include <ucontext.h>
-#endif
-#if SANITIZER_OPENBSD
-#include <sys/futex.h>
-#include <sys/sysctl.h>
-#endif
-#include <unistd.h>
-
-#if SANITIZER_LINUX
-#include <sys/utsname.h>
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-#include <sys/personality.h>
-#endif
-
-#if SANITIZER_FREEBSD
-#include <sys/exec.h>
-#include <sys/sysctl.h>
-#include <machine/atomic.h>
-extern "C" {
-// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
-// FreeBSD 9.2 and 10.0.
-#include <sys/umtx.h>
-}
-#include <sys/thr.h>
-#endif // SANITIZER_FREEBSD
-
-#if SANITIZER_NETBSD
-#include <limits.h> // For NAME_MAX
-#include <sys/sysctl.h>
-#include <sys/exec.h>
-extern struct ps_strings *__ps_strings;
-#endif // SANITIZER_NETBSD
-
-#if SANITIZER_SOLARIS
-#include <stdlib.h>
-#include <thread.h>
-#define environ _environ
-#endif
-
-extern char **environ;
-
-#if SANITIZER_LINUX
-// <linux/time.h>
-struct kernel_timeval {
- long tv_sec;
- long tv_usec;
-};
-
-// <linux/futex.h> is broken on some linux distributions.
-const int FUTEX_WAIT = 0;
-const int FUTEX_WAKE = 1;
-const int FUTEX_PRIVATE_FLAG = 128;
-const int FUTEX_WAIT_PRIVATE = FUTEX_WAIT | FUTEX_PRIVATE_FLAG;
-const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
-#endif // SANITIZER_LINUX
-
-// Are we using 32-bit or 64-bit Linux syscalls?
-// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
-// but it still needs to use 64-bit syscalls.
-#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
- SANITIZER_WORDSIZE == 64)
-# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
-#else
-# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
-#endif
-
-#if defined(__x86_64__) || SANITIZER_MIPS64
-extern "C" {
-extern void internal_sigreturn();
-}
-#endif
-
-// Note : FreeBSD had implemented both
-// Linux and OpenBSD apis, available from
-// future 12.x version most likely
-#if SANITIZER_LINUX && defined(__NR_getrandom)
-# if !defined(GRND_NONBLOCK)
-# define GRND_NONBLOCK 1
-# endif
-# define SANITIZER_USE_GETRANDOM 1
-#else
-# define SANITIZER_USE_GETRANDOM 0
-#endif // SANITIZER_LINUX && defined(__NR_getrandom)
-
-#if SANITIZER_OPENBSD
-# define SANITIZER_USE_GETENTROPY 1
-#else
-# if SANITIZER_FREEBSD && __FreeBSD_version >= 1200000
-# define SANITIZER_USE_GETENTROPY 1
-# else
-# define SANITIZER_USE_GETENTROPY 0
-# endif
-#endif // SANITIZER_USE_GETENTROPY
-
-namespace __sanitizer {
-
-#if SANITIZER_LINUX && defined(__x86_64__)
-#include "sanitizer_syscall_linux_x86_64.inc"
-#elif SANITIZER_LINUX && defined(__aarch64__)
-#include "sanitizer_syscall_linux_aarch64.inc"
-#elif SANITIZER_LINUX && defined(__arm__)
-#include "sanitizer_syscall_linux_arm.inc"
-#else
-#include "sanitizer_syscall_generic.inc"
-#endif
-
-// --------------- sanitizer_libc.h
-#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-#if !SANITIZER_S390 && !SANITIZER_OPENBSD
-uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
- OFF_T offset) {
-#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
- return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
- offset);
-#else
- // mmap2 specifies file offset in 4096-byte units.
- CHECK(IsAligned(offset, 4096));
- return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
- offset / 4096);
-#endif
-}
-#endif // !SANITIZER_S390 && !SANITIZER_OPENBSD
-
-#if !SANITIZER_OPENBSD
-uptr internal_munmap(void *addr, uptr length) {
- return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
-}
-
-int internal_mprotect(void *addr, uptr length, int prot) {
- return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
-}
-#endif
-
-uptr internal_close(fd_t fd) {
- return internal_syscall(SYSCALL(close), fd);
-}
-
-uptr internal_open(const char *filename, int flags) {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags);
-#else
- return internal_syscall(SYSCALL(open), (uptr)filename, flags);
-#endif
-}
-
-uptr internal_open(const char *filename, int flags, u32 mode) {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags,
- mode);
-#else
- return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode);
-#endif
-}
-
-uptr internal_read(fd_t fd, void *buf, uptr count) {
- sptr res;
- HANDLE_EINTR(res,
- (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf, count));
- return res;
-}
-
-uptr internal_write(fd_t fd, const void *buf, uptr count) {
- sptr res;
- HANDLE_EINTR(res,
- (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf, count));
- return res;
-}
-
-uptr internal_ftruncate(fd_t fd, uptr size) {
- sptr res;
- HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
- (OFF_T)size));
- return res;
-}
-
-#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX
-static void stat64_to_stat(struct stat64 *in, struct stat *out) {
- internal_memset(out, 0, sizeof(*out));
- out->st_dev = in->st_dev;
- out->st_ino = in->st_ino;
- out->st_mode = in->st_mode;
- out->st_nlink = in->st_nlink;
- out->st_uid = in->st_uid;
- out->st_gid = in->st_gid;
- out->st_rdev = in->st_rdev;
- out->st_size = in->st_size;
- out->st_blksize = in->st_blksize;
- out->st_blocks = in->st_blocks;
- out->st_atime = in->st_atime;
- out->st_mtime = in->st_mtime;
- out->st_ctime = in->st_ctime;
-}
-#endif
-
-#if defined(__mips64)
-// Undefine compatibility macros from <sys/stat.h>
-// so that they would not clash with the kernel_stat
-// st_[a|m|c]time fields
-#undef st_atime
-#undef st_mtime
-#undef st_ctime
-#if defined(SANITIZER_ANDROID)
-// Bionic sys/stat.h defines additional macros
-// for compatibility with the old NDKs and
-// they clash with the kernel_stat structure
-// st_[a|m|c]time_nsec fields.
-#undef st_atime_nsec
-#undef st_mtime_nsec
-#undef st_ctime_nsec
-#endif
-static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
- internal_memset(out, 0, sizeof(*out));
- out->st_dev = in->st_dev;
- out->st_ino = in->st_ino;
- out->st_mode = in->st_mode;
- out->st_nlink = in->st_nlink;
- out->st_uid = in->st_uid;
- out->st_gid = in->st_gid;
- out->st_rdev = in->st_rdev;
- out->st_size = in->st_size;
- out->st_blksize = in->st_blksize;
- out->st_blocks = in->st_blocks;
-#if defined(__USE_MISC) || \
- defined(__USE_XOPEN2K8) || \
- defined(SANITIZER_ANDROID)
- out->st_atim.tv_sec = in->st_atime;
- out->st_atim.tv_nsec = in->st_atime_nsec;
- out->st_mtim.tv_sec = in->st_mtime;
- out->st_mtim.tv_nsec = in->st_mtime_nsec;
- out->st_ctim.tv_sec = in->st_ctime;
- out->st_ctim.tv_nsec = in->st_ctime_nsec;
-#else
- out->st_atime = in->st_atime;
- out->st_atimensec = in->st_atime_nsec;
- out->st_mtime = in->st_mtime;
- out->st_mtimensec = in->st_mtime_nsec;
- out->st_ctime = in->st_ctime;
- out->st_atimensec = in->st_ctime_nsec;
-#endif
-}
-#endif
-
-uptr internal_stat(const char *path, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0);
-#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
- 0);
-#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
-# if defined(__mips64)
- // For mips64, stat syscall fills buffer in the format of kernel_stat
- struct kernel_stat kbuf;
- int res = internal_syscall(SYSCALL(stat), path, &kbuf);
- kernel_stat_to_stat(&kbuf, (struct stat *)buf);
- return res;
-# else
- return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf);
-# endif
-#else
- struct stat64 buf64;
- int res = internal_syscall(SYSCALL(stat64), path, &buf64);
- stat64_to_stat(&buf64, (struct stat *)buf);
- return res;
-#endif
-}
-
-uptr internal_lstat(const char *path, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
- AT_SYMLINK_NOFOLLOW);
-#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
- AT_SYMLINK_NOFOLLOW);
-#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
-# if SANITIZER_MIPS64
- // For mips64, lstat syscall fills buffer in the format of kernel_stat
- struct kernel_stat kbuf;
- int res = internal_syscall(SYSCALL(lstat), path, &kbuf);
- kernel_stat_to_stat(&kbuf, (struct stat *)buf);
- return res;
-# else
- return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf);
-# endif
-#else
- struct stat64 buf64;
- int res = internal_syscall(SYSCALL(lstat64), path, &buf64);
- stat64_to_stat(&buf64, (struct stat *)buf);
- return res;
-#endif
-}
-
-uptr internal_fstat(fd_t fd, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD || \
- SANITIZER_LINUX_USES_64BIT_SYSCALLS
-#if SANITIZER_MIPS64 && !SANITIZER_OPENBSD
- // For mips64, fstat syscall fills buffer in the format of kernel_stat
- struct kernel_stat kbuf;
- int res = internal_syscall(SYSCALL(fstat), fd, &kbuf);
- kernel_stat_to_stat(&kbuf, (struct stat *)buf);
- return res;
-# else
- return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
-# endif
-#else
- struct stat64 buf64;
- int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
- stat64_to_stat(&buf64, (struct stat *)buf);
- return res;
-#endif
-}
-
-uptr internal_filesize(fd_t fd) {
- struct stat st;
- if (internal_fstat(fd, &st))
- return -1;
- return (uptr)st.st_size;
-}
-
-uptr internal_dup2(int oldfd, int newfd) {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0);
-#else
- return internal_syscall(SYSCALL(dup2), oldfd, newfd);
-#endif
-}
-
-uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
- bufsize);
-#elif SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
- bufsize);
-#else
- return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize);
-#endif
-}
-
-uptr internal_unlink(const char *path) {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS || SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
-#else
- return internal_syscall(SYSCALL(unlink), (uptr)path);
-#endif
-}
-
-uptr internal_rename(const char *oldpath, const char *newpath) {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS || SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
- (uptr)newpath);
-#else
- return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
-#endif
-}
-
-uptr internal_sched_yield() {
- return internal_syscall(SYSCALL(sched_yield));
-}
-
-void internal__exit(int exitcode) {
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
- internal_syscall(SYSCALL(exit), exitcode);
-#else
- internal_syscall(SYSCALL(exit_group), exitcode);
-#endif
- Die(); // Unreachable.
-}
-
-unsigned int internal_sleep(unsigned int seconds) {
- struct timespec ts;
- ts.tv_sec = 1;
- ts.tv_nsec = 0;
- int res = internal_syscall(SYSCALL(nanosleep), &ts, &ts);
- if (res) return ts.tv_sec;
- return 0;
-}
-
-uptr internal_execve(const char *filename, char *const argv[],
- char *const envp[]) {
- return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv,
- (uptr)envp);
-}
-#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-
-// ----------------- sanitizer_common.h
-bool FileExists(const char *filename) {
- struct stat st;
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0))
-#else
- if (internal_stat(filename, &st))
-#endif
- return false;
- // Sanity check: filename is a regular file.
- return S_ISREG(st.st_mode);
-}
-
-#if !SANITIZER_NETBSD
-tid_t GetTid() {
-#if SANITIZER_FREEBSD
- long Tid;
- thr_self(&Tid);
- return Tid;
-#elif SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(getthrid));
-#elif SANITIZER_SOLARIS
- return thr_self();
-#else
- return internal_syscall(SYSCALL(gettid));
-#endif
-}
-
-int TgKill(pid_t pid, tid_t tid, int sig) {
-#if SANITIZER_LINUX
- return internal_syscall(SYSCALL(tgkill), pid, tid, sig);
-#elif SANITIZER_FREEBSD
- return internal_syscall(SYSCALL(thr_kill2), pid, tid, sig);
-#elif SANITIZER_OPENBSD
- (void)pid;
- return internal_syscall(SYSCALL(thrkill), tid, sig, nullptr);
-#elif SANITIZER_SOLARIS
- (void)pid;
- return thr_kill(tid, sig);
-#endif
-}
-#endif
-
-#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-u64 NanoTime() {
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
- timeval tv;
-#else
- kernel_timeval tv;
-#endif
- internal_memset(&tv, 0, sizeof(tv));
- internal_syscall(SYSCALL(gettimeofday), &tv, 0);
- return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
-}
-
-uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
- return internal_syscall(SYSCALL(clock_gettime), clk_id, tp);
-}
-#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-
-// Like getenv, but reads env directly from /proc (on Linux) or parses the
-// 'environ' array (on some others) and does not use libc. This function
-// should be called first inside __asan_init.
-const char *GetEnv(const char *name) {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || \
- SANITIZER_SOLARIS
- if (::environ != 0) {
- uptr NameLen = internal_strlen(name);
- for (char **Env = ::environ; *Env != 0; Env++) {
- if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
- return (*Env) + NameLen + 1;
- }
- }
- return 0; // Not found.
-#elif SANITIZER_LINUX
- static char *environ;
- static uptr len;
- static bool inited;
- if (!inited) {
- inited = true;
- uptr environ_size;
- if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
- environ = nullptr;
- }
- if (!environ || len == 0) return nullptr;
- uptr namelen = internal_strlen(name);
- const char *p = environ;
- while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
- // proc file has the format NAME=value\0NAME=value\0NAME=value\0...
- const char* endp =
- (char*)internal_memchr(p, '\0', len - (p - environ));
- if (!endp) // this entry isn't NUL terminated
- return nullptr;
- else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
- return p + namelen + 1; // point after =
- p = endp + 1;
- }
- return nullptr; // Not found.
-#else
-#error "Unsupported platform"
-#endif
-}
-
-#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_OPENBSD
-extern "C" {
-SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end;
-}
-#endif
-
-#if !SANITIZER_GO && !SANITIZER_FREEBSD && !SANITIZER_NETBSD && \
- !SANITIZER_OPENBSD
-static void ReadNullSepFileToArray(const char *path, char ***arr,
- int arr_size) {
- char *buff;
- uptr buff_size;
- uptr buff_len;
- *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
- if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) {
- (*arr)[0] = nullptr;
- return;
- }
- (*arr)[0] = buff;
- int count, i;
- for (count = 1, i = 1; ; i++) {
- if (buff[i] == 0) {
- if (buff[i+1] == 0) break;
- (*arr)[count] = &buff[i+1];
- CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.
- count++;
- }
- }
- (*arr)[count] = nullptr;
-}
-#endif
-
-#if !SANITIZER_OPENBSD
-static void GetArgsAndEnv(char ***argv, char ***envp) {
-#if SANITIZER_FREEBSD
- // On FreeBSD, retrieving the argument and environment arrays is done via the
- // kern.ps_strings sysctl, which returns a pointer to a structure containing
- // this information. See also <sys/exec.h>.
- ps_strings *pss;
- uptr sz = sizeof(pss);
- if (internal_sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
- Printf("sysctl kern.ps_strings failed\n");
- Die();
- }
- *argv = pss->ps_argvstr;
- *envp = pss->ps_envstr;
-#elif SANITIZER_NETBSD
- *argv = __ps_strings->ps_argvstr;
- *envp = __ps_strings->ps_envstr;
-#else // SANITIZER_FREEBSD
-#if !SANITIZER_GO
- if (&__libc_stack_end) {
-#endif // !SANITIZER_GO
- uptr* stack_end = (uptr*)__libc_stack_end;
- int argc = *stack_end;
- *argv = (char**)(stack_end + 1);
- *envp = (char**)(stack_end + argc + 2);
-#if !SANITIZER_GO
- } else {
- static const int kMaxArgv = 2000, kMaxEnvp = 2000;
- ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv);
- ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp);
- }
-#endif // !SANITIZER_GO
-#endif // SANITIZER_FREEBSD
-}
-
-char **GetArgv() {
- char **argv, **envp;
- GetArgsAndEnv(&argv, &envp);
- return argv;
-}
-
-void ReExec() {
- char **argv, **envp;
- const char *pathname = "/proc/self/exe";
-
-#if SANITIZER_NETBSD
- static const int name[] = {
- CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME,
- };
- char path[400];
- uptr len;
-
- len = sizeof(path);
- if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
- pathname = path;
-#elif SANITIZER_SOLARIS
- pathname = getexecname();
- CHECK_NE(pathname, NULL);
-#endif
-
- GetArgsAndEnv(&argv, &envp);
- uptr rv = internal_execve(pathname, argv, envp);
- int rverrno;
- CHECK_EQ(internal_iserror(rv, &rverrno), true);
- Printf("execve failed, errno %d\n", rverrno);
- Die();
-}
-#endif
-
-#if !SANITIZER_SOLARIS
-enum MutexState {
- MtxUnlocked = 0,
- MtxLocked = 1,
- MtxSleeping = 2
-};
-
-BlockingMutex::BlockingMutex() {
- internal_memset(this, 0, sizeof(*this));
-}
-
-void BlockingMutex::Lock() {
- CHECK_EQ(owner_, 0);
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
- return;
- while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
-#if SANITIZER_FREEBSD
- _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0);
-#elif SANITIZER_NETBSD
- sched_yield(); /* No userspace futex-like synchronization */
-#else
- internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT_PRIVATE, MtxSleeping,
- 0, 0, 0);
-#endif
- }
-}
-
-void BlockingMutex::Unlock() {
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
- CHECK_NE(v, MtxUnlocked);
- if (v == MtxSleeping) {
-#if SANITIZER_FREEBSD
- _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0);
-#elif SANITIZER_NETBSD
- /* No userspace futex-like synchronization */
-#else
- internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0);
-#endif
- }
-}
-
-void BlockingMutex::CheckLocked() {
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
-}
-#endif // !SANITIZER_SOLARIS
-
-// ----------------- sanitizer_linux.h
-// The actual size of this structure is specified by d_reclen.
-// Note that getdents64 uses a different structure format. We only provide the
-// 32-bit syscall here.
-#if SANITIZER_NETBSD
-// Not used
-#elif SANITIZER_OPENBSD
-// struct dirent is different for Linux and us. At this moment, we use only
-// d_fileno (Linux call this d_ino), d_reclen, and d_name.
-struct linux_dirent {
- u64 d_ino; // d_fileno
- u16 d_reclen;
- u16 d_namlen; // not used
- u8 d_type; // not used
- char d_name[NAME_MAX + 1];
-};
-#else
-struct linux_dirent {
-#if SANITIZER_X32 || defined(__aarch64__)
- u64 d_ino;
- u64 d_off;
-#else
- unsigned long d_ino;
- unsigned long d_off;
-#endif
- unsigned short d_reclen;
-#ifdef __aarch64__
- unsigned char d_type;
-#endif
- char d_name[256];
-};
-#endif
-
-#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
-// Syscall wrappers.
-uptr internal_ptrace(int request, int pid, void *addr, void *data) {
- return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
- (uptr)data);
-}
-
-uptr internal_waitpid(int pid, int *status, int options) {
- return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options,
- 0 /* rusage */);
-}
-
-uptr internal_getpid() {
- return internal_syscall(SYSCALL(getpid));
-}
-
-uptr internal_getppid() {
- return internal_syscall(SYSCALL(getppid));
-}
-
-uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
-#if SANITIZER_FREEBSD
- return internal_syscall(SYSCALL(getdirentries), fd, (uptr)dirp, count, NULL);
-#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count);
-#else
- return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count);
-#endif
-}
-
-uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
- return internal_syscall(SYSCALL(lseek), fd, offset, whence);
-}
-
-#if SANITIZER_LINUX
-uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
- return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5);
-}
-#endif
-
-uptr internal_sigaltstack(const void *ss, void *oss) {
- return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
-}
-
-int internal_fork() {
-#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
- return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
-#else
- return internal_syscall(SYSCALL(fork));
-#endif
-}
-
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
-int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
- uptr *oldlenp, const void *newp, uptr newlen) {
-#if SANITIZER_OPENBSD
- return sysctl(name, namelen, oldp, (size_t *)oldlenp, (void *)newp,
- (size_t)newlen);
-#else
- return sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
-#endif
-}
-
-#if SANITIZER_FREEBSD
-int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
- const void *newp, uptr newlen) {
- return sysctlbyname(sname, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
-}
-#endif
-#endif
-
-#if SANITIZER_LINUX
-#define SA_RESTORER 0x04000000
-// Doesn't set sa_restorer if the caller did not set it, so use with caution
-//(see below).
-int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
- __sanitizer_kernel_sigaction_t k_act, k_oldact;
- internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));
- internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));
- const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act;
- __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;
- if (u_act) {
- k_act.handler = u_act->handler;
- k_act.sigaction = u_act->sigaction;
- internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
- sizeof(__sanitizer_kernel_sigset_t));
- // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
- k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
- // FIXME: most often sa_restorer is unset, however the kernel requires it
- // to point to a valid signal restorer that calls the rt_sigreturn syscall.
- // If sa_restorer passed to the kernel is NULL, the program may crash upon
- // signal delivery or fail to unwind the stack in the signal handler.
- // libc implementation of sigaction() passes its own restorer to
- // rt_sigaction, so we need to do the same (we'll need to reimplement the
- // restorers; for x86_64 the restorer address can be obtained from
- // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).
-#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
- k_act.sa_restorer = u_act->sa_restorer;
-#endif
- }
-
- uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum,
- (uptr)(u_act ? &k_act : nullptr),
- (uptr)(u_oldact ? &k_oldact : nullptr),
- (uptr)sizeof(__sanitizer_kernel_sigset_t));
-
- if ((result == 0) && u_oldact) {
- u_oldact->handler = k_oldact.handler;
- u_oldact->sigaction = k_oldact.sigaction;
- internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,
- sizeof(__sanitizer_kernel_sigset_t));
- u_oldact->sa_flags = k_oldact.sa_flags;
-#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
- u_oldact->sa_restorer = k_oldact.sa_restorer;
-#endif
- }
- return result;
-}
-
-// Invokes sigaction via a raw syscall with a restorer, but does not support
-// all platforms yet.
-// We disable for Go simply because we have not yet added to buildgo.sh.
-#if (defined(__x86_64__) || SANITIZER_MIPS64) && !SANITIZER_GO
-int internal_sigaction_syscall(int signum, const void *act, void *oldact) {
- if (act == nullptr)
- return internal_sigaction_norestorer(signum, act, oldact);
- __sanitizer_sigaction u_adjust;
- internal_memcpy(&u_adjust, act, sizeof(u_adjust));
-#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
- if (u_adjust.sa_restorer == nullptr) {
- u_adjust.sa_restorer = internal_sigreturn;
- }
-#endif
- return internal_sigaction_norestorer(signum, (const void *)&u_adjust, oldact);
-}
-#endif // defined(__x86_64__) && !SANITIZER_GO
-#endif // SANITIZER_LINUX
-
-uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
- __sanitizer_sigset_t *oldset) {
-#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
- return internal_syscall(SYSCALL(sigprocmask), how, set, oldset);
-#else
- __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
- __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;
- return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how,
- (uptr)&k_set->sig[0], (uptr)&k_oldset->sig[0],
- sizeof(__sanitizer_kernel_sigset_t));
-#endif
-}
-
-void internal_sigfillset(__sanitizer_sigset_t *set) {
- internal_memset(set, 0xff, sizeof(*set));
-}
-
-void internal_sigemptyset(__sanitizer_sigset_t *set) {
- internal_memset(set, 0, sizeof(*set));
-}
-
-#if SANITIZER_LINUX
-void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
- signum -= 1;
- CHECK_GE(signum, 0);
- CHECK_LT(signum, sizeof(*set) * 8);
- __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
- const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
- const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
- k_set->sig[idx] &= ~(1 << bit);
-}
-
-bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
- signum -= 1;
- CHECK_GE(signum, 0);
- CHECK_LT(signum, sizeof(*set) * 8);
- __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
- const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
- const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
- return k_set->sig[idx] & (1 << bit);
-}
-#elif SANITIZER_FREEBSD
-void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
- sigset_t *rset = reinterpret_cast<sigset_t *>(set);
- sigdelset(rset, signum);
-}
-
-bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
- sigset_t *rset = reinterpret_cast<sigset_t *>(set);
- return sigismember(rset, signum);
-}
-#endif
-#endif // !SANITIZER_SOLARIS
-
-#if !SANITIZER_NETBSD
-// ThreadLister implementation.
-ThreadLister::ThreadLister(pid_t pid) : pid_(pid), buffer_(4096) {
- char task_directory_path[80];
- internal_snprintf(task_directory_path, sizeof(task_directory_path),
- "/proc/%d/task/", pid);
- descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);
- if (internal_iserror(descriptor_)) {
- Report("Can't open /proc/%d/task for reading.\n", pid);
- }
-}
-
-ThreadLister::Result ThreadLister::ListThreads(
- InternalMmapVector<tid_t> *threads) {
- if (internal_iserror(descriptor_))
- return Error;
- internal_lseek(descriptor_, 0, SEEK_SET);
- threads->clear();
-
- Result result = Ok;
- for (bool first_read = true;; first_read = false) {
- // Resize to max capacity if it was downsized by IsAlive.
- buffer_.resize(buffer_.capacity());
- CHECK_GE(buffer_.size(), 4096);
- uptr read = internal_getdents(
- descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size());
- if (!read)
- return result;
- if (internal_iserror(read)) {
- Report("Can't read directory entries from /proc/%d/task.\n", pid_);
- return Error;
- }
-
- for (uptr begin = (uptr)buffer_.data(), end = begin + read; begin < end;) {
- struct linux_dirent *entry = (struct linux_dirent *)begin;
- begin += entry->d_reclen;
- if (entry->d_ino == 1) {
- // Inode 1 is for bad blocks and also can be a reason for early return.
- // Should be emitted if kernel tried to output terminating thread.
- // See proc_task_readdir implementation in Linux.
- result = Incomplete;
- }
- if (entry->d_ino && *entry->d_name >= '0' && *entry->d_name <= '9')
- threads->push_back(internal_atoll(entry->d_name));
- }
-
- // Now we are going to detect short-read or early EOF. In such cases Linux
- // can return inconsistent list with missing alive threads.
- // Code will just remember that the list can be incomplete but it will
- // continue reads to return as much as possible.
- if (!first_read) {
- // The first one was a short-read by definition.
- result = Incomplete;
- } else if (read > buffer_.size() - 1024) {
- // Read was close to the buffer size. So double the size and assume the
- // worst.
- buffer_.resize(buffer_.size() * 2);
- result = Incomplete;
- } else if (!threads->empty() && !IsAlive(threads->back())) {
- // Maybe Linux early returned from read on terminated thread (!pid_alive)
- // and failed to restore read position.
- // See next_tid and proc_task_instantiate in Linux.
- result = Incomplete;
- }
- }
-}
-
-bool ThreadLister::IsAlive(int tid) {
- // /proc/%d/task/%d/status uses same call to detect alive threads as
- // proc_task_readdir. See task_state implementation in Linux.
- char path[80];
- internal_snprintf(path, sizeof(path), "/proc/%d/task/%d/status", pid_, tid);
- if (!ReadFileToVector(path, &buffer_) || buffer_.empty())
- return false;
- buffer_.push_back(0);
- static const char kPrefix[] = "\nPPid:";
- const char *field = internal_strstr(buffer_.data(), kPrefix);
- if (!field)
- return false;
- field += internal_strlen(kPrefix);
- return (int)internal_atoll(field) != 0;
-}
-
-ThreadLister::~ThreadLister() {
- if (!internal_iserror(descriptor_))
- internal_close(descriptor_);
-}
-#endif
-
-#if SANITIZER_WORDSIZE == 32
-// Take care of unusable kernel area in top gigabyte.
-static uptr GetKernelAreaSize() {
-#if SANITIZER_LINUX && !SANITIZER_X32
- const uptr gbyte = 1UL << 30;
-
- // Firstly check if there are writable segments
- // mapped to top gigabyte (e.g. stack).
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- MemoryMappedSegment segment;
- while (proc_maps.Next(&segment)) {
- if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0;
- }
-
-#if !SANITIZER_ANDROID
- // Even if nothing is mapped, top Gb may still be accessible
- // if we are running on 64-bit kernel.
- // Uname may report misleading results if personality type
- // is modified (e.g. under schroot) so check this as well.
- struct utsname uname_info;
- int pers = personality(0xffffffffUL);
- if (!(pers & PER_MASK)
- && uname(&uname_info) == 0
- && internal_strstr(uname_info.machine, "64"))
- return 0;
-#endif // SANITIZER_ANDROID
-
- // Top gigabyte is reserved for kernel.
- return gbyte;
-#else
- return 0;
-#endif // SANITIZER_LINUX && !SANITIZER_X32
-}
-#endif // SANITIZER_WORDSIZE == 32
-
-uptr GetMaxVirtualAddress() {
-#if (SANITIZER_NETBSD || SANITIZER_OPENBSD) && defined(__x86_64__)
- return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE)
-#elif SANITIZER_WORDSIZE == 64
-# if defined(__powerpc64__) || defined(__aarch64__)
- // On PowerPC64 we have two different address space layouts: 44- and 46-bit.
- // We somehow need to figure out which one we are using now and choose
- // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
- // Note that with 'ulimit -s unlimited' the stack is moved away from the top
- // of the address space, so simply checking the stack address is not enough.
- // This should (does) work for both PowerPC64 Endian modes.
- // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
- return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
-# elif defined(__mips64)
- return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
-# elif defined(__s390x__)
- return (1ULL << 53) - 1; // 0x001fffffffffffffUL;
-# elif defined(__sparc__)
- return ~(uptr)0;
-# else
- return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
-# endif
-#else // SANITIZER_WORDSIZE == 32
-# if defined(__s390__)
- return (1ULL << 31) - 1; // 0x7fffffff;
-# else
- return (1ULL << 32) - 1; // 0xffffffff;
-# endif
-#endif // SANITIZER_WORDSIZE
-}
-
-uptr GetMaxUserVirtualAddress() {
- uptr addr = GetMaxVirtualAddress();
-#if SANITIZER_WORDSIZE == 32 && !defined(__s390__)
- if (!common_flags()->full_address_space)
- addr -= GetKernelAreaSize();
- CHECK_LT(reinterpret_cast<uptr>(&addr), addr);
-#endif
- return addr;
-}
-
-uptr GetPageSize() {
-// Android post-M sysconf(_SC_PAGESIZE) crashes if called from .preinit_array.
-#if SANITIZER_ANDROID
- return 4096;
-#elif SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__))
- return EXEC_PAGESIZE;
-#elif SANITIZER_USE_GETAUXVAL
- return getauxval(AT_PAGESZ);
-#else
- return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.
-#endif
-}
-
-#if !SANITIZER_OPENBSD
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
-#if SANITIZER_SOLARIS
- const char *default_module_name = getexecname();
- CHECK_NE(default_module_name, NULL);
- return internal_snprintf(buf, buf_len, "%s", default_module_name);
-#else
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD
-#if SANITIZER_FREEBSD
- const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
-#else
- const int Mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME};
-#endif
- const char *default_module_name = "kern.proc.pathname";
- uptr Size = buf_len;
- bool IsErr =
- (internal_sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
- int readlink_error = IsErr ? errno : 0;
- uptr module_name_len = Size;
-#else
- const char *default_module_name = "/proc/self/exe";
- uptr module_name_len = internal_readlink(
- default_module_name, buf, buf_len);
- int readlink_error;
- bool IsErr = internal_iserror(module_name_len, &readlink_error);
-#endif // SANITIZER_SOLARIS
- if (IsErr) {
- // We can't read binary name for some reason, assume it's unknown.
- Report("WARNING: reading executable name failed with errno %d, "
- "some stack frames may not be symbolized\n", readlink_error);
- module_name_len = internal_snprintf(buf, buf_len, "%s",
- default_module_name);
- CHECK_LT(module_name_len, buf_len);
- }
- return module_name_len;
-#endif
-}
-#endif // !SANITIZER_OPENBSD
-
-uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
-#if SANITIZER_LINUX
- char *tmpbuf;
- uptr tmpsize;
- uptr tmplen;
- if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen,
- 1024 * 1024)) {
- internal_strncpy(buf, tmpbuf, buf_len);
- UnmapOrDie(tmpbuf, tmpsize);
- return internal_strlen(buf);
- }
-#endif
- return ReadBinaryName(buf, buf_len);
-}
-
-// Match full names of the form /path/to/base_name{-,.}*
-bool LibraryNameIs(const char *full_name, const char *base_name) {
- const char *name = full_name;
- // Strip path.
- while (*name != '\0') name++;
- while (name > full_name && *name != '/') name--;
- if (*name == '/') name++;
- uptr base_name_length = internal_strlen(base_name);
- if (internal_strncmp(name, base_name, base_name_length)) return false;
- return (name[base_name_length] == '-' || name[base_name_length] == '.');
-}
-
-#if !SANITIZER_ANDROID
-// Call cb for each region mapped by map.
-void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
- CHECK_NE(map, nullptr);
-#if !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
- typedef ElfW(Phdr) Elf_Phdr;
- typedef ElfW(Ehdr) Elf_Ehdr;
-#endif // !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
- char *base = (char *)map->l_addr;
- Elf_Ehdr *ehdr = (Elf_Ehdr *)base;
- char *phdrs = base + ehdr->e_phoff;
- char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize;
-
- // Find the segment with the minimum base so we can "relocate" the p_vaddr
- // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC
- // objects have a non-zero base.
- uptr preferred_base = (uptr)-1;
- for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {
- Elf_Phdr *phdr = (Elf_Phdr *)iter;
- if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr)
- preferred_base = (uptr)phdr->p_vaddr;
- }
-
- // Compute the delta from the real base to get a relocation delta.
- sptr delta = (uptr)base - preferred_base;
- // Now we can figure out what the loader really mapped.
- for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {
- Elf_Phdr *phdr = (Elf_Phdr *)iter;
- if (phdr->p_type == PT_LOAD) {
- uptr seg_start = phdr->p_vaddr + delta;
- uptr seg_end = seg_start + phdr->p_memsz;
- // None of these values are aligned. We consider the ragged edges of the
- // load command as defined, since they are mapped from the file.
- seg_start = RoundDownTo(seg_start, GetPageSizeCached());
- seg_end = RoundUpTo(seg_end, GetPageSizeCached());
- cb((void *)seg_start, seg_end - seg_start);
- }
- }
-}
-#endif
-
-#if defined(__x86_64__) && SANITIZER_LINUX
-// We cannot use glibc's clone wrapper, because it messes with the child
-// task's TLS. It writes the PID and TID of the child task to its thread
-// descriptor, but in our case the child task shares the thread descriptor with
-// the parent (because we don't know how to allocate a new thread
-// descriptor to keep glibc happy). So the stock version of clone(), when
-// used with CLONE_VM, would end up corrupting the parent's thread descriptor.
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- long long res;
- if (!fn || !child_stack)
- return -EINVAL;
- CHECK_EQ(0, (uptr)child_stack % 16);
- child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
- ((unsigned long long *)child_stack)[0] = (uptr)fn;
- ((unsigned long long *)child_stack)[1] = (uptr)arg;
- register void *r8 __asm__("r8") = newtls;
- register int *r10 __asm__("r10") = child_tidptr;
- __asm__ __volatile__(
- /* %rax = syscall(%rax = SYSCALL(clone),
- * %rdi = flags,
- * %rsi = child_stack,
- * %rdx = parent_tidptr,
- * %r8 = new_tls,
- * %r10 = child_tidptr)
- */
- "syscall\n"
-
- /* if (%rax != 0)
- * return;
- */
- "testq %%rax,%%rax\n"
- "jnz 1f\n"
-
- /* In the child. Terminate unwind chain. */
- // XXX: We should also terminate the CFI unwind chain
- // here. Unfortunately clang 3.2 doesn't support the
- // necessary CFI directives, so we skip that part.
- "xorq %%rbp,%%rbp\n"
-
- /* Call "fn(arg)". */
- "popq %%rax\n"
- "popq %%rdi\n"
- "call *%%rax\n"
-
- /* Call _exit(%rax). */
- "movq %%rax,%%rdi\n"
- "movq %2,%%rax\n"
- "syscall\n"
-
- /* Return to parent. */
- "1:\n"
- : "=a" (res)
- : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
- "S"(child_stack),
- "D"(flags),
- "d"(parent_tidptr),
- "r"(r8),
- "r"(r10)
- : "memory", "r11", "rcx");
- return res;
-}
-#elif defined(__mips__)
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- long long res;
- if (!fn || !child_stack)
- return -EINVAL;
- CHECK_EQ(0, (uptr)child_stack % 16);
- child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
- ((unsigned long long *)child_stack)[0] = (uptr)fn;
- ((unsigned long long *)child_stack)[1] = (uptr)arg;
- register void *a3 __asm__("$7") = newtls;
- register int *a4 __asm__("$8") = child_tidptr;
- // We don't have proper CFI directives here because it requires alot of code
- // for very marginal benefits.
- __asm__ __volatile__(
- /* $v0 = syscall($v0 = __NR_clone,
- * $a0 = flags,
- * $a1 = child_stack,
- * $a2 = parent_tidptr,
- * $a3 = new_tls,
- * $a4 = child_tidptr)
- */
- ".cprestore 16;\n"
- "move $4,%1;\n"
- "move $5,%2;\n"
- "move $6,%3;\n"
- "move $7,%4;\n"
- /* Store the fifth argument on stack
- * if we are using 32-bit abi.
- */
-#if SANITIZER_WORDSIZE == 32
- "lw %5,16($29);\n"
-#else
- "move $8,%5;\n"
-#endif
- "li $2,%6;\n"
- "syscall;\n"
-
- /* if ($v0 != 0)
- * return;
- */
- "bnez $2,1f;\n"
-
- /* Call "fn(arg)". */
-#if SANITIZER_WORDSIZE == 32
-#ifdef __BIG_ENDIAN__
- "lw $25,4($29);\n"
- "lw $4,12($29);\n"
-#else
- "lw $25,0($29);\n"
- "lw $4,8($29);\n"
-#endif
-#else
- "ld $25,0($29);\n"
- "ld $4,8($29);\n"
-#endif
- "jal $25;\n"
-
- /* Call _exit($v0). */
- "move $4,$2;\n"
- "li $2,%7;\n"
- "syscall;\n"
-
- /* Return to parent. */
- "1:\n"
- : "=r" (res)
- : "r"(flags),
- "r"(child_stack),
- "r"(parent_tidptr),
- "r"(a3),
- "r"(a4),
- "i"(__NR_clone),
- "i"(__NR_exit)
- : "memory", "$29" );
- return res;
-}
-#elif defined(__aarch64__)
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- long long res;
- if (!fn || !child_stack)
- return -EINVAL;
- CHECK_EQ(0, (uptr)child_stack % 16);
- child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
- ((unsigned long long *)child_stack)[0] = (uptr)fn;
- ((unsigned long long *)child_stack)[1] = (uptr)arg;
-
- register int (*__fn)(void *) __asm__("x0") = fn;
- register void *__stack __asm__("x1") = child_stack;
- register int __flags __asm__("x2") = flags;
- register void *__arg __asm__("x3") = arg;
- register int *__ptid __asm__("x4") = parent_tidptr;
- register void *__tls __asm__("x5") = newtls;
- register int *__ctid __asm__("x6") = child_tidptr;
-
- __asm__ __volatile__(
- "mov x0,x2\n" /* flags */
- "mov x2,x4\n" /* ptid */
- "mov x3,x5\n" /* tls */
- "mov x4,x6\n" /* ctid */
- "mov x8,%9\n" /* clone */
-
- "svc 0x0\n"
-
- /* if (%r0 != 0)
- * return %r0;
- */
- "cmp x0, #0\n"
- "bne 1f\n"
-
- /* In the child, now. Call "fn(arg)". */
- "ldp x1, x0, [sp], #16\n"
- "blr x1\n"
-
- /* Call _exit(%r0). */
- "mov x8, %10\n"
- "svc 0x0\n"
- "1:\n"
-
- : "=r" (res)
- : "i"(-EINVAL),
- "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
- "r"(__ptid), "r"(__tls), "r"(__ctid),
- "i"(__NR_clone), "i"(__NR_exit)
- : "x30", "memory");
- return res;
-}
-#elif defined(__powerpc64__)
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- long long res;
-// Stack frame structure.
-#if SANITIZER_PPC64V1
-// Back chain == 0 (SP + 112)
-// Frame (112 bytes):
-// Parameter save area (SP + 48), 8 doublewords
-// TOC save area (SP + 40)
-// Link editor doubleword (SP + 32)
-// Compiler doubleword (SP + 24)
-// LR save area (SP + 16)
-// CR save area (SP + 8)
-// Back chain (SP + 0)
-# define FRAME_SIZE 112
-# define FRAME_TOC_SAVE_OFFSET 40
-#elif SANITIZER_PPC64V2
-// Back chain == 0 (SP + 32)
-// Frame (32 bytes):
-// TOC save area (SP + 24)
-// LR save area (SP + 16)
-// CR save area (SP + 8)
-// Back chain (SP + 0)
-# define FRAME_SIZE 32
-# define FRAME_TOC_SAVE_OFFSET 24
-#else
-# error "Unsupported PPC64 ABI"
-#endif
- if (!fn || !child_stack)
- return -EINVAL;
- CHECK_EQ(0, (uptr)child_stack % 16);
-
- register int (*__fn)(void *) __asm__("r3") = fn;
- register void *__cstack __asm__("r4") = child_stack;
- register int __flags __asm__("r5") = flags;
- register void *__arg __asm__("r6") = arg;
- register int *__ptidptr __asm__("r7") = parent_tidptr;
- register void *__newtls __asm__("r8") = newtls;
- register int *__ctidptr __asm__("r9") = child_tidptr;
-
- __asm__ __volatile__(
- /* fn and arg are saved across the syscall */
- "mr 28, %5\n\t"
- "mr 27, %8\n\t"
-
- /* syscall
- r0 == __NR_clone
- r3 == flags
- r4 == child_stack
- r5 == parent_tidptr
- r6 == newtls
- r7 == child_tidptr */
- "mr 3, %7\n\t"
- "mr 5, %9\n\t"
- "mr 6, %10\n\t"
- "mr 7, %11\n\t"
- "li 0, %3\n\t"
- "sc\n\t"
-
- /* Test if syscall was successful */
- "cmpdi cr1, 3, 0\n\t"
- "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
- "bne- cr1, 1f\n\t"
-
- /* Set up stack frame */
- "li 29, 0\n\t"
- "stdu 29, -8(1)\n\t"
- "stdu 1, -%12(1)\n\t"
- /* Do the function call */
- "std 2, %13(1)\n\t"
-#if SANITIZER_PPC64V1
- "ld 0, 0(28)\n\t"
- "ld 2, 8(28)\n\t"
- "mtctr 0\n\t"
-#elif SANITIZER_PPC64V2
- "mr 12, 28\n\t"
- "mtctr 12\n\t"
-#else
-# error "Unsupported PPC64 ABI"
-#endif
- "mr 3, 27\n\t"
- "bctrl\n\t"
- "ld 2, %13(1)\n\t"
-
- /* Call _exit(r3) */
- "li 0, %4\n\t"
- "sc\n\t"
-
- /* Return to parent */
- "1:\n\t"
- "mr %0, 3\n\t"
- : "=r" (res)
- : "0" (-1),
- "i" (EINVAL),
- "i" (__NR_clone),
- "i" (__NR_exit),
- "r" (__fn),
- "r" (__cstack),
- "r" (__flags),
- "r" (__arg),
- "r" (__ptidptr),
- "r" (__newtls),
- "r" (__ctidptr),
- "i" (FRAME_SIZE),
- "i" (FRAME_TOC_SAVE_OFFSET)
- : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
- return res;
-}
-#elif defined(__i386__) && SANITIZER_LINUX
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- int res;
- if (!fn || !child_stack)
- return -EINVAL;
- CHECK_EQ(0, (uptr)child_stack % 16);
- child_stack = (char *)child_stack - 7 * sizeof(unsigned int);
- ((unsigned int *)child_stack)[0] = (uptr)flags;
- ((unsigned int *)child_stack)[1] = (uptr)0;
- ((unsigned int *)child_stack)[2] = (uptr)fn;
- ((unsigned int *)child_stack)[3] = (uptr)arg;
- __asm__ __volatile__(
- /* %eax = syscall(%eax = SYSCALL(clone),
- * %ebx = flags,
- * %ecx = child_stack,
- * %edx = parent_tidptr,
- * %esi = new_tls,
- * %edi = child_tidptr)
- */
-
- /* Obtain flags */
- "movl (%%ecx), %%ebx\n"
- /* Do the system call */
- "pushl %%ebx\n"
- "pushl %%esi\n"
- "pushl %%edi\n"
- /* Remember the flag value. */
- "movl %%ebx, (%%ecx)\n"
- "int $0x80\n"
- "popl %%edi\n"
- "popl %%esi\n"
- "popl %%ebx\n"
-
- /* if (%eax != 0)
- * return;
- */
-
- "test %%eax,%%eax\n"
- "jnz 1f\n"
-
- /* terminate the stack frame */
- "xorl %%ebp,%%ebp\n"
- /* Call FN. */
- "call *%%ebx\n"
-#ifdef PIC
- "call here\n"
- "here:\n"
- "popl %%ebx\n"
- "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
-#endif
- /* Call exit */
- "movl %%eax, %%ebx\n"
- "movl %2, %%eax\n"
- "int $0x80\n"
- "1:\n"
- : "=a" (res)
- : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
- "c"(child_stack),
- "d"(parent_tidptr),
- "S"(newtls),
- "D"(child_tidptr)
- : "memory");
- return res;
-}
-#elif defined(__arm__) && SANITIZER_LINUX
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- unsigned int res;
- if (!fn || !child_stack)
- return -EINVAL;
- child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
- ((unsigned int *)child_stack)[0] = (uptr)fn;
- ((unsigned int *)child_stack)[1] = (uptr)arg;
- register int r0 __asm__("r0") = flags;
- register void *r1 __asm__("r1") = child_stack;
- register int *r2 __asm__("r2") = parent_tidptr;
- register void *r3 __asm__("r3") = newtls;
- register int *r4 __asm__("r4") = child_tidptr;
- register int r7 __asm__("r7") = __NR_clone;
-
-#if __ARM_ARCH > 4 || defined (__ARM_ARCH_4T__)
-# define ARCH_HAS_BX
-#endif
-#if __ARM_ARCH > 4
-# define ARCH_HAS_BLX
-#endif
-
-#ifdef ARCH_HAS_BX
-# ifdef ARCH_HAS_BLX
-# define BLX(R) "blx " #R "\n"
-# else
-# define BLX(R) "mov lr, pc; bx " #R "\n"
-# endif
-#else
-# define BLX(R) "mov lr, pc; mov pc," #R "\n"
-#endif
-
- __asm__ __volatile__(
- /* %r0 = syscall(%r7 = SYSCALL(clone),
- * %r0 = flags,
- * %r1 = child_stack,
- * %r2 = parent_tidptr,
- * %r3 = new_tls,
- * %r4 = child_tidptr)
- */
-
- /* Do the system call */
- "swi 0x0\n"
-
- /* if (%r0 != 0)
- * return %r0;
- */
- "cmp r0, #0\n"
- "bne 1f\n"
-
- /* In the child, now. Call "fn(arg)". */
- "ldr r0, [sp, #4]\n"
- "ldr ip, [sp], #8\n"
- BLX(ip)
- /* Call _exit(%r0). */
- "mov r7, %7\n"
- "swi 0x0\n"
- "1:\n"
- "mov %0, r0\n"
- : "=r"(res)
- : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
- "i"(__NR_exit)
- : "memory");
- return res;
-}
-#endif // defined(__x86_64__) && SANITIZER_LINUX
-
-#if SANITIZER_ANDROID
-#if __ANDROID_API__ < 21
-extern "C" __attribute__((weak)) int dl_iterate_phdr(
- int (*)(struct dl_phdr_info *, size_t, void *), void *);
-#endif
-
-static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
- void *data) {
- // Any name starting with "lib" indicates a bug in L where library base names
- // are returned instead of paths.
- if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
- info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
- *(bool *)data = true;
- return 1;
- }
- return 0;
-}
-
-static atomic_uint32_t android_api_level;
-
-static AndroidApiLevel AndroidDetectApiLevelStatic() {
-#if __ANDROID_API__ <= 19
- return ANDROID_KITKAT;
-#elif __ANDROID_API__ <= 22
- return ANDROID_LOLLIPOP_MR1;
-#else
- return ANDROID_POST_LOLLIPOP;
-#endif
-}
-
-static AndroidApiLevel AndroidDetectApiLevel() {
- if (!&dl_iterate_phdr)
- return ANDROID_KITKAT; // K or lower
- bool base_name_seen = false;
- dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
- if (base_name_seen)
- return ANDROID_LOLLIPOP_MR1; // L MR1
- return ANDROID_POST_LOLLIPOP; // post-L
- // Plain L (API level 21) is completely broken wrt ASan and not very
- // interesting to detect.
-}
-
-extern "C" __attribute__((weak)) void* _DYNAMIC;
-
-AndroidApiLevel AndroidGetApiLevel() {
- AndroidApiLevel level =
- (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
- if (level) return level;
- level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic()
- : AndroidDetectApiLevel();
- atomic_store(&android_api_level, level, memory_order_relaxed);
- return level;
-}
-
-#endif
-
-static HandleSignalMode GetHandleSignalModeImpl(int signum) {
- switch (signum) {
- case SIGABRT:
- return common_flags()->handle_abort;
- case SIGILL:
- return common_flags()->handle_sigill;
- case SIGTRAP:
- return common_flags()->handle_sigtrap;
- case SIGFPE:
- return common_flags()->handle_sigfpe;
- case SIGSEGV:
- return common_flags()->handle_segv;
- case SIGBUS:
- return common_flags()->handle_sigbus;
- }
- return kHandleSignalNo;
-}
-
-HandleSignalMode GetHandleSignalMode(int signum) {
- HandleSignalMode result = GetHandleSignalModeImpl(signum);
- if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler)
- return kHandleSignalExclusive;
- return result;
-}
-
-#if !SANITIZER_GO
-void *internal_start_thread(void(*func)(void *arg), void *arg) {
- // Start the thread with signals blocked, otherwise it can steal user signals.
- __sanitizer_sigset_t set, old;
- internal_sigfillset(&set);
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
- // on any thread, setuid call hangs (see test/tsan/setuid.c).
- internal_sigdelset(&set, 33);
-#endif
- internal_sigprocmask(SIG_SETMASK, &set, &old);
- void *th;
- real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg);
- internal_sigprocmask(SIG_SETMASK, &old, nullptr);
- return th;
-}
-
-void internal_join_thread(void *th) {
- real_pthread_join(th, nullptr);
-}
-#else
-void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
-
-void internal_join_thread(void *th) {}
-#endif
-
-#if defined(__aarch64__)
-// Android headers in the older NDK releases miss this definition.
-struct __sanitizer_esr_context {
- struct _aarch64_ctx head;
- uint64_t esr;
-};
-
-static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
- static const u32 kEsrMagic = 0x45535201;
- u8 *aux = ucontext->uc_mcontext.__reserved;
- while (true) {
- _aarch64_ctx *ctx = (_aarch64_ctx *)aux;
- if (ctx->size == 0) break;
- if (ctx->magic == kEsrMagic) {
- *esr = ((__sanitizer_esr_context *)ctx)->esr;
- return true;
- }
- aux += ctx->size;
- }
- return false;
-}
-#endif
-
-#if SANITIZER_OPENBSD
-using Context = sigcontext;
-#else
-using Context = ucontext_t;
-#endif
-
-SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
- Context *ucontext = (Context *)context;
-#if defined(__x86_64__) || defined(__i386__)
- static const uptr PF_WRITE = 1U << 1;
-#if SANITIZER_FREEBSD
- uptr err = ucontext->uc_mcontext.mc_err;
-#elif SANITIZER_NETBSD
- uptr err = ucontext->uc_mcontext.__gregs[_REG_ERR];
-#elif SANITIZER_OPENBSD
- uptr err = ucontext->sc_err;
-#elif SANITIZER_SOLARIS && defined(__i386__)
- const int Err = 13;
- uptr err = ucontext->uc_mcontext.gregs[Err];
-#else
- uptr err = ucontext->uc_mcontext.gregs[REG_ERR];
-#endif // SANITIZER_FREEBSD
- return err & PF_WRITE ? WRITE : READ;
-#elif defined(__mips__)
- uint32_t *exception_source;
- uint32_t faulty_instruction;
- uint32_t op_code;
-
- exception_source = (uint32_t *)ucontext->uc_mcontext.pc;
- faulty_instruction = (uint32_t)(*exception_source);
-
- op_code = (faulty_instruction >> 26) & 0x3f;
-
- // FIXME: Add support for FPU, microMIPS, DSP, MSA memory instructions.
- switch (op_code) {
- case 0x28: // sb
- case 0x29: // sh
- case 0x2b: // sw
- case 0x3f: // sd
-#if __mips_isa_rev < 6
- case 0x2c: // sdl
- case 0x2d: // sdr
- case 0x2a: // swl
- case 0x2e: // swr
-#endif
- return SignalContext::WRITE;
-
- case 0x20: // lb
- case 0x24: // lbu
- case 0x21: // lh
- case 0x25: // lhu
- case 0x23: // lw
- case 0x27: // lwu
- case 0x37: // ld
-#if __mips_isa_rev < 6
- case 0x1a: // ldl
- case 0x1b: // ldr
- case 0x22: // lwl
- case 0x26: // lwr
-#endif
- return SignalContext::READ;
-#if __mips_isa_rev == 6
- case 0x3b: // pcrel
- op_code = (faulty_instruction >> 19) & 0x3;
- switch (op_code) {
- case 0x1: // lwpc
- case 0x2: // lwupc
- return SignalContext::READ;
- }
-#endif
- }
- return SignalContext::UNKNOWN;
-#elif defined(__arm__)
- static const uptr FSR_WRITE = 1U << 11;
- uptr fsr = ucontext->uc_mcontext.error_code;
- return fsr & FSR_WRITE ? WRITE : READ;
-#elif defined(__aarch64__)
- static const u64 ESR_ELx_WNR = 1U << 6;
- u64 esr;
- if (!Aarch64GetESR(ucontext, &esr)) return UNKNOWN;
- return esr & ESR_ELx_WNR ? WRITE : READ;
-#elif defined(__sparc__)
- // Decode the instruction to determine the access type.
- // From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype).
-# if SANITIZER_SOLARIS
- uptr pc = ucontext->uc_mcontext.gregs[REG_PC];
-# else
- // Historical BSDism here.
- struct sigcontext *scontext = (struct sigcontext *)context;
-# if defined(__arch64__)
- uptr pc = scontext->sigc_regs.tpc;
-# else
- uptr pc = scontext->si_regs.pc;
-# endif
-# endif
- u32 instr = *(u32 *)pc;
- return (instr >> 21) & 1 ? WRITE: READ;
-#else
- (void)ucontext;
- return UNKNOWN; // FIXME: Implement.
-#endif
-}
-
-void SignalContext::DumpAllRegisters(void *context) {
- // FIXME: Implement this.
-}
-
-static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
-#if SANITIZER_NETBSD
- // This covers all NetBSD architectures
- ucontext_t *ucontext = (ucontext_t *)context;
- *pc = _UC_MACHINE_PC(ucontext);
- *bp = _UC_MACHINE_FP(ucontext);
- *sp = _UC_MACHINE_SP(ucontext);
-#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(__aarch64__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.pc;
- *bp = ucontext->uc_mcontext.regs[29];
- *sp = ucontext->uc_mcontext.sp;
-#elif defined(__hppa__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.sc_iaoq[0];
- /* GCC uses %r3 whenever a frame pointer is needed. */
- *bp = ucontext->uc_mcontext.sc_gr[3];
- *sp = ucontext->uc_mcontext.sc_gr[30];
-#elif defined(__x86_64__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.mc_rip;
- *bp = ucontext->uc_mcontext.mc_rbp;
- *sp = ucontext->uc_mcontext.mc_rsp;
-#elif SANITIZER_OPENBSD
- sigcontext *ucontext = (sigcontext *)context;
- *pc = ucontext->sc_rip;
- *bp = ucontext->sc_rbp;
- *sp = ucontext->sc_rsp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[REG_RIP];
- *bp = ucontext->uc_mcontext.gregs[REG_RBP];
- *sp = ucontext->uc_mcontext.gregs[REG_RSP];
-# endif
-#elif defined(__i386__)
-# if SANITIZER_FREEBSD
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.mc_eip;
- *bp = ucontext->uc_mcontext.mc_ebp;
- *sp = ucontext->uc_mcontext.mc_esp;
-#elif SANITIZER_OPENBSD
- sigcontext *ucontext = (sigcontext *)context;
- *pc = ucontext->sc_eip;
- *bp = ucontext->sc_ebp;
- *sp = ucontext->sc_esp;
-# else
- ucontext_t *ucontext = (ucontext_t*)context;
-# if SANITIZER_SOLARIS
- /* Use the numeric values: the symbolic ones are undefined by llvm
- include/llvm/Support/Solaris.h. */
-# ifndef REG_EIP
-# define REG_EIP 14 // REG_PC
-# endif
-# ifndef REG_EBP
-# define REG_EBP 6 // REG_FP
-# endif
-# ifndef REG_ESP
-# define REG_ESP 17 // REG_SP
-# endif
-# endif
- *pc = ucontext->uc_mcontext.gregs[REG_EIP];
- *bp = ucontext->uc_mcontext.gregs[REG_EBP];
- *sp = ucontext->uc_mcontext.gregs[REG_ESP];
-# endif
-#elif defined(__powerpc__) || defined(__powerpc64__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.regs->nip;
- *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
- // The powerpc{,64}-linux ABIs do not specify r31 as the frame
- // pointer, but GCC always uses r31 when we need a frame pointer.
- *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
-#elif defined(__sparc__)
-# if defined(__arch64__) || defined(__sparcv9)
-# define STACK_BIAS 2047
-# else
-# define STACK_BIAS 0
-# endif
-# if SANITIZER_SOLARIS
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.gregs[REG_PC];
- *sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS;
-# else
- // Historical BSDism here.
- struct sigcontext *scontext = (struct sigcontext *)context;
-# if defined(__arch64__)
- *pc = scontext->sigc_regs.tpc;
- *sp = scontext->sigc_regs.u_regs[14] + STACK_BIAS;
-# else
- *pc = scontext->si_regs.pc;
- *sp = scontext->si_regs.u_regs[14];
-# endif
-# endif
- *bp = (uptr) ((uhwptr *) *sp)[14] + STACK_BIAS;
-#elif defined(__mips__)
- ucontext_t *ucontext = (ucontext_t*)context;
- *pc = ucontext->uc_mcontext.pc;
- *bp = ucontext->uc_mcontext.gregs[30];
- *sp = ucontext->uc_mcontext.gregs[29];
-#elif defined(__s390__)
- ucontext_t *ucontext = (ucontext_t*)context;
-# if defined(__s390x__)
- *pc = ucontext->uc_mcontext.psw.addr;
-# else
- *pc = ucontext->uc_mcontext.psw.addr & 0x7fffffff;
-# endif
- *bp = ucontext->uc_mcontext.gregs[11];
- *sp = ucontext->uc_mcontext.gregs[15];
-#else
-# error "Unsupported arch"
-#endif
-}
-
-void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
-
-void MaybeReexec() {
- // No need to re-exec on Linux.
-}
-
-void CheckASLR() {
-#if SANITIZER_NETBSD
- int mib[3];
- int paxflags;
- uptr len = sizeof(paxflags);
-
- mib[0] = CTL_PROC;
- mib[1] = internal_getpid();
- mib[2] = PROC_PID_PAXFLAGS;
-
- if (UNLIKELY(internal_sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1)) {
- Printf("sysctl failed\n");
- Die();
- }
-
- if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_ASLR)) {
- Printf("This sanitizer is not compatible with enabled ASLR\n");
- Die();
- }
-#elif SANITIZER_PPC64V2
- // Disable ASLR for Linux PPC64LE.
- int old_personality = personality(0xffffffff);
- if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) {
- VReport(1, "WARNING: Program is being run with address space layout "
- "randomization (ASLR) enabled which prevents the thread and "
- "memory sanitizers from working on powerpc64le.\n"
- "ASLR will be disabled and the program re-executed.\n");
- CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
- ReExec();
- }
-#else
- // Do nothing
-#endif
-}
-
-void PrintModuleMap() { }
-
-void CheckNoDeepBind(const char *filename, int flag) {
-#ifdef RTLD_DEEPBIND
- if (flag & RTLD_DEEPBIND) {
- Report(
- "You are trying to dlopen a %s shared library with RTLD_DEEPBIND flag"
- " which is incompatibe with sanitizer runtime "
- "(see https://github.com/google/sanitizers/issues/611 for details"
- "). If you want to run %s library under sanitizers please remove "
- "RTLD_DEEPBIND from dlopen flags.\n",
- filename, filename);
- Die();
- }
-#endif
-}
-
-uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
- uptr *largest_gap_found,
- uptr *max_occupied_addr) {
- UNREACHABLE("FindAvailableMemoryRange is not available");
- return 0;
-}
-
-bool GetRandom(void *buffer, uptr length, bool blocking) {
- if (!buffer || !length || length > 256)
- return false;
-#if SANITIZER_USE_GETENTROPY
- uptr rnd = getentropy(buffer, length);
- int rverrno = 0;
- if (internal_iserror(rnd, &rverrno) && rverrno == EFAULT)
- return false;
- else if (rnd == 0)
- return true;
-#endif // SANITIZER_USE_GETENTROPY
-
-#if SANITIZER_USE_GETRANDOM
- static atomic_uint8_t skip_getrandom_syscall;
- if (!atomic_load_relaxed(&skip_getrandom_syscall)) {
- // Up to 256 bytes, getrandom will not be interrupted.
- uptr res = internal_syscall(SYSCALL(getrandom), buffer, length,
- blocking ? 0 : GRND_NONBLOCK);
- int rverrno = 0;
- if (internal_iserror(res, &rverrno) && rverrno == ENOSYS)
- atomic_store_relaxed(&skip_getrandom_syscall, 1);
- else if (res == length)
- return true;
- }
-#endif // SANITIZER_USE_GETRANDOM
- // Up to 256 bytes, a read off /dev/urandom will not be interrupted.
- // blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
- uptr fd = internal_open("/dev/urandom", O_RDONLY);
- if (internal_iserror(fd))
- return false;
- uptr res = internal_read(fd, buffer, length);
- if (internal_iserror(res))
- return false;
- internal_close(fd);
- return true;
-}
-
-} // namespace __sanitizer
-
-#endif
--- /dev/null
+//===-- sanitizer_linux.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements linux-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+ SANITIZER_OPENBSD || SANITIZER_SOLARIS
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_getauxval.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#if SANITIZER_LINUX
+#include <asm/param.h>
+#endif
+
+// For mips64, syscall(__NR_stat) fills the buffer in the 'struct kernel_stat'
+// format. Struct kernel_stat is defined as 'struct stat' in asm/stat.h. To
+// access stat from asm/stat.h, without conflicting with definition in
+// sys/stat.h, we use this trick.
+#if defined(__mips64)
+#include <asm/unistd.h>
+#include <sys/types.h>
+#define stat kernel_stat
+#include <asm/stat.h>
+#undef stat
+#endif
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#if !SANITIZER_SOLARIS
+#include <sys/ptrace.h>
+#endif
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#if !SANITIZER_OPENBSD
+#include <ucontext.h>
+#endif
+#if SANITIZER_OPENBSD
+#include <sys/futex.h>
+#include <sys/sysctl.h>
+#endif
+#include <unistd.h>
+
+#if SANITIZER_LINUX
+#include <sys/utsname.h>
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#include <sys/personality.h>
+#endif
+
+#if SANITIZER_FREEBSD
+#include <sys/exec.h>
+#include <sys/sysctl.h>
+#include <machine/atomic.h>
+extern "C" {
+// <sys/umtx.h> must be included after <errno.h> and <sys/types.h> on
+// FreeBSD 9.2 and 10.0.
+#include <sys/umtx.h>
+}
+#include <sys/thr.h>
+#endif // SANITIZER_FREEBSD
+
+#if SANITIZER_NETBSD
+#include <limits.h> // For NAME_MAX
+#include <sys/sysctl.h>
+#include <sys/exec.h>
+extern struct ps_strings *__ps_strings;
+#endif // SANITIZER_NETBSD
+
+#if SANITIZER_SOLARIS
+#include <stdlib.h>
+#include <thread.h>
+#define environ _environ
+#endif
+
+extern char **environ;
+
+#if SANITIZER_LINUX
+// <linux/time.h>
+struct kernel_timeval {
+ long tv_sec;
+ long tv_usec;
+};
+
+// <linux/futex.h> is broken on some linux distributions.
+const int FUTEX_WAIT = 0;
+const int FUTEX_WAKE = 1;
+const int FUTEX_PRIVATE_FLAG = 128;
+const int FUTEX_WAIT_PRIVATE = FUTEX_WAIT | FUTEX_PRIVATE_FLAG;
+const int FUTEX_WAKE_PRIVATE = FUTEX_WAKE | FUTEX_PRIVATE_FLAG;
+#endif // SANITIZER_LINUX
+
+// Are we using 32-bit or 64-bit Linux syscalls?
+// x32 (which defines __x86_64__) has SANITIZER_WORDSIZE == 32
+// but it still needs to use 64-bit syscalls.
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__powerpc64__) || \
+ SANITIZER_WORDSIZE == 64)
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 1
+#else
+# define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0
+#endif
+
+// Note : FreeBSD had implemented both
+// Linux and OpenBSD apis, available from
+// future 12.x version most likely
+#if SANITIZER_LINUX && defined(__NR_getrandom)
+# if !defined(GRND_NONBLOCK)
+# define GRND_NONBLOCK 1
+# endif
+# define SANITIZER_USE_GETRANDOM 1
+#else
+# define SANITIZER_USE_GETRANDOM 0
+#endif // SANITIZER_LINUX && defined(__NR_getrandom)
+
+#if SANITIZER_OPENBSD
+# define SANITIZER_USE_GETENTROPY 1
+#else
+# if SANITIZER_FREEBSD && __FreeBSD_version >= 1200000
+# define SANITIZER_USE_GETENTROPY 1
+# else
+# define SANITIZER_USE_GETENTROPY 0
+# endif
+#endif // SANITIZER_USE_GETENTROPY
+
+namespace __sanitizer {
+
+#if SANITIZER_LINUX && defined(__x86_64__)
+#include "sanitizer_syscall_linux_x86_64.inc"
+#elif SANITIZER_LINUX && defined(__aarch64__)
+#include "sanitizer_syscall_linux_aarch64.inc"
+#elif SANITIZER_LINUX && defined(__arm__)
+#include "sanitizer_syscall_linux_arm.inc"
+#else
+#include "sanitizer_syscall_generic.inc"
+#endif
+
+// --------------- sanitizer_libc.h
+#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+#if !SANITIZER_S390 && !SANITIZER_OPENBSD
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
+#if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS
+ return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd,
+ offset);
+#else
+ // mmap2 specifies file offset in 4096-byte units.
+ CHECK(IsAligned(offset, 4096));
+ return internal_syscall(SYSCALL(mmap2), addr, length, prot, flags, fd,
+ offset / 4096);
+#endif
+}
+#endif // !SANITIZER_S390 && !SANITIZER_OPENBSD
+
+#if !SANITIZER_OPENBSD
+uptr internal_munmap(void *addr, uptr length) {
+ return internal_syscall(SYSCALL(munmap), (uptr)addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot);
+}
+#endif
+
+uptr internal_close(fd_t fd) {
+ return internal_syscall(SYSCALL(close), fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags);
+#else
+ return internal_syscall(SYSCALL(open), (uptr)filename, flags);
+#endif
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(openat), AT_FDCWD, (uptr)filename, flags,
+ mode);
+#else
+ return internal_syscall(SYSCALL(open), (uptr)filename, flags, mode);
+#endif
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+ sptr res;
+ HANDLE_EINTR(res,
+ (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf, count));
+ return res;
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+ sptr res;
+ HANDLE_EINTR(res,
+ (sptr)internal_syscall(SYSCALL(write), fd, (uptr)buf, count));
+ return res;
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ sptr res;
+ HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd,
+ (OFF_T)size));
+ return res;
+}
+
+#if !SANITIZER_LINUX_USES_64BIT_SYSCALLS && SANITIZER_LINUX
+static void stat64_to_stat(struct stat64 *in, struct stat *out) {
+ internal_memset(out, 0, sizeof(*out));
+ out->st_dev = in->st_dev;
+ out->st_ino = in->st_ino;
+ out->st_mode = in->st_mode;
+ out->st_nlink = in->st_nlink;
+ out->st_uid = in->st_uid;
+ out->st_gid = in->st_gid;
+ out->st_rdev = in->st_rdev;
+ out->st_size = in->st_size;
+ out->st_blksize = in->st_blksize;
+ out->st_blocks = in->st_blocks;
+ out->st_atime = in->st_atime;
+ out->st_mtime = in->st_mtime;
+ out->st_ctime = in->st_ctime;
+}
+#endif
+
+#if defined(__mips64)
+// Undefine compatibility macros from <sys/stat.h>
+// so that they would not clash with the kernel_stat
+// st_[a|m|c]time fields
+#undef st_atime
+#undef st_mtime
+#undef st_ctime
+#if defined(SANITIZER_ANDROID)
+// Bionic sys/stat.h defines additional macros
+// for compatibility with the old NDKs and
+// they clash with the kernel_stat structure
+// st_[a|m|c]time_nsec fields.
+#undef st_atime_nsec
+#undef st_mtime_nsec
+#undef st_ctime_nsec
+#endif
+static void kernel_stat_to_stat(struct kernel_stat *in, struct stat *out) {
+ internal_memset(out, 0, sizeof(*out));
+ out->st_dev = in->st_dev;
+ out->st_ino = in->st_ino;
+ out->st_mode = in->st_mode;
+ out->st_nlink = in->st_nlink;
+ out->st_uid = in->st_uid;
+ out->st_gid = in->st_gid;
+ out->st_rdev = in->st_rdev;
+ out->st_size = in->st_size;
+ out->st_blksize = in->st_blksize;
+ out->st_blocks = in->st_blocks;
+#if defined(__USE_MISC) || \
+ defined(__USE_XOPEN2K8) || \
+ defined(SANITIZER_ANDROID)
+ out->st_atim.tv_sec = in->st_atime;
+ out->st_atim.tv_nsec = in->st_atime_nsec;
+ out->st_mtim.tv_sec = in->st_mtime;
+ out->st_mtim.tv_nsec = in->st_mtime_nsec;
+ out->st_ctim.tv_sec = in->st_ctime;
+ out->st_ctim.tv_nsec = in->st_ctime_nsec;
+#else
+ out->st_atime = in->st_atime;
+ out->st_atimensec = in->st_atime_nsec;
+ out->st_mtime = in->st_mtime;
+ out->st_mtimensec = in->st_mtime_nsec;
+ out->st_ctime = in->st_ctime;
+ out->st_atimensec = in->st_ctime_nsec;
+#endif
+}
+#endif
+
+uptr internal_stat(const char *path, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf, 0);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
+ 0);
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if defined(__mips64)
+ // For mips64, stat syscall fills buffer in the format of kernel_stat
+ struct kernel_stat kbuf;
+ int res = internal_syscall(SYSCALL(stat), path, &kbuf);
+ kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+ return res;
+# else
+ return internal_syscall(SYSCALL(stat), (uptr)path, (uptr)buf);
+# endif
+#else
+ struct stat64 buf64;
+ int res = internal_syscall(SYSCALL(stat64), path, &buf64);
+ stat64_to_stat(&buf64, (struct stat *)buf);
+ return res;
+#endif
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(fstatat), AT_FDCWD, (uptr)path, (uptr)buf,
+ AT_SYMLINK_NOFOLLOW);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(newfstatat), AT_FDCWD, (uptr)path, (uptr)buf,
+ AT_SYMLINK_NOFOLLOW);
+#elif SANITIZER_LINUX_USES_64BIT_SYSCALLS
+# if SANITIZER_MIPS64
+ // For mips64, lstat syscall fills buffer in the format of kernel_stat
+ struct kernel_stat kbuf;
+ int res = internal_syscall(SYSCALL(lstat), path, &kbuf);
+ kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+ return res;
+# else
+ return internal_syscall(SYSCALL(lstat), (uptr)path, (uptr)buf);
+# endif
+#else
+ struct stat64 buf64;
+ int res = internal_syscall(SYSCALL(lstat64), path, &buf64);
+ stat64_to_stat(&buf64, (struct stat *)buf);
+ return res;
+#endif
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD || \
+ SANITIZER_LINUX_USES_64BIT_SYSCALLS
+#if SANITIZER_MIPS64 && !SANITIZER_OPENBSD
+ // For mips64, fstat syscall fills buffer in the format of kernel_stat
+ struct kernel_stat kbuf;
+ int res = internal_syscall(SYSCALL(fstat), fd, &kbuf);
+ kernel_stat_to_stat(&kbuf, (struct stat *)buf);
+ return res;
+# else
+ return internal_syscall(SYSCALL(fstat), fd, (uptr)buf);
+# endif
+#else
+ struct stat64 buf64;
+ int res = internal_syscall(SYSCALL(fstat64), fd, &buf64);
+ stat64_to_stat(&buf64, (struct stat *)buf);
+ return res;
+#endif
+}
+
+uptr internal_filesize(fd_t fd) {
+ struct stat st;
+ if (internal_fstat(fd, &st))
+ return -1;
+ return (uptr)st.st_size;
+}
+
+uptr internal_dup(int oldfd) {
+ return internal_syscall(SYSCALL(dup), oldfd);
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(dup3), oldfd, newfd, 0);
+#else
+ return internal_syscall(SYSCALL(dup2), oldfd, newfd);
+#endif
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
+ bufsize);
+#elif SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(readlinkat), AT_FDCWD, (uptr)path, (uptr)buf,
+ bufsize);
+#else
+ return internal_syscall(SYSCALL(readlink), (uptr)path, (uptr)buf, bufsize);
+#endif
+}
+
+uptr internal_unlink(const char *path) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(unlinkat), AT_FDCWD, (uptr)path, 0);
+#else
+ return internal_syscall(SYSCALL(unlink), (uptr)path);
+#endif
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(renameat), AT_FDCWD, (uptr)oldpath, AT_FDCWD,
+ (uptr)newpath);
+#else
+ return internal_syscall(SYSCALL(rename), (uptr)oldpath, (uptr)newpath);
+#endif
+}
+
+uptr internal_sched_yield() {
+ return internal_syscall(SYSCALL(sched_yield));
+}
+
+void internal__exit(int exitcode) {
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ internal_syscall(SYSCALL(exit), exitcode);
+#else
+ internal_syscall(SYSCALL(exit_group), exitcode);
+#endif
+ Die(); // Unreachable.
+}
+
+unsigned int internal_sleep(unsigned int seconds) {
+ struct timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = 0;
+ int res = internal_syscall(SYSCALL(nanosleep), &ts, &ts);
+ if (res) return ts.tv_sec;
+ return 0;
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+ char *const envp[]) {
+ return internal_syscall(SYSCALL(execve), (uptr)filename, (uptr)argv,
+ (uptr)envp);
+}
+#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+
+// ----------------- sanitizer_common.h
+bool FileExists(const char *filename) {
+ if (ShouldMockFailureToOpen(filename))
+ return false;
+ struct stat st;
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ if (internal_syscall(SYSCALL(newfstatat), AT_FDCWD, filename, &st, 0))
+#else
+ if (internal_stat(filename, &st))
+#endif
+ return false;
+ // Sanity check: filename is a regular file.
+ return S_ISREG(st.st_mode);
+}
+
+#if !SANITIZER_NETBSD
+tid_t GetTid() {
+#if SANITIZER_FREEBSD
+ long Tid;
+ thr_self(&Tid);
+ return Tid;
+#elif SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(getthrid));
+#elif SANITIZER_SOLARIS
+ return thr_self();
+#else
+ return internal_syscall(SYSCALL(gettid));
+#endif
+}
+
+int TgKill(pid_t pid, tid_t tid, int sig) {
+#if SANITIZER_LINUX
+ return internal_syscall(SYSCALL(tgkill), pid, tid, sig);
+#elif SANITIZER_FREEBSD
+ return internal_syscall(SYSCALL(thr_kill2), pid, tid, sig);
+#elif SANITIZER_OPENBSD
+ (void)pid;
+ return internal_syscall(SYSCALL(thrkill), tid, sig, nullptr);
+#elif SANITIZER_SOLARIS
+ (void)pid;
+ return thr_kill(tid, sig);
+#endif
+}
+#endif
+
+#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+u64 NanoTime() {
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ timeval tv;
+#else
+ kernel_timeval tv;
+#endif
+ internal_memset(&tv, 0, sizeof(tv));
+ internal_syscall(SYSCALL(gettimeofday), &tv, 0);
+ return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
+}
+
+uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
+ return internal_syscall(SYSCALL(clock_gettime), clk_id, tp);
+}
+#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+
+// Like getenv, but reads env directly from /proc (on Linux) or parses the
+// 'environ' array (on some others) and does not use libc. This function
+// should be called first inside __asan_init.
+const char *GetEnv(const char *name) {
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD || \
+ SANITIZER_SOLARIS
+ if (::environ != 0) {
+ uptr NameLen = internal_strlen(name);
+ for (char **Env = ::environ; *Env != 0; Env++) {
+ if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
+ return (*Env) + NameLen + 1;
+ }
+ }
+ return 0; // Not found.
+#elif SANITIZER_LINUX
+ static char *environ;
+ static uptr len;
+ static bool inited;
+ if (!inited) {
+ inited = true;
+ uptr environ_size;
+ if (!ReadFileToBuffer("/proc/self/environ", &environ, &environ_size, &len))
+ environ = nullptr;
+ }
+ if (!environ || len == 0) return nullptr;
+ uptr namelen = internal_strlen(name);
+ const char *p = environ;
+ while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
+ // proc file has the format NAME=value\0NAME=value\0NAME=value\0...
+ const char* endp =
+ (char*)internal_memchr(p, '\0', len - (p - environ));
+ if (!endp) // this entry isn't NUL terminated
+ return nullptr;
+ else if (!internal_memcmp(p, name, namelen) && p[namelen] == '=') // Match.
+ return p + namelen + 1; // point after =
+ p = endp + 1;
+ }
+ return nullptr; // Not found.
+#else
+#error "Unsupported platform"
+#endif
+}
+
+#if !SANITIZER_FREEBSD && !SANITIZER_NETBSD && !SANITIZER_OPENBSD
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE extern void *__libc_stack_end;
+}
+#endif
+
+#if !SANITIZER_GO && !SANITIZER_FREEBSD && !SANITIZER_NETBSD && \
+ !SANITIZER_OPENBSD
+static void ReadNullSepFileToArray(const char *path, char ***arr,
+ int arr_size) {
+ char *buff;
+ uptr buff_size;
+ uptr buff_len;
+ *arr = (char **)MmapOrDie(arr_size * sizeof(char *), "NullSepFileArray");
+ if (!ReadFileToBuffer(path, &buff, &buff_size, &buff_len, 1024 * 1024)) {
+ (*arr)[0] = nullptr;
+ return;
+ }
+ (*arr)[0] = buff;
+ int count, i;
+ for (count = 1, i = 1; ; i++) {
+ if (buff[i] == 0) {
+ if (buff[i+1] == 0) break;
+ (*arr)[count] = &buff[i+1];
+ CHECK_LE(count, arr_size - 1); // FIXME: make this more flexible.
+ count++;
+ }
+ }
+ (*arr)[count] = nullptr;
+}
+#endif
+
+#if !SANITIZER_OPENBSD
+static void GetArgsAndEnv(char ***argv, char ***envp) {
+#if SANITIZER_FREEBSD
+ // On FreeBSD, retrieving the argument and environment arrays is done via the
+ // kern.ps_strings sysctl, which returns a pointer to a structure containing
+ // this information. See also <sys/exec.h>.
+ ps_strings *pss;
+ uptr sz = sizeof(pss);
+ if (internal_sysctlbyname("kern.ps_strings", &pss, &sz, NULL, 0) == -1) {
+ Printf("sysctl kern.ps_strings failed\n");
+ Die();
+ }
+ *argv = pss->ps_argvstr;
+ *envp = pss->ps_envstr;
+#elif SANITIZER_NETBSD
+ *argv = __ps_strings->ps_argvstr;
+ *envp = __ps_strings->ps_envstr;
+#else // SANITIZER_FREEBSD
+#if !SANITIZER_GO
+ if (&__libc_stack_end) {
+#endif // !SANITIZER_GO
+ uptr* stack_end = (uptr*)__libc_stack_end;
+ int argc = *stack_end;
+ *argv = (char**)(stack_end + 1);
+ *envp = (char**)(stack_end + argc + 2);
+#if !SANITIZER_GO
+ } else {
+ static const int kMaxArgv = 2000, kMaxEnvp = 2000;
+ ReadNullSepFileToArray("/proc/self/cmdline", argv, kMaxArgv);
+ ReadNullSepFileToArray("/proc/self/environ", envp, kMaxEnvp);
+ }
+#endif // !SANITIZER_GO
+#endif // SANITIZER_FREEBSD
+}
+
+char **GetArgv() {
+ char **argv, **envp;
+ GetArgsAndEnv(&argv, &envp);
+ return argv;
+}
+
+char **GetEnviron() {
+ char **argv, **envp;
+ GetArgsAndEnv(&argv, &envp);
+ return envp;
+}
+
+#endif // !SANITIZER_OPENBSD
+
+#if !SANITIZER_SOLARIS
+enum MutexState {
+ MtxUnlocked = 0,
+ MtxLocked = 1,
+ MtxSleeping = 2
+};
+
+BlockingMutex::BlockingMutex() {
+ internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+ CHECK_EQ(owner_, 0);
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+ return;
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
+#if SANITIZER_FREEBSD
+ _umtx_op(m, UMTX_OP_WAIT_UINT, MtxSleeping, 0, 0);
+#elif SANITIZER_NETBSD
+ sched_yield(); /* No userspace futex-like synchronization */
+#else
+ internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAIT_PRIVATE, MtxSleeping,
+ 0, 0, 0);
+#endif
+ }
+}
+
+void BlockingMutex::Unlock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
+ CHECK_NE(v, MtxUnlocked);
+ if (v == MtxSleeping) {
+#if SANITIZER_FREEBSD
+ _umtx_op(m, UMTX_OP_WAKE, 1, 0, 0);
+#elif SANITIZER_NETBSD
+ /* No userspace futex-like synchronization */
+#else
+ internal_syscall(SYSCALL(futex), (uptr)m, FUTEX_WAKE_PRIVATE, 1, 0, 0, 0);
+#endif
+ }
+}
+
+void BlockingMutex::CheckLocked() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
+}
+#endif // !SANITIZER_SOLARIS
+
+// ----------------- sanitizer_linux.h
+// The actual size of this structure is specified by d_reclen.
+// Note that getdents64 uses a different structure format. We only provide the
+// 32-bit syscall here.
+#if SANITIZER_NETBSD
+// Not used
+#elif SANITIZER_OPENBSD
+// struct dirent is different for Linux and us. At this moment, we use only
+// d_fileno (Linux call this d_ino), d_reclen, and d_name.
+struct linux_dirent {
+ u64 d_ino; // d_fileno
+ u16 d_reclen;
+ u16 d_namlen; // not used
+ u8 d_type; // not used
+ char d_name[NAME_MAX + 1];
+};
+#else
+struct linux_dirent {
+#if SANITIZER_X32 || defined(__aarch64__)
+ u64 d_ino;
+ u64 d_off;
+#else
+ unsigned long d_ino;
+ unsigned long d_off;
+#endif
+ unsigned short d_reclen;
+#ifdef __aarch64__
+ unsigned char d_type;
+#endif
+ char d_name[256];
+};
+#endif
+
+#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD
+// Syscall wrappers.
+uptr internal_ptrace(int request, int pid, void *addr, void *data) {
+ return internal_syscall(SYSCALL(ptrace), request, pid, (uptr)addr,
+ (uptr)data);
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+ return internal_syscall(SYSCALL(wait4), pid, (uptr)status, options,
+ 0 /* rusage */);
+}
+
+uptr internal_getpid() {
+ return internal_syscall(SYSCALL(getpid));
+}
+
+uptr internal_getppid() {
+ return internal_syscall(SYSCALL(getppid));
+}
+
+uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count) {
+#if SANITIZER_FREEBSD
+ return internal_syscall(SYSCALL(getdirentries), fd, (uptr)dirp, count, NULL);
+#elif SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(getdents64), fd, (uptr)dirp, count);
+#else
+ return internal_syscall(SYSCALL(getdents), fd, (uptr)dirp, count);
+#endif
+}
+
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
+ return internal_syscall(SYSCALL(lseek), fd, offset, whence);
+}
+
+#if SANITIZER_LINUX
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
+ return internal_syscall(SYSCALL(prctl), option, arg2, arg3, arg4, arg5);
+}
+#endif
+
+uptr internal_sigaltstack(const void *ss, void *oss) {
+ return internal_syscall(SYSCALL(sigaltstack), (uptr)ss, (uptr)oss);
+}
+
+int internal_fork() {
+#if SANITIZER_USES_CANONICAL_LINUX_SYSCALLS
+ return internal_syscall(SYSCALL(clone), SIGCHLD, 0);
+#else
+ return internal_syscall(SYSCALL(fork));
+#endif
+}
+
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+#if SANITIZER_OPENBSD
+ return sysctl(name, namelen, oldp, (size_t *)oldlenp, (void *)newp,
+ (size_t)newlen);
+#else
+ return internal_syscall(SYSCALL(__sysctl), name, namelen, oldp,
+ (size_t *)oldlenp, newp, (size_t)newlen);
+#endif
+}
+
+#if SANITIZER_FREEBSD
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ static decltype(sysctlbyname) *real = nullptr;
+ if (!real)
+ real = (decltype(sysctlbyname) *)dlsym(RTLD_NEXT, "sysctlbyname");
+ CHECK(real);
+ return real(sname, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+}
+#endif
+#endif
+
+#if SANITIZER_LINUX
+#define SA_RESTORER 0x04000000
+// Doesn't set sa_restorer if the caller did not set it, so use with caution
+//(see below).
+int internal_sigaction_norestorer(int signum, const void *act, void *oldact) {
+ __sanitizer_kernel_sigaction_t k_act, k_oldact;
+ internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t));
+ internal_memset(&k_oldact, 0, sizeof(__sanitizer_kernel_sigaction_t));
+ const __sanitizer_sigaction *u_act = (const __sanitizer_sigaction *)act;
+ __sanitizer_sigaction *u_oldact = (__sanitizer_sigaction *)oldact;
+ if (u_act) {
+ k_act.handler = u_act->handler;
+ k_act.sigaction = u_act->sigaction;
+ internal_memcpy(&k_act.sa_mask, &u_act->sa_mask,
+ sizeof(__sanitizer_kernel_sigset_t));
+ // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL).
+ k_act.sa_flags = u_act->sa_flags | SA_RESTORER;
+ // FIXME: most often sa_restorer is unset, however the kernel requires it
+ // to point to a valid signal restorer that calls the rt_sigreturn syscall.
+ // If sa_restorer passed to the kernel is NULL, the program may crash upon
+ // signal delivery or fail to unwind the stack in the signal handler.
+ // libc implementation of sigaction() passes its own restorer to
+ // rt_sigaction, so we need to do the same (we'll need to reimplement the
+ // restorers; for x86_64 the restorer address can be obtained from
+ // oldact->sa_restorer upon a call to sigaction(xxx, NULL, oldact).
+#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
+ k_act.sa_restorer = u_act->sa_restorer;
+#endif
+ }
+
+ uptr result = internal_syscall(SYSCALL(rt_sigaction), (uptr)signum,
+ (uptr)(u_act ? &k_act : nullptr),
+ (uptr)(u_oldact ? &k_oldact : nullptr),
+ (uptr)sizeof(__sanitizer_kernel_sigset_t));
+
+ if ((result == 0) && u_oldact) {
+ u_oldact->handler = k_oldact.handler;
+ u_oldact->sigaction = k_oldact.sigaction;
+ internal_memcpy(&u_oldact->sa_mask, &k_oldact.sa_mask,
+ sizeof(__sanitizer_kernel_sigset_t));
+ u_oldact->sa_flags = k_oldact.sa_flags;
+#if !SANITIZER_ANDROID || !SANITIZER_MIPS32
+ u_oldact->sa_restorer = k_oldact.sa_restorer;
+#endif
+ }
+ return result;
+}
+#endif // SANITIZER_LINUX
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+#if SANITIZER_FREEBSD || SANITIZER_OPENBSD
+ return internal_syscall(SYSCALL(sigprocmask), how, set, oldset);
+#else
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+ __sanitizer_kernel_sigset_t *k_oldset = (__sanitizer_kernel_sigset_t *)oldset;
+ return internal_syscall(SYSCALL(rt_sigprocmask), (uptr)how,
+ (uptr)&k_set->sig[0], (uptr)&k_oldset->sig[0],
+ sizeof(__sanitizer_kernel_sigset_t));
+#endif
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) {
+ internal_memset(set, 0xff, sizeof(*set));
+}
+
+void internal_sigemptyset(__sanitizer_sigset_t *set) {
+ internal_memset(set, 0, sizeof(*set));
+}
+
+#if SANITIZER_LINUX
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
+ signum -= 1;
+ CHECK_GE(signum, 0);
+ CHECK_LT(signum, sizeof(*set) * 8);
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+ const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
+ const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
+ k_set->sig[idx] &= ~(1 << bit);
+}
+
+bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
+ signum -= 1;
+ CHECK_GE(signum, 0);
+ CHECK_LT(signum, sizeof(*set) * 8);
+ __sanitizer_kernel_sigset_t *k_set = (__sanitizer_kernel_sigset_t *)set;
+ const uptr idx = signum / (sizeof(k_set->sig[0]) * 8);
+ const uptr bit = signum % (sizeof(k_set->sig[0]) * 8);
+ return k_set->sig[idx] & (1 << bit);
+}
+#elif SANITIZER_FREEBSD
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum) {
+ sigset_t *rset = reinterpret_cast<sigset_t *>(set);
+ sigdelset(rset, signum);
+}
+
+bool internal_sigismember(__sanitizer_sigset_t *set, int signum) {
+ sigset_t *rset = reinterpret_cast<sigset_t *>(set);
+ return sigismember(rset, signum);
+}
+#endif
+#endif // !SANITIZER_SOLARIS
+
+#if !SANITIZER_NETBSD
+// ThreadLister implementation.
+ThreadLister::ThreadLister(pid_t pid) : pid_(pid), buffer_(4096) {
+ char task_directory_path[80];
+ internal_snprintf(task_directory_path, sizeof(task_directory_path),
+ "/proc/%d/task/", pid);
+ descriptor_ = internal_open(task_directory_path, O_RDONLY | O_DIRECTORY);
+ if (internal_iserror(descriptor_)) {
+ Report("Can't open /proc/%d/task for reading.\n", pid);
+ }
+}
+
+ThreadLister::Result ThreadLister::ListThreads(
+ InternalMmapVector<tid_t> *threads) {
+ if (internal_iserror(descriptor_))
+ return Error;
+ internal_lseek(descriptor_, 0, SEEK_SET);
+ threads->clear();
+
+ Result result = Ok;
+ for (bool first_read = true;; first_read = false) {
+ // Resize to max capacity if it was downsized by IsAlive.
+ buffer_.resize(buffer_.capacity());
+ CHECK_GE(buffer_.size(), 4096);
+ uptr read = internal_getdents(
+ descriptor_, (struct linux_dirent *)buffer_.data(), buffer_.size());
+ if (!read)
+ return result;
+ if (internal_iserror(read)) {
+ Report("Can't read directory entries from /proc/%d/task.\n", pid_);
+ return Error;
+ }
+
+ for (uptr begin = (uptr)buffer_.data(), end = begin + read; begin < end;) {
+ struct linux_dirent *entry = (struct linux_dirent *)begin;
+ begin += entry->d_reclen;
+ if (entry->d_ino == 1) {
+ // Inode 1 is for bad blocks and also can be a reason for early return.
+ // Should be emitted if kernel tried to output terminating thread.
+ // See proc_task_readdir implementation in Linux.
+ result = Incomplete;
+ }
+ if (entry->d_ino && *entry->d_name >= '0' && *entry->d_name <= '9')
+ threads->push_back(internal_atoll(entry->d_name));
+ }
+
+ // Now we are going to detect short-read or early EOF. In such cases Linux
+ // can return inconsistent list with missing alive threads.
+ // Code will just remember that the list can be incomplete but it will
+ // continue reads to return as much as possible.
+ if (!first_read) {
+ // The first one was a short-read by definition.
+ result = Incomplete;
+ } else if (read > buffer_.size() - 1024) {
+ // Read was close to the buffer size. So double the size and assume the
+ // worst.
+ buffer_.resize(buffer_.size() * 2);
+ result = Incomplete;
+ } else if (!threads->empty() && !IsAlive(threads->back())) {
+ // Maybe Linux early returned from read on terminated thread (!pid_alive)
+ // and failed to restore read position.
+ // See next_tid and proc_task_instantiate in Linux.
+ result = Incomplete;
+ }
+ }
+}
+
+bool ThreadLister::IsAlive(int tid) {
+ // /proc/%d/task/%d/status uses same call to detect alive threads as
+ // proc_task_readdir. See task_state implementation in Linux.
+ char path[80];
+ internal_snprintf(path, sizeof(path), "/proc/%d/task/%d/status", pid_, tid);
+ if (!ReadFileToVector(path, &buffer_) || buffer_.empty())
+ return false;
+ buffer_.push_back(0);
+ static const char kPrefix[] = "\nPPid:";
+ const char *field = internal_strstr(buffer_.data(), kPrefix);
+ if (!field)
+ return false;
+ field += internal_strlen(kPrefix);
+ return (int)internal_atoll(field) != 0;
+}
+
+ThreadLister::~ThreadLister() {
+ if (!internal_iserror(descriptor_))
+ internal_close(descriptor_);
+}
+#endif
+
+#if SANITIZER_WORDSIZE == 32
+// Take care of unusable kernel area in top gigabyte.
+static uptr GetKernelAreaSize() {
+#if SANITIZER_LINUX && !SANITIZER_X32
+ const uptr gbyte = 1UL << 30;
+
+ // Firstly check if there are writable segments
+ // mapped to top gigabyte (e.g. stack).
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ if (proc_maps.Error())
+ return 0;
+ MemoryMappedSegment segment;
+ while (proc_maps.Next(&segment)) {
+ if ((segment.end >= 3 * gbyte) && segment.IsWritable()) return 0;
+ }
+
+#if !SANITIZER_ANDROID
+ // Even if nothing is mapped, top Gb may still be accessible
+ // if we are running on 64-bit kernel.
+ // Uname may report misleading results if personality type
+ // is modified (e.g. under schroot) so check this as well.
+ struct utsname uname_info;
+ int pers = personality(0xffffffffUL);
+ if (!(pers & PER_MASK)
+ && uname(&uname_info) == 0
+ && internal_strstr(uname_info.machine, "64"))
+ return 0;
+#endif // SANITIZER_ANDROID
+
+ // Top gigabyte is reserved for kernel.
+ return gbyte;
+#else
+ return 0;
+#endif // SANITIZER_LINUX && !SANITIZER_X32
+}
+#endif // SANITIZER_WORDSIZE == 32
+
+uptr GetMaxVirtualAddress() {
+#if (SANITIZER_NETBSD || SANITIZER_OPENBSD) && defined(__x86_64__)
+ return 0x7f7ffffff000ULL; // (0x00007f8000000000 - PAGE_SIZE)
+#elif SANITIZER_WORDSIZE == 64
+# if defined(__powerpc64__) || defined(__aarch64__)
+ // On PowerPC64 we have two different address space layouts: 44- and 46-bit.
+ // We somehow need to figure out which one we are using now and choose
+ // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
+ // Note that with 'ulimit -s unlimited' the stack is moved away from the top
+ // of the address space, so simply checking the stack address is not enough.
+ // This should (does) work for both PowerPC64 Endian modes.
+ // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit.
+ return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
+# elif defined(__mips64)
+ return (1ULL << 40) - 1; // 0x000000ffffffffffUL;
+# elif defined(__s390x__)
+ return (1ULL << 53) - 1; // 0x001fffffffffffffUL;
+#elif defined(__sparc__)
+ return ~(uptr)0;
+# else
+ return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
+# endif
+#else // SANITIZER_WORDSIZE == 32
+# if defined(__s390__)
+ return (1ULL << 31) - 1; // 0x7fffffff;
+# else
+ return (1ULL << 32) - 1; // 0xffffffff;
+# endif
+#endif // SANITIZER_WORDSIZE
+}
+
+uptr GetMaxUserVirtualAddress() {
+ uptr addr = GetMaxVirtualAddress();
+#if SANITIZER_WORDSIZE == 32 && !defined(__s390__)
+ if (!common_flags()->full_address_space)
+ addr -= GetKernelAreaSize();
+ CHECK_LT(reinterpret_cast<uptr>(&addr), addr);
+#endif
+ return addr;
+}
+
+#if !SANITIZER_ANDROID
+uptr GetPageSize() {
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__i386__))
+ return EXEC_PAGESIZE;
+#elif SANITIZER_USE_GETAUXVAL
+ return getauxval(AT_PAGESZ);
+#elif SANITIZER_FREEBSD || SANITIZER_NETBSD
+// Use sysctl as sysconf can trigger interceptors internally.
+ int pz = 0;
+ uptr pzl = sizeof(pz);
+ int mib[2] = {CTL_HW, HW_PAGESIZE};
+ int rv = internal_sysctl(mib, 2, &pz, &pzl, nullptr, 0);
+ CHECK_EQ(rv, 0);
+ return (uptr)pz;
+#else
+ return sysconf(_SC_PAGESIZE); // EXEC_PAGESIZE may not be trustworthy.
+#endif
+}
+#endif // !SANITIZER_ANDROID
+
+#if !SANITIZER_OPENBSD
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+#if SANITIZER_SOLARIS
+ const char *default_module_name = getexecname();
+ CHECK_NE(default_module_name, NULL);
+ return internal_snprintf(buf, buf_len, "%s", default_module_name);
+#else
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD
+#if SANITIZER_FREEBSD
+ const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+#else
+ const int Mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME};
+#endif
+ const char *default_module_name = "kern.proc.pathname";
+ uptr Size = buf_len;
+ bool IsErr =
+ (internal_sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0);
+ int readlink_error = IsErr ? errno : 0;
+ uptr module_name_len = Size;
+#else
+ const char *default_module_name = "/proc/self/exe";
+ uptr module_name_len = internal_readlink(
+ default_module_name, buf, buf_len);
+ int readlink_error;
+ bool IsErr = internal_iserror(module_name_len, &readlink_error);
+#endif // SANITIZER_SOLARIS
+ if (IsErr) {
+ // We can't read binary name for some reason, assume it's unknown.
+ Report("WARNING: reading executable name failed with errno %d, "
+ "some stack frames may not be symbolized\n", readlink_error);
+ module_name_len = internal_snprintf(buf, buf_len, "%s",
+ default_module_name);
+ CHECK_LT(module_name_len, buf_len);
+ }
+ return module_name_len;
+#endif
+}
+#endif // !SANITIZER_OPENBSD
+
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+#if SANITIZER_LINUX
+ char *tmpbuf;
+ uptr tmpsize;
+ uptr tmplen;
+ if (ReadFileToBuffer("/proc/self/cmdline", &tmpbuf, &tmpsize, &tmplen,
+ 1024 * 1024)) {
+ internal_strncpy(buf, tmpbuf, buf_len);
+ UnmapOrDie(tmpbuf, tmpsize);
+ return internal_strlen(buf);
+ }
+#endif
+ return ReadBinaryName(buf, buf_len);
+}
+
+// Match full names of the form /path/to/base_name{-,.}*
+bool LibraryNameIs(const char *full_name, const char *base_name) {
+ const char *name = full_name;
+ // Strip path.
+ while (*name != '\0') name++;
+ while (name > full_name && *name != '/') name--;
+ if (*name == '/') name++;
+ uptr base_name_length = internal_strlen(base_name);
+ if (internal_strncmp(name, base_name, base_name_length)) return false;
+ return (name[base_name_length] == '-' || name[base_name_length] == '.');
+}
+
+#if !SANITIZER_ANDROID
+// Call cb for each region mapped by map.
+void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)) {
+ CHECK_NE(map, nullptr);
+#if !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
+ typedef ElfW(Phdr) Elf_Phdr;
+ typedef ElfW(Ehdr) Elf_Ehdr;
+#endif // !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
+ char *base = (char *)map->l_addr;
+ Elf_Ehdr *ehdr = (Elf_Ehdr *)base;
+ char *phdrs = base + ehdr->e_phoff;
+ char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize;
+
+ // Find the segment with the minimum base so we can "relocate" the p_vaddr
+ // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC
+ // objects have a non-zero base.
+ uptr preferred_base = (uptr)-1;
+ for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {
+ Elf_Phdr *phdr = (Elf_Phdr *)iter;
+ if (phdr->p_type == PT_LOAD && preferred_base > (uptr)phdr->p_vaddr)
+ preferred_base = (uptr)phdr->p_vaddr;
+ }
+
+ // Compute the delta from the real base to get a relocation delta.
+ sptr delta = (uptr)base - preferred_base;
+ // Now we can figure out what the loader really mapped.
+ for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) {
+ Elf_Phdr *phdr = (Elf_Phdr *)iter;
+ if (phdr->p_type == PT_LOAD) {
+ uptr seg_start = phdr->p_vaddr + delta;
+ uptr seg_end = seg_start + phdr->p_memsz;
+ // None of these values are aligned. We consider the ragged edges of the
+ // load command as defined, since they are mapped from the file.
+ seg_start = RoundDownTo(seg_start, GetPageSizeCached());
+ seg_end = RoundUpTo(seg_end, GetPageSizeCached());
+ cb((void *)seg_start, seg_end - seg_start);
+ }
+ }
+}
+#endif
+
+#if defined(__x86_64__) && SANITIZER_LINUX
+// We cannot use glibc's clone wrapper, because it messes with the child
+// task's TLS. It writes the PID and TID of the child task to its thread
+// descriptor, but in our case the child task shares the thread descriptor with
+// the parent (because we don't know how to allocate a new thread
+// descriptor to keep glibc happy). So the stock version of clone(), when
+// used with CLONE_VM, would end up corrupting the parent's thread descriptor.
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+ register void *r8 __asm__("r8") = newtls;
+ register int *r10 __asm__("r10") = child_tidptr;
+ __asm__ __volatile__(
+ /* %rax = syscall(%rax = SYSCALL(clone),
+ * %rdi = flags,
+ * %rsi = child_stack,
+ * %rdx = parent_tidptr,
+ * %r8 = new_tls,
+ * %r10 = child_tidptr)
+ */
+ "syscall\n"
+
+ /* if (%rax != 0)
+ * return;
+ */
+ "testq %%rax,%%rax\n"
+ "jnz 1f\n"
+
+ /* In the child. Terminate unwind chain. */
+ // XXX: We should also terminate the CFI unwind chain
+ // here. Unfortunately clang 3.2 doesn't support the
+ // necessary CFI directives, so we skip that part.
+ "xorq %%rbp,%%rbp\n"
+
+ /* Call "fn(arg)". */
+ "popq %%rax\n"
+ "popq %%rdi\n"
+ "call *%%rax\n"
+
+ /* Call _exit(%rax). */
+ "movq %%rax,%%rdi\n"
+ "movq %2,%%rax\n"
+ "syscall\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=a" (res)
+ : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
+ "S"(child_stack),
+ "D"(flags),
+ "d"(parent_tidptr),
+ "r"(r8),
+ "r"(r10)
+ : "memory", "r11", "rcx");
+ return res;
+}
+#elif defined(__mips__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+ register void *a3 __asm__("$7") = newtls;
+ register int *a4 __asm__("$8") = child_tidptr;
+ // We don't have proper CFI directives here because it requires alot of code
+ // for very marginal benefits.
+ __asm__ __volatile__(
+ /* $v0 = syscall($v0 = __NR_clone,
+ * $a0 = flags,
+ * $a1 = child_stack,
+ * $a2 = parent_tidptr,
+ * $a3 = new_tls,
+ * $a4 = child_tidptr)
+ */
+ ".cprestore 16;\n"
+ "move $4,%1;\n"
+ "move $5,%2;\n"
+ "move $6,%3;\n"
+ "move $7,%4;\n"
+ /* Store the fifth argument on stack
+ * if we are using 32-bit abi.
+ */
+#if SANITIZER_WORDSIZE == 32
+ "lw %5,16($29);\n"
+#else
+ "move $8,%5;\n"
+#endif
+ "li $2,%6;\n"
+ "syscall;\n"
+
+ /* if ($v0 != 0)
+ * return;
+ */
+ "bnez $2,1f;\n"
+
+ /* Call "fn(arg)". */
+#if SANITIZER_WORDSIZE == 32
+#ifdef __BIG_ENDIAN__
+ "lw $25,4($29);\n"
+ "lw $4,12($29);\n"
+#else
+ "lw $25,0($29);\n"
+ "lw $4,8($29);\n"
+#endif
+#else
+ "ld $25,0($29);\n"
+ "ld $4,8($29);\n"
+#endif
+ "jal $25;\n"
+
+ /* Call _exit($v0). */
+ "move $4,$2;\n"
+ "li $2,%7;\n"
+ "syscall;\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r" (res)
+ : "r"(flags),
+ "r"(child_stack),
+ "r"(parent_tidptr),
+ "r"(a3),
+ "r"(a4),
+ "i"(__NR_clone),
+ "i"(__NR_exit)
+ : "memory", "$29" );
+ return res;
+}
+#elif defined(__aarch64__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned long long);
+ ((unsigned long long *)child_stack)[0] = (uptr)fn;
+ ((unsigned long long *)child_stack)[1] = (uptr)arg;
+
+ register int (*__fn)(void *) __asm__("x0") = fn;
+ register void *__stack __asm__("x1") = child_stack;
+ register int __flags __asm__("x2") = flags;
+ register void *__arg __asm__("x3") = arg;
+ register int *__ptid __asm__("x4") = parent_tidptr;
+ register void *__tls __asm__("x5") = newtls;
+ register int *__ctid __asm__("x6") = child_tidptr;
+
+ __asm__ __volatile__(
+ "mov x0,x2\n" /* flags */
+ "mov x2,x4\n" /* ptid */
+ "mov x3,x5\n" /* tls */
+ "mov x4,x6\n" /* ctid */
+ "mov x8,%9\n" /* clone */
+
+ "svc 0x0\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "cmp x0, #0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)". */
+ "ldp x1, x0, [sp], #16\n"
+ "blr x1\n"
+
+ /* Call _exit(%r0). */
+ "mov x8, %10\n"
+ "svc 0x0\n"
+ "1:\n"
+
+ : "=r" (res)
+ : "i"(-EINVAL),
+ "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg),
+ "r"(__ptid), "r"(__tls), "r"(__ctid),
+ "i"(__NR_clone), "i"(__NR_exit)
+ : "x30", "memory");
+ return res;
+}
+#elif defined(__powerpc64__)
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ long long res;
+// Stack frame structure.
+#if SANITIZER_PPC64V1
+// Back chain == 0 (SP + 112)
+// Frame (112 bytes):
+// Parameter save area (SP + 48), 8 doublewords
+// TOC save area (SP + 40)
+// Link editor doubleword (SP + 32)
+// Compiler doubleword (SP + 24)
+// LR save area (SP + 16)
+// CR save area (SP + 8)
+// Back chain (SP + 0)
+# define FRAME_SIZE 112
+# define FRAME_TOC_SAVE_OFFSET 40
+#elif SANITIZER_PPC64V2
+// Back chain == 0 (SP + 32)
+// Frame (32 bytes):
+// TOC save area (SP + 24)
+// LR save area (SP + 16)
+// CR save area (SP + 8)
+// Back chain (SP + 0)
+# define FRAME_SIZE 32
+# define FRAME_TOC_SAVE_OFFSET 24
+#else
+# error "Unsupported PPC64 ABI"
+#endif
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+
+ register int (*__fn)(void *) __asm__("r3") = fn;
+ register void *__cstack __asm__("r4") = child_stack;
+ register int __flags __asm__("r5") = flags;
+ register void *__arg __asm__("r6") = arg;
+ register int *__ptidptr __asm__("r7") = parent_tidptr;
+ register void *__newtls __asm__("r8") = newtls;
+ register int *__ctidptr __asm__("r9") = child_tidptr;
+
+ __asm__ __volatile__(
+ /* fn and arg are saved across the syscall */
+ "mr 28, %5\n\t"
+ "mr 27, %8\n\t"
+
+ /* syscall
+ r0 == __NR_clone
+ r3 == flags
+ r4 == child_stack
+ r5 == parent_tidptr
+ r6 == newtls
+ r7 == child_tidptr */
+ "mr 3, %7\n\t"
+ "mr 5, %9\n\t"
+ "mr 6, %10\n\t"
+ "mr 7, %11\n\t"
+ "li 0, %3\n\t"
+ "sc\n\t"
+
+ /* Test if syscall was successful */
+ "cmpdi cr1, 3, 0\n\t"
+ "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
+ "bne- cr1, 1f\n\t"
+
+ /* Set up stack frame */
+ "li 29, 0\n\t"
+ "stdu 29, -8(1)\n\t"
+ "stdu 1, -%12(1)\n\t"
+ /* Do the function call */
+ "std 2, %13(1)\n\t"
+#if SANITIZER_PPC64V1
+ "ld 0, 0(28)\n\t"
+ "ld 2, 8(28)\n\t"
+ "mtctr 0\n\t"
+#elif SANITIZER_PPC64V2
+ "mr 12, 28\n\t"
+ "mtctr 12\n\t"
+#else
+# error "Unsupported PPC64 ABI"
+#endif
+ "mr 3, 27\n\t"
+ "bctrl\n\t"
+ "ld 2, %13(1)\n\t"
+
+ /* Call _exit(r3) */
+ "li 0, %4\n\t"
+ "sc\n\t"
+
+ /* Return to parent */
+ "1:\n\t"
+ "mr %0, 3\n\t"
+ : "=r" (res)
+ : "0" (-1),
+ "i" (EINVAL),
+ "i" (__NR_clone),
+ "i" (__NR_exit),
+ "r" (__fn),
+ "r" (__cstack),
+ "r" (__flags),
+ "r" (__arg),
+ "r" (__ptidptr),
+ "r" (__newtls),
+ "r" (__ctidptr),
+ "i" (FRAME_SIZE),
+ "i" (FRAME_TOC_SAVE_OFFSET)
+ : "cr0", "cr1", "memory", "ctr", "r0", "r27", "r28", "r29");
+ return res;
+}
+#elif defined(__i386__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ int res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ child_stack = (char *)child_stack - 7 * sizeof(unsigned int);
+ ((unsigned int *)child_stack)[0] = (uptr)flags;
+ ((unsigned int *)child_stack)[1] = (uptr)0;
+ ((unsigned int *)child_stack)[2] = (uptr)fn;
+ ((unsigned int *)child_stack)[3] = (uptr)arg;
+ __asm__ __volatile__(
+ /* %eax = syscall(%eax = SYSCALL(clone),
+ * %ebx = flags,
+ * %ecx = child_stack,
+ * %edx = parent_tidptr,
+ * %esi = new_tls,
+ * %edi = child_tidptr)
+ */
+
+ /* Obtain flags */
+ "movl (%%ecx), %%ebx\n"
+ /* Do the system call */
+ "pushl %%ebx\n"
+ "pushl %%esi\n"
+ "pushl %%edi\n"
+ /* Remember the flag value. */
+ "movl %%ebx, (%%ecx)\n"
+ "int $0x80\n"
+ "popl %%edi\n"
+ "popl %%esi\n"
+ "popl %%ebx\n"
+
+ /* if (%eax != 0)
+ * return;
+ */
+
+ "test %%eax,%%eax\n"
+ "jnz 1f\n"
+
+ /* terminate the stack frame */
+ "xorl %%ebp,%%ebp\n"
+ /* Call FN. */
+ "call *%%ebx\n"
+#ifdef PIC
+ "call here\n"
+ "here:\n"
+ "popl %%ebx\n"
+ "addl $_GLOBAL_OFFSET_TABLE_+[.-here], %%ebx\n"
+#endif
+ /* Call exit */
+ "movl %%eax, %%ebx\n"
+ "movl %2, %%eax\n"
+ "int $0x80\n"
+ "1:\n"
+ : "=a" (res)
+ : "a"(SYSCALL(clone)), "i"(SYSCALL(exit)),
+ "c"(child_stack),
+ "d"(parent_tidptr),
+ "S"(newtls),
+ "D"(child_tidptr)
+ : "memory");
+ return res;
+}
+#elif defined(__arm__) && SANITIZER_LINUX
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ unsigned int res;
+ if (!fn || !child_stack)
+ return -EINVAL;
+ child_stack = (char *)child_stack - 2 * sizeof(unsigned int);
+ ((unsigned int *)child_stack)[0] = (uptr)fn;
+ ((unsigned int *)child_stack)[1] = (uptr)arg;
+ register int r0 __asm__("r0") = flags;
+ register void *r1 __asm__("r1") = child_stack;
+ register int *r2 __asm__("r2") = parent_tidptr;
+ register void *r3 __asm__("r3") = newtls;
+ register int *r4 __asm__("r4") = child_tidptr;
+ register int r7 __asm__("r7") = __NR_clone;
+
+#if __ARM_ARCH > 4 || defined (__ARM_ARCH_4T__)
+# define ARCH_HAS_BX
+#endif
+#if __ARM_ARCH > 4
+# define ARCH_HAS_BLX
+#endif
+
+#ifdef ARCH_HAS_BX
+# ifdef ARCH_HAS_BLX
+# define BLX(R) "blx " #R "\n"
+# else
+# define BLX(R) "mov lr, pc; bx " #R "\n"
+# endif
+#else
+# define BLX(R) "mov lr, pc; mov pc," #R "\n"
+#endif
+
+ __asm__ __volatile__(
+ /* %r0 = syscall(%r7 = SYSCALL(clone),
+ * %r0 = flags,
+ * %r1 = child_stack,
+ * %r2 = parent_tidptr,
+ * %r3 = new_tls,
+ * %r4 = child_tidptr)
+ */
+
+ /* Do the system call */
+ "swi 0x0\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "cmp r0, #0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)". */
+ "ldr r0, [sp, #4]\n"
+ "ldr ip, [sp], #8\n"
+ BLX(ip)
+ /* Call _exit(%r0). */
+ "mov r7, %7\n"
+ "swi 0x0\n"
+ "1:\n"
+ "mov %0, r0\n"
+ : "=r"(res)
+ : "r"(r0), "r"(r1), "r"(r2), "r"(r3), "r"(r4), "r"(r7),
+ "i"(__NR_exit)
+ : "memory");
+ return res;
+}
+#endif // defined(__x86_64__) && SANITIZER_LINUX
+
+#if SANITIZER_ANDROID
+#if __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size,
+ void *data) {
+ // Any name starting with "lib" indicates a bug in L where library base names
+ // are returned instead of paths.
+ if (info->dlpi_name && info->dlpi_name[0] == 'l' &&
+ info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') {
+ *(bool *)data = true;
+ return 1;
+ }
+ return 0;
+}
+
+static atomic_uint32_t android_api_level;
+
+static AndroidApiLevel AndroidDetectApiLevelStatic() {
+#if __ANDROID_API__ <= 19
+ return ANDROID_KITKAT;
+#elif __ANDROID_API__ <= 22
+ return ANDROID_LOLLIPOP_MR1;
+#else
+ return ANDROID_POST_LOLLIPOP;
+#endif
+}
+
+static AndroidApiLevel AndroidDetectApiLevel() {
+ if (!&dl_iterate_phdr)
+ return ANDROID_KITKAT; // K or lower
+ bool base_name_seen = false;
+ dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen);
+ if (base_name_seen)
+ return ANDROID_LOLLIPOP_MR1; // L MR1
+ return ANDROID_POST_LOLLIPOP; // post-L
+ // Plain L (API level 21) is completely broken wrt ASan and not very
+ // interesting to detect.
+}
+
+extern "C" __attribute__((weak)) void* _DYNAMIC;
+
+AndroidApiLevel AndroidGetApiLevel() {
+ AndroidApiLevel level =
+ (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed);
+ if (level) return level;
+ level = &_DYNAMIC == nullptr ? AndroidDetectApiLevelStatic()
+ : AndroidDetectApiLevel();
+ atomic_store(&android_api_level, level, memory_order_relaxed);
+ return level;
+}
+
+#endif
+
+static HandleSignalMode GetHandleSignalModeImpl(int signum) {
+ switch (signum) {
+ case SIGABRT:
+ return common_flags()->handle_abort;
+ case SIGILL:
+ return common_flags()->handle_sigill;
+ case SIGTRAP:
+ return common_flags()->handle_sigtrap;
+ case SIGFPE:
+ return common_flags()->handle_sigfpe;
+ case SIGSEGV:
+ return common_flags()->handle_segv;
+ case SIGBUS:
+ return common_flags()->handle_sigbus;
+ }
+ return kHandleSignalNo;
+}
+
+HandleSignalMode GetHandleSignalMode(int signum) {
+ HandleSignalMode result = GetHandleSignalModeImpl(signum);
+ if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler)
+ return kHandleSignalExclusive;
+ return result;
+}
+
+#if !SANITIZER_GO
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+ // Start the thread with signals blocked, otherwise it can steal user signals.
+ __sanitizer_sigset_t set, old;
+ internal_sigfillset(&set);
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked
+ // on any thread, setuid call hangs (see test/tsan/setuid.c).
+ internal_sigdelset(&set, 33);
+#endif
+ internal_sigprocmask(SIG_SETMASK, &set, &old);
+ void *th;
+ real_pthread_create(&th, nullptr, (void*(*)(void *arg))func, arg);
+ internal_sigprocmask(SIG_SETMASK, &old, nullptr);
+ return th;
+}
+
+void internal_join_thread(void *th) {
+ real_pthread_join(th, nullptr);
+}
+#else
+void *internal_start_thread(void (*func)(void *), void *arg) { return 0; }
+
+void internal_join_thread(void *th) {}
+#endif
+
+#if defined(__aarch64__)
+// Android headers in the older NDK releases miss this definition.
+struct __sanitizer_esr_context {
+ struct _aarch64_ctx head;
+ uint64_t esr;
+};
+
+static bool Aarch64GetESR(ucontext_t *ucontext, u64 *esr) {
+ static const u32 kEsrMagic = 0x45535201;
+ u8 *aux = ucontext->uc_mcontext.__reserved;
+ while (true) {
+ _aarch64_ctx *ctx = (_aarch64_ctx *)aux;
+ if (ctx->size == 0) break;
+ if (ctx->magic == kEsrMagic) {
+ *esr = ((__sanitizer_esr_context *)ctx)->esr;
+ return true;
+ }
+ aux += ctx->size;
+ }
+ return false;
+}
+#endif
+
+#if SANITIZER_OPENBSD
+using Context = sigcontext;
+#else
+using Context = ucontext_t;
+#endif
+
+SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
+ Context *ucontext = (Context *)context;
+#if defined(__x86_64__) || defined(__i386__)
+ static const uptr PF_WRITE = 1U << 1;
+#if SANITIZER_FREEBSD
+ uptr err = ucontext->uc_mcontext.mc_err;
+#elif SANITIZER_NETBSD
+ uptr err = ucontext->uc_mcontext.__gregs[_REG_ERR];
+#elif SANITIZER_OPENBSD
+ uptr err = ucontext->sc_err;
+#elif SANITIZER_SOLARIS && defined(__i386__)
+ const int Err = 13;
+ uptr err = ucontext->uc_mcontext.gregs[Err];
+#else
+ uptr err = ucontext->uc_mcontext.gregs[REG_ERR];
+#endif // SANITIZER_FREEBSD
+ return err & PF_WRITE ? WRITE : READ;
+#elif defined(__mips__)
+ uint32_t *exception_source;
+ uint32_t faulty_instruction;
+ uint32_t op_code;
+
+ exception_source = (uint32_t *)ucontext->uc_mcontext.pc;
+ faulty_instruction = (uint32_t)(*exception_source);
+
+ op_code = (faulty_instruction >> 26) & 0x3f;
+
+ // FIXME: Add support for FPU, microMIPS, DSP, MSA memory instructions.
+ switch (op_code) {
+ case 0x28: // sb
+ case 0x29: // sh
+ case 0x2b: // sw
+ case 0x3f: // sd
+#if __mips_isa_rev < 6
+ case 0x2c: // sdl
+ case 0x2d: // sdr
+ case 0x2a: // swl
+ case 0x2e: // swr
+#endif
+ return SignalContext::WRITE;
+
+ case 0x20: // lb
+ case 0x24: // lbu
+ case 0x21: // lh
+ case 0x25: // lhu
+ case 0x23: // lw
+ case 0x27: // lwu
+ case 0x37: // ld
+#if __mips_isa_rev < 6
+ case 0x1a: // ldl
+ case 0x1b: // ldr
+ case 0x22: // lwl
+ case 0x26: // lwr
+#endif
+ return SignalContext::READ;
+#if __mips_isa_rev == 6
+ case 0x3b: // pcrel
+ op_code = (faulty_instruction >> 19) & 0x3;
+ switch (op_code) {
+ case 0x1: // lwpc
+ case 0x2: // lwupc
+ return SignalContext::READ;
+ }
+#endif
+ }
+ return SignalContext::UNKNOWN;
+#elif defined(__arm__)
+ static const uptr FSR_WRITE = 1U << 11;
+ uptr fsr = ucontext->uc_mcontext.error_code;
+ return fsr & FSR_WRITE ? WRITE : READ;
+#elif defined(__aarch64__)
+ static const u64 ESR_ELx_WNR = 1U << 6;
+ u64 esr;
+ if (!Aarch64GetESR(ucontext, &esr)) return UNKNOWN;
+ return esr & ESR_ELx_WNR ? WRITE : READ;
+#elif defined(__sparc__)
+ // Decode the instruction to determine the access type.
+ // From OpenSolaris $SRC/uts/sun4/os/trap.c (get_accesstype).
+#if SANITIZER_SOLARIS
+ uptr pc = ucontext->uc_mcontext.gregs[REG_PC];
+#else
+ // Historical BSDism here.
+ struct sigcontext *scontext = (struct sigcontext *)context;
+#if defined(__arch64__)
+ uptr pc = scontext->sigc_regs.tpc;
+#else
+ uptr pc = scontext->si_regs.pc;
+#endif
+#endif
+ u32 instr = *(u32 *)pc;
+ return (instr >> 21) & 1 ? WRITE: READ;
+#else
+ (void)ucontext;
+ return UNKNOWN; // FIXME: Implement.
+#endif
+}
+
+void SignalContext::DumpAllRegisters(void *context) {
+ // FIXME: Implement this.
+}
+
+static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+#if SANITIZER_NETBSD
+ // This covers all NetBSD architectures
+ ucontext_t *ucontext = (ucontext_t *)context;
+ *pc = _UC_MACHINE_PC(ucontext);
+ *bp = _UC_MACHINE_FP(ucontext);
+ *sp = _UC_MACHINE_SP(ucontext);
+#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(__aarch64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.regs[29];
+ *sp = ucontext->uc_mcontext.sp;
+#elif defined(__hppa__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.sc_iaoq[0];
+ /* GCC uses %r3 whenever a frame pointer is needed. */
+ *bp = ucontext->uc_mcontext.sc_gr[3];
+ *sp = ucontext->uc_mcontext.sc_gr[30];
+#elif defined(__x86_64__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_rip;
+ *bp = ucontext->uc_mcontext.mc_rbp;
+ *sp = ucontext->uc_mcontext.mc_rsp;
+#elif SANITIZER_OPENBSD
+ sigcontext *ucontext = (sigcontext *)context;
+ *pc = ucontext->sc_rip;
+ *bp = ucontext->sc_rbp;
+ *sp = ucontext->sc_rsp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_RIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_RBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_RSP];
+# endif
+#elif defined(__i386__)
+# if SANITIZER_FREEBSD
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.mc_eip;
+ *bp = ucontext->uc_mcontext.mc_ebp;
+ *sp = ucontext->uc_mcontext.mc_esp;
+#elif SANITIZER_OPENBSD
+ sigcontext *ucontext = (sigcontext *)context;
+ *pc = ucontext->sc_eip;
+ *bp = ucontext->sc_ebp;
+ *sp = ucontext->sc_esp;
+# else
+ ucontext_t *ucontext = (ucontext_t*)context;
+# if SANITIZER_SOLARIS
+ /* Use the numeric values: the symbolic ones are undefined by llvm
+ include/llvm/Support/Solaris.h. */
+# ifndef REG_EIP
+# define REG_EIP 14 // REG_PC
+# endif
+# ifndef REG_EBP
+# define REG_EBP 6 // REG_FP
+# endif
+# ifndef REG_ESP
+# define REG_ESP 17 // REG_SP
+# endif
+# endif
+ *pc = ucontext->uc_mcontext.gregs[REG_EIP];
+ *bp = ucontext->uc_mcontext.gregs[REG_EBP];
+ *sp = ucontext->uc_mcontext.gregs[REG_ESP];
+# endif
+#elif defined(__powerpc__) || defined(__powerpc64__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.regs->nip;
+ *sp = ucontext->uc_mcontext.regs->gpr[PT_R1];
+ // The powerpc{,64}-linux ABIs do not specify r31 as the frame
+ // pointer, but GCC always uses r31 when we need a frame pointer.
+ *bp = ucontext->uc_mcontext.regs->gpr[PT_R31];
+#elif defined(__sparc__)
+#if defined(__arch64__) || defined(__sparcv9)
+#define STACK_BIAS 2047
+#else
+#define STACK_BIAS 0
+# endif
+# if SANITIZER_SOLARIS
+ ucontext_t *ucontext = (ucontext_t *)context;
+ *pc = ucontext->uc_mcontext.gregs[REG_PC];
+ *sp = ucontext->uc_mcontext.gregs[REG_O6] + STACK_BIAS;
+#else
+ // Historical BSDism here.
+ struct sigcontext *scontext = (struct sigcontext *)context;
+#if defined(__arch64__)
+ *pc = scontext->sigc_regs.tpc;
+ *sp = scontext->sigc_regs.u_regs[14] + STACK_BIAS;
+#else
+ *pc = scontext->si_regs.pc;
+ *sp = scontext->si_regs.u_regs[14];
+#endif
+# endif
+ *bp = (uptr)((uhwptr *)*sp)[14] + STACK_BIAS;
+#elif defined(__mips__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+ *pc = ucontext->uc_mcontext.pc;
+ *bp = ucontext->uc_mcontext.gregs[30];
+ *sp = ucontext->uc_mcontext.gregs[29];
+#elif defined(__s390__)
+ ucontext_t *ucontext = (ucontext_t*)context;
+# if defined(__s390x__)
+ *pc = ucontext->uc_mcontext.psw.addr;
+# else
+ *pc = ucontext->uc_mcontext.psw.addr & 0x7fffffff;
+# endif
+ *bp = ucontext->uc_mcontext.gregs[11];
+ *sp = ucontext->uc_mcontext.gregs[15];
+#else
+# error "Unsupported arch"
+#endif
+}
+
+void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
+
+void InitializePlatformEarly() {
+ // Do nothing.
+}
+
+void MaybeReexec() {
+ // No need to re-exec on Linux.
+}
+
+void CheckASLR() {
+#if SANITIZER_NETBSD
+ int mib[3];
+ int paxflags;
+ uptr len = sizeof(paxflags);
+
+ mib[0] = CTL_PROC;
+ mib[1] = internal_getpid();
+ mib[2] = PROC_PID_PAXFLAGS;
+
+ if (UNLIKELY(internal_sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1)) {
+ Printf("sysctl failed\n");
+ Die();
+ }
+
+ if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_ASLR)) {
+ Printf("This sanitizer is not compatible with enabled ASLR\n");
+ Die();
+ }
+#elif SANITIZER_PPC64V2
+ // Disable ASLR for Linux PPC64LE.
+ int old_personality = personality(0xffffffff);
+ if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) {
+ VReport(1, "WARNING: Program is being run with address space layout "
+ "randomization (ASLR) enabled which prevents the thread and "
+ "memory sanitizers from working on powerpc64le.\n"
+ "ASLR will be disabled and the program re-executed.\n");
+ CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
+ ReExec();
+ }
+#else
+ // Do nothing
+#endif
+}
+
+void CheckMPROTECT() {
+#if SANITIZER_NETBSD
+ int mib[3];
+ int paxflags;
+ uptr len = sizeof(paxflags);
+
+ mib[0] = CTL_PROC;
+ mib[1] = internal_getpid();
+ mib[2] = PROC_PID_PAXFLAGS;
+
+ if (UNLIKELY(internal_sysctl(mib, 3, &paxflags, &len, NULL, 0) == -1)) {
+ Printf("sysctl failed\n");
+ Die();
+ }
+
+ if (UNLIKELY(paxflags & CTL_PROC_PAXFLAGS_MPROTECT)) {
+ Printf("This sanitizer is not compatible with enabled MPROTECT\n");
+ Die();
+ }
+#else
+ // Do nothing
+#endif
+}
+
+void PrintModuleMap() { }
+
+void CheckNoDeepBind(const char *filename, int flag) {
+#ifdef RTLD_DEEPBIND
+ if (flag & RTLD_DEEPBIND) {
+ Report(
+ "You are trying to dlopen a %s shared library with RTLD_DEEPBIND flag"
+ " which is incompatibe with sanitizer runtime "
+ "(see https://github.com/google/sanitizers/issues/611 for details"
+ "). If you want to run %s library under sanitizers please remove "
+ "RTLD_DEEPBIND from dlopen flags.\n",
+ filename, filename);
+ Die();
+ }
+#endif
+}
+
+uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
+ uptr *largest_gap_found,
+ uptr *max_occupied_addr) {
+ UNREACHABLE("FindAvailableMemoryRange is not available");
+ return 0;
+}
+
+bool GetRandom(void *buffer, uptr length, bool blocking) {
+ if (!buffer || !length || length > 256)
+ return false;
+#if SANITIZER_USE_GETENTROPY
+ uptr rnd = getentropy(buffer, length);
+ int rverrno = 0;
+ if (internal_iserror(rnd, &rverrno) && rverrno == EFAULT)
+ return false;
+ else if (rnd == 0)
+ return true;
+#endif // SANITIZER_USE_GETENTROPY
+
+#if SANITIZER_USE_GETRANDOM
+ static atomic_uint8_t skip_getrandom_syscall;
+ if (!atomic_load_relaxed(&skip_getrandom_syscall)) {
+ // Up to 256 bytes, getrandom will not be interrupted.
+ uptr res = internal_syscall(SYSCALL(getrandom), buffer, length,
+ blocking ? 0 : GRND_NONBLOCK);
+ int rverrno = 0;
+ if (internal_iserror(res, &rverrno) && rverrno == ENOSYS)
+ atomic_store_relaxed(&skip_getrandom_syscall, 1);
+ else if (res == length)
+ return true;
+ }
+#endif // SANITIZER_USE_GETRANDOM
+ // Up to 256 bytes, a read off /dev/urandom will not be interrupted.
+ // blocking is moot here, O_NONBLOCK has no effect when opening /dev/urandom.
+ uptr fd = internal_open("/dev/urandom", O_RDONLY);
+ if (internal_iserror(fd))
+ return false;
+ uptr res = internal_read(fd, buffer, length);
+ if (internal_iserror(res))
+ return false;
+ internal_close(fd);
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif
//===-- sanitizer_linux.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
SANITIZER_OPENBSD || SANITIZER_SOLARIS
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_freebsd.h"
#include "sanitizer_platform_limits_netbsd.h"
#include "sanitizer_platform_limits_openbsd.h"
#include "sanitizer_platform_limits_posix.h"
// (like the process-wide error reporting SEGV handler) must use
// internal_sigaction instead.
int internal_sigaction_norestorer(int signum, const void *act, void *oldact);
-#if (defined(__x86_64__) || SANITIZER_MIPS64) && !SANITIZER_GO
-// Uses a raw system call to avoid interceptors.
-int internal_sigaction_syscall(int signum, const void *act, void *oldact);
-#endif
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \
|| defined(__powerpc64__) || defined(__s390__) || defined(__i386__) \
#endif
#elif SANITIZER_FREEBSD
void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
+#elif SANITIZER_NETBSD
+void internal_sigdelset(__sanitizer_sigset_t *set, int signum);
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
#endif // SANITIZER_LINUX
// This class reads thread IDs from /proc/<pid>/task using only syscalls.
// Call cb for each region mapped by map.
void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr));
+// Releases memory pages entirely within the [beg, end] address range.
+// The pages no longer count toward RSS; reads are guaranteed to return 0.
+// Requires (but does not verify!) that pages are MAP_PRIVATE.
+INLINE void ReleaseMemoryPagesToOSAndZeroFill(uptr beg, uptr end) {
+ // man madvise on Linux promises zero-fill for anonymous private pages.
+ // Testing shows the same behaviour for private (but not anonymous) mappings
+ // of shm_open() files, as long as the underlying file is untouched.
+ CHECK(SANITIZER_LINUX);
+ ReleaseMemoryPagesToOS(beg, end);
+}
+
#if SANITIZER_ANDROID
#if defined(__aarch64__)
#error "Unsupported architecture."
#endif
-// The Android Bionic team has allocated a TLS slot for TSan starting with N,
-// given that Android currently doesn't support ELF TLS. It is used to store
-// Sanitizers thread specific data.
-static const int TLS_SLOT_TSAN = 8;
+// The Android Bionic team has allocated a TLS slot for sanitizers starting
+// with Q, given that Android currently doesn't support ELF TLS. It is used to
+// store sanitizer thread specific data.
+static const int TLS_SLOT_SANITIZER = 6;
ALWAYS_INLINE uptr *get_android_tls_ptr() {
- return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_TSAN]);
+ return reinterpret_cast<uptr *>(&__get_tls()[TLS_SLOT_SANITIZER]);
}
#endif // SANITIZER_ANDROID
+++ /dev/null
-//===-- sanitizer_linux_libcdep.cc ----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements linux-specific functions from
-// sanitizer_libc.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_atomic.h"
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_freebsd.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-
-#include <dlfcn.h> // for dlsym()
-#include <link.h>
-#include <pthread.h>
-#include <signal.h>
-#include <sys/resource.h>
-#include <syslog.h>
-
-#if SANITIZER_FREEBSD
-#include <pthread_np.h>
-#include <osreldate.h>
-#include <sys/sysctl.h>
-#define pthread_getattr_np pthread_attr_get_np
-#endif
-
-#if SANITIZER_OPENBSD
-#include <pthread_np.h>
-#include <sys/sysctl.h>
-#endif
-
-#if SANITIZER_NETBSD
-#include <sys/sysctl.h>
-#include <sys/tls.h>
-#endif
-
-#if SANITIZER_SOLARIS
-#include <thread.h>
-#endif
-
-#if SANITIZER_ANDROID
-#include <android/api-level.h>
-#if !defined(CPU_COUNT) && !defined(__aarch64__)
-#include <dirent.h>
-#include <fcntl.h>
-struct __sanitizer::linux_dirent {
- long d_ino;
- off_t d_off;
- unsigned short d_reclen;
- char d_name[];
-};
-#endif
-#endif
-
-#if !SANITIZER_ANDROID
-#include <elf.h>
-#include <unistd.h>
-#endif
-
-namespace __sanitizer {
-
-SANITIZER_WEAK_ATTRIBUTE int
-real_sigaction(int signum, const void *act, void *oldact);
-
-int internal_sigaction(int signum, const void *act, void *oldact) {
-#if !SANITIZER_GO
- if (&real_sigaction)
- return real_sigaction(signum, act, oldact);
-#endif
- return sigaction(signum, (const struct sigaction *)act,
- (struct sigaction *)oldact);
-}
-
-void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
- uptr *stack_bottom) {
- CHECK(stack_top);
- CHECK(stack_bottom);
- if (at_initialization) {
- // This is the main thread. Libpthread may not be initialized yet.
- struct rlimit rl;
- CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
-
- // Find the mapping that contains a stack variable.
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- MemoryMappedSegment segment;
- uptr prev_end = 0;
- while (proc_maps.Next(&segment)) {
- if ((uptr)&rl < segment.end) break;
- prev_end = segment.end;
- }
- CHECK((uptr)&rl >= segment.start && (uptr)&rl < segment.end);
-
- // Get stacksize from rlimit, but clip it so that it does not overlap
- // with other mappings.
- uptr stacksize = rl.rlim_cur;
- if (stacksize > segment.end - prev_end) stacksize = segment.end - prev_end;
- // When running with unlimited stack size, we still want to set some limit.
- // The unlimited stack size is caused by 'ulimit -s unlimited'.
- // Also, for some reason, GNU make spawns subprocesses with unlimited stack.
- if (stacksize > kMaxThreadStackSize)
- stacksize = kMaxThreadStackSize;
- *stack_top = segment.end;
- *stack_bottom = segment.end - stacksize;
- return;
- }
- uptr stacksize = 0;
- void *stackaddr = nullptr;
-#if SANITIZER_SOLARIS
- stack_t ss;
- CHECK_EQ(thr_stksegment(&ss), 0);
- stacksize = ss.ss_size;
- stackaddr = (char *)ss.ss_sp - stacksize;
-#elif SANITIZER_OPENBSD
- stack_t sattr;
- CHECK_EQ(pthread_stackseg_np(pthread_self(), &sattr), 0);
- stackaddr = sattr.ss_sp;
- stacksize = sattr.ss_size;
-#else // !SANITIZER_SOLARIS
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
- my_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
- pthread_attr_destroy(&attr);
-#endif // SANITIZER_SOLARIS
-
- *stack_top = (uptr)stackaddr + stacksize;
- *stack_bottom = (uptr)stackaddr;
-}
-
-#if !SANITIZER_GO
-bool SetEnv(const char *name, const char *value) {
- void *f = dlsym(RTLD_NEXT, "setenv");
- if (!f)
- return false;
- typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);
- setenv_ft setenv_f;
- CHECK_EQ(sizeof(setenv_f), sizeof(f));
- internal_memcpy(&setenv_f, &f, sizeof(f));
- return setenv_f(name, value, 1) == 0;
-}
-#endif
-
-__attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
- int *patch) {
-#ifdef _CS_GNU_LIBC_VERSION
- char buf[64];
- uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
- if (len >= sizeof(buf))
- return false;
- buf[len] = 0;
- static const char kGLibC[] = "glibc ";
- if (internal_strncmp(buf, kGLibC, sizeof(kGLibC) - 1) != 0)
- return false;
- const char *p = buf + sizeof(kGLibC) - 1;
- *major = internal_simple_strtoll(p, &p, 10);
- *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
- *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
- return true;
-#else
- return false;
-#endif
-}
-
-#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO && \
- !SANITIZER_NETBSD && !SANITIZER_OPENBSD && !SANITIZER_SOLARIS
-static uptr g_tls_size;
-
-#ifdef __i386__
-# ifndef __GLIBC_PREREQ
-# define CHECK_GET_TLS_STATIC_INFO_VERSION 1
-# else
-# define CHECK_GET_TLS_STATIC_INFO_VERSION (!__GLIBC_PREREQ(2, 27))
-# endif
-#else
-# define CHECK_GET_TLS_STATIC_INFO_VERSION 0
-#endif
-
-#if CHECK_GET_TLS_STATIC_INFO_VERSION
-# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
-#else
-# define DL_INTERNAL_FUNCTION
-#endif
-
-namespace {
-struct GetTlsStaticInfoCall {
- typedef void (*get_tls_func)(size_t*, size_t*);
-};
-struct GetTlsStaticInfoRegparmCall {
- typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
-};
-
-template <typename T>
-void CallGetTls(void* ptr, size_t* size, size_t* align) {
- typename T::get_tls_func get_tls;
- CHECK_EQ(sizeof(get_tls), sizeof(ptr));
- internal_memcpy(&get_tls, &ptr, sizeof(ptr));
- CHECK_NE(get_tls, 0);
- get_tls(size, align);
-}
-
-bool CmpLibcVersion(int major, int minor, int patch) {
- int ma;
- int mi;
- int pa;
- if (!GetLibcVersion(&ma, &mi, &pa))
- return false;
- if (ma > major)
- return true;
- if (ma < major)
- return false;
- if (mi > minor)
- return true;
- if (mi < minor)
- return false;
- return pa >= patch;
-}
-
-} // namespace
-
-void InitTlsSize() {
- // all current supported platforms have 16 bytes stack alignment
- const size_t kStackAlign = 16;
- void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
- size_t tls_size = 0;
- size_t tls_align = 0;
- // On i?86, _dl_get_tls_static_info used to be internal_function, i.e.
- // __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal
- // function in 2.27 and later.
- if (CHECK_GET_TLS_STATIC_INFO_VERSION && !CmpLibcVersion(2, 27, 0))
- CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr,
- &tls_size, &tls_align);
- else
- CallGetTls<GetTlsStaticInfoCall>(get_tls_static_info_ptr,
- &tls_size, &tls_align);
- if (tls_align < kStackAlign)
- tls_align = kStackAlign;
- g_tls_size = RoundUpTo(tls_size, tls_align);
-}
-#else
-void InitTlsSize() { }
-#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO &&
- // !SANITIZER_NETBSD && !SANITIZER_SOLARIS
-
-#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) || \
- defined(__aarch64__) || defined(__powerpc64__) || defined(__s390__) || \
- defined(__arm__)) && \
- SANITIZER_LINUX && !SANITIZER_ANDROID
-// sizeof(struct pthread) from glibc.
-static atomic_uintptr_t thread_descriptor_size;
-
-uptr ThreadDescriptorSize() {
- uptr val = atomic_load_relaxed(&thread_descriptor_size);
- if (val)
- return val;
-#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
- int major;
- int minor;
- int patch;
- if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
- /* sizeof(struct pthread) values from various glibc versions. */
- if (SANITIZER_X32)
- val = 1728; // Assume only one particular version for x32.
- // For ARM sizeof(struct pthread) changed in Glibc 2.23.
- else if (SANITIZER_ARM)
- val = minor <= 22 ? 1120 : 1216;
- else if (minor <= 3)
- val = FIRST_32_SECOND_64(1104, 1696);
- else if (minor == 4)
- val = FIRST_32_SECOND_64(1120, 1728);
- else if (minor == 5)
- val = FIRST_32_SECOND_64(1136, 1728);
- else if (minor <= 9)
- val = FIRST_32_SECOND_64(1136, 1712);
- else if (minor == 10)
- val = FIRST_32_SECOND_64(1168, 1776);
- else if (minor == 11 || (minor == 12 && patch == 1))
- val = FIRST_32_SECOND_64(1168, 2288);
- else if (minor <= 14)
- val = FIRST_32_SECOND_64(1168, 2304);
- else
- val = FIRST_32_SECOND_64(1216, 2304);
- }
-#elif defined(__mips__)
- // TODO(sagarthakur): add more values as per different glibc versions.
- val = FIRST_32_SECOND_64(1152, 1776);
-#elif defined(__aarch64__)
- // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
- val = 1776;
-#elif defined(__powerpc64__)
- val = 1776; // from glibc.ppc64le 2.20-8.fc21
-#elif defined(__s390__)
- val = FIRST_32_SECOND_64(1152, 1776); // valid for glibc 2.22
-#endif
- if (val)
- atomic_store_relaxed(&thread_descriptor_size, val);
- return val;
-}
-
-// The offset at which pointer to self is located in the thread descriptor.
-const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16);
-
-uptr ThreadSelfOffset() {
- return kThreadSelfOffset;
-}
-
-#if defined(__mips__) || defined(__powerpc64__)
-// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
-// head structure. It lies before the static tls blocks.
-static uptr TlsPreTcbSize() {
-# if defined(__mips__)
- const uptr kTcbHead = 16; // sizeof (tcbhead_t)
-# elif defined(__powerpc64__)
- const uptr kTcbHead = 88; // sizeof (tcbhead_t)
-# endif
- const uptr kTlsAlign = 16;
- const uptr kTlsPreTcbSize =
- RoundUpTo(ThreadDescriptorSize() + kTcbHead, kTlsAlign);
- return kTlsPreTcbSize;
-}
-#endif
-
-uptr ThreadSelf() {
- uptr descr_addr;
-# if defined(__i386__)
- asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
-# elif defined(__x86_64__)
- asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
-# elif defined(__mips__)
- // MIPS uses TLS variant I. The thread pointer (in hardware register $29)
- // points to the end of the TCB + 0x7000. The pthread_descr structure is
- // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
- // TCB and the size of pthread_descr.
- const uptr kTlsTcbOffset = 0x7000;
- uptr thread_pointer;
- asm volatile(".set push;\
- .set mips64r2;\
- rdhwr %0,$29;\
- .set pop" : "=r" (thread_pointer));
- descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
-# elif defined(__aarch64__) || defined(__arm__)
- descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
- ThreadDescriptorSize();
-# elif defined(__s390__)
- descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
-# elif defined(__powerpc64__)
- // PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
- // points to the end of the TCB + 0x7000. The pthread_descr structure is
- // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
- // TCB and the size of pthread_descr.
- const uptr kTlsTcbOffset = 0x7000;
- uptr thread_pointer;
- asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
- descr_addr = thread_pointer - TlsPreTcbSize();
-# else
-# error "unsupported CPU arch"
-# endif
- return descr_addr;
-}
-#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
-
-#if SANITIZER_FREEBSD
-static void **ThreadSelfSegbase() {
- void **segbase = 0;
-# if defined(__i386__)
- // sysarch(I386_GET_GSBASE, segbase);
- __asm __volatile("mov %%gs:0, %0" : "=r" (segbase));
-# elif defined(__x86_64__)
- // sysarch(AMD64_GET_FSBASE, segbase);
- __asm __volatile("movq %%fs:0, %0" : "=r" (segbase));
-# else
-# error "unsupported CPU arch"
-# endif
- return segbase;
-}
-
-uptr ThreadSelf() {
- return (uptr)ThreadSelfSegbase()[2];
-}
-#endif // SANITIZER_FREEBSD
-
-#if SANITIZER_NETBSD
-static struct tls_tcb * ThreadSelfTlsTcb() {
- struct tls_tcb * tcb;
-# ifdef __HAVE___LWP_GETTCB_FAST
- tcb = (struct tls_tcb *)__lwp_gettcb_fast();
-# elif defined(__HAVE___LWP_GETPRIVATE_FAST)
- tcb = (struct tls_tcb *)__lwp_getprivate_fast();
-# endif
- return tcb;
-}
-
-uptr ThreadSelf() {
- return (uptr)ThreadSelfTlsTcb()->tcb_pthread;
-}
-
-int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) {
- const Elf_Phdr *hdr = info->dlpi_phdr;
- const Elf_Phdr *last_hdr = hdr + info->dlpi_phnum;
-
- for (; hdr != last_hdr; ++hdr) {
- if (hdr->p_type == PT_TLS && info->dlpi_tls_modid == 1) {
- *(uptr*)data = hdr->p_memsz;
- break;
- }
- }
- return 0;
-}
-#endif // SANITIZER_NETBSD
-
-#if !SANITIZER_GO
-static void GetTls(uptr *addr, uptr *size) {
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-# if defined(__x86_64__) || defined(__i386__) || defined(__s390__)
- *addr = ThreadSelf();
- *size = GetTlsSize();
- *addr -= *size;
- *addr += ThreadDescriptorSize();
-# elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) \
- || defined(__arm__)
- *addr = ThreadSelf();
- *size = GetTlsSize();
-# else
- *addr = 0;
- *size = 0;
-# endif
-#elif SANITIZER_FREEBSD
- void** segbase = ThreadSelfSegbase();
- *addr = 0;
- *size = 0;
- if (segbase != 0) {
- // tcbalign = 16
- // tls_size = round(tls_static_space, tcbalign);
- // dtv = segbase[1];
- // dtv[2] = segbase - tls_static_space;
- void **dtv = (void**) segbase[1];
- *addr = (uptr) dtv[2];
- *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]);
- }
-#elif SANITIZER_NETBSD
- struct tls_tcb * const tcb = ThreadSelfTlsTcb();
- *addr = 0;
- *size = 0;
- if (tcb != 0) {
- // Find size (p_memsz) of dlpi_tls_modid 1 (TLS block of the main program).
- // ld.elf_so hardcodes the index 1.
- dl_iterate_phdr(GetSizeFromHdr, size);
-
- if (*size != 0) {
- // The block has been found and tcb_dtv[1] contains the base address
- *addr = (uptr)tcb->tcb_dtv[1];
- }
- }
-#elif SANITIZER_OPENBSD
- *addr = 0;
- *size = 0;
-#elif SANITIZER_ANDROID
- *addr = 0;
- *size = 0;
-#elif SANITIZER_SOLARIS
- // FIXME
- *addr = 0;
- *size = 0;
-#else
-# error "Unknown OS"
-#endif
-}
-#endif
-
-#if !SANITIZER_GO
-uptr GetTlsSize() {
-#if SANITIZER_FREEBSD || SANITIZER_ANDROID || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
- uptr addr, size;
- GetTls(&addr, &size);
- return size;
-#elif defined(__mips__) || defined(__powerpc64__)
- return RoundUpTo(g_tls_size + TlsPreTcbSize(), 16);
-#else
- return g_tls_size;
-#endif
-}
-#endif
-
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
-#if SANITIZER_GO
- // Stub implementation for Go.
- *stk_addr = *stk_size = *tls_addr = *tls_size = 0;
-#else
- GetTls(tls_addr, tls_size);
-
- uptr stack_top, stack_bottom;
- GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
-
- if (!main) {
- // If stack and tls intersect, make them non-intersecting.
- if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
- CHECK_GT(*tls_addr + *tls_size, *stk_addr);
- CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
- *stk_size -= *tls_size;
- *tls_addr = *stk_addr + *stk_size;
- }
- }
-#endif
-}
-
-#if !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
-typedef ElfW(Phdr) Elf_Phdr;
-#elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
-#define Elf_Phdr XElf32_Phdr
-#define dl_phdr_info xdl_phdr_info
-#define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
-#endif // !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
-
-struct DlIteratePhdrData {
- InternalMmapVectorNoCtor<LoadedModule> *modules;
- bool first;
-};
-
-static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
- DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
- InternalScopedString module_name(kMaxPathLength);
- if (data->first) {
- data->first = false;
- // First module is the binary itself.
- ReadBinaryNameCached(module_name.data(), module_name.size());
- } else if (info->dlpi_name) {
- module_name.append("%s", info->dlpi_name);
- }
- if (module_name[0] == '\0')
- return 0;
- LoadedModule cur_module;
- cur_module.set(module_name.data(), info->dlpi_addr);
- for (int i = 0; i < (int)info->dlpi_phnum; i++) {
- const Elf_Phdr *phdr = &info->dlpi_phdr[i];
- if (phdr->p_type == PT_LOAD) {
- uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
- uptr cur_end = cur_beg + phdr->p_memsz;
- bool executable = phdr->p_flags & PF_X;
- bool writable = phdr->p_flags & PF_W;
- cur_module.addAddressRange(cur_beg, cur_end, executable,
- writable);
- }
- }
- data->modules->push_back(cur_module);
- return 0;
-}
-
-#if SANITIZER_ANDROID && __ANDROID_API__ < 21
-extern "C" __attribute__((weak)) int dl_iterate_phdr(
- int (*)(struct dl_phdr_info *, size_t, void *), void *);
-#endif
-
-static bool requiresProcmaps() {
-#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
- // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
- // The runtime check allows the same library to work with
- // both K and L (and future) Android releases.
- return AndroidGetApiLevel() <= ANDROID_LOLLIPOP_MR1;
-#else
- return false;
-#endif
-}
-
-static void procmapsInit(InternalMmapVectorNoCtor<LoadedModule> *modules) {
- MemoryMappingLayout memory_mapping(/*cache_enabled*/true);
- memory_mapping.DumpListOfModules(modules);
-}
-
-void ListOfModules::init() {
- clearOrInit();
- if (requiresProcmaps()) {
- procmapsInit(&modules_);
- } else {
- DlIteratePhdrData data = {&modules_, true};
- dl_iterate_phdr(dl_iterate_phdr_cb, &data);
- }
-}
-
-// When a custom loader is used, dl_iterate_phdr may not contain the full
-// list of modules. Allow callers to fall back to using procmaps.
-void ListOfModules::fallbackInit() {
- if (!requiresProcmaps()) {
- clearOrInit();
- procmapsInit(&modules_);
- } else {
- clear();
- }
-}
-
-// getrusage does not give us the current RSS, only the max RSS.
-// Still, this is better than nothing if /proc/self/statm is not available
-// for some reason, e.g. due to a sandbox.
-static uptr GetRSSFromGetrusage() {
- struct rusage usage;
- if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox.
- return 0;
- return usage.ru_maxrss << 10; // ru_maxrss is in Kb.
-}
-
-uptr GetRSS() {
- if (!common_flags()->can_use_proc_maps_statm)
- return GetRSSFromGetrusage();
- fd_t fd = OpenFile("/proc/self/statm", RdOnly);
- if (fd == kInvalidFd)
- return GetRSSFromGetrusage();
- char buf[64];
- uptr len = internal_read(fd, buf, sizeof(buf) - 1);
- internal_close(fd);
- if ((sptr)len <= 0)
- return 0;
- buf[len] = 0;
- // The format of the file is:
- // 1084 89 69 11 0 79 0
- // We need the second number which is RSS in pages.
- char *pos = buf;
- // Skip the first number.
- while (*pos >= '0' && *pos <= '9')
- pos++;
- // Skip whitespaces.
- while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
- pos++;
- // Read the number.
- uptr rss = 0;
- while (*pos >= '0' && *pos <= '9')
- rss = rss * 10 + *pos++ - '0';
- return rss * GetPageSizeCached();
-}
-
-// sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used on most platforms as
-// they allocate memory.
-u32 GetNumberOfCPUs() {
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
- u32 ncpu;
- int req[2];
- uptr len = sizeof(ncpu);
- req[0] = CTL_HW;
- req[1] = HW_NCPU;
- CHECK_EQ(internal_sysctl(req, 2, &ncpu, &len, NULL, 0), 0);
- return ncpu;
-#elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__)
- // Fall back to /sys/devices/system/cpu on Android when cpu_set_t doesn't
- // exist in sched.h. That is the case for toolchains generated with older
- // NDKs.
- // This code doesn't work on AArch64 because internal_getdents makes use of
- // the 64bit getdents syscall, but cpu_set_t seems to always exist on AArch64.
- uptr fd = internal_open("/sys/devices/system/cpu", O_RDONLY | O_DIRECTORY);
- if (internal_iserror(fd))
- return 0;
- InternalMmapVector<u8> buffer(4096);
- uptr bytes_read = buffer.size();
- uptr n_cpus = 0;
- u8 *d_type;
- struct linux_dirent *entry = (struct linux_dirent *)&buffer[bytes_read];
- while (true) {
- if ((u8 *)entry >= &buffer[bytes_read]) {
- bytes_read = internal_getdents(fd, (struct linux_dirent *)buffer.data(),
- buffer.size());
- if (internal_iserror(bytes_read) || !bytes_read)
- break;
- entry = (struct linux_dirent *)buffer.data();
- }
- d_type = (u8 *)entry + entry->d_reclen - 1;
- if (d_type >= &buffer[bytes_read] ||
- (u8 *)&entry->d_name[3] >= &buffer[bytes_read])
- break;
- if (entry->d_ino != 0 && *d_type == DT_DIR) {
- if (entry->d_name[0] == 'c' && entry->d_name[1] == 'p' &&
- entry->d_name[2] == 'u' &&
- entry->d_name[3] >= '0' && entry->d_name[3] <= '9')
- n_cpus++;
- }
- entry = (struct linux_dirent *)(((u8 *)entry) + entry->d_reclen);
- }
- internal_close(fd);
- return n_cpus;
-#elif SANITIZER_SOLARIS
- return sysconf(_SC_NPROCESSORS_ONLN);
-#else
-#if defined(CPU_COUNT)
- cpu_set_t CPUs;
- CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0);
- return CPU_COUNT(&CPUs);
-#else
- return 1;
-#endif
-#endif
-}
-
-#if SANITIZER_LINUX
-
-# if SANITIZER_ANDROID
-static atomic_uint8_t android_log_initialized;
-
-void AndroidLogInit() {
- openlog(GetProcessName(), 0, LOG_USER);
- atomic_store(&android_log_initialized, 1, memory_order_release);
-}
-
-static bool ShouldLogAfterPrintf() {
- return atomic_load(&android_log_initialized, memory_order_acquire);
-}
-
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-int async_safe_write_log(int pri, const char* tag, const char* msg);
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-int __android_log_write(int prio, const char* tag, const char* msg);
-
-// ANDROID_LOG_INFO is 4, but can't be resolved at runtime.
-#define SANITIZER_ANDROID_LOG_INFO 4
-
-// async_safe_write_log is a new public version of __libc_write_log that is
-// used behind syslog. It is preferable to syslog as it will not do any dynamic
-// memory allocation or formatting.
-// If the function is not available, syslog is preferred for L+ (it was broken
-// pre-L) as __android_log_write triggers a racey behavior with the strncpy
-// interceptor. Fallback to __android_log_write pre-L.
-void WriteOneLineToSyslog(const char *s) {
- if (&async_safe_write_log) {
- async_safe_write_log(SANITIZER_ANDROID_LOG_INFO, GetProcessName(), s);
- } else if (AndroidGetApiLevel() > ANDROID_KITKAT) {
- syslog(LOG_INFO, "%s", s);
- } else {
- CHECK(&__android_log_write);
- __android_log_write(SANITIZER_ANDROID_LOG_INFO, nullptr, s);
- }
-}
-
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-void android_set_abort_message(const char *);
-
-void SetAbortMessage(const char *str) {
- if (&android_set_abort_message)
- android_set_abort_message(str);
-}
-# else
-void AndroidLogInit() {}
-
-static bool ShouldLogAfterPrintf() { return true; }
-
-void WriteOneLineToSyslog(const char *s) { syslog(LOG_INFO, "%s", s); }
-
-void SetAbortMessage(const char *str) {}
-# endif // SANITIZER_ANDROID
-
-void LogMessageOnPrintf(const char *str) {
- if (common_flags()->log_to_syslog && ShouldLogAfterPrintf())
- WriteToSyslog(str);
-}
-
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_LINUX && !SANITIZER_GO
-// glibc crashes when using clock_gettime from a preinit_array function as the
-// vDSO function pointers haven't been initialized yet. __progname is
-// initialized after the vDSO function pointers, so if it exists, is not null
-// and is not empty, we can use clock_gettime.
-extern "C" SANITIZER_WEAK_ATTRIBUTE char *__progname;
-INLINE bool CanUseVDSO() {
- // Bionic is safe, it checks for the vDSO function pointers to be initialized.
- if (SANITIZER_ANDROID)
- return true;
- if (&__progname && __progname && *__progname)
- return true;
- return false;
-}
-
-// MonotonicNanoTime is a timing function that can leverage the vDSO by calling
-// clock_gettime. real_clock_gettime only exists if clock_gettime is
-// intercepted, so define it weakly and use it if available.
-extern "C" SANITIZER_WEAK_ATTRIBUTE
-int real_clock_gettime(u32 clk_id, void *tp);
-u64 MonotonicNanoTime() {
- timespec ts;
- if (CanUseVDSO()) {
- if (&real_clock_gettime)
- real_clock_gettime(CLOCK_MONOTONIC, &ts);
- else
- clock_gettime(CLOCK_MONOTONIC, &ts);
- } else {
- internal_clock_gettime(CLOCK_MONOTONIC, &ts);
- }
- return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
-}
-#else
-// Non-Linux & Go always use the syscall.
-u64 MonotonicNanoTime() {
- timespec ts;
- internal_clock_gettime(CLOCK_MONOTONIC, &ts);
- return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
-}
-#endif // SANITIZER_LINUX && !SANITIZER_GO
-
-} // namespace __sanitizer
-
-#endif
--- /dev/null
+//===-- sanitizer_linux_libcdep.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements linux-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+ SANITIZER_OPENBSD || SANITIZER_SOLARIS
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_atomic.h"
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_freebsd.h"
+#include "sanitizer_getauxval.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <dlfcn.h> // for dlsym()
+#include <link.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include <syslog.h>
+
+#if SANITIZER_FREEBSD
+#include <pthread_np.h>
+#include <osreldate.h>
+#include <sys/sysctl.h>
+#define pthread_getattr_np pthread_attr_get_np
+#endif
+
+#if SANITIZER_OPENBSD
+#include <pthread_np.h>
+#include <sys/sysctl.h>
+#endif
+
+#if SANITIZER_NETBSD
+#include <sys/sysctl.h>
+#include <sys/tls.h>
+#endif
+
+#if SANITIZER_SOLARIS
+#include <stdlib.h>
+#include <thread.h>
+#endif
+
+#if SANITIZER_ANDROID
+#include <android/api-level.h>
+#if !defined(CPU_COUNT) && !defined(__aarch64__)
+#include <dirent.h>
+#include <fcntl.h>
+struct __sanitizer::linux_dirent {
+ long d_ino;
+ off_t d_off;
+ unsigned short d_reclen;
+ char d_name[];
+};
+#endif
+#endif
+
+#if !SANITIZER_ANDROID
+#include <elf.h>
+#include <unistd.h>
+#endif
+
+namespace __sanitizer {
+
+SANITIZER_WEAK_ATTRIBUTE int
+real_sigaction(int signum, const void *act, void *oldact);
+
+int internal_sigaction(int signum, const void *act, void *oldact) {
+#if !SANITIZER_GO
+ if (&real_sigaction)
+ return real_sigaction(signum, act, oldact);
+#endif
+ return sigaction(signum, (const struct sigaction *)act,
+ (struct sigaction *)oldact);
+}
+
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+ uptr *stack_bottom) {
+ CHECK(stack_top);
+ CHECK(stack_bottom);
+ if (at_initialization) {
+ // This is the main thread. Libpthread may not be initialized yet.
+ struct rlimit rl;
+ CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
+
+ // Find the mapping that contains a stack variable.
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ if (proc_maps.Error()) {
+ *stack_top = *stack_bottom = 0;
+ return;
+ }
+ MemoryMappedSegment segment;
+ uptr prev_end = 0;
+ while (proc_maps.Next(&segment)) {
+ if ((uptr)&rl < segment.end) break;
+ prev_end = segment.end;
+ }
+ CHECK((uptr)&rl >= segment.start && (uptr)&rl < segment.end);
+
+ // Get stacksize from rlimit, but clip it so that it does not overlap
+ // with other mappings.
+ uptr stacksize = rl.rlim_cur;
+ if (stacksize > segment.end - prev_end) stacksize = segment.end - prev_end;
+ // When running with unlimited stack size, we still want to set some limit.
+ // The unlimited stack size is caused by 'ulimit -s unlimited'.
+ // Also, for some reason, GNU make spawns subprocesses with unlimited stack.
+ if (stacksize > kMaxThreadStackSize)
+ stacksize = kMaxThreadStackSize;
+ *stack_top = segment.end;
+ *stack_bottom = segment.end - stacksize;
+ return;
+ }
+ uptr stacksize = 0;
+ void *stackaddr = nullptr;
+#if SANITIZER_SOLARIS
+ stack_t ss;
+ CHECK_EQ(thr_stksegment(&ss), 0);
+ stacksize = ss.ss_size;
+ stackaddr = (char *)ss.ss_sp - stacksize;
+#elif SANITIZER_OPENBSD
+ stack_t sattr;
+ CHECK_EQ(pthread_stackseg_np(pthread_self(), &sattr), 0);
+ stackaddr = sattr.ss_sp;
+ stacksize = sattr.ss_size;
+#else // !SANITIZER_SOLARIS
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
+ my_pthread_attr_getstack(&attr, &stackaddr, &stacksize);
+ pthread_attr_destroy(&attr);
+#endif // SANITIZER_SOLARIS
+
+ *stack_top = (uptr)stackaddr + stacksize;
+ *stack_bottom = (uptr)stackaddr;
+}
+
+#if !SANITIZER_GO
+bool SetEnv(const char *name, const char *value) {
+ void *f = dlsym(RTLD_NEXT, "setenv");
+ if (!f)
+ return false;
+ typedef int(*setenv_ft)(const char *name, const char *value, int overwrite);
+ setenv_ft setenv_f;
+ CHECK_EQ(sizeof(setenv_f), sizeof(f));
+ internal_memcpy(&setenv_f, &f, sizeof(f));
+ return setenv_f(name, value, 1) == 0;
+}
+#endif
+
+__attribute__((unused)) static bool GetLibcVersion(int *major, int *minor,
+ int *patch) {
+#ifdef _CS_GNU_LIBC_VERSION
+ char buf[64];
+ uptr len = confstr(_CS_GNU_LIBC_VERSION, buf, sizeof(buf));
+ if (len >= sizeof(buf))
+ return false;
+ buf[len] = 0;
+ static const char kGLibC[] = "glibc ";
+ if (internal_strncmp(buf, kGLibC, sizeof(kGLibC) - 1) != 0)
+ return false;
+ const char *p = buf + sizeof(kGLibC) - 1;
+ *major = internal_simple_strtoll(p, &p, 10);
+ *minor = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
+ *patch = (*p == '.') ? internal_simple_strtoll(p + 1, &p, 10) : 0;
+ return true;
+#else
+ return false;
+#endif
+}
+
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO && \
+ !SANITIZER_NETBSD && !SANITIZER_OPENBSD && !SANITIZER_SOLARIS
+static uptr g_tls_size;
+
+#ifdef __i386__
+# ifndef __GLIBC_PREREQ
+# define CHECK_GET_TLS_STATIC_INFO_VERSION 1
+# else
+# define CHECK_GET_TLS_STATIC_INFO_VERSION (!__GLIBC_PREREQ(2, 27))
+# endif
+#else
+# define CHECK_GET_TLS_STATIC_INFO_VERSION 0
+#endif
+
+#if CHECK_GET_TLS_STATIC_INFO_VERSION
+# define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall))
+#else
+# define DL_INTERNAL_FUNCTION
+#endif
+
+namespace {
+struct GetTlsStaticInfoCall {
+ typedef void (*get_tls_func)(size_t*, size_t*);
+};
+struct GetTlsStaticInfoRegparmCall {
+ typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION;
+};
+
+template <typename T>
+void CallGetTls(void* ptr, size_t* size, size_t* align) {
+ typename T::get_tls_func get_tls;
+ CHECK_EQ(sizeof(get_tls), sizeof(ptr));
+ internal_memcpy(&get_tls, &ptr, sizeof(ptr));
+ CHECK_NE(get_tls, 0);
+ get_tls(size, align);
+}
+
+bool CmpLibcVersion(int major, int minor, int patch) {
+ int ma;
+ int mi;
+ int pa;
+ if (!GetLibcVersion(&ma, &mi, &pa))
+ return false;
+ if (ma > major)
+ return true;
+ if (ma < major)
+ return false;
+ if (mi > minor)
+ return true;
+ if (mi < minor)
+ return false;
+ return pa >= patch;
+}
+
+} // namespace
+
+void InitTlsSize() {
+ // all current supported platforms have 16 bytes stack alignment
+ const size_t kStackAlign = 16;
+ void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info");
+ size_t tls_size = 0;
+ size_t tls_align = 0;
+ // On i?86, _dl_get_tls_static_info used to be internal_function, i.e.
+ // __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal
+ // function in 2.27 and later.
+ if (CHECK_GET_TLS_STATIC_INFO_VERSION && !CmpLibcVersion(2, 27, 0))
+ CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr,
+ &tls_size, &tls_align);
+ else
+ CallGetTls<GetTlsStaticInfoCall>(get_tls_static_info_ptr,
+ &tls_size, &tls_align);
+ if (tls_align < kStackAlign)
+ tls_align = kStackAlign;
+ g_tls_size = RoundUpTo(tls_size, tls_align);
+}
+#else
+void InitTlsSize() { }
+#endif // !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO &&
+ // !SANITIZER_NETBSD && !SANITIZER_SOLARIS
+
+#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) || \
+ defined(__aarch64__) || defined(__powerpc64__) || defined(__s390__) || \
+ defined(__arm__)) && \
+ SANITIZER_LINUX && !SANITIZER_ANDROID
+// sizeof(struct pthread) from glibc.
+static atomic_uintptr_t thread_descriptor_size;
+
+uptr ThreadDescriptorSize() {
+ uptr val = atomic_load_relaxed(&thread_descriptor_size);
+ if (val)
+ return val;
+#if defined(__x86_64__) || defined(__i386__) || defined(__arm__)
+ int major;
+ int minor;
+ int patch;
+ if (GetLibcVersion(&major, &minor, &patch) && major == 2) {
+ /* sizeof(struct pthread) values from various glibc versions. */
+ if (SANITIZER_X32)
+ val = 1728; // Assume only one particular version for x32.
+ // For ARM sizeof(struct pthread) changed in Glibc 2.23.
+ else if (SANITIZER_ARM)
+ val = minor <= 22 ? 1120 : 1216;
+ else if (minor <= 3)
+ val = FIRST_32_SECOND_64(1104, 1696);
+ else if (minor == 4)
+ val = FIRST_32_SECOND_64(1120, 1728);
+ else if (minor == 5)
+ val = FIRST_32_SECOND_64(1136, 1728);
+ else if (minor <= 9)
+ val = FIRST_32_SECOND_64(1136, 1712);
+ else if (minor == 10)
+ val = FIRST_32_SECOND_64(1168, 1776);
+ else if (minor == 11 || (minor == 12 && patch == 1))
+ val = FIRST_32_SECOND_64(1168, 2288);
+ else if (minor <= 14)
+ val = FIRST_32_SECOND_64(1168, 2304);
+ else
+ val = FIRST_32_SECOND_64(1216, 2304);
+ }
+#elif defined(__mips__)
+ // TODO(sagarthakur): add more values as per different glibc versions.
+ val = FIRST_32_SECOND_64(1152, 1776);
+#elif defined(__aarch64__)
+ // The sizeof (struct pthread) is the same from GLIBC 2.17 to 2.22.
+ val = 1776;
+#elif defined(__powerpc64__)
+ val = 1776; // from glibc.ppc64le 2.20-8.fc21
+#elif defined(__s390__)
+ val = FIRST_32_SECOND_64(1152, 1776); // valid for glibc 2.22
+#endif
+ if (val)
+ atomic_store_relaxed(&thread_descriptor_size, val);
+ return val;
+}
+
+// The offset at which pointer to self is located in the thread descriptor.
+const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16);
+
+uptr ThreadSelfOffset() {
+ return kThreadSelfOffset;
+}
+
+#if defined(__mips__) || defined(__powerpc64__)
+// TlsPreTcbSize includes size of struct pthread_descr and size of tcb
+// head structure. It lies before the static tls blocks.
+static uptr TlsPreTcbSize() {
+# if defined(__mips__)
+ const uptr kTcbHead = 16; // sizeof (tcbhead_t)
+# elif defined(__powerpc64__)
+ const uptr kTcbHead = 88; // sizeof (tcbhead_t)
+# endif
+ const uptr kTlsAlign = 16;
+ const uptr kTlsPreTcbSize =
+ RoundUpTo(ThreadDescriptorSize() + kTcbHead, kTlsAlign);
+ return kTlsPreTcbSize;
+}
+#endif
+
+uptr ThreadSelf() {
+ uptr descr_addr;
+# if defined(__i386__)
+ asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
+# elif defined(__x86_64__)
+ asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset));
+# elif defined(__mips__)
+ // MIPS uses TLS variant I. The thread pointer (in hardware register $29)
+ // points to the end of the TCB + 0x7000. The pthread_descr structure is
+ // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+ // TCB and the size of pthread_descr.
+ const uptr kTlsTcbOffset = 0x7000;
+ uptr thread_pointer;
+ asm volatile(".set push;\
+ .set mips64r2;\
+ rdhwr %0,$29;\
+ .set pop" : "=r" (thread_pointer));
+ descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize();
+# elif defined(__aarch64__) || defined(__arm__)
+ descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) -
+ ThreadDescriptorSize();
+# elif defined(__s390__)
+ descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer());
+# elif defined(__powerpc64__)
+ // PPC64LE uses TLS variant I. The thread pointer (in GPR 13)
+ // points to the end of the TCB + 0x7000. The pthread_descr structure is
+ // immediately in front of the TCB. TlsPreTcbSize() includes the size of the
+ // TCB and the size of pthread_descr.
+ const uptr kTlsTcbOffset = 0x7000;
+ uptr thread_pointer;
+ asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset));
+ descr_addr = thread_pointer - TlsPreTcbSize();
+# else
+# error "unsupported CPU arch"
+# endif
+ return descr_addr;
+}
+#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX
+
+#if SANITIZER_FREEBSD
+static void **ThreadSelfSegbase() {
+ void **segbase = 0;
+# if defined(__i386__)
+ // sysarch(I386_GET_GSBASE, segbase);
+ __asm __volatile("mov %%gs:0, %0" : "=r" (segbase));
+# elif defined(__x86_64__)
+ // sysarch(AMD64_GET_FSBASE, segbase);
+ __asm __volatile("movq %%fs:0, %0" : "=r" (segbase));
+# else
+# error "unsupported CPU arch"
+# endif
+ return segbase;
+}
+
+uptr ThreadSelf() {
+ return (uptr)ThreadSelfSegbase()[2];
+}
+#endif // SANITIZER_FREEBSD
+
+#if SANITIZER_NETBSD
+static struct tls_tcb * ThreadSelfTlsTcb() {
+ struct tls_tcb * tcb;
+# ifdef __HAVE___LWP_GETTCB_FAST
+ tcb = (struct tls_tcb *)__lwp_gettcb_fast();
+# elif defined(__HAVE___LWP_GETPRIVATE_FAST)
+ tcb = (struct tls_tcb *)__lwp_getprivate_fast();
+# endif
+ return tcb;
+}
+
+uptr ThreadSelf() {
+ return (uptr)ThreadSelfTlsTcb()->tcb_pthread;
+}
+
+int GetSizeFromHdr(struct dl_phdr_info *info, size_t size, void *data) {
+ const Elf_Phdr *hdr = info->dlpi_phdr;
+ const Elf_Phdr *last_hdr = hdr + info->dlpi_phnum;
+
+ for (; hdr != last_hdr; ++hdr) {
+ if (hdr->p_type == PT_TLS && info->dlpi_tls_modid == 1) {
+ *(uptr*)data = hdr->p_memsz;
+ break;
+ }
+ }
+ return 0;
+}
+#endif // SANITIZER_NETBSD
+
+#if !SANITIZER_GO
+static void GetTls(uptr *addr, uptr *size) {
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+# if defined(__x86_64__) || defined(__i386__) || defined(__s390__)
+ *addr = ThreadSelf();
+ *size = GetTlsSize();
+ *addr -= *size;
+ *addr += ThreadDescriptorSize();
+# elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) \
+ || defined(__arm__)
+ *addr = ThreadSelf();
+ *size = GetTlsSize();
+# else
+ *addr = 0;
+ *size = 0;
+# endif
+#elif SANITIZER_FREEBSD
+ void** segbase = ThreadSelfSegbase();
+ *addr = 0;
+ *size = 0;
+ if (segbase != 0) {
+ // tcbalign = 16
+ // tls_size = round(tls_static_space, tcbalign);
+ // dtv = segbase[1];
+ // dtv[2] = segbase - tls_static_space;
+ void **dtv = (void**) segbase[1];
+ *addr = (uptr) dtv[2];
+ *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]);
+ }
+#elif SANITIZER_NETBSD
+ struct tls_tcb * const tcb = ThreadSelfTlsTcb();
+ *addr = 0;
+ *size = 0;
+ if (tcb != 0) {
+ // Find size (p_memsz) of dlpi_tls_modid 1 (TLS block of the main program).
+ // ld.elf_so hardcodes the index 1.
+ dl_iterate_phdr(GetSizeFromHdr, size);
+
+ if (*size != 0) {
+ // The block has been found and tcb_dtv[1] contains the base address
+ *addr = (uptr)tcb->tcb_dtv[1];
+ }
+ }
+#elif SANITIZER_OPENBSD
+ *addr = 0;
+ *size = 0;
+#elif SANITIZER_ANDROID
+ *addr = 0;
+ *size = 0;
+#elif SANITIZER_SOLARIS
+ // FIXME
+ *addr = 0;
+ *size = 0;
+#else
+# error "Unknown OS"
+#endif
+}
+#endif
+
+#if !SANITIZER_GO
+uptr GetTlsSize() {
+#if SANITIZER_FREEBSD || SANITIZER_ANDROID || SANITIZER_NETBSD || \
+ SANITIZER_OPENBSD || SANITIZER_SOLARIS
+ uptr addr, size;
+ GetTls(&addr, &size);
+ return size;
+#elif defined(__mips__) || defined(__powerpc64__)
+ return RoundUpTo(g_tls_size + TlsPreTcbSize(), 16);
+#else
+ return g_tls_size;
+#endif
+}
+#endif
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+#if SANITIZER_GO
+ // Stub implementation for Go.
+ *stk_addr = *stk_size = *tls_addr = *tls_size = 0;
+#else
+ GetTls(tls_addr, tls_size);
+
+ uptr stack_top, stack_bottom;
+ GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+ *stk_addr = stack_bottom;
+ *stk_size = stack_top - stack_bottom;
+
+ if (!main) {
+ // If stack and tls intersect, make them non-intersecting.
+ if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) {
+ CHECK_GT(*tls_addr + *tls_size, *stk_addr);
+ CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size);
+ *stk_size -= *tls_size;
+ *tls_addr = *stk_addr + *stk_size;
+ }
+ }
+#endif
+}
+
+#if !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
+typedef ElfW(Phdr) Elf_Phdr;
+#elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2
+#define Elf_Phdr XElf32_Phdr
+#define dl_phdr_info xdl_phdr_info
+#define dl_iterate_phdr(c, b) xdl_iterate_phdr((c), (b))
+#endif // !SANITIZER_FREEBSD && !SANITIZER_OPENBSD
+
+struct DlIteratePhdrData {
+ InternalMmapVectorNoCtor<LoadedModule> *modules;
+ bool first;
+};
+
+static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) {
+ DlIteratePhdrData *data = (DlIteratePhdrData*)arg;
+ InternalScopedString module_name(kMaxPathLength);
+ if (data->first) {
+ data->first = false;
+ // First module is the binary itself.
+ ReadBinaryNameCached(module_name.data(), module_name.size());
+ } else if (info->dlpi_name) {
+ module_name.append("%s", info->dlpi_name);
+ }
+ if (module_name[0] == '\0')
+ return 0;
+ LoadedModule cur_module;
+ cur_module.set(module_name.data(), info->dlpi_addr);
+ for (int i = 0; i < (int)info->dlpi_phnum; i++) {
+ const Elf_Phdr *phdr = &info->dlpi_phdr[i];
+ if (phdr->p_type == PT_LOAD) {
+ uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
+ uptr cur_end = cur_beg + phdr->p_memsz;
+ bool executable = phdr->p_flags & PF_X;
+ bool writable = phdr->p_flags & PF_W;
+ cur_module.addAddressRange(cur_beg, cur_end, executable,
+ writable);
+ }
+ }
+ data->modules->push_back(cur_module);
+ return 0;
+}
+
+#if SANITIZER_ANDROID && __ANDROID_API__ < 21
+extern "C" __attribute__((weak)) int dl_iterate_phdr(
+ int (*)(struct dl_phdr_info *, size_t, void *), void *);
+#endif
+
+static bool requiresProcmaps() {
+#if SANITIZER_ANDROID && __ANDROID_API__ <= 22
+ // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken.
+ // The runtime check allows the same library to work with
+ // both K and L (and future) Android releases.
+ return AndroidGetApiLevel() <= ANDROID_LOLLIPOP_MR1;
+#else
+ return false;
+#endif
+}
+
+static void procmapsInit(InternalMmapVectorNoCtor<LoadedModule> *modules) {
+ MemoryMappingLayout memory_mapping(/*cache_enabled*/true);
+ memory_mapping.DumpListOfModules(modules);
+}
+
+void ListOfModules::init() {
+ clearOrInit();
+ if (requiresProcmaps()) {
+ procmapsInit(&modules_);
+ } else {
+ DlIteratePhdrData data = {&modules_, true};
+ dl_iterate_phdr(dl_iterate_phdr_cb, &data);
+ }
+}
+
+// When a custom loader is used, dl_iterate_phdr may not contain the full
+// list of modules. Allow callers to fall back to using procmaps.
+void ListOfModules::fallbackInit() {
+ if (!requiresProcmaps()) {
+ clearOrInit();
+ procmapsInit(&modules_);
+ } else {
+ clear();
+ }
+}
+
+// getrusage does not give us the current RSS, only the max RSS.
+// Still, this is better than nothing if /proc/self/statm is not available
+// for some reason, e.g. due to a sandbox.
+static uptr GetRSSFromGetrusage() {
+ struct rusage usage;
+ if (getrusage(RUSAGE_SELF, &usage)) // Failed, probably due to a sandbox.
+ return 0;
+ return usage.ru_maxrss << 10; // ru_maxrss is in Kb.
+}
+
+uptr GetRSS() {
+ if (!common_flags()->can_use_proc_maps_statm)
+ return GetRSSFromGetrusage();
+ fd_t fd = OpenFile("/proc/self/statm", RdOnly);
+ if (fd == kInvalidFd)
+ return GetRSSFromGetrusage();
+ char buf[64];
+ uptr len = internal_read(fd, buf, sizeof(buf) - 1);
+ internal_close(fd);
+ if ((sptr)len <= 0)
+ return 0;
+ buf[len] = 0;
+ // The format of the file is:
+ // 1084 89 69 11 0 79 0
+ // We need the second number which is RSS in pages.
+ char *pos = buf;
+ // Skip the first number.
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ // Skip whitespaces.
+ while (!(*pos >= '0' && *pos <= '9') && *pos != 0)
+ pos++;
+ // Read the number.
+ uptr rss = 0;
+ while (*pos >= '0' && *pos <= '9')
+ rss = rss * 10 + *pos++ - '0';
+ return rss * GetPageSizeCached();
+}
+
+// sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used on most platforms as
+// they allocate memory.
+u32 GetNumberOfCPUs() {
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+ u32 ncpu;
+ int req[2];
+ uptr len = sizeof(ncpu);
+ req[0] = CTL_HW;
+ req[1] = HW_NCPU;
+ CHECK_EQ(internal_sysctl(req, 2, &ncpu, &len, NULL, 0), 0);
+ return ncpu;
+#elif SANITIZER_ANDROID && !defined(CPU_COUNT) && !defined(__aarch64__)
+ // Fall back to /sys/devices/system/cpu on Android when cpu_set_t doesn't
+ // exist in sched.h. That is the case for toolchains generated with older
+ // NDKs.
+ // This code doesn't work on AArch64 because internal_getdents makes use of
+ // the 64bit getdents syscall, but cpu_set_t seems to always exist on AArch64.
+ uptr fd = internal_open("/sys/devices/system/cpu", O_RDONLY | O_DIRECTORY);
+ if (internal_iserror(fd))
+ return 0;
+ InternalMmapVector<u8> buffer(4096);
+ uptr bytes_read = buffer.size();
+ uptr n_cpus = 0;
+ u8 *d_type;
+ struct linux_dirent *entry = (struct linux_dirent *)&buffer[bytes_read];
+ while (true) {
+ if ((u8 *)entry >= &buffer[bytes_read]) {
+ bytes_read = internal_getdents(fd, (struct linux_dirent *)buffer.data(),
+ buffer.size());
+ if (internal_iserror(bytes_read) || !bytes_read)
+ break;
+ entry = (struct linux_dirent *)buffer.data();
+ }
+ d_type = (u8 *)entry + entry->d_reclen - 1;
+ if (d_type >= &buffer[bytes_read] ||
+ (u8 *)&entry->d_name[3] >= &buffer[bytes_read])
+ break;
+ if (entry->d_ino != 0 && *d_type == DT_DIR) {
+ if (entry->d_name[0] == 'c' && entry->d_name[1] == 'p' &&
+ entry->d_name[2] == 'u' &&
+ entry->d_name[3] >= '0' && entry->d_name[3] <= '9')
+ n_cpus++;
+ }
+ entry = (struct linux_dirent *)(((u8 *)entry) + entry->d_reclen);
+ }
+ internal_close(fd);
+ return n_cpus;
+#elif SANITIZER_SOLARIS
+ return sysconf(_SC_NPROCESSORS_ONLN);
+#else
+ cpu_set_t CPUs;
+ CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0);
+ return CPU_COUNT(&CPUs);
+#endif
+}
+
+#if SANITIZER_LINUX
+
+# if SANITIZER_ANDROID
+static atomic_uint8_t android_log_initialized;
+
+void AndroidLogInit() {
+ openlog(GetProcessName(), 0, LOG_USER);
+ atomic_store(&android_log_initialized, 1, memory_order_release);
+}
+
+static bool ShouldLogAfterPrintf() {
+ return atomic_load(&android_log_initialized, memory_order_acquire);
+}
+
+extern "C" SANITIZER_WEAK_ATTRIBUTE
+int async_safe_write_log(int pri, const char* tag, const char* msg);
+extern "C" SANITIZER_WEAK_ATTRIBUTE
+int __android_log_write(int prio, const char* tag, const char* msg);
+
+// ANDROID_LOG_INFO is 4, but can't be resolved at runtime.
+#define SANITIZER_ANDROID_LOG_INFO 4
+
+// async_safe_write_log is a new public version of __libc_write_log that is
+// used behind syslog. It is preferable to syslog as it will not do any dynamic
+// memory allocation or formatting.
+// If the function is not available, syslog is preferred for L+ (it was broken
+// pre-L) as __android_log_write triggers a racey behavior with the strncpy
+// interceptor. Fallback to __android_log_write pre-L.
+void WriteOneLineToSyslog(const char *s) {
+ if (&async_safe_write_log) {
+ async_safe_write_log(SANITIZER_ANDROID_LOG_INFO, GetProcessName(), s);
+ } else if (AndroidGetApiLevel() > ANDROID_KITKAT) {
+ syslog(LOG_INFO, "%s", s);
+ } else {
+ CHECK(&__android_log_write);
+ __android_log_write(SANITIZER_ANDROID_LOG_INFO, nullptr, s);
+ }
+}
+
+extern "C" SANITIZER_WEAK_ATTRIBUTE
+void android_set_abort_message(const char *);
+
+void SetAbortMessage(const char *str) {
+ if (&android_set_abort_message)
+ android_set_abort_message(str);
+}
+# else
+void AndroidLogInit() {}
+
+static bool ShouldLogAfterPrintf() { return true; }
+
+void WriteOneLineToSyslog(const char *s) { syslog(LOG_INFO, "%s", s); }
+
+void SetAbortMessage(const char *str) {}
+# endif // SANITIZER_ANDROID
+
+void LogMessageOnPrintf(const char *str) {
+ if (common_flags()->log_to_syslog && ShouldLogAfterPrintf())
+ WriteToSyslog(str);
+}
+
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX && !SANITIZER_GO
+// glibc crashes when using clock_gettime from a preinit_array function as the
+// vDSO function pointers haven't been initialized yet. __progname is
+// initialized after the vDSO function pointers, so if it exists, is not null
+// and is not empty, we can use clock_gettime.
+extern "C" SANITIZER_WEAK_ATTRIBUTE char *__progname;
+INLINE bool CanUseVDSO() {
+ // Bionic is safe, it checks for the vDSO function pointers to be initialized.
+ if (SANITIZER_ANDROID)
+ return true;
+ if (&__progname && __progname && *__progname)
+ return true;
+ return false;
+}
+
+// MonotonicNanoTime is a timing function that can leverage the vDSO by calling
+// clock_gettime. real_clock_gettime only exists if clock_gettime is
+// intercepted, so define it weakly and use it if available.
+extern "C" SANITIZER_WEAK_ATTRIBUTE
+int real_clock_gettime(u32 clk_id, void *tp);
+u64 MonotonicNanoTime() {
+ timespec ts;
+ if (CanUseVDSO()) {
+ if (&real_clock_gettime)
+ real_clock_gettime(CLOCK_MONOTONIC, &ts);
+ else
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ } else {
+ internal_clock_gettime(CLOCK_MONOTONIC, &ts);
+ }
+ return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+#else
+// Non-Linux & Go always use the syscall.
+u64 MonotonicNanoTime() {
+ timespec ts;
+ internal_clock_gettime(CLOCK_MONOTONIC, &ts);
+ return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec;
+}
+#endif // SANITIZER_LINUX && !SANITIZER_GO
+
+#if !SANITIZER_OPENBSD
+void ReExec() {
+ const char *pathname = "/proc/self/exe";
+
+#if SANITIZER_NETBSD
+ static const int name[] = {
+ CTL_KERN,
+ KERN_PROC_ARGS,
+ -1,
+ KERN_PROC_PATHNAME,
+ };
+ char path[400];
+ uptr len;
+
+ len = sizeof(path);
+ if (internal_sysctl(name, ARRAY_SIZE(name), path, &len, NULL, 0) != -1)
+ pathname = path;
+#elif SANITIZER_SOLARIS
+ pathname = getexecname();
+ CHECK_NE(pathname, NULL);
+#elif SANITIZER_USE_GETAUXVAL
+ // Calling execve with /proc/self/exe sets that as $EXEC_ORIGIN. Binaries that
+ // rely on that will fail to load shared libraries. Query AT_EXECFN instead.
+ pathname = reinterpret_cast<const char *>(getauxval(AT_EXECFN));
+#endif
+
+ uptr rv = internal_execve(pathname, GetArgv(), GetEnviron());
+ int rverrno;
+ CHECK_EQ(internal_iserror(rv, &rverrno), true);
+ Printf("execve failed, errno %d\n", rverrno);
+ Die();
+}
+#endif // !SANITIZER_OPENBSD
+
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-
-// Avoid being marked as needing an executable stack:
-#if defined(__linux__) && defined(__ELF__)
-.section .note.GNU-stack,"",%progbits
-#endif
-
-// Further contents are mips64 only:
-#if defined(__linux__) && defined(__mips64)
-
-.section .text
-.set noreorder
-.globl internal_sigreturn
-.type internal_sigreturn, @function
-internal_sigreturn:
-
- li $v0,5211 // #5211 is for SYS_rt_sigreturn
- syscall
-
-.size internal_sigreturn, .-internal_sigreturn
-
-#endif // defined(__linux__) && defined(__mips64)
+++ /dev/null
-//===-- sanitizer_linux_s390.cc -------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements s390-linux-specific functions from
-// sanitizer_libc.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_LINUX && SANITIZER_S390
-
-#include "sanitizer_libc.h"
-#include "sanitizer_linux.h"
-
-#include <errno.h>
-#include <sys/syscall.h>
-#include <sys/utsname.h>
-#include <unistd.h>
-
-namespace __sanitizer {
-
-// --------------- sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
- OFF_T offset) {
- struct s390_mmap_params {
- unsigned long addr;
- unsigned long length;
- unsigned long prot;
- unsigned long flags;
- unsigned long fd;
- unsigned long offset;
- } params = {
- (unsigned long)addr,
- (unsigned long)length,
- (unsigned long)prot,
- (unsigned long)flags,
- (unsigned long)fd,
-# ifdef __s390x__
- (unsigned long)offset,
-# else
- (unsigned long)(offset / 4096),
-# endif
- };
-# ifdef __s390x__
- return syscall(__NR_mmap, ¶ms);
-# else
- return syscall(__NR_mmap2, ¶ms);
-# endif
-}
-
-uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- if (!fn || !child_stack)
- return -EINVAL;
- CHECK_EQ(0, (uptr)child_stack % 16);
- // Minimum frame size.
-#ifdef __s390x__
- child_stack = (char *)child_stack - 160;
-#else
- child_stack = (char *)child_stack - 96;
-#endif
- // Terminate unwind chain.
- ((unsigned long *)child_stack)[0] = 0;
- // And pass parameters.
- ((unsigned long *)child_stack)[1] = (uptr)fn;
- ((unsigned long *)child_stack)[2] = (uptr)arg;
- register long res __asm__("r2");
- register void *__cstack __asm__("r2") = child_stack;
- register int __flags __asm__("r3") = flags;
- register int * __ptidptr __asm__("r4") = parent_tidptr;
- register int * __ctidptr __asm__("r5") = child_tidptr;
- register void * __newtls __asm__("r6") = newtls;
-
- __asm__ __volatile__(
- /* Clone. */
- "svc %1\n"
-
- /* if (%r2 != 0)
- * return;
- */
-#ifdef __s390x__
- "cghi %%r2, 0\n"
-#else
- "chi %%r2, 0\n"
-#endif
- "jne 1f\n"
-
- /* Call "fn(arg)". */
-#ifdef __s390x__
- "lmg %%r1, %%r2, 8(%%r15)\n"
-#else
- "lm %%r1, %%r2, 4(%%r15)\n"
-#endif
- "basr %%r14, %%r1\n"
-
- /* Call _exit(%r2). */
- "svc %2\n"
-
- /* Return to parent. */
- "1:\n"
- : "=r" (res)
- : "i"(__NR_clone), "i"(__NR_exit),
- "r"(__cstack),
- "r"(__flags),
- "r"(__ptidptr),
- "r"(__ctidptr),
- "r"(__newtls)
- : "memory", "cc");
- return res;
-}
-
-#if SANITIZER_S390_64
-static bool FixedCVE_2016_2143() {
- // Try to determine if the running kernel has a fix for CVE-2016-2143,
- // return false if in doubt (better safe than sorry). Distros may want to
- // adjust this for their own kernels.
- struct utsname buf;
- unsigned int major, minor, patch = 0;
- // This should never fail, but just in case...
- if (uname(&buf))
- return false;
- const char *ptr = buf.release;
- major = internal_simple_strtoll(ptr, &ptr, 10);
- // At least first 2 should be matched.
- if (ptr[0] != '.')
- return false;
- minor = internal_simple_strtoll(ptr+1, &ptr, 10);
- // Third is optional.
- if (ptr[0] == '.')
- patch = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (major < 3) {
- if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
- internal_strstr(ptr, ".el6")) {
- // Check RHEL6
- int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r1 >= 657) // 2.6.32-657.el6 or later
- return true;
- if (r1 == 642 && ptr[0] == '.') {
- int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
- return true;
- }
- }
- // <3.0 is bad.
- return false;
- } else if (major == 3) {
- // 3.2.79+ is OK.
- if (minor == 2 && patch >= 79)
- return true;
- // 3.12.58+ is OK.
- if (minor == 12 && patch >= 58)
- return true;
- if (minor == 10 && patch == 0 && ptr[0] == '-' &&
- internal_strstr(ptr, ".el7")) {
- // Check RHEL7
- int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r1 >= 426) // 3.10.0-426.el7 or later
- return true;
- if (r1 == 327 && ptr[0] == '.') {
- int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
- return true;
- }
- }
- // Otherwise, bad.
- return false;
- } else if (major == 4) {
- // 4.1.21+ is OK.
- if (minor == 1 && patch >= 21)
- return true;
- // 4.4.6+ is OK.
- if (minor == 4 && patch >= 6)
- return true;
- if (minor == 4 && patch == 0 && ptr[0] == '-' &&
- internal_strstr(buf.version, "Ubuntu")) {
- // Check Ubuntu 16.04
- int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
- if (r1 >= 13) // 4.4.0-13 or later
- return true;
- }
- // Otherwise, OK if 4.5+.
- return minor >= 5;
- } else {
- // Linux 5 and up are fine.
- return true;
- }
-}
-
-void AvoidCVE_2016_2143() {
- // Older kernels are affected by CVE-2016-2143 - they will crash hard
- // if someone uses 4-level page tables (ie. virtual addresses >= 4TB)
- // and fork() in the same process. Unfortunately, sanitizers tend to
- // require such addresses. Since this is very likely to crash the whole
- // machine (sanitizers themselves use fork() for llvm-symbolizer, for one),
- // abort the process at initialization instead.
- if (FixedCVE_2016_2143())
- return;
- if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
- return;
- Report(
- "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n"
- "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
- "machine, or worse.\n"
- "\n"
- "If you are certain your kernel is not vulnerable (you have compiled it\n"
- "yourself, or are using an unrecognized distribution kernel), you can\n"
- "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
- "with any value.\n");
- Die();
-}
-#endif
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_LINUX && SANITIZER_S390
--- /dev/null
+//===-- sanitizer_linux_s390.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements s390-linux-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_LINUX && SANITIZER_S390
+
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+
+#include <errno.h>
+#include <sys/syscall.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+namespace __sanitizer {
+
+// --------------- sanitizer_libc.h
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
+ struct s390_mmap_params {
+ unsigned long addr;
+ unsigned long length;
+ unsigned long prot;
+ unsigned long flags;
+ unsigned long fd;
+ unsigned long offset;
+ } params = {
+ (unsigned long)addr,
+ (unsigned long)length,
+ (unsigned long)prot,
+ (unsigned long)flags,
+ (unsigned long)fd,
+# ifdef __s390x__
+ (unsigned long)offset,
+# else
+ (unsigned long)(offset / 4096),
+# endif
+ };
+# ifdef __s390x__
+ return syscall(__NR_mmap, ¶ms);
+# else
+ return syscall(__NR_mmap2, ¶ms);
+# endif
+}
+
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
+ int *parent_tidptr, void *newtls, int *child_tidptr) {
+ if (!fn || !child_stack)
+ return -EINVAL;
+ CHECK_EQ(0, (uptr)child_stack % 16);
+ // Minimum frame size.
+#ifdef __s390x__
+ child_stack = (char *)child_stack - 160;
+#else
+ child_stack = (char *)child_stack - 96;
+#endif
+ // Terminate unwind chain.
+ ((unsigned long *)child_stack)[0] = 0;
+ // And pass parameters.
+ ((unsigned long *)child_stack)[1] = (uptr)fn;
+ ((unsigned long *)child_stack)[2] = (uptr)arg;
+ register long res __asm__("r2");
+ register void *__cstack __asm__("r2") = child_stack;
+ register int __flags __asm__("r3") = flags;
+ register int * __ptidptr __asm__("r4") = parent_tidptr;
+ register int * __ctidptr __asm__("r5") = child_tidptr;
+ register void * __newtls __asm__("r6") = newtls;
+
+ __asm__ __volatile__(
+ /* Clone. */
+ "svc %1\n"
+
+ /* if (%r2 != 0)
+ * return;
+ */
+#ifdef __s390x__
+ "cghi %%r2, 0\n"
+#else
+ "chi %%r2, 0\n"
+#endif
+ "jne 1f\n"
+
+ /* Call "fn(arg)". */
+#ifdef __s390x__
+ "lmg %%r1, %%r2, 8(%%r15)\n"
+#else
+ "lm %%r1, %%r2, 4(%%r15)\n"
+#endif
+ "basr %%r14, %%r1\n"
+
+ /* Call _exit(%r2). */
+ "svc %2\n"
+
+ /* Return to parent. */
+ "1:\n"
+ : "=r" (res)
+ : "i"(__NR_clone), "i"(__NR_exit),
+ "r"(__cstack),
+ "r"(__flags),
+ "r"(__ptidptr),
+ "r"(__ctidptr),
+ "r"(__newtls)
+ : "memory", "cc");
+ return res;
+}
+
+#if SANITIZER_S390_64
+static bool FixedCVE_2016_2143() {
+ // Try to determine if the running kernel has a fix for CVE-2016-2143,
+ // return false if in doubt (better safe than sorry). Distros may want to
+ // adjust this for their own kernels.
+ struct utsname buf;
+ unsigned int major, minor, patch = 0;
+ // This should never fail, but just in case...
+ if (uname(&buf))
+ return false;
+ const char *ptr = buf.release;
+ major = internal_simple_strtoll(ptr, &ptr, 10);
+ // At least first 2 should be matched.
+ if (ptr[0] != '.')
+ return false;
+ minor = internal_simple_strtoll(ptr+1, &ptr, 10);
+ // Third is optional.
+ if (ptr[0] == '.')
+ patch = internal_simple_strtoll(ptr+1, &ptr, 10);
+ if (major < 3) {
+ if (major == 2 && minor == 6 && patch == 32 && ptr[0] == '-' &&
+ internal_strstr(ptr, ".el6")) {
+ // Check RHEL6
+ int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
+ if (r1 >= 657) // 2.6.32-657.el6 or later
+ return true;
+ if (r1 == 642 && ptr[0] == '.') {
+ int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
+ if (r2 >= 9) // 2.6.32-642.9.1.el6 or later
+ return true;
+ }
+ }
+ // <3.0 is bad.
+ return false;
+ } else if (major == 3) {
+ // 3.2.79+ is OK.
+ if (minor == 2 && patch >= 79)
+ return true;
+ // 3.12.58+ is OK.
+ if (minor == 12 && patch >= 58)
+ return true;
+ if (minor == 10 && patch == 0 && ptr[0] == '-' &&
+ internal_strstr(ptr, ".el7")) {
+ // Check RHEL7
+ int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
+ if (r1 >= 426) // 3.10.0-426.el7 or later
+ return true;
+ if (r1 == 327 && ptr[0] == '.') {
+ int r2 = internal_simple_strtoll(ptr+1, &ptr, 10);
+ if (r2 >= 27) // 3.10.0-327.27.1.el7 or later
+ return true;
+ }
+ }
+ // Otherwise, bad.
+ return false;
+ } else if (major == 4) {
+ // 4.1.21+ is OK.
+ if (minor == 1 && patch >= 21)
+ return true;
+ // 4.4.6+ is OK.
+ if (minor == 4 && patch >= 6)
+ return true;
+ if (minor == 4 && patch == 0 && ptr[0] == '-' &&
+ internal_strstr(buf.version, "Ubuntu")) {
+ // Check Ubuntu 16.04
+ int r1 = internal_simple_strtoll(ptr+1, &ptr, 10);
+ if (r1 >= 13) // 4.4.0-13 or later
+ return true;
+ }
+ // Otherwise, OK if 4.5+.
+ return minor >= 5;
+ } else {
+ // Linux 5 and up are fine.
+ return true;
+ }
+}
+
+void AvoidCVE_2016_2143() {
+ // Older kernels are affected by CVE-2016-2143 - they will crash hard
+ // if someone uses 4-level page tables (ie. virtual addresses >= 4TB)
+ // and fork() in the same process. Unfortunately, sanitizers tend to
+ // require such addresses. Since this is very likely to crash the whole
+ // machine (sanitizers themselves use fork() for llvm-symbolizer, for one),
+ // abort the process at initialization instead.
+ if (FixedCVE_2016_2143())
+ return;
+ if (GetEnv("SANITIZER_IGNORE_CVE_2016_2143"))
+ return;
+ Report(
+ "ERROR: Your kernel seems to be vulnerable to CVE-2016-2143. Using ASan,\n"
+ "MSan, TSan, DFSan or LSan with such kernel can and will crash your\n"
+ "machine, or worse.\n"
+ "\n"
+ "If you are certain your kernel is not vulnerable (you have compiled it\n"
+ "yourself, or are using an unrecognized distribution kernel), you can\n"
+ "override this safety check by exporting SANITIZER_IGNORE_CVE_2016_2143\n"
+ "with any value.\n");
+ Die();
+}
+#endif
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX && SANITIZER_S390
+++ /dev/null
-// This file is dual licensed under the MIT and the University of Illinois Open
-// Source Licenses. See LICENSE.TXT for details.
-
-// Avoid being marked as needing an executable stack:
-#if defined(__linux__) && defined(__ELF__)
-.section .note.GNU-stack,"",%progbits
-#endif
-
-// Further contents are x86_64-only:
-#if defined(__linux__) && defined(__x86_64__)
-
-#include "../builtins/assembly.h"
-
-// If the "naked" function attribute were supported for x86 we could
-// do this via inline asm.
-.text
-.balign 4
-DEFINE_COMPILERRT_FUNCTION(internal_sigreturn)
- mov $0xf, %eax // 0xf == SYS_rt_sigreturn
- mov %rcx, %r10
- syscall
- ret // Won't normally reach here.
-END_COMPILERRT_FUNCTION(internal_sigreturn)
-
-#endif // defined(__linux__) && defined(__x86_64__)
//===-- sanitizer_list.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
--- /dev/null
+//===-- sanitizer_local_address_space_view.h --------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// `LocalAddressSpaceView` provides the local (i.e. target and current address
+// space are the same) implementation of the `AddressSpaveView` interface which
+// provides a simple interface to load memory from another process (i.e.
+// out-of-process)
+//
+// The `AddressSpaceView` interface requires that the type can be used as a
+// template parameter to objects that wish to be able to operate in an
+// out-of-process manner. In normal usage, objects are in-process and are thus
+// instantiated with the `LocalAddressSpaceView` type. This type is used to
+// load any pointers in instance methods. This implementation is effectively
+// a no-op. When an object is to be used in an out-of-process manner it is
+// instansiated with the `RemoteAddressSpaceView` type.
+//
+// By making `AddressSpaceView` a template parameter of an object, it can
+// change its implementation at compile time which has no run time overhead.
+// This also allows unifying in-process and out-of-process code which avoids
+// code duplication.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_LOCAL_ADDRES_SPACE_VIEW_H
+#define SANITIZER_LOCAL_ADDRES_SPACE_VIEW_H
+
+namespace __sanitizer {
+struct LocalAddressSpaceView {
+ // Load memory `sizeof(T) * num_elements` bytes of memory from the target
+ // process (always local for this implementation) starting at address
+ // `target_address`. The local copy of this memory is returned as a pointer.
+ // The caller should not write to this memory. The behaviour when doing so is
+ // undefined. Callers should use `LoadWritable()` to get access to memory
+ // that is writable.
+ //
+ // The lifetime of loaded memory is implementation defined.
+ template <typename T>
+ static const T *Load(const T *target_address, uptr num_elements = 1) {
+ // The target address space is the local address space so
+ // nothing needs to be copied. Just return the pointer.
+ return target_address;
+ }
+
+ // Load memory `sizeof(T) * num_elements` bytes of memory from the target
+ // process (always local for this implementation) starting at address
+ // `target_address`. The local copy of this memory is returned as a pointer.
+ // The memory returned may be written to.
+ //
+ // Writes made to the returned memory will be visible in the memory returned
+ // by subsequent `Load()` or `LoadWritable()` calls provided the
+ // `target_address` parameter is the same. It is not guaranteed that the
+ // memory returned by previous calls to `Load()` will contain any performed
+ // writes. If two or more overlapping regions of memory are loaded via
+ // separate calls to `LoadWritable()`, it is implementation defined whether
+ // writes made to the region returned by one call are visible in the regions
+ // returned by other calls.
+ //
+ // Given the above it is recommended to load the largest possible object
+ // that requires modification (e.g. a class) rather than individual fields
+ // from a class to avoid issues with overlapping writable regions.
+ //
+ // The lifetime of loaded memory is implementation defined.
+ template <typename T>
+ static T *LoadWritable(T *target_address, uptr num_elements = 1) {
+ // The target address space is the local address space so
+ // nothing needs to be copied. Just return the pointer.
+ return target_address;
+ }
+};
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-//===-- sanitizer_mac.cc --------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries and
-// implements OSX-specific functions.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_MAC
-#include "sanitizer_mac.h"
-
-// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
-// the clients will most certainly use 64-bit ones as well.
-#ifndef _DARWIN_USE_64_BIT_INODE
-#define _DARWIN_USE_64_BIT_INODE 1
-#endif
-#include <stdio.h>
-
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_platform_limits_posix.h"
-#include "sanitizer_procmaps.h"
-
-#if !SANITIZER_IOS
-#include <crt_externs.h> // for _NSGetEnviron
-#else
-extern char **environ;
-#endif
-
-#if defined(__has_include) && __has_include(<os/trace.h>) && defined(__BLOCKS__)
-#define SANITIZER_OS_TRACE 1
-#include <os/trace.h>
-#else
-#define SANITIZER_OS_TRACE 0
-#endif
-
-#if !SANITIZER_IOS
-#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
-#else
-extern "C" {
- extern char ***_NSGetArgv(void);
-}
-#endif
-
-#include <asl.h>
-#include <dlfcn.h> // for dladdr()
-#include <errno.h>
-#include <fcntl.h>
-#include <libkern/OSAtomic.h>
-#include <mach-o/dyld.h>
-#include <mach/mach.h>
-#include <mach/mach_time.h>
-#include <mach/vm_statistics.h>
-#include <malloc/malloc.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <util.h>
-
-// From <crt_externs.h>, but we don't have that file on iOS.
-extern "C" {
- extern char ***_NSGetArgv(void);
- extern char ***_NSGetEnviron(void);
-}
-
-// From <mach/mach_vm.h>, but we don't have that file on iOS.
-extern "C" {
- extern kern_return_t mach_vm_region_recurse(
- vm_map_t target_task,
- mach_vm_address_t *address,
- mach_vm_size_t *size,
- natural_t *nesting_depth,
- vm_region_recurse_info_t info,
- mach_msg_type_number_t *infoCnt);
-}
-
-namespace __sanitizer {
-
-#include "sanitizer_syscall_generic.inc"
-
-// Direct syscalls, don't call libmalloc hooks (but not available on 10.6).
-extern "C" void *__mmap(void *addr, size_t len, int prot, int flags, int fildes,
- off_t off) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
-
-// ---------------------- sanitizer_libc.h
-
-// From <mach/vm_statistics.h>, but not on older OSs.
-#ifndef VM_MEMORY_SANITIZER
-#define VM_MEMORY_SANITIZER 99
-#endif
-
-uptr internal_mmap(void *addr, size_t length, int prot, int flags,
- int fd, u64 offset) {
- if (fd == -1) fd = VM_MAKE_TAG(VM_MEMORY_SANITIZER);
- if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset);
- return (uptr)mmap(addr, length, prot, flags, fd, offset);
-}
-
-uptr internal_munmap(void *addr, uptr length) {
- if (&__munmap) return __munmap(addr, length);
- return munmap(addr, length);
-}
-
-int internal_mprotect(void *addr, uptr length, int prot) {
- return mprotect(addr, length, prot);
-}
-
-uptr internal_close(fd_t fd) {
- return close(fd);
-}
-
-uptr internal_open(const char *filename, int flags) {
- return open(filename, flags);
-}
-
-uptr internal_open(const char *filename, int flags, u32 mode) {
- return open(filename, flags, mode);
-}
-
-uptr internal_read(fd_t fd, void *buf, uptr count) {
- return read(fd, buf, count);
-}
-
-uptr internal_write(fd_t fd, const void *buf, uptr count) {
- return write(fd, buf, count);
-}
-
-uptr internal_stat(const char *path, void *buf) {
- return stat(path, (struct stat *)buf);
-}
-
-uptr internal_lstat(const char *path, void *buf) {
- return lstat(path, (struct stat *)buf);
-}
-
-uptr internal_fstat(fd_t fd, void *buf) {
- return fstat(fd, (struct stat *)buf);
-}
-
-uptr internal_filesize(fd_t fd) {
- struct stat st;
- if (internal_fstat(fd, &st))
- return -1;
- return (uptr)st.st_size;
-}
-
-uptr internal_dup2(int oldfd, int newfd) {
- return dup2(oldfd, newfd);
-}
-
-uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
- return readlink(path, buf, bufsize);
-}
-
-uptr internal_unlink(const char *path) {
- return unlink(path);
-}
-
-uptr internal_sched_yield() {
- return sched_yield();
-}
-
-void internal__exit(int exitcode) {
- _exit(exitcode);
-}
-
-unsigned int internal_sleep(unsigned int seconds) {
- return sleep(seconds);
-}
-
-uptr internal_getpid() {
- return getpid();
-}
-
-int internal_sigaction(int signum, const void *act, void *oldact) {
- return sigaction(signum,
- (const struct sigaction *)act, (struct sigaction *)oldact);
-}
-
-void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
-
-uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
- __sanitizer_sigset_t *oldset) {
- // Don't use sigprocmask here, because it affects all threads.
- return pthread_sigmask(how, set, oldset);
-}
-
-// Doesn't call pthread_atfork() handlers (but not available on 10.6).
-extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE;
-
-int internal_fork() {
- if (&__fork)
- return __fork();
- return fork();
-}
-
-int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
- uptr *oldlenp, const void *newp, uptr newlen) {
- return sysctl(const_cast<int *>(name), namelen, oldp, (size_t *)oldlenp,
- const_cast<void *>(newp), (size_t)newlen);
-}
-
-int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
- const void *newp, uptr newlen) {
- return sysctlbyname(sname, oldp, (size_t *)oldlenp, const_cast<void *>(newp),
- (size_t)newlen);
-}
-
-int internal_forkpty(int *amaster) {
- int master, slave;
- if (openpty(&master, &slave, nullptr, nullptr, nullptr) == -1) return -1;
- int pid = internal_fork();
- if (pid == -1) {
- close(master);
- close(slave);
- return -1;
- }
- if (pid == 0) {
- close(master);
- if (login_tty(slave) != 0) {
- // We already forked, there's not much we can do. Let's quit.
- Report("login_tty failed (errno %d)\n", errno);
- internal__exit(1);
- }
- } else {
- *amaster = master;
- close(slave);
- }
- return pid;
-}
-
-uptr internal_rename(const char *oldpath, const char *newpath) {
- return rename(oldpath, newpath);
-}
-
-uptr internal_ftruncate(fd_t fd, uptr size) {
- return ftruncate(fd, size);
-}
-
-uptr internal_execve(const char *filename, char *const argv[],
- char *const envp[]) {
- return execve(filename, argv, envp);
-}
-
-uptr internal_waitpid(int pid, int *status, int options) {
- return waitpid(pid, status, options);
-}
-
-// ----------------- sanitizer_common.h
-bool FileExists(const char *filename) {
- struct stat st;
- if (stat(filename, &st))
- return false;
- // Sanity check: filename is a regular file.
- return S_ISREG(st.st_mode);
-}
-
-tid_t GetTid() {
- tid_t tid;
- pthread_threadid_np(nullptr, &tid);
- return tid;
-}
-
-void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
- uptr *stack_bottom) {
- CHECK(stack_top);
- CHECK(stack_bottom);
- uptr stacksize = pthread_get_stacksize_np(pthread_self());
- // pthread_get_stacksize_np() returns an incorrect stack size for the main
- // thread on Mavericks. See
- // https://github.com/google/sanitizers/issues/261
- if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
- stacksize == (1 << 19)) {
- struct rlimit rl;
- CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
- // Most often rl.rlim_cur will be the desired 8M.
- if (rl.rlim_cur < kMaxThreadStackSize) {
- stacksize = rl.rlim_cur;
- } else {
- stacksize = kMaxThreadStackSize;
- }
- }
- void *stackaddr = pthread_get_stackaddr_np(pthread_self());
- *stack_top = (uptr)stackaddr;
- *stack_bottom = *stack_top - stacksize;
-}
-
-char **GetEnviron() {
-#if !SANITIZER_IOS
- char ***env_ptr = _NSGetEnviron();
- if (!env_ptr) {
- Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
- "called after libSystem_initializer().\n");
- CHECK(env_ptr);
- }
- char **environ = *env_ptr;
-#endif
- CHECK(environ);
- return environ;
-}
-
-const char *GetEnv(const char *name) {
- char **env = GetEnviron();
- uptr name_len = internal_strlen(name);
- while (*env != 0) {
- uptr len = internal_strlen(*env);
- if (len > name_len) {
- const char *p = *env;
- if (!internal_memcmp(p, name, name_len) &&
- p[name_len] == '=') { // Match.
- return *env + name_len + 1; // String starting after =.
- }
- }
- env++;
- }
- return 0;
-}
-
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
- CHECK_LE(kMaxPathLength, buf_len);
-
- // On OS X the executable path is saved to the stack by dyld. Reading it
- // from there is much faster than calling dladdr, especially for large
- // binaries with symbols.
- InternalScopedString exe_path(kMaxPathLength);
- uint32_t size = exe_path.size();
- if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
- realpath(exe_path.data(), buf) != 0) {
- return internal_strlen(buf);
- }
- return 0;
-}
-
-uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
- return ReadBinaryName(buf, buf_len);
-}
-
-void ReExec() {
- UNIMPLEMENTED();
-}
-
-void CheckASLR() {
- // Do nothing
-}
-
-uptr GetPageSize() {
- return sysconf(_SC_PAGESIZE);
-}
-
-extern "C" unsigned malloc_num_zones;
-extern "C" malloc_zone_t **malloc_zones;
-malloc_zone_t sanitizer_zone;
-
-// We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
-// libmalloc tries to set up a different zone as malloc_zones[0], it will call
-// mprotect(malloc_zones, ..., PROT_READ). This interceptor will catch that and
-// make sure we are still the first (default) zone.
-void MprotectMallocZones(void *addr, int prot) {
- if (addr == malloc_zones && prot == PROT_READ) {
- if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
- for (unsigned i = 1; i < malloc_num_zones; i++) {
- if (malloc_zones[i] == &sanitizer_zone) {
- // Swap malloc_zones[0] and malloc_zones[i].
- malloc_zones[i] = malloc_zones[0];
- malloc_zones[0] = &sanitizer_zone;
- break;
- }
- }
- }
- }
-}
-
-BlockingMutex::BlockingMutex() {
- internal_memset(this, 0, sizeof(*this));
-}
-
-void BlockingMutex::Lock() {
- CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
- CHECK_EQ(OS_SPINLOCK_INIT, 0);
- CHECK_EQ(owner_, 0);
- OSSpinLockLock((OSSpinLock*)&opaque_storage_);
-}
-
-void BlockingMutex::Unlock() {
- OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
-}
-
-void BlockingMutex::CheckLocked() {
- CHECK_NE(*(OSSpinLock*)&opaque_storage_, 0);
-}
-
-u64 NanoTime() {
- timeval tv;
- internal_memset(&tv, 0, sizeof(tv));
- gettimeofday(&tv, 0);
- return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
-}
-
-// This needs to be called during initialization to avoid being racy.
-u64 MonotonicNanoTime() {
- static mach_timebase_info_data_t timebase_info;
- if (timebase_info.denom == 0) mach_timebase_info(&timebase_info);
- return (mach_absolute_time() * timebase_info.numer) / timebase_info.denom;
-}
-
-uptr GetTlsSize() {
- return 0;
-}
-
-void InitTlsSize() {
-}
-
-uptr TlsBaseAddr() {
- uptr segbase = 0;
-#if defined(__x86_64__)
- asm("movq %%gs:0,%0" : "=r"(segbase));
-#elif defined(__i386__)
- asm("movl %%gs:0,%0" : "=r"(segbase));
-#endif
- return segbase;
-}
-
-// The size of the tls on darwin does not appear to be well documented,
-// however the vm memory map suggests that it is 1024 uptrs in size,
-// with a size of 0x2000 bytes on x86_64 and 0x1000 bytes on i386.
-uptr TlsSize() {
-#if defined(__x86_64__) || defined(__i386__)
- return 1024 * sizeof(uptr);
-#else
- return 0;
-#endif
-}
-
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
-#if !SANITIZER_GO
- uptr stack_top, stack_bottom;
- GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
- *tls_addr = TlsBaseAddr();
- *tls_size = TlsSize();
-#else
- *stk_addr = 0;
- *stk_size = 0;
- *tls_addr = 0;
- *tls_size = 0;
-#endif
-}
-
-void ListOfModules::init() {
- clearOrInit();
- MemoryMappingLayout memory_mapping(false);
- memory_mapping.DumpListOfModules(&modules_);
-}
-
-void ListOfModules::fallbackInit() { clear(); }
-
-static HandleSignalMode GetHandleSignalModeImpl(int signum) {
- switch (signum) {
- case SIGABRT:
- return common_flags()->handle_abort;
- case SIGILL:
- return common_flags()->handle_sigill;
- case SIGTRAP:
- return common_flags()->handle_sigtrap;
- case SIGFPE:
- return common_flags()->handle_sigfpe;
- case SIGSEGV:
- return common_flags()->handle_segv;
- case SIGBUS:
- return common_flags()->handle_sigbus;
- }
- return kHandleSignalNo;
-}
-
-HandleSignalMode GetHandleSignalMode(int signum) {
- // Handling fatal signals on watchOS and tvOS devices is disallowed.
- if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM))
- return kHandleSignalNo;
- HandleSignalMode result = GetHandleSignalModeImpl(signum);
- if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler)
- return kHandleSignalExclusive;
- return result;
-}
-
-MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
-
-MacosVersion GetMacosVersionInternal() {
- int mib[2] = { CTL_KERN, KERN_OSRELEASE };
- char version[100];
- uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
- for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
- // Get the version length.
- CHECK_NE(internal_sysctl(mib, 2, 0, &len, 0, 0), -1);
- CHECK_LT(len, maxlen);
- CHECK_NE(internal_sysctl(mib, 2, version, &len, 0, 0), -1);
- switch (version[0]) {
- case '9': return MACOS_VERSION_LEOPARD;
- case '1': {
- switch (version[1]) {
- case '0': return MACOS_VERSION_SNOW_LEOPARD;
- case '1': return MACOS_VERSION_LION;
- case '2': return MACOS_VERSION_MOUNTAIN_LION;
- case '3': return MACOS_VERSION_MAVERICKS;
- case '4': return MACOS_VERSION_YOSEMITE;
- case '5': return MACOS_VERSION_EL_CAPITAN;
- case '6': return MACOS_VERSION_SIERRA;
- case '7': return MACOS_VERSION_HIGH_SIERRA;
- case '8': return MACOS_VERSION_MOJAVE;
- default:
- if (IsDigit(version[1]))
- return MACOS_VERSION_UNKNOWN_NEWER;
- else
- return MACOS_VERSION_UNKNOWN;
- }
- }
- default: return MACOS_VERSION_UNKNOWN;
- }
-}
-
-MacosVersion GetMacosVersion() {
- atomic_uint32_t *cache =
- reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
- MacosVersion result =
- static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
- if (result == MACOS_VERSION_UNINITIALIZED) {
- result = GetMacosVersionInternal();
- atomic_store(cache, result, memory_order_release);
- }
- return result;
-}
-
-bool PlatformHasDifferentMemcpyAndMemmove() {
- // On OS X 10.7 memcpy() and memmove() are both resolved
- // into memmove$VARIANT$sse42.
- // See also https://github.com/google/sanitizers/issues/34.
- // TODO(glider): need to check dynamically that memcpy() and memmove() are
- // actually the same function.
- return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
-}
-
-uptr GetRSS() {
- struct task_basic_info info;
- unsigned count = TASK_BASIC_INFO_COUNT;
- kern_return_t result =
- task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
- if (UNLIKELY(result != KERN_SUCCESS)) {
- Report("Cannot get task info. Error: %d\n", result);
- Die();
- }
- return info.resident_size;
-}
-
-void *internal_start_thread(void(*func)(void *arg), void *arg) {
- // Start the thread with signals blocked, otherwise it can steal user signals.
- __sanitizer_sigset_t set, old;
- internal_sigfillset(&set);
- internal_sigprocmask(SIG_SETMASK, &set, &old);
- pthread_t th;
- pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
- internal_sigprocmask(SIG_SETMASK, &old, 0);
- return th;
-}
-
-void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
-
-#if !SANITIZER_GO
-static BlockingMutex syslog_lock(LINKER_INITIALIZED);
-#endif
-
-void WriteOneLineToSyslog(const char *s) {
-#if !SANITIZER_GO
- syslog_lock.CheckLocked();
- asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
-#endif
-}
-
-void LogMessageOnPrintf(const char *str) {
- // Log all printf output to CrashLog.
- if (common_flags()->abort_on_error)
- CRAppendCrashLogMessage(str);
-}
-
-void LogFullErrorReport(const char *buffer) {
-#if !SANITIZER_GO
- // Log with os_trace. This will make it into the crash log.
-#if SANITIZER_OS_TRACE
- if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) {
- // os_trace requires the message (format parameter) to be a string literal.
- if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
- sizeof("AddressSanitizer") - 1) == 0)
- os_trace("Address Sanitizer reported a failure.");
- else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer",
- sizeof("UndefinedBehaviorSanitizer") - 1) == 0)
- os_trace("Undefined Behavior Sanitizer reported a failure.");
- else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer",
- sizeof("ThreadSanitizer") - 1) == 0)
- os_trace("Thread Sanitizer reported a failure.");
- else
- os_trace("Sanitizer tool reported a failure.");
-
- if (common_flags()->log_to_syslog)
- os_trace("Consult syslog for more information.");
- }
-#endif
-
- // Log to syslog.
- // The logging on OS X may call pthread_create so we need the threading
- // environment to be fully initialized. Also, this should never be called when
- // holding the thread registry lock since that may result in a deadlock. If
- // the reporting thread holds the thread registry mutex, and asl_log waits
- // for GCD to dispatch a new thread, the process will deadlock, because the
- // pthread_create wrapper needs to acquire the lock as well.
- BlockingMutexLock l(&syslog_lock);
- if (common_flags()->log_to_syslog)
- WriteToSyslog(buffer);
-
- // The report is added to CrashLog as part of logging all of Printf output.
-#endif
-}
-
-SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
-#if defined(__x86_64__) || defined(__i386__)
- ucontext_t *ucontext = static_cast<ucontext_t*>(context);
- return ucontext->uc_mcontext->__es.__err & 2 /*T_PF_WRITE*/ ? WRITE : READ;
-#else
- return UNKNOWN;
-#endif
-}
-
-static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
- ucontext_t *ucontext = (ucontext_t*)context;
-# if defined(__aarch64__)
- *pc = ucontext->uc_mcontext->__ss.__pc;
-# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
- *bp = ucontext->uc_mcontext->__ss.__fp;
-# else
- *bp = ucontext->uc_mcontext->__ss.__lr;
-# endif
- *sp = ucontext->uc_mcontext->__ss.__sp;
-# elif defined(__x86_64__)
- *pc = ucontext->uc_mcontext->__ss.__rip;
- *bp = ucontext->uc_mcontext->__ss.__rbp;
- *sp = ucontext->uc_mcontext->__ss.__rsp;
-# elif defined(__arm__)
- *pc = ucontext->uc_mcontext->__ss.__pc;
- *bp = ucontext->uc_mcontext->__ss.__r[7];
- *sp = ucontext->uc_mcontext->__ss.__sp;
-# elif defined(__i386__)
- *pc = ucontext->uc_mcontext->__ss.__eip;
- *bp = ucontext->uc_mcontext->__ss.__ebp;
- *sp = ucontext->uc_mcontext->__ss.__esp;
-# else
-# error "Unknown architecture"
-# endif
-}
-
-void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
-
-#if !SANITIZER_GO
-static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
-LowLevelAllocator allocator_for_env;
-
-// Change the value of the env var |name|, leaking the original value.
-// If |name_value| is NULL, the variable is deleted from the environment,
-// otherwise the corresponding "NAME=value" string is replaced with
-// |name_value|.
-void LeakyResetEnv(const char *name, const char *name_value) {
- char **env = GetEnviron();
- uptr name_len = internal_strlen(name);
- while (*env != 0) {
- uptr len = internal_strlen(*env);
- if (len > name_len) {
- const char *p = *env;
- if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
- // Match.
- if (name_value) {
- // Replace the old value with the new one.
- *env = const_cast<char*>(name_value);
- } else {
- // Shift the subsequent pointers back.
- char **del = env;
- do {
- del[0] = del[1];
- } while (*del++);
- }
- }
- }
- env++;
- }
-}
-
-SANITIZER_WEAK_CXX_DEFAULT_IMPL
-bool ReexecDisabled() {
- return false;
-}
-
-extern "C" SANITIZER_WEAK_ATTRIBUTE double dyldVersionNumber;
-static const double kMinDyldVersionWithAutoInterposition = 360.0;
-
-bool DyldNeedsEnvVariable() {
- // Although sanitizer support was added to LLVM on OS X 10.7+, GCC users
- // still may want use them on older systems. On older Darwin platforms, dyld
- // doesn't export dyldVersionNumber symbol and we simply return true.
- if (!&dyldVersionNumber) return true;
- // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
- // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
- // GetMacosVersion() doesn't work for the simulator. Let's instead check
- // `dyldVersionNumber`, which is exported by dyld, against a known version
- // number from the first OS release where this appeared.
- return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
-}
-
-void MaybeReexec() {
- // FIXME: This should really live in some "InitializePlatform" method.
- MonotonicNanoTime();
-
- if (ReexecDisabled()) return;
-
- // Make sure the dynamic runtime library is preloaded so that the
- // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
- // ourselves.
- Dl_info info;
- RAW_CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info));
- char *dyld_insert_libraries =
- const_cast<char*>(GetEnv(kDyldInsertLibraries));
- uptr old_env_len = dyld_insert_libraries ?
- internal_strlen(dyld_insert_libraries) : 0;
- uptr fname_len = internal_strlen(info.dli_fname);
- const char *dylib_name = StripModuleName(info.dli_fname);
- uptr dylib_name_len = internal_strlen(dylib_name);
-
- bool lib_is_in_env = dyld_insert_libraries &&
- internal_strstr(dyld_insert_libraries, dylib_name);
- if (DyldNeedsEnvVariable() && !lib_is_in_env) {
- // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
- // library.
- InternalScopedString program_name(1024);
- uint32_t buf_size = program_name.size();
- _NSGetExecutablePath(program_name.data(), &buf_size);
- char *new_env = const_cast<char*>(info.dli_fname);
- if (dyld_insert_libraries) {
- // Append the runtime dylib name to the existing value of
- // DYLD_INSERT_LIBRARIES.
- new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
- internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
- new_env[old_env_len] = ':';
- // Copy fname_len and add a trailing zero.
- internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
- fname_len + 1);
- // Ok to use setenv() since the wrappers don't depend on the value of
- // asan_inited.
- setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
- } else {
- // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
- setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
- }
- VReport(1, "exec()-ing the program with\n");
- VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
- VReport(1, "to enable wrappers.\n");
- execv(program_name.data(), *_NSGetArgv());
-
- // We get here only if execv() failed.
- Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
- "which is required for the sanitizer to work. We tried to set the "
- "environment variable and re-execute itself, but execv() failed, "
- "possibly because of sandbox restrictions. Make sure to launch the "
- "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
- RAW_CHECK("execv failed" && 0);
- }
-
- // Verify that interceptors really work. We'll use dlsym to locate
- // "pthread_create", if interceptors are working, it should really point to
- // "wrap_pthread_create" within our own dylib.
- Dl_info info_pthread_create;
- void *dlopen_addr = dlsym(RTLD_DEFAULT, "pthread_create");
- RAW_CHECK(dladdr(dlopen_addr, &info_pthread_create));
- if (internal_strcmp(info.dli_fname, info_pthread_create.dli_fname) != 0) {
- Report(
- "ERROR: Interceptors are not working. This may be because %s is "
- "loaded too late (e.g. via dlopen). Please launch the executable "
- "with:\n%s=%s\n",
- SanitizerToolName, kDyldInsertLibraries, info.dli_fname);
- RAW_CHECK("interceptors not installed" && 0);
- }
-
- if (!lib_is_in_env)
- return;
-
- if (!common_flags()->strip_env)
- return;
-
- // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
- // the dylib from the environment variable, because interceptors are installed
- // and we don't want our children to inherit the variable.
-
- uptr env_name_len = internal_strlen(kDyldInsertLibraries);
- // Allocate memory to hold the previous env var name, its value, the '='
- // sign and the '\0' char.
- char *new_env = (char*)allocator_for_env.Allocate(
- old_env_len + 2 + env_name_len);
- RAW_CHECK(new_env);
- internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
- internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
- new_env[env_name_len] = '=';
- char *new_env_pos = new_env + env_name_len + 1;
-
- // Iterate over colon-separated pieces of |dyld_insert_libraries|.
- char *piece_start = dyld_insert_libraries;
- char *piece_end = NULL;
- char *old_env_end = dyld_insert_libraries + old_env_len;
- do {
- if (piece_start[0] == ':') piece_start++;
- piece_end = internal_strchr(piece_start, ':');
- if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
- if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
- uptr piece_len = piece_end - piece_start;
-
- char *filename_start =
- (char *)internal_memrchr(piece_start, '/', piece_len);
- uptr filename_len = piece_len;
- if (filename_start) {
- filename_start += 1;
- filename_len = piece_len - (filename_start - piece_start);
- } else {
- filename_start = piece_start;
- }
-
- // If the current piece isn't the runtime library name,
- // append it to new_env.
- if ((dylib_name_len != filename_len) ||
- (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
- if (new_env_pos != new_env + env_name_len + 1) {
- new_env_pos[0] = ':';
- new_env_pos++;
- }
- internal_strncpy(new_env_pos, piece_start, piece_len);
- new_env_pos += piece_len;
- }
- // Move on to the next piece.
- piece_start = piece_end;
- } while (piece_start < old_env_end);
-
- // Can't use setenv() here, because it requires the allocator to be
- // initialized.
- // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
- // a separate function called after InitializeAllocator().
- if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
- LeakyResetEnv(kDyldInsertLibraries, new_env);
-}
-#endif // SANITIZER_GO
-
-char **GetArgv() {
- return *_NSGetArgv();
-}
-
-#if defined(__aarch64__) && SANITIZER_IOS && !SANITIZER_IOSSIM
-// The task_vm_info struct is normally provided by the macOS SDK, but we need
-// fields only available in 10.12+. Declare the struct manually to be able to
-// build against older SDKs.
-struct __sanitizer_task_vm_info {
- mach_vm_size_t virtual_size;
- integer_t region_count;
- integer_t page_size;
- mach_vm_size_t resident_size;
- mach_vm_size_t resident_size_peak;
- mach_vm_size_t device;
- mach_vm_size_t device_peak;
- mach_vm_size_t internal;
- mach_vm_size_t internal_peak;
- mach_vm_size_t external;
- mach_vm_size_t external_peak;
- mach_vm_size_t reusable;
- mach_vm_size_t reusable_peak;
- mach_vm_size_t purgeable_volatile_pmap;
- mach_vm_size_t purgeable_volatile_resident;
- mach_vm_size_t purgeable_volatile_virtual;
- mach_vm_size_t compressed;
- mach_vm_size_t compressed_peak;
- mach_vm_size_t compressed_lifetime;
- mach_vm_size_t phys_footprint;
- mach_vm_address_t min_address;
- mach_vm_address_t max_address;
-};
-#define __SANITIZER_TASK_VM_INFO_COUNT ((mach_msg_type_number_t) \
- (sizeof(__sanitizer_task_vm_info) / sizeof(natural_t)))
-
-uptr GetTaskInfoMaxAddress() {
- __sanitizer_task_vm_info vm_info = {} /* zero initialize */;
- mach_msg_type_number_t count = __SANITIZER_TASK_VM_INFO_COUNT;
- int err = task_info(mach_task_self(), TASK_VM_INFO, (int *)&vm_info, &count);
- if (err == 0 && vm_info.max_address != 0) {
- return vm_info.max_address - 1;
- } else {
- // xnu cannot provide vm address limit
- return 0x200000000 - 1;
- }
-}
-#endif
-
-uptr GetMaxUserVirtualAddress() {
-#if SANITIZER_WORDSIZE == 64
-# if defined(__aarch64__) && SANITIZER_IOS && !SANITIZER_IOSSIM
- // Get the maximum VM address
- static uptr max_vm = GetTaskInfoMaxAddress();
- CHECK(max_vm);
- return max_vm;
-# else
- return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
-# endif
-#else // SANITIZER_WORDSIZE == 32
- return (1ULL << 32) - 1; // 0xffffffff;
-#endif // SANITIZER_WORDSIZE
-}
-
-uptr GetMaxVirtualAddress() {
- return GetMaxUserVirtualAddress();
-}
-
-uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
- uptr *largest_gap_found,
- uptr *max_occupied_addr) {
- typedef vm_region_submap_short_info_data_64_t RegionInfo;
- enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
- // Start searching for available memory region past PAGEZERO, which is
- // 4KB on 32-bit and 4GB on 64-bit.
- mach_vm_address_t start_address =
- (SANITIZER_WORDSIZE == 32) ? 0x000000001000 : 0x000100000000;
-
- mach_vm_address_t address = start_address;
- mach_vm_address_t free_begin = start_address;
- kern_return_t kr = KERN_SUCCESS;
- if (largest_gap_found) *largest_gap_found = 0;
- if (max_occupied_addr) *max_occupied_addr = 0;
- while (kr == KERN_SUCCESS) {
- mach_vm_size_t vmsize = 0;
- natural_t depth = 0;
- RegionInfo vminfo;
- mach_msg_type_number_t count = kRegionInfoSize;
- kr = mach_vm_region_recurse(mach_task_self(), &address, &vmsize, &depth,
- (vm_region_info_t)&vminfo, &count);
- if (kr == KERN_INVALID_ADDRESS) {
- // No more regions beyond "address", consider the gap at the end of VM.
- address = GetMaxVirtualAddress() + 1;
- vmsize = 0;
- } else {
- if (max_occupied_addr) *max_occupied_addr = address + vmsize;
- }
- if (free_begin != address) {
- // We found a free region [free_begin..address-1].
- uptr gap_start = RoundUpTo((uptr)free_begin + left_padding, alignment);
- uptr gap_end = RoundDownTo((uptr)address, alignment);
- uptr gap_size = gap_end > gap_start ? gap_end - gap_start : 0;
- if (size < gap_size) {
- return gap_start;
- }
-
- if (largest_gap_found && *largest_gap_found < gap_size) {
- *largest_gap_found = gap_size;
- }
- }
- // Move to the next region.
- address += vmsize;
- free_begin = address;
- }
-
- // We looked at all free regions and could not find one large enough.
- return 0;
-}
-
-// FIXME implement on this platform.
-void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { }
-
-void SignalContext::DumpAllRegisters(void *context) {
- Report("Register values:\n");
-
- ucontext_t *ucontext = (ucontext_t*)context;
-# define DUMPREG64(r) \
- Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r);
-# define DUMPREG32(r) \
- Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r);
-# define DUMPREG_(r) Printf(" "); DUMPREG(r);
-# define DUMPREG__(r) Printf(" "); DUMPREG(r);
-# define DUMPREG___(r) Printf(" "); DUMPREG(r);
-
-# if defined(__x86_64__)
-# define DUMPREG(r) DUMPREG64(r)
- DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n");
- DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n");
- DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n");
- DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n");
-# elif defined(__i386__)
-# define DUMPREG(r) DUMPREG32(r)
- DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n");
- DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n");
-# elif defined(__aarch64__)
-# define DUMPREG(r) DUMPREG64(r)
- DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n");
- DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n");
- DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n");
- DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n");
- DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n");
- DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n");
- DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n");
- DUMPREG(x[28]); DUMPREG___(fp); DUMPREG___(lr); DUMPREG___(sp); Printf("\n");
-# elif defined(__arm__)
-# define DUMPREG(r) DUMPREG32(r)
- DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n");
- DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n");
- DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n");
- DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n");
-# else
-# error "Unknown architecture"
-# endif
-
-# undef DUMPREG64
-# undef DUMPREG32
-# undef DUMPREG_
-# undef DUMPREG__
-# undef DUMPREG___
-# undef DUMPREG
-}
-
-static inline bool CompareBaseAddress(const LoadedModule &a,
- const LoadedModule &b) {
- return a.base_address() < b.base_address();
-}
-
-void FormatUUID(char *out, uptr size, const u8 *uuid) {
- internal_snprintf(out, size,
- "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-"
- "%02X%02X%02X%02X%02X%02X>",
- uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
- uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
- uuid[12], uuid[13], uuid[14], uuid[15]);
-}
-
-void PrintModuleMap() {
- Printf("Process module map:\n");
- MemoryMappingLayout memory_mapping(false);
- InternalMmapVector<LoadedModule> modules;
- modules.reserve(128);
- memory_mapping.DumpListOfModules(&modules);
- Sort(modules.data(), modules.size(), CompareBaseAddress);
- for (uptr i = 0; i < modules.size(); ++i) {
- char uuid_str[128];
- FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
- Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
- modules[i].max_executable_address(), modules[i].full_name(),
- ModuleArchToString(modules[i].arch()), uuid_str);
- }
- Printf("End of module map.\n");
-}
-
-void CheckNoDeepBind(const char *filename, int flag) {
- // Do nothing.
-}
-
-bool GetRandom(void *buffer, uptr length, bool blocking) {
- if (!buffer || !length || length > 256)
- return false;
- // arc4random never fails.
- arc4random_buf(buffer, length);
- return true;
-}
-
-u32 GetNumberOfCPUs() {
- return (u32)sysconf(_SC_NPROCESSORS_ONLN);
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- sanitizer_mac.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements OSX-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_mac.h"
+
+// Use 64-bit inodes in file operations. ASan does not support OS X 10.5, so
+// the clients will most certainly use 64-bit ones as well.
+#ifndef _DARWIN_USE_64_BIT_INODE
+#define _DARWIN_USE_64_BIT_INODE 1
+#endif
+#include <stdio.h>
+
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_procmaps.h"
+
+#if !SANITIZER_IOS
+#include <crt_externs.h> // for _NSGetEnviron
+#else
+extern char **environ;
+#endif
+
+#if defined(__has_include) && __has_include(<os/trace.h>)
+#define SANITIZER_OS_TRACE 1
+#include <os/trace.h>
+#else
+#define SANITIZER_OS_TRACE 0
+#endif
+
+#if !SANITIZER_IOS
+#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
+#else
+extern "C" {
+ extern char ***_NSGetArgv(void);
+}
+#endif
+
+#include <asl.h>
+#include <dlfcn.h> // for dladdr()
+#include <errno.h>
+#include <fcntl.h>
+#include <libkern/OSAtomic.h>
+#include <mach-o/dyld.h>
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <mach/vm_statistics.h>
+#include <malloc/malloc.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+// From <crt_externs.h>, but we don't have that file on iOS.
+extern "C" {
+ extern char ***_NSGetArgv(void);
+ extern char ***_NSGetEnviron(void);
+}
+
+// From <mach/mach_vm.h>, but we don't have that file on iOS.
+extern "C" {
+ extern kern_return_t mach_vm_region_recurse(
+ vm_map_t target_task,
+ mach_vm_address_t *address,
+ mach_vm_size_t *size,
+ natural_t *nesting_depth,
+ vm_region_recurse_info_t info,
+ mach_msg_type_number_t *infoCnt);
+}
+
+namespace __sanitizer {
+
+#include "sanitizer_syscall_generic.inc"
+
+// Direct syscalls, don't call libmalloc hooks (but not available on 10.6).
+extern "C" void *__mmap(void *addr, size_t len, int prot, int flags, int fildes,
+ off_t off) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __munmap(void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+
+// ---------------------- sanitizer_libc.h
+
+// From <mach/vm_statistics.h>, but not on older OSs.
+#ifndef VM_MEMORY_SANITIZER
+#define VM_MEMORY_SANITIZER 99
+#endif
+
+// XNU on Darwin provides a mmap flag that optimizes allocation/deallocation of
+// giant memory regions (i.e. shadow memory regions).
+#define kXnuFastMmapFd 0x4
+static size_t kXnuFastMmapThreshold = 2 << 30; // 2 GB
+static bool use_xnu_fast_mmap = false;
+
+uptr internal_mmap(void *addr, size_t length, int prot, int flags,
+ int fd, u64 offset) {
+ if (fd == -1) {
+ fd = VM_MAKE_TAG(VM_MEMORY_SANITIZER);
+ if (length >= kXnuFastMmapThreshold) {
+ if (use_xnu_fast_mmap) fd |= kXnuFastMmapFd;
+ }
+ }
+ if (&__mmap) return (uptr)__mmap(addr, length, prot, flags, fd, offset);
+ return (uptr)mmap(addr, length, prot, flags, fd, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+ if (&__munmap) return __munmap(addr, length);
+ return munmap(addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return mprotect(addr, length, prot);
+}
+
+uptr internal_close(fd_t fd) {
+ return close(fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+ return open(filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+ return open(filename, flags, mode);
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+ return read(fd, buf, count);
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+ return write(fd, buf, count);
+}
+
+uptr internal_stat(const char *path, void *buf) {
+ return stat(path, (struct stat *)buf);
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+ return lstat(path, (struct stat *)buf);
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+ return fstat(fd, (struct stat *)buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+ struct stat st;
+ if (internal_fstat(fd, &st))
+ return -1;
+ return (uptr)st.st_size;
+}
+
+uptr internal_dup(int oldfd) {
+ return dup(oldfd);
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+ return dup2(oldfd, newfd);
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+ return readlink(path, buf, bufsize);
+}
+
+uptr internal_unlink(const char *path) {
+ return unlink(path);
+}
+
+uptr internal_sched_yield() {
+ return sched_yield();
+}
+
+void internal__exit(int exitcode) {
+ _exit(exitcode);
+}
+
+unsigned int internal_sleep(unsigned int seconds) {
+ return sleep(seconds);
+}
+
+uptr internal_getpid() {
+ return getpid();
+}
+
+int internal_sigaction(int signum, const void *act, void *oldact) {
+ return sigaction(signum,
+ (const struct sigaction *)act, (struct sigaction *)oldact);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); }
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ // Don't use sigprocmask here, because it affects all threads.
+ return pthread_sigmask(how, set, oldset);
+}
+
+// Doesn't call pthread_atfork() handlers (but not available on 10.6).
+extern "C" pid_t __fork(void) SANITIZER_WEAK_ATTRIBUTE;
+
+int internal_fork() {
+ if (&__fork)
+ return __fork();
+ return fork();
+}
+
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+ return sysctl(const_cast<int *>(name), namelen, oldp, (size_t *)oldlenp,
+ const_cast<void *>(newp), (size_t)newlen);
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ return sysctlbyname(sname, oldp, (size_t *)oldlenp, const_cast<void *>(newp),
+ (size_t)newlen);
+}
+
+int internal_forkpty(int *aparent) {
+ int parent, worker;
+ if (openpty(&parent, &worker, nullptr, nullptr, nullptr) == -1) return -1;
+ int pid = internal_fork();
+ if (pid == -1) {
+ close(parent);
+ close(worker);
+ return -1;
+ }
+ if (pid == 0) {
+ close(parent);
+ if (login_tty(worker) != 0) {
+ // We already forked, there's not much we can do. Let's quit.
+ Report("login_tty failed (errno %d)\n", errno);
+ internal__exit(1);
+ }
+ } else {
+ *aparent = parent;
+ close(worker);
+ }
+ return pid;
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+ return rename(oldpath, newpath);
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ return ftruncate(fd, size);
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+ char *const envp[]) {
+ return execve(filename, argv, envp);
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+ return waitpid(pid, status, options);
+}
+
+// ----------------- sanitizer_common.h
+bool FileExists(const char *filename) {
+ if (ShouldMockFailureToOpen(filename))
+ return false;
+ struct stat st;
+ if (stat(filename, &st))
+ return false;
+ // Sanity check: filename is a regular file.
+ return S_ISREG(st.st_mode);
+}
+
+tid_t GetTid() {
+ tid_t tid;
+ pthread_threadid_np(nullptr, &tid);
+ return tid;
+}
+
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+ uptr *stack_bottom) {
+ CHECK(stack_top);
+ CHECK(stack_bottom);
+ uptr stacksize = pthread_get_stacksize_np(pthread_self());
+ // pthread_get_stacksize_np() returns an incorrect stack size for the main
+ // thread on Mavericks. See
+ // https://github.com/google/sanitizers/issues/261
+ if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
+ stacksize == (1 << 19)) {
+ struct rlimit rl;
+ CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
+ // Most often rl.rlim_cur will be the desired 8M.
+ if (rl.rlim_cur < kMaxThreadStackSize) {
+ stacksize = rl.rlim_cur;
+ } else {
+ stacksize = kMaxThreadStackSize;
+ }
+ }
+ void *stackaddr = pthread_get_stackaddr_np(pthread_self());
+ *stack_top = (uptr)stackaddr;
+ *stack_bottom = *stack_top - stacksize;
+}
+
+char **GetEnviron() {
+#if !SANITIZER_IOS
+ char ***env_ptr = _NSGetEnviron();
+ if (!env_ptr) {
+ Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is "
+ "called after libSystem_initializer().\n");
+ CHECK(env_ptr);
+ }
+ char **environ = *env_ptr;
+#endif
+ CHECK(environ);
+ return environ;
+}
+
+const char *GetEnv(const char *name) {
+ char **env = GetEnviron();
+ uptr name_len = internal_strlen(name);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
+ if (len > name_len) {
+ const char *p = *env;
+ if (!internal_memcmp(p, name, name_len) &&
+ p[name_len] == '=') { // Match.
+ return *env + name_len + 1; // String starting after =.
+ }
+ }
+ env++;
+ }
+ return 0;
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ CHECK_LE(kMaxPathLength, buf_len);
+
+ // On OS X the executable path is saved to the stack by dyld. Reading it
+ // from there is much faster than calling dladdr, especially for large
+ // binaries with symbols.
+ InternalScopedString exe_path(kMaxPathLength);
+ uint32_t size = exe_path.size();
+ if (_NSGetExecutablePath(exe_path.data(), &size) == 0 &&
+ realpath(exe_path.data(), buf) != 0) {
+ return internal_strlen(buf);
+ }
+ return 0;
+}
+
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
+}
+
+void ReExec() {
+ UNIMPLEMENTED();
+}
+
+void CheckASLR() {
+ // Do nothing
+}
+
+void CheckMPROTECT() {
+ // Do nothing
+}
+
+uptr GetPageSize() {
+ return sysconf(_SC_PAGESIZE);
+}
+
+extern "C" unsigned malloc_num_zones;
+extern "C" malloc_zone_t **malloc_zones;
+malloc_zone_t sanitizer_zone;
+
+// We need to make sure that sanitizer_zone is registered as malloc_zones[0]. If
+// libmalloc tries to set up a different zone as malloc_zones[0], it will call
+// mprotect(malloc_zones, ..., PROT_READ). This interceptor will catch that and
+// make sure we are still the first (default) zone.
+void MprotectMallocZones(void *addr, int prot) {
+ if (addr == malloc_zones && prot == PROT_READ) {
+ if (malloc_num_zones > 1 && malloc_zones[0] != &sanitizer_zone) {
+ for (unsigned i = 1; i < malloc_num_zones; i++) {
+ if (malloc_zones[i] == &sanitizer_zone) {
+ // Swap malloc_zones[0] and malloc_zones[i].
+ malloc_zones[i] = malloc_zones[0];
+ malloc_zones[0] = &sanitizer_zone;
+ break;
+ }
+ }
+ }
+ }
+}
+
+BlockingMutex::BlockingMutex() {
+ internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+ CHECK(sizeof(OSSpinLock) <= sizeof(opaque_storage_));
+ CHECK_EQ(OS_SPINLOCK_INIT, 0);
+ CHECK_EQ(owner_, 0);
+ OSSpinLockLock((OSSpinLock*)&opaque_storage_);
+}
+
+void BlockingMutex::Unlock() {
+ OSSpinLockUnlock((OSSpinLock*)&opaque_storage_);
+}
+
+void BlockingMutex::CheckLocked() {
+ CHECK_NE(*(OSSpinLock*)&opaque_storage_, 0);
+}
+
+u64 NanoTime() {
+ timeval tv;
+ internal_memset(&tv, 0, sizeof(tv));
+ gettimeofday(&tv, 0);
+ return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000;
+}
+
+// This needs to be called during initialization to avoid being racy.
+u64 MonotonicNanoTime() {
+ static mach_timebase_info_data_t timebase_info;
+ if (timebase_info.denom == 0) mach_timebase_info(&timebase_info);
+ return (mach_absolute_time() * timebase_info.numer) / timebase_info.denom;
+}
+
+uptr GetTlsSize() {
+ return 0;
+}
+
+void InitTlsSize() {
+}
+
+uptr TlsBaseAddr() {
+ uptr segbase = 0;
+#if defined(__x86_64__)
+ asm("movq %%gs:0,%0" : "=r"(segbase));
+#elif defined(__i386__)
+ asm("movl %%gs:0,%0" : "=r"(segbase));
+#endif
+ return segbase;
+}
+
+// The size of the tls on darwin does not appear to be well documented,
+// however the vm memory map suggests that it is 1024 uptrs in size,
+// with a size of 0x2000 bytes on x86_64 and 0x1000 bytes on i386.
+uptr TlsSize() {
+#if defined(__x86_64__) || defined(__i386__)
+ return 1024 * sizeof(uptr);
+#else
+ return 0;
+#endif
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+#if !SANITIZER_GO
+ uptr stack_top, stack_bottom;
+ GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+ *stk_addr = stack_bottom;
+ *stk_size = stack_top - stack_bottom;
+ *tls_addr = TlsBaseAddr();
+ *tls_size = TlsSize();
+#else
+ *stk_addr = 0;
+ *stk_size = 0;
+ *tls_addr = 0;
+ *tls_size = 0;
+#endif
+}
+
+void ListOfModules::init() {
+ clearOrInit();
+ MemoryMappingLayout memory_mapping(false);
+ memory_mapping.DumpListOfModules(&modules_);
+}
+
+void ListOfModules::fallbackInit() { clear(); }
+
+static HandleSignalMode GetHandleSignalModeImpl(int signum) {
+ switch (signum) {
+ case SIGABRT:
+ return common_flags()->handle_abort;
+ case SIGILL:
+ return common_flags()->handle_sigill;
+ case SIGTRAP:
+ return common_flags()->handle_sigtrap;
+ case SIGFPE:
+ return common_flags()->handle_sigfpe;
+ case SIGSEGV:
+ return common_flags()->handle_segv;
+ case SIGBUS:
+ return common_flags()->handle_sigbus;
+ }
+ return kHandleSignalNo;
+}
+
+HandleSignalMode GetHandleSignalMode(int signum) {
+ // Handling fatal signals on watchOS and tvOS devices is disallowed.
+ if ((SANITIZER_WATCHOS || SANITIZER_TVOS) && !(SANITIZER_IOSSIM))
+ return kHandleSignalNo;
+ HandleSignalMode result = GetHandleSignalModeImpl(signum);
+ if (result == kHandleSignalYes && !common_flags()->allow_user_segv_handler)
+ return kHandleSignalExclusive;
+ return result;
+}
+
+MacosVersion cached_macos_version = MACOS_VERSION_UNINITIALIZED;
+
+MacosVersion GetMacosVersionInternal() {
+ int mib[2] = { CTL_KERN, KERN_OSRELEASE };
+ char version[100];
+ uptr len = 0, maxlen = sizeof(version) / sizeof(version[0]);
+ for (uptr i = 0; i < maxlen; i++) version[i] = '\0';
+ // Get the version length.
+ CHECK_NE(internal_sysctl(mib, 2, 0, &len, 0, 0), -1);
+ CHECK_LT(len, maxlen);
+ CHECK_NE(internal_sysctl(mib, 2, version, &len, 0, 0), -1);
+
+ // Expect <major>.<minor>(.<patch>)
+ CHECK_GE(len, 3);
+ const char *p = version;
+ int major = internal_simple_strtoll(p, &p, /*base=*/10);
+ if (*p != '.') return MACOS_VERSION_UNKNOWN;
+ p += 1;
+ int minor = internal_simple_strtoll(p, &p, /*base=*/10);
+ if (*p != '.') return MACOS_VERSION_UNKNOWN;
+
+ switch (major) {
+ case 9: return MACOS_VERSION_LEOPARD;
+ case 10: return MACOS_VERSION_SNOW_LEOPARD;
+ case 11: return MACOS_VERSION_LION;
+ case 12: return MACOS_VERSION_MOUNTAIN_LION;
+ case 13: return MACOS_VERSION_MAVERICKS;
+ case 14: return MACOS_VERSION_YOSEMITE;
+ case 15: return MACOS_VERSION_EL_CAPITAN;
+ case 16: return MACOS_VERSION_SIERRA;
+ case 17:
+ // Not a typo, 17.5 Darwin Kernel Version maps to High Sierra 10.13.4.
+ if (minor >= 5)
+ return MACOS_VERSION_HIGH_SIERRA_DOT_RELEASE_4;
+ return MACOS_VERSION_HIGH_SIERRA;
+ case 18: return MACOS_VERSION_MOJAVE;
+ case 19: return MACOS_VERSION_CATALINA;
+ default:
+ if (major < 9) return MACOS_VERSION_UNKNOWN;
+ return MACOS_VERSION_UNKNOWN_NEWER;
+ }
+}
+
+MacosVersion GetMacosVersion() {
+ atomic_uint32_t *cache =
+ reinterpret_cast<atomic_uint32_t*>(&cached_macos_version);
+ MacosVersion result =
+ static_cast<MacosVersion>(atomic_load(cache, memory_order_acquire));
+ if (result == MACOS_VERSION_UNINITIALIZED) {
+ result = GetMacosVersionInternal();
+ atomic_store(cache, result, memory_order_release);
+ }
+ return result;
+}
+
+bool PlatformHasDifferentMemcpyAndMemmove() {
+ // On OS X 10.7 memcpy() and memmove() are both resolved
+ // into memmove$VARIANT$sse42.
+ // See also https://github.com/google/sanitizers/issues/34.
+ // TODO(glider): need to check dynamically that memcpy() and memmove() are
+ // actually the same function.
+ return GetMacosVersion() == MACOS_VERSION_SNOW_LEOPARD;
+}
+
+uptr GetRSS() {
+ struct task_basic_info info;
+ unsigned count = TASK_BASIC_INFO_COUNT;
+ kern_return_t result =
+ task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count);
+ if (UNLIKELY(result != KERN_SUCCESS)) {
+ Report("Cannot get task info. Error: %d\n", result);
+ Die();
+ }
+ return info.resident_size;
+}
+
+void *internal_start_thread(void(*func)(void *arg), void *arg) {
+ // Start the thread with signals blocked, otherwise it can steal user signals.
+ __sanitizer_sigset_t set, old;
+ internal_sigfillset(&set);
+ internal_sigprocmask(SIG_SETMASK, &set, &old);
+ pthread_t th;
+ pthread_create(&th, 0, (void*(*)(void *arg))func, arg);
+ internal_sigprocmask(SIG_SETMASK, &old, 0);
+ return th;
+}
+
+void internal_join_thread(void *th) { pthread_join((pthread_t)th, 0); }
+
+#if !SANITIZER_GO
+static BlockingMutex syslog_lock(LINKER_INITIALIZED);
+#endif
+
+void WriteOneLineToSyslog(const char *s) {
+#if !SANITIZER_GO
+ syslog_lock.CheckLocked();
+ asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s);
+#endif
+}
+
+void LogMessageOnPrintf(const char *str) {
+ // Log all printf output to CrashLog.
+ if (common_flags()->abort_on_error)
+ CRAppendCrashLogMessage(str);
+}
+
+void LogFullErrorReport(const char *buffer) {
+#if !SANITIZER_GO
+ // Log with os_trace. This will make it into the crash log.
+#if SANITIZER_OS_TRACE
+ if (GetMacosVersion() >= MACOS_VERSION_YOSEMITE) {
+ // os_trace requires the message (format parameter) to be a string literal.
+ if (internal_strncmp(SanitizerToolName, "AddressSanitizer",
+ sizeof("AddressSanitizer") - 1) == 0)
+ os_trace("Address Sanitizer reported a failure.");
+ else if (internal_strncmp(SanitizerToolName, "UndefinedBehaviorSanitizer",
+ sizeof("UndefinedBehaviorSanitizer") - 1) == 0)
+ os_trace("Undefined Behavior Sanitizer reported a failure.");
+ else if (internal_strncmp(SanitizerToolName, "ThreadSanitizer",
+ sizeof("ThreadSanitizer") - 1) == 0)
+ os_trace("Thread Sanitizer reported a failure.");
+ else
+ os_trace("Sanitizer tool reported a failure.");
+
+ if (common_flags()->log_to_syslog)
+ os_trace("Consult syslog for more information.");
+ }
+#endif
+
+ // Log to syslog.
+ // The logging on OS X may call pthread_create so we need the threading
+ // environment to be fully initialized. Also, this should never be called when
+ // holding the thread registry lock since that may result in a deadlock. If
+ // the reporting thread holds the thread registry mutex, and asl_log waits
+ // for GCD to dispatch a new thread, the process will deadlock, because the
+ // pthread_create wrapper needs to acquire the lock as well.
+ BlockingMutexLock l(&syslog_lock);
+ if (common_flags()->log_to_syslog)
+ WriteToSyslog(buffer);
+
+ // The report is added to CrashLog as part of logging all of Printf output.
+#endif
+}
+
+SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
+#if defined(__x86_64__) || defined(__i386__)
+ ucontext_t *ucontext = static_cast<ucontext_t*>(context);
+ return ucontext->uc_mcontext->__es.__err & 2 /*T_PF_WRITE*/ ? WRITE : READ;
+#else
+ return UNKNOWN;
+#endif
+}
+
+static void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) {
+ ucontext_t *ucontext = (ucontext_t*)context;
+# if defined(__aarch64__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
+ *bp = ucontext->uc_mcontext->__ss.__fp;
+# else
+ *bp = ucontext->uc_mcontext->__ss.__lr;
+# endif
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__x86_64__)
+ *pc = ucontext->uc_mcontext->__ss.__rip;
+ *bp = ucontext->uc_mcontext->__ss.__rbp;
+ *sp = ucontext->uc_mcontext->__ss.__rsp;
+# elif defined(__arm__)
+ *pc = ucontext->uc_mcontext->__ss.__pc;
+ *bp = ucontext->uc_mcontext->__ss.__r[7];
+ *sp = ucontext->uc_mcontext->__ss.__sp;
+# elif defined(__i386__)
+ *pc = ucontext->uc_mcontext->__ss.__eip;
+ *bp = ucontext->uc_mcontext->__ss.__ebp;
+ *sp = ucontext->uc_mcontext->__ss.__esp;
+# else
+# error "Unknown architecture"
+# endif
+}
+
+void SignalContext::InitPcSpBp() { GetPcSpBp(context, &pc, &sp, &bp); }
+
+void InitializePlatformEarly() {
+ // Only use xnu_fast_mmap when on x86_64 and the OS supports it.
+ use_xnu_fast_mmap =
+#if defined(__x86_64__)
+ GetMacosVersion() >= MACOS_VERSION_HIGH_SIERRA_DOT_RELEASE_4;
+#else
+ false;
+#endif
+}
+
+#if !SANITIZER_GO
+static const char kDyldInsertLibraries[] = "DYLD_INSERT_LIBRARIES";
+LowLevelAllocator allocator_for_env;
+
+// Change the value of the env var |name|, leaking the original value.
+// If |name_value| is NULL, the variable is deleted from the environment,
+// otherwise the corresponding "NAME=value" string is replaced with
+// |name_value|.
+void LeakyResetEnv(const char *name, const char *name_value) {
+ char **env = GetEnviron();
+ uptr name_len = internal_strlen(name);
+ while (*env != 0) {
+ uptr len = internal_strlen(*env);
+ if (len > name_len) {
+ const char *p = *env;
+ if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') {
+ // Match.
+ if (name_value) {
+ // Replace the old value with the new one.
+ *env = const_cast<char*>(name_value);
+ } else {
+ // Shift the subsequent pointers back.
+ char **del = env;
+ do {
+ del[0] = del[1];
+ } while (*del++);
+ }
+ }
+ }
+ env++;
+ }
+}
+
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+bool ReexecDisabled() {
+ return false;
+}
+
+extern "C" SANITIZER_WEAK_ATTRIBUTE double dyldVersionNumber;
+static const double kMinDyldVersionWithAutoInterposition = 360.0;
+
+bool DyldNeedsEnvVariable() {
+ // Although sanitizer support was added to LLVM on OS X 10.7+, GCC users
+ // still may want use them on older systems. On older Darwin platforms, dyld
+ // doesn't export dyldVersionNumber symbol and we simply return true.
+ if (!&dyldVersionNumber) return true;
+ // If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
+ // DYLD_INSERT_LIBRARIES is not set. However, checking OS version via
+ // GetMacosVersion() doesn't work for the simulator. Let's instead check
+ // `dyldVersionNumber`, which is exported by dyld, against a known version
+ // number from the first OS release where this appeared.
+ return dyldVersionNumber < kMinDyldVersionWithAutoInterposition;
+}
+
+void MaybeReexec() {
+ // FIXME: This should really live in some "InitializePlatform" method.
+ MonotonicNanoTime();
+
+ if (ReexecDisabled()) return;
+
+ // Make sure the dynamic runtime library is preloaded so that the
+ // wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
+ // ourselves.
+ Dl_info info;
+ RAW_CHECK(dladdr((void*)((uptr)&__sanitizer_report_error_summary), &info));
+ char *dyld_insert_libraries =
+ const_cast<char*>(GetEnv(kDyldInsertLibraries));
+ uptr old_env_len = dyld_insert_libraries ?
+ internal_strlen(dyld_insert_libraries) : 0;
+ uptr fname_len = internal_strlen(info.dli_fname);
+ const char *dylib_name = StripModuleName(info.dli_fname);
+ uptr dylib_name_len = internal_strlen(dylib_name);
+
+ bool lib_is_in_env = dyld_insert_libraries &&
+ internal_strstr(dyld_insert_libraries, dylib_name);
+ if (DyldNeedsEnvVariable() && !lib_is_in_env) {
+ // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime
+ // library.
+ InternalScopedString program_name(1024);
+ uint32_t buf_size = program_name.size();
+ _NSGetExecutablePath(program_name.data(), &buf_size);
+ char *new_env = const_cast<char*>(info.dli_fname);
+ if (dyld_insert_libraries) {
+ // Append the runtime dylib name to the existing value of
+ // DYLD_INSERT_LIBRARIES.
+ new_env = (char*)allocator_for_env.Allocate(old_env_len + fname_len + 2);
+ internal_strncpy(new_env, dyld_insert_libraries, old_env_len);
+ new_env[old_env_len] = ':';
+ // Copy fname_len and add a trailing zero.
+ internal_strncpy(new_env + old_env_len + 1, info.dli_fname,
+ fname_len + 1);
+ // Ok to use setenv() since the wrappers don't depend on the value of
+ // asan_inited.
+ setenv(kDyldInsertLibraries, new_env, /*overwrite*/1);
+ } else {
+ // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name.
+ setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0);
+ }
+ VReport(1, "exec()-ing the program with\n");
+ VReport(1, "%s=%s\n", kDyldInsertLibraries, new_env);
+ VReport(1, "to enable wrappers.\n");
+ execv(program_name.data(), *_NSGetArgv());
+
+ // We get here only if execv() failed.
+ Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
+ "which is required for the sanitizer to work. We tried to set the "
+ "environment variable and re-execute itself, but execv() failed, "
+ "possibly because of sandbox restrictions. Make sure to launch the "
+ "executable with:\n%s=%s\n", kDyldInsertLibraries, new_env);
+ RAW_CHECK("execv failed" && 0);
+ }
+
+ // Verify that interceptors really work. We'll use dlsym to locate
+ // "pthread_create", if interceptors are working, it should really point to
+ // "wrap_pthread_create" within our own dylib.
+ Dl_info info_pthread_create;
+ void *dlopen_addr = dlsym(RTLD_DEFAULT, "pthread_create");
+ RAW_CHECK(dladdr(dlopen_addr, &info_pthread_create));
+ if (internal_strcmp(info.dli_fname, info_pthread_create.dli_fname) != 0) {
+ Report(
+ "ERROR: Interceptors are not working. This may be because %s is "
+ "loaded too late (e.g. via dlopen). Please launch the executable "
+ "with:\n%s=%s\n",
+ SanitizerToolName, kDyldInsertLibraries, info.dli_fname);
+ RAW_CHECK("interceptors not installed" && 0);
+ }
+
+ if (!lib_is_in_env)
+ return;
+
+ if (!common_flags()->strip_env)
+ return;
+
+ // DYLD_INSERT_LIBRARIES is set and contains the runtime library. Let's remove
+ // the dylib from the environment variable, because interceptors are installed
+ // and we don't want our children to inherit the variable.
+
+ uptr env_name_len = internal_strlen(kDyldInsertLibraries);
+ // Allocate memory to hold the previous env var name, its value, the '='
+ // sign and the '\0' char.
+ char *new_env = (char*)allocator_for_env.Allocate(
+ old_env_len + 2 + env_name_len);
+ RAW_CHECK(new_env);
+ internal_memset(new_env, '\0', old_env_len + 2 + env_name_len);
+ internal_strncpy(new_env, kDyldInsertLibraries, env_name_len);
+ new_env[env_name_len] = '=';
+ char *new_env_pos = new_env + env_name_len + 1;
+
+ // Iterate over colon-separated pieces of |dyld_insert_libraries|.
+ char *piece_start = dyld_insert_libraries;
+ char *piece_end = NULL;
+ char *old_env_end = dyld_insert_libraries + old_env_len;
+ do {
+ if (piece_start[0] == ':') piece_start++;
+ piece_end = internal_strchr(piece_start, ':');
+ if (!piece_end) piece_end = dyld_insert_libraries + old_env_len;
+ if ((uptr)(piece_start - dyld_insert_libraries) > old_env_len) break;
+ uptr piece_len = piece_end - piece_start;
+
+ char *filename_start =
+ (char *)internal_memrchr(piece_start, '/', piece_len);
+ uptr filename_len = piece_len;
+ if (filename_start) {
+ filename_start += 1;
+ filename_len = piece_len - (filename_start - piece_start);
+ } else {
+ filename_start = piece_start;
+ }
+
+ // If the current piece isn't the runtime library name,
+ // append it to new_env.
+ if ((dylib_name_len != filename_len) ||
+ (internal_memcmp(filename_start, dylib_name, dylib_name_len) != 0)) {
+ if (new_env_pos != new_env + env_name_len + 1) {
+ new_env_pos[0] = ':';
+ new_env_pos++;
+ }
+ internal_strncpy(new_env_pos, piece_start, piece_len);
+ new_env_pos += piece_len;
+ }
+ // Move on to the next piece.
+ piece_start = piece_end;
+ } while (piece_start < old_env_end);
+
+ // Can't use setenv() here, because it requires the allocator to be
+ // initialized.
+ // FIXME: instead of filtering DYLD_INSERT_LIBRARIES here, do it in
+ // a separate function called after InitializeAllocator().
+ if (new_env_pos == new_env + env_name_len + 1) new_env = NULL;
+ LeakyResetEnv(kDyldInsertLibraries, new_env);
+}
+#endif // SANITIZER_GO
+
+char **GetArgv() {
+ return *_NSGetArgv();
+}
+
+#if SANITIZER_IOS
+// The task_vm_info struct is normally provided by the macOS SDK, but we need
+// fields only available in 10.12+. Declare the struct manually to be able to
+// build against older SDKs.
+struct __sanitizer_task_vm_info {
+ mach_vm_size_t virtual_size;
+ integer_t region_count;
+ integer_t page_size;
+ mach_vm_size_t resident_size;
+ mach_vm_size_t resident_size_peak;
+ mach_vm_size_t device;
+ mach_vm_size_t device_peak;
+ mach_vm_size_t internal;
+ mach_vm_size_t internal_peak;
+ mach_vm_size_t external;
+ mach_vm_size_t external_peak;
+ mach_vm_size_t reusable;
+ mach_vm_size_t reusable_peak;
+ mach_vm_size_t purgeable_volatile_pmap;
+ mach_vm_size_t purgeable_volatile_resident;
+ mach_vm_size_t purgeable_volatile_virtual;
+ mach_vm_size_t compressed;
+ mach_vm_size_t compressed_peak;
+ mach_vm_size_t compressed_lifetime;
+ mach_vm_size_t phys_footprint;
+ mach_vm_address_t min_address;
+ mach_vm_address_t max_address;
+};
+#define __SANITIZER_TASK_VM_INFO_COUNT ((mach_msg_type_number_t) \
+ (sizeof(__sanitizer_task_vm_info) / sizeof(natural_t)))
+
+static uptr GetTaskInfoMaxAddress() {
+ __sanitizer_task_vm_info vm_info = {} /* zero initialize */;
+ mach_msg_type_number_t count = __SANITIZER_TASK_VM_INFO_COUNT;
+ int err = task_info(mach_task_self(), TASK_VM_INFO, (int *)&vm_info, &count);
+ return err ? 0 : vm_info.max_address;
+}
+
+uptr GetMaxUserVirtualAddress() {
+ static uptr max_vm = GetTaskInfoMaxAddress();
+ if (max_vm != 0)
+ return max_vm - 1;
+
+ // xnu cannot provide vm address limit
+# if SANITIZER_WORDSIZE == 32
+ return 0xffe00000 - 1;
+# else
+ return 0x200000000 - 1;
+# endif
+}
+
+#else // !SANITIZER_IOS
+
+uptr GetMaxUserVirtualAddress() {
+# if SANITIZER_WORDSIZE == 64
+ return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
+# else // SANITIZER_WORDSIZE == 32
+ static_assert(SANITIZER_WORDSIZE == 32, "Wrong wordsize");
+ return (1ULL << 32) - 1; // 0xffffffff;
+# endif
+}
+#endif
+
+uptr GetMaxVirtualAddress() {
+ return GetMaxUserVirtualAddress();
+}
+
+uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
+ uptr *largest_gap_found,
+ uptr *max_occupied_addr) {
+ typedef vm_region_submap_short_info_data_64_t RegionInfo;
+ enum { kRegionInfoSize = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64 };
+ // Start searching for available memory region past PAGEZERO, which is
+ // 4KB on 32-bit and 4GB on 64-bit.
+ mach_vm_address_t start_address =
+ (SANITIZER_WORDSIZE == 32) ? 0x000000001000 : 0x000100000000;
+
+ mach_vm_address_t address = start_address;
+ mach_vm_address_t free_begin = start_address;
+ kern_return_t kr = KERN_SUCCESS;
+ if (largest_gap_found) *largest_gap_found = 0;
+ if (max_occupied_addr) *max_occupied_addr = 0;
+ while (kr == KERN_SUCCESS) {
+ mach_vm_size_t vmsize = 0;
+ natural_t depth = 0;
+ RegionInfo vminfo;
+ mach_msg_type_number_t count = kRegionInfoSize;
+ kr = mach_vm_region_recurse(mach_task_self(), &address, &vmsize, &depth,
+ (vm_region_info_t)&vminfo, &count);
+ if (kr == KERN_INVALID_ADDRESS) {
+ // No more regions beyond "address", consider the gap at the end of VM.
+ address = GetMaxVirtualAddress() + 1;
+ vmsize = 0;
+ } else {
+ if (max_occupied_addr) *max_occupied_addr = address + vmsize;
+ }
+ if (free_begin != address) {
+ // We found a free region [free_begin..address-1].
+ uptr gap_start = RoundUpTo((uptr)free_begin + left_padding, alignment);
+ uptr gap_end = RoundDownTo((uptr)address, alignment);
+ uptr gap_size = gap_end > gap_start ? gap_end - gap_start : 0;
+ if (size < gap_size) {
+ return gap_start;
+ }
+
+ if (largest_gap_found && *largest_gap_found < gap_size) {
+ *largest_gap_found = gap_size;
+ }
+ }
+ // Move to the next region.
+ address += vmsize;
+ free_begin = address;
+ }
+
+ // We looked at all free regions and could not find one large enough.
+ return 0;
+}
+
+// FIXME implement on this platform.
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { }
+
+void SignalContext::DumpAllRegisters(void *context) {
+ Report("Register values:\n");
+
+ ucontext_t *ucontext = (ucontext_t*)context;
+# define DUMPREG64(r) \
+ Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r);
+# define DUMPREG32(r) \
+ Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r);
+# define DUMPREG_(r) Printf(" "); DUMPREG(r);
+# define DUMPREG__(r) Printf(" "); DUMPREG(r);
+# define DUMPREG___(r) Printf(" "); DUMPREG(r);
+
+# if defined(__x86_64__)
+# define DUMPREG(r) DUMPREG64(r)
+ DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n");
+ DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n");
+ DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n");
+ DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n");
+# elif defined(__i386__)
+# define DUMPREG(r) DUMPREG32(r)
+ DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n");
+ DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n");
+# elif defined(__aarch64__)
+# define DUMPREG(r) DUMPREG64(r)
+ DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n");
+ DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n");
+ DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n");
+ DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n");
+ DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n");
+ DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n");
+ DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n");
+ DUMPREG(x[28]); DUMPREG___(fp); DUMPREG___(lr); DUMPREG___(sp); Printf("\n");
+# elif defined(__arm__)
+# define DUMPREG(r) DUMPREG32(r)
+ DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n");
+ DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n");
+ DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n");
+ DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n");
+# else
+# error "Unknown architecture"
+# endif
+
+# undef DUMPREG64
+# undef DUMPREG32
+# undef DUMPREG_
+# undef DUMPREG__
+# undef DUMPREG___
+# undef DUMPREG
+}
+
+static inline bool CompareBaseAddress(const LoadedModule &a,
+ const LoadedModule &b) {
+ return a.base_address() < b.base_address();
+}
+
+void FormatUUID(char *out, uptr size, const u8 *uuid) {
+ internal_snprintf(out, size,
+ "<%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-"
+ "%02X%02X%02X%02X%02X%02X>",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5],
+ uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15]);
+}
+
+void PrintModuleMap() {
+ Printf("Process module map:\n");
+ MemoryMappingLayout memory_mapping(false);
+ InternalMmapVector<LoadedModule> modules;
+ modules.reserve(128);
+ memory_mapping.DumpListOfModules(&modules);
+ Sort(modules.data(), modules.size(), CompareBaseAddress);
+ for (uptr i = 0; i < modules.size(); ++i) {
+ char uuid_str[128];
+ FormatUUID(uuid_str, sizeof(uuid_str), modules[i].uuid());
+ Printf("0x%zx-0x%zx %s (%s) %s\n", modules[i].base_address(),
+ modules[i].max_executable_address(), modules[i].full_name(),
+ ModuleArchToString(modules[i].arch()), uuid_str);
+ }
+ Printf("End of module map.\n");
+}
+
+void CheckNoDeepBind(const char *filename, int flag) {
+ // Do nothing.
+}
+
+bool GetRandom(void *buffer, uptr length, bool blocking) {
+ if (!buffer || !length || length > 256)
+ return false;
+ // arc4random never fails.
+ arc4random_buf(buffer, length);
+ return true;
+}
+
+u32 GetNumberOfCPUs() {
+ return (u32)sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
//===-- sanitizer_mac.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
MACOS_VERSION_EL_CAPITAN,
MACOS_VERSION_SIERRA,
MACOS_VERSION_HIGH_SIERRA,
+ MACOS_VERSION_HIGH_SIERRA_DOT_RELEASE_4,
MACOS_VERSION_MOJAVE,
+ MACOS_VERSION_CATALINA,
MACOS_VERSION_UNKNOWN_NEWER
};
+++ /dev/null
-//===-- sanitizer_mac_libcdep.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries and
-// implements OSX-specific functions.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_MAC
-#include "sanitizer_mac.h"
-
-#include <sys/mman.h>
-
-namespace __sanitizer {
-
-void RestrictMemoryToMaxAddress(uptr max_address) {
- uptr size_to_mmap = GetMaxUserVirtualAddress() + 1 - max_address;
- void *res = MmapFixedNoAccess(max_address, size_to_mmap, "high gap");
- CHECK(res != MAP_FAILED);
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- sanitizer_mac_libcdep.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements OSX-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_mac.h"
+
+#include <sys/mman.h>
+
+namespace __sanitizer {
+
+void RestrictMemoryToMaxAddress(uptr max_address) {
+ uptr size_to_mmap = GetMaxUserVirtualAddress() + 1 - max_address;
+ void *res = MmapFixedNoAccess(max_address, size_to_mmap, "high gap");
+ CHECK(res != MAP_FAILED);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
//===-- sanitizer_malloc_mac.inc --------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// https://github.com/gperftools/gperftools.
namespace __sanitizer {
+
extern malloc_zone_t sanitizer_zone;
+
+struct sanitizer_malloc_introspection_t : public malloc_introspection_t {
+ // IMPORTANT: Do not change the order, alignment, or types of these fields to
+ // maintain binary compatibility. You should only add fields to this struct.
+
+ // Used to track changes to the allocator that will affect
+ // zone enumeration.
+ u64 allocator_enumeration_version;
+ uptr allocator_ptr;
+ uptr allocator_size;
+};
+
+u64 GetMallocZoneAllocatorEnumerationVersion() {
+ // This represents the current allocator ABI version.
+ // This field should be incremented every time the Allocator
+ // ABI changes in a way that breaks allocator enumeration.
+ return 0;
}
+} // namespace __sanitizer
+
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
vm_size_t start_size, unsigned zone_flags) {
COMMON_MALLOC_ENTER();
return &sanitizer_zone;
}
+INTERCEPTOR(malloc_zone_t *, malloc_zone_from_ptr, const void *ptr) {
+ COMMON_MALLOC_ENTER();
+ size_t size = sanitizer_zone.size(&sanitizer_zone, ptr);
+ if (size) { // Claimed by sanitizer zone?
+ return &sanitizer_zone;
+ }
+ return REAL(malloc_zone_from_ptr)(ptr);
+}
+
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
// FIXME: ASan should support purgeable allocations.
// https://github.com/google/sanitizers/issues/139
}
#define GET_ZONE_FOR_PTR(ptr) \
- malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
+ malloc_zone_t *zone_ptr = WRAP(malloc_zone_from_ptr)(ptr); \
const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
extern "C"
return p;
}
+// This public API exists purely for testing purposes.
+extern "C"
+SANITIZER_INTERFACE_ATTRIBUTE
+malloc_zone_t* __sanitizer_mz_default_zone() {
+ return &sanitizer_zone;
+}
+
// This function is currently unused, and we build with -Werror.
#if 0
void __sanitizer_mz_free_definite_size(
}
#endif
-kern_return_t mi_enumerator(task_t task, void *,
- unsigned type_mask, vm_address_t zone_address,
- memory_reader_t reader,
+#ifndef COMMON_MALLOC_HAS_ZONE_ENUMERATOR
+#error "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be defined"
+#endif
+static_assert((COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 0 ||
+ (COMMON_MALLOC_HAS_ZONE_ENUMERATOR) == 1,
+ "COMMON_MALLOC_HAS_ZONE_ENUMERATOR must be 0 or 1");
+
+#if COMMON_MALLOC_HAS_ZONE_ENUMERATOR
+// Forward declare and expect the implementation to provided by
+// includer.
+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);
+#else
+// Provide stub implementation that fails.
+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.
+ // Not supported.
return KERN_FAILURE;
}
+#endif
+
+#ifndef COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
+#error "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be defined"
+#endif
+static_assert((COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 0 ||
+ (COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT) == 1,
+ "COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT must be 0 or 1");
+#if COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT
+// Forward declare and expect the implementation to provided by
+// includer.
+void mi_extra_init(
+ sanitizer_malloc_introspection_t *mi);
+#else
+void mi_extra_init(
+ sanitizer_malloc_introspection_t *mi) {
+ // Just zero initialize the fields.
+ mi->allocator_ptr = 0;
+ mi->allocator_size = 0;
+}
+#endif
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.
namespace COMMON_MALLOC_NAMESPACE {
-void ReplaceSystemMalloc() {
- static malloc_introspection_t sanitizer_zone_introspection;
+void InitMallocZoneFields() {
+ static sanitizer_malloc_introspection_t sanitizer_zone_introspection;
// Ok to use internal_memset, these places are not performance-critical.
internal_memset(&sanitizer_zone_introspection, 0,
sizeof(sanitizer_zone_introspection));
sanitizer_zone_introspection.statistics = &mi_statistics;
sanitizer_zone_introspection.zone_locked = &mi_zone_locked;
+ // Set current allocator enumeration version.
+ sanitizer_zone_introspection.allocator_enumeration_version =
+ GetMallocZoneAllocatorEnumerationVersion();
+
+ // Perform any sanitizer specific initialization.
+ mi_extra_init(&sanitizer_zone_introspection);
+
internal_memset(&sanitizer_zone, 0, sizeof(malloc_zone_t));
// Use version 6 for OSX >= 10.6.
sanitizer_zone.free_definite_size = 0;
sanitizer_zone.memalign = &__sanitizer_mz_memalign;
sanitizer_zone.introspect = &sanitizer_zone_introspection;
+}
+
+void ReplaceSystemMalloc() {
+ InitMallocZoneFields();
// Register the zone.
malloc_zone_register(&sanitizer_zone);
//===-- sanitizer_mutex.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_netbsd.cc -----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between Sanitizer run-time libraries and implements
-// NetBSD-specific functions from sanitizer_libc.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_NETBSD
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_getauxval.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-
-#include <sys/param.h>
-#include <sys/types.h>
-
-#include <sys/exec.h>
-#include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/sysctl.h>
-#include <sys/time.h>
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <link.h>
-#include <lwp.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <ucontext.h>
-#include <unistd.h>
-
-extern "C" void *__mmap(void *, size_t, int, int, int, int,
- off_t) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int __sysctl(const int *, unsigned int, void *, size_t *,
- const void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys_close(int) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys_open(const char *, int, ...) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" ssize_t _sys_read(int, void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" ssize_t _sys_write(int, const void *,
- size_t) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int __ftruncate(int, int, off_t) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" ssize_t _sys_readlink(const char *, char *,
- size_t) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys_sched_yield() SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys___nanosleep50(const void *,
- void *) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys_execve(const char *, char *const[],
- char *const[]) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" off_t __lseek(int, int, off_t, int) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int __fork() SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys___sigprocmask14(int, const void *,
- void *) SANITIZER_WEAK_ATTRIBUTE;
-extern "C" int _sys___wait450(int wpid, int *, int,
- void *) SANITIZER_WEAK_ATTRIBUTE;
-
-namespace __sanitizer {
-
-static void *GetRealLibcAddress(const char *symbol) {
- void *real = dlsym(RTLD_NEXT, symbol);
- if (!real)
- real = dlsym(RTLD_DEFAULT, symbol);
- if (!real) {
- Printf("GetRealLibcAddress failed for symbol=%s", symbol);
- Die();
- }
- return real;
-}
-
-#define _REAL(func, ...) real##_##func(__VA_ARGS__)
-#define DEFINE__REAL(ret_type, func, ...) \
- static ret_type (*real_##func)(__VA_ARGS__) = NULL; \
- if (!real_##func) { \
- real_##func = (ret_type(*)(__VA_ARGS__))GetRealLibcAddress(#func); \
- } \
- CHECK(real_##func);
-
-// --------------- sanitizer_libc.h
-uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
- OFF_T offset) {
- CHECK(&__mmap);
- return (uptr)__mmap(addr, length, prot, flags, fd, 0, offset);
-}
-
-uptr internal_munmap(void *addr, uptr length) {
- DEFINE__REAL(int, munmap, void *a, uptr b);
- return _REAL(munmap, addr, length);
-}
-
-int internal_mprotect(void *addr, uptr length, int prot) {
- DEFINE__REAL(int, mprotect, void *a, uptr b, int c);
- return _REAL(mprotect, addr, length, prot);
-}
-
-uptr internal_close(fd_t fd) {
- CHECK(&_sys_close);
- return _sys_close(fd);
-}
-
-uptr internal_open(const char *filename, int flags) {
- CHECK(&_sys_open);
- return _sys_open(filename, flags);
-}
-
-uptr internal_open(const char *filename, int flags, u32 mode) {
- CHECK(&_sys_open);
- return _sys_open(filename, flags, mode);
-}
-
-uptr internal_read(fd_t fd, void *buf, uptr count) {
- sptr res;
- CHECK(&_sys_read);
- HANDLE_EINTR(res, (sptr)_sys_read(fd, buf, (size_t)count));
- return res;
-}
-
-uptr internal_write(fd_t fd, const void *buf, uptr count) {
- sptr res;
- CHECK(&_sys_write);
- HANDLE_EINTR(res, (sptr)_sys_write(fd, buf, count));
- return res;
-}
-
-uptr internal_ftruncate(fd_t fd, uptr size) {
- sptr res;
- CHECK(&__ftruncate);
- HANDLE_EINTR(res, __ftruncate(fd, 0, (s64)size));
- return res;
-}
-
-uptr internal_stat(const char *path, void *buf) {
- DEFINE__REAL(int, __stat50, const char *a, void *b);
- return _REAL(__stat50, path, buf);
-}
-
-uptr internal_lstat(const char *path, void *buf) {
- DEFINE__REAL(int, __lstat50, const char *a, void *b);
- return _REAL(__lstat50, path, buf);
-}
-
-uptr internal_fstat(fd_t fd, void *buf) {
- DEFINE__REAL(int, __fstat50, int a, void *b);
- return _REAL(__fstat50, fd, buf);
-}
-
-uptr internal_filesize(fd_t fd) {
- struct stat st;
- if (internal_fstat(fd, &st))
- return -1;
- return (uptr)st.st_size;
-}
-
-uptr internal_dup2(int oldfd, int newfd) {
- DEFINE__REAL(int, dup2, int a, int b);
- return _REAL(dup2, oldfd, newfd);
-}
-
-uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
- CHECK(&_sys_readlink);
- return (uptr)_sys_readlink(path, buf, bufsize);
-}
-
-uptr internal_unlink(const char *path) {
- DEFINE__REAL(int, unlink, const char *a);
- return _REAL(unlink, path);
-}
-
-uptr internal_rename(const char *oldpath, const char *newpath) {
- DEFINE__REAL(int, rename, const char *a, const char *b);
- return _REAL(rename, oldpath, newpath);
-}
-
-uptr internal_sched_yield() {
- CHECK(&_sys_sched_yield);
- return _sys_sched_yield();
-}
-
-void internal__exit(int exitcode) {
- DEFINE__REAL(void, _exit, int a);
- _REAL(_exit, exitcode);
- Die(); // Unreachable.
-}
-
-unsigned int internal_sleep(unsigned int seconds) {
- struct timespec ts;
- ts.tv_sec = 1;
- ts.tv_nsec = 0;
- CHECK(&_sys___nanosleep50);
- int res = _sys___nanosleep50(&ts, &ts);
- if (res)
- return ts.tv_sec;
- return 0;
-}
-
-uptr internal_execve(const char *filename, char *const argv[],
- char *const envp[]) {
- CHECK(&_sys_execve);
- return _sys_execve(filename, argv, envp);
-}
-
-tid_t GetTid() {
- DEFINE__REAL(int, _lwp_self);
- return _REAL(_lwp_self);
-}
-
-int TgKill(pid_t pid, tid_t tid, int sig) {
- DEFINE__REAL(int, _lwp_kill, int a, int b);
- (void)pid;
- return _REAL(_lwp_kill, tid, sig);
-}
-
-u64 NanoTime() {
- timeval tv;
- DEFINE__REAL(int, __gettimeofday50, void *a, void *b);
- internal_memset(&tv, 0, sizeof(tv));
- _REAL(__gettimeofday50, &tv, 0);
- return (u64)tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000;
-}
-
-uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
- DEFINE__REAL(int, __clock_gettime50, __sanitizer_clockid_t a, void *b);
- return _REAL(__clock_gettime50, clk_id, tp);
-}
-
-uptr internal_ptrace(int request, int pid, void *addr, void *data) {
- Printf("internal_ptrace not implemented for NetBSD");
- Die();
- return 0;
-}
-
-uptr internal_waitpid(int pid, int *status, int options) {
- CHECK(&_sys___wait450);
- return _sys___wait450(pid, status, options, 0 /* rusage */);
-}
-
-uptr internal_getpid() {
- DEFINE__REAL(int, getpid);
- return _REAL(getpid);
-}
-
-uptr internal_getppid() {
- DEFINE__REAL(int, getppid);
- return _REAL(getppid);
-}
-
-uptr internal_getdents(fd_t fd, void *dirp, unsigned int count) {
- DEFINE__REAL(int, __getdents30, int a, void *b, size_t c);
- return _REAL(__getdents30, fd, dirp, count);
-}
-
-uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
- CHECK(&__lseek);
- return __lseek(fd, 0, offset, whence);
-}
-
-uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
- Printf("internal_prctl not implemented for NetBSD");
- Die();
- return 0;
-}
-
-uptr internal_sigaltstack(const void *ss, void *oss) {
- DEFINE__REAL(int, __sigaltstack14, const void *a, void *b);
- return _REAL(__sigaltstack14, ss, oss);
-}
-
-int internal_fork() {
- CHECK(&__fork);
- return __fork();
-}
-
-int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
- uptr *oldlenp, const void *newp, uptr newlen) {
- CHECK(&__sysctl);
- return __sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
-}
-
-int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
- const void *newp, uptr newlen) {
- DEFINE__REAL(int, sysctlbyname, const char *a, void *b, size_t *c,
- const void *d, size_t e);
- return _REAL(sysctlbyname, sname, oldp, (size_t *)oldlenp, newp,
- (size_t)newlen);
-}
-
-uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
- __sanitizer_sigset_t *oldset) {
- CHECK(&_sys___sigprocmask14);
- return _sys___sigprocmask14(how, set, oldset);
-}
-
-void internal_sigfillset(__sanitizer_sigset_t *set) {
- DEFINE__REAL(int, __sigfillset14, const void *a);
- (void)_REAL(__sigfillset14, set);
-}
-
-void internal_sigemptyset(__sanitizer_sigset_t *set) {
- DEFINE__REAL(int, __sigemptyset14, const void *a);
- (void)_REAL(__sigemptyset14, set);
-}
-
-uptr intrnal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg,
- int *parent_tidptr, void *newtls, int *child_tidptr) {
- Printf("internal_clone not implemented for NetBSD");
- Die();
- return 0;
-}
-
-} // namespace __sanitizer
-
-#endif
--- /dev/null
+//===-- sanitizer_netbsd.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between Sanitizer run-time libraries and implements
+// NetBSD-specific functions from sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_NETBSD
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_getauxval.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/exec.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <link.h>
+#include <lwp.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+extern "C" void *__mmap(void *, size_t, int, int, int, int,
+ off_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __sysctl(const int *, unsigned int, void *, size_t *,
+ const void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_close(int) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_open(const char *, int, ...) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_read(int, void *, size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_write(int, const void *,
+ size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __ftruncate(int, int, off_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" ssize_t _sys_readlink(const char *, char *,
+ size_t) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_sched_yield() SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___nanosleep50(const void *,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys_execve(const char *, char *const[],
+ char *const[]) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" off_t __lseek(int, int, off_t, int) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int __fork() SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___sigprocmask14(int, const void *,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+extern "C" int _sys___wait450(int wpid, int *, int,
+ void *) SANITIZER_WEAK_ATTRIBUTE;
+
+namespace __sanitizer {
+
+static void *GetRealLibcAddress(const char *symbol) {
+ void *real = dlsym(RTLD_NEXT, symbol);
+ if (!real)
+ real = dlsym(RTLD_DEFAULT, symbol);
+ if (!real) {
+ Printf("GetRealLibcAddress failed for symbol=%s", symbol);
+ Die();
+ }
+ return real;
+}
+
+#define _REAL(func, ...) real##_##func(__VA_ARGS__)
+#define DEFINE__REAL(ret_type, func, ...) \
+ static ret_type (*real_##func)(__VA_ARGS__) = NULL; \
+ if (!real_##func) { \
+ real_##func = (ret_type(*)(__VA_ARGS__))GetRealLibcAddress(#func); \
+ } \
+ CHECK(real_##func);
+
+// --------------- sanitizer_libc.h
+uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd,
+ OFF_T offset) {
+ CHECK(&__mmap);
+ return (uptr)__mmap(addr, length, prot, flags, fd, 0, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) {
+ DEFINE__REAL(int, munmap, void *a, uptr b);
+ return _REAL(munmap, addr, length);
+}
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ DEFINE__REAL(int, mprotect, void *a, uptr b, int c);
+ return _REAL(mprotect, addr, length, prot);
+}
+
+uptr internal_close(fd_t fd) {
+ CHECK(&_sys_close);
+ return _sys_close(fd);
+}
+
+uptr internal_open(const char *filename, int flags) {
+ CHECK(&_sys_open);
+ return _sys_open(filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+ CHECK(&_sys_open);
+ return _sys_open(filename, flags, mode);
+}
+
+uptr internal_read(fd_t fd, void *buf, uptr count) {
+ sptr res;
+ CHECK(&_sys_read);
+ HANDLE_EINTR(res, (sptr)_sys_read(fd, buf, (size_t)count));
+ return res;
+}
+
+uptr internal_write(fd_t fd, const void *buf, uptr count) {
+ sptr res;
+ CHECK(&_sys_write);
+ HANDLE_EINTR(res, (sptr)_sys_write(fd, buf, count));
+ return res;
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ sptr res;
+ CHECK(&__ftruncate);
+ HANDLE_EINTR(res, __ftruncate(fd, 0, (s64)size));
+ return res;
+}
+
+uptr internal_stat(const char *path, void *buf) {
+ DEFINE__REAL(int, __stat50, const char *a, void *b);
+ return _REAL(__stat50, path, buf);
+}
+
+uptr internal_lstat(const char *path, void *buf) {
+ DEFINE__REAL(int, __lstat50, const char *a, void *b);
+ return _REAL(__lstat50, path, buf);
+}
+
+uptr internal_fstat(fd_t fd, void *buf) {
+ DEFINE__REAL(int, __fstat50, int a, void *b);
+ return _REAL(__fstat50, fd, buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+ struct stat st;
+ if (internal_fstat(fd, &st))
+ return -1;
+ return (uptr)st.st_size;
+}
+
+uptr internal_dup(int oldfd) {
+ DEFINE__REAL(int, dup, int a);
+ return _REAL(dup, oldfd);
+}
+
+uptr internal_dup2(int oldfd, int newfd) {
+ DEFINE__REAL(int, dup2, int a, int b);
+ return _REAL(dup2, oldfd, newfd);
+}
+
+uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
+ CHECK(&_sys_readlink);
+ return (uptr)_sys_readlink(path, buf, bufsize);
+}
+
+uptr internal_unlink(const char *path) {
+ DEFINE__REAL(int, unlink, const char *a);
+ return _REAL(unlink, path);
+}
+
+uptr internal_rename(const char *oldpath, const char *newpath) {
+ DEFINE__REAL(int, rename, const char *a, const char *b);
+ return _REAL(rename, oldpath, newpath);
+}
+
+uptr internal_sched_yield() {
+ CHECK(&_sys_sched_yield);
+ return _sys_sched_yield();
+}
+
+void internal__exit(int exitcode) {
+ DEFINE__REAL(void, _exit, int a);
+ _REAL(_exit, exitcode);
+ Die(); // Unreachable.
+}
+
+unsigned int internal_sleep(unsigned int seconds) {
+ struct timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = 0;
+ CHECK(&_sys___nanosleep50);
+ int res = _sys___nanosleep50(&ts, &ts);
+ if (res)
+ return ts.tv_sec;
+ return 0;
+}
+
+uptr internal_execve(const char *filename, char *const argv[],
+ char *const envp[]) {
+ CHECK(&_sys_execve);
+ return _sys_execve(filename, argv, envp);
+}
+
+tid_t GetTid() {
+ DEFINE__REAL(int, _lwp_self);
+ return _REAL(_lwp_self);
+}
+
+int TgKill(pid_t pid, tid_t tid, int sig) {
+ DEFINE__REAL(int, _lwp_kill, int a, int b);
+ (void)pid;
+ return _REAL(_lwp_kill, tid, sig);
+}
+
+u64 NanoTime() {
+ timeval tv;
+ DEFINE__REAL(int, __gettimeofday50, void *a, void *b);
+ internal_memset(&tv, 0, sizeof(tv));
+ _REAL(__gettimeofday50, &tv, 0);
+ return (u64)tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000;
+}
+
+uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
+ DEFINE__REAL(int, __clock_gettime50, __sanitizer_clockid_t a, void *b);
+ return _REAL(__clock_gettime50, clk_id, tp);
+}
+
+uptr internal_ptrace(int request, int pid, void *addr, int data) {
+ DEFINE__REAL(int, ptrace, int a, int b, void *c, int d);
+ return _REAL(ptrace, request, pid, addr, data);
+}
+
+uptr internal_waitpid(int pid, int *status, int options) {
+ CHECK(&_sys___wait450);
+ return _sys___wait450(pid, status, options, 0 /* rusage */);
+}
+
+uptr internal_getpid() {
+ DEFINE__REAL(int, getpid);
+ return _REAL(getpid);
+}
+
+uptr internal_getppid() {
+ DEFINE__REAL(int, getppid);
+ return _REAL(getppid);
+}
+
+uptr internal_getdents(fd_t fd, void *dirp, unsigned int count) {
+ DEFINE__REAL(int, __getdents30, int a, void *b, size_t c);
+ return _REAL(__getdents30, fd, dirp, count);
+}
+
+uptr internal_lseek(fd_t fd, OFF_T offset, int whence) {
+ CHECK(&__lseek);
+ return __lseek(fd, 0, offset, whence);
+}
+
+uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5) {
+ Printf("internal_prctl not implemented for NetBSD");
+ Die();
+ return 0;
+}
+
+uptr internal_sigaltstack(const void *ss, void *oss) {
+ DEFINE__REAL(int, __sigaltstack14, const void *a, void *b);
+ return _REAL(__sigaltstack14, ss, oss);
+}
+
+int internal_fork() {
+ CHECK(&__fork);
+ return __fork();
+}
+
+int internal_sysctl(const int *name, unsigned int namelen, void *oldp,
+ uptr *oldlenp, const void *newp, uptr newlen) {
+ CHECK(&__sysctl);
+ return __sysctl(name, namelen, oldp, (size_t *)oldlenp, newp, (size_t)newlen);
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ DEFINE__REAL(int, sysctlbyname, const char *a, void *b, size_t *c,
+ const void *d, size_t e);
+ return _REAL(sysctlbyname, sname, oldp, (size_t *)oldlenp, newp,
+ (size_t)newlen);
+}
+
+uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ CHECK(&_sys___sigprocmask14);
+ return _sys___sigprocmask14(how, set, oldset);
+}
+
+void internal_sigfillset(__sanitizer_sigset_t *set) {
+ DEFINE__REAL(int, __sigfillset14, const void *a);
+ (void)_REAL(__sigfillset14, set);
+}
+
+void internal_sigemptyset(__sanitizer_sigset_t *set) {
+ DEFINE__REAL(int, __sigemptyset14, const void *a);
+ (void)_REAL(__sigemptyset14, set);
+}
+
+void internal_sigdelset(__sanitizer_sigset_t *set, int signo) {
+ DEFINE__REAL(int, __sigdelset14, const void *a, int b);
+ (void)_REAL(__sigdelset14, set, signo);
+}
+
+uptr internal_clone(int (*fn)(void *), void *child_stack, int flags,
+ void *arg) {
+ DEFINE__REAL(int, clone, int (*a)(void *b), void *c, int d, void *e);
+
+ return _REAL(clone, fn, child_stack, flags, arg);
+}
+
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-//===-- sanitizer_openbsd.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries and
-// implements Solaris-specific functions.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_OPENBSD
-
-#include <stdio.h>
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_platform_limits_posix.h"
-#include "sanitizer_procmaps.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <pthread.h>
-#include <sched.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/shm.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-extern char **environ;
-
-namespace __sanitizer {
-
-uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd,
- u64 offset) {
- return (uptr)mmap(addr, length, prot, flags, fd, offset);
-}
-
-uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); }
-
-int internal_mprotect(void *addr, uptr length, int prot) {
- return mprotect(addr, length, prot);
-}
-
-int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
- const void *newp, uptr newlen) {
- Printf("internal_sysctlbyname not implemented for OpenBSD");
- Die();
- return 0;
-}
-
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
- // On OpenBSD we cannot get the full path
- struct kinfo_proc kp;
- uptr kl;
- const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
- if (internal_sysctl(Mib, ARRAY_SIZE(Mib), &kp, &kl, NULL, 0) != -1)
- return internal_snprintf(buf,
- (KI_MAXCOMLEN < buf_len ? KI_MAXCOMLEN : buf_len),
- "%s", kp.p_comm);
- return (uptr)0;
-}
-
-static void GetArgsAndEnv(char ***argv, char ***envp) {
- uptr nargv;
- uptr nenv;
- int argvmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV};
- int envmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ENV};
- if (internal_sysctl(argvmib, 4, NULL, &nargv, NULL, 0) == -1) {
- Printf("sysctl KERN_PROC_NARGV failed\n");
- Die();
- }
- if (internal_sysctl(envmib, 4, NULL, &nenv, NULL, 0) == -1) {
- Printf("sysctl KERN_PROC_NENV failed\n");
- Die();
- }
- if (internal_sysctl(argvmib, 4, &argv, &nargv, NULL, 0) == -1) {
- Printf("sysctl KERN_PROC_ARGV failed\n");
- Die();
- }
- if (internal_sysctl(envmib, 4, &envp, &nenv, NULL, 0) == -1) {
- Printf("sysctl KERN_PROC_ENV failed\n");
- Die();
- }
-}
-
-char **GetArgv() {
- char **argv, **envp;
- GetArgsAndEnv(&argv, &envp);
- return argv;
-}
-
-void ReExec() {
- UNIMPLEMENTED();
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_OPENBSD
--- /dev/null
+//===-- sanitizer_openbsd.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements Solaris-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_OPENBSD
+
+#include <stdio.h>
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_procmaps.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+extern char **environ;
+
+namespace __sanitizer {
+
+uptr internal_mmap(void *addr, size_t length, int prot, int flags, int fd,
+ u64 offset) {
+ return (uptr)mmap(addr, length, prot, flags, fd, offset);
+}
+
+uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); }
+
+int internal_mprotect(void *addr, uptr length, int prot) {
+ return mprotect(addr, length, prot);
+}
+
+int internal_sysctlbyname(const char *sname, void *oldp, uptr *oldlenp,
+ const void *newp, uptr newlen) {
+ Printf("internal_sysctlbyname not implemented for OpenBSD");
+ Die();
+ return 0;
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ // On OpenBSD we cannot get the full path
+ struct kinfo_proc kp;
+ uptr kl;
+ const int Mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
+ if (internal_sysctl(Mib, ARRAY_SIZE(Mib), &kp, &kl, NULL, 0) != -1)
+ return internal_snprintf(buf,
+ (KI_MAXCOMLEN < buf_len ? KI_MAXCOMLEN : buf_len),
+ "%s", kp.p_comm);
+ return (uptr)0;
+}
+
+static void GetArgsAndEnv(char ***argv, char ***envp) {
+ uptr nargv;
+ uptr nenv;
+ int argvmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV};
+ int envmib[4] = {CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ENV};
+ if (internal_sysctl(argvmib, 4, NULL, &nargv, NULL, 0) == -1) {
+ Printf("sysctl KERN_PROC_NARGV failed\n");
+ Die();
+ }
+ if (internal_sysctl(envmib, 4, NULL, &nenv, NULL, 0) == -1) {
+ Printf("sysctl KERN_PROC_NENV failed\n");
+ Die();
+ }
+ if (internal_sysctl(argvmib, 4, &argv, &nargv, NULL, 0) == -1) {
+ Printf("sysctl KERN_PROC_ARGV failed\n");
+ Die();
+ }
+ if (internal_sysctl(envmib, 4, &envp, &nenv, NULL, 0) == -1) {
+ Printf("sysctl KERN_PROC_ENV failed\n");
+ Die();
+ }
+}
+
+char **GetArgv() {
+ char **argv, **envp;
+ GetArgsAndEnv(&argv, &envp);
+ return argv;
+}
+
+char **GetEnviron() {
+ char **argv, **envp;
+ GetArgsAndEnv(&argv, &envp);
+ return envp;
+}
+
+void ReExec() {
+ UNIMPLEMENTED();
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_OPENBSD
+++ /dev/null
-//===-- sanitizer_persistent_allocator.cc -----------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-#include "sanitizer_persistent_allocator.h"
-
-namespace __sanitizer {
-
-PersistentAllocator thePersistentAllocator;
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_persistent_allocator.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+#include "sanitizer_persistent_allocator.h"
+
+namespace __sanitizer {
+
+PersistentAllocator thePersistentAllocator;
+
+} // namespace __sanitizer
//===-- sanitizer_persistent_allocator.h ------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_placement_new.h -------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_platform.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48)
# endif
#elif defined(__sparc__)
-# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 52)
+#define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 52)
#else
# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47)
#endif
// The SPARC64 Linux port implements this to split the VMA space into two
// non-contiguous halves with a huge hole in the middle.
#if defined(__sparc__) && SANITIZER_WORDSIZE == 64
-# define SANITIZER_SIGN_EXTENDED_ADDRESSES 1
+#define SANITIZER_SIGN_EXTENDED_ADDRESSES 1
#else
-# define SANITIZER_SIGN_EXTENDED_ADDRESSES 0
+#define SANITIZER_SIGN_EXTENDED_ADDRESSES 0
#endif
// The AArch64 linux port uses the canonical syscall set as mandated by
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
#endif
-// Assume obsolete RPC headers are available by default
-#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H)
-# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID)
-# define HAVE_TIRPC_RPC_XDR_H 0
-#endif
-
/// \macro MSC_PREREQ
/// \brief Is the compiler MSVC of at least the specified version?
/// The common \param version values to check for are:
#define SANITIZER_SYMBOLIZER_MARKUP 0
#endif
+// Enable ability to support sanitizer initialization that is
+// compatible with the sanitizer library being loaded via
+// `dlopen()`.
+#if SANITIZER_MAC
+#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 1
+#else
+#define SANITIZER_SUPPORTS_INIT_FOR_DLOPEN 0
+#endif
+
#endif // SANITIZER_PLATFORM_H
//===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#endif
#if SI_POSIX
+# include "sanitizer_platform_limits_freebsd.h"
# include "sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_platform_limits_openbsd.h"
+# include "sanitizer_platform_limits_openbsd.h"
# include "sanitizer_platform_limits_posix.h"
# include "sanitizer_platform_limits_solaris.h"
#endif
#define SANITIZER_INTERCEPT_MEMMOVE 1
#define SANITIZER_INTERCEPT_MEMCPY 1
#define SANITIZER_INTERCEPT_MEMCMP SI_NOT_FUCHSIA
+#define SANITIZER_INTERCEPT_BCMP \
+ SANITIZER_INTERCEPT_MEMCMP && \
+ ((SI_POSIX && _GNU_SOURCE) || SI_NETBSD || SI_OPENBSD || SI_FREEBSD)
#define SANITIZER_INTERCEPT_STRNDUP SI_POSIX
#define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
#define SANITIZER_INTERCEPT_GETPWENT \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
SI_SOLARIS)
+#define SANITIZER_INTERCEPT_FGETGRENT_R \
+ (SI_FREEBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_GETPWENT_R \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
+#define SANITIZER_INTERCEPT_FGETPWENT_R \
+ (SI_FREEBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_SETPWENT \
(SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_CLOCK_GETTIME \
#define SANITIZER_INTERCEPT_WCRTOMB \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
SI_SOLARIS)
+#define SANITIZER_INTERCEPT_WCTOMB \
+ (SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_MAC || SI_LINUX_NOT_ANDROID || \
+ SI_SOLARIS)
#define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_REALPATH SI_POSIX
#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME \
(SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_SIGPENDING SI_POSIX
#define SANITIZER_INTERCEPT_SIGPROCMASK SI_POSIX
+#define SANITIZER_INTERCEPT_PTHREAD_SIGMASK SI_POSIX
#define SANITIZER_INTERCEPT_BACKTRACE \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX
#define SANITIZER_INTERCEPT_THR_EXIT SI_FREEBSD
#define SANITIZER_INTERCEPT_TMPNAM SI_POSIX
#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID || SI_SOLARIS
+#define SANITIZER_INTERCEPT_TTYNAME SI_POSIX
#define SANITIZER_INTERCEPT_TTYNAME_R SI_POSIX
#define SANITIZER_INTERCEPT_TEMPNAM SI_POSIX
#define SANITIZER_INTERCEPT_SINCOS SI_LINUX || SI_SOLARIS
#define SANITIZER_INTERCEPT_REMQUO SI_POSIX
+#define SANITIZER_INTERCEPT_REMQUOL (SI_POSIX && !SI_NETBSD)
#define SANITIZER_INTERCEPT_LGAMMA SI_POSIX
+#define SANITIZER_INTERCEPT_LGAMMAL (SI_POSIX && !SI_NETBSD)
#define SANITIZER_INTERCEPT_LGAMMA_R (SI_FREEBSD || SI_LINUX || SI_SOLARIS)
#define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_PTHREAD_GETNAME_NP \
- (SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
+ (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#define SANITIZER_INTERCEPT_TLS_GET_ADDR \
(SI_FREEBSD || SI_NETBSD || SI_OPENBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS)
#else
#define SANITIZER_INTERCEPT_AEABI_MEM 0
#endif
-#define SANITIZER_INTERCEPT___BZERO SI_MAC
+#define SANITIZER_INTERCEPT___BZERO SI_MAC || SI_LINUX_NOT_ANDROID
+#define SANITIZER_INTERCEPT_BZERO SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_FTIME \
(!SI_FREEBSD && !SI_NETBSD && !SI_OPENBSD && SI_POSIX)
#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID || SI_SOLARIS
#define SANITIZER_INTERCEPT_CFREE \
(!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_OPENBSD && SI_NOT_FUCHSIA && \
SI_NOT_RTEMS)
+#define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX
#define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC && SI_NOT_RTEMS)
#define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_OPENBSD)
#define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_WCSCAT SI_POSIX
+#define SANITIZER_INTERCEPT_WCSDUP SI_POSIX
#define SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA)
#define SANITIZER_INTERCEPT_BSD_SIGNAL SI_ANDROID
#define SANITIZER_INTERCEPT_TTYENT SI_NETBSD
#define SANITIZER_INTERCEPT_PROTOENT SI_NETBSD
#define SANITIZER_INTERCEPT_NETENT SI_NETBSD
+#define SANITIZER_INTERCEPT_SETVBUF (SI_NETBSD || SI_FREEBSD || \
+ SI_LINUX || SI_MAC)
+#define SANITIZER_INTERCEPT_GETMNTINFO (SI_NETBSD || SI_FREEBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_MI_VECTOR_HASH SI_NETBSD
+#define SANITIZER_INTERCEPT_GETVFSSTAT SI_NETBSD
+#define SANITIZER_INTERCEPT_REGEX (SI_NETBSD || SI_FREEBSD || SI_LINUX)
+#define SANITIZER_INTERCEPT_REGEXSUB SI_NETBSD
+#define SANITIZER_INTERCEPT_FTS (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_SYSCTL (SI_NETBSD || SI_FREEBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_ASYSCTL SI_NETBSD
+#define SANITIZER_INTERCEPT_SYSCTLGETMIBINFO SI_NETBSD
+#define SANITIZER_INTERCEPT_NL_LANGINFO (SI_NETBSD || SI_FREEBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_MODCTL SI_NETBSD
+#define SANITIZER_INTERCEPT_CAPSICUM SI_FREEBSD
+#define SANITIZER_INTERCEPT_STRTONUM (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_FPARSELN SI_NETBSD
+#define SANITIZER_INTERCEPT_STATVFS1 SI_NETBSD
+#define SANITIZER_INTERCEPT_STRTOI SI_NETBSD
+#define SANITIZER_INTERCEPT_CAPSICUM SI_FREEBSD
+#define SANITIZER_INTERCEPT_SHA1 SI_NETBSD
+#define SANITIZER_INTERCEPT_MD4 SI_NETBSD
+#define SANITIZER_INTERCEPT_RMD160 SI_NETBSD
+#define SANITIZER_INTERCEPT_MD5 SI_NETBSD
+#define SANITIZER_INTERCEPT_FSEEK (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_MD2 SI_NETBSD
+#define SANITIZER_INTERCEPT_SHA2 SI_NETBSD
+#define SANITIZER_INTERCEPT_CDB SI_NETBSD
+#define SANITIZER_INTERCEPT_VIS (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_POPEN SI_POSIX
+#define SANITIZER_INTERCEPT_POPENVE SI_NETBSD
+#define SANITIZER_INTERCEPT_PCLOSE SI_POSIX
+#define SANITIZER_INTERCEPT_FUNOPEN (SI_NETBSD || SI_FREEBSD)
+#define SANITIZER_INTERCEPT_FUNOPEN2 SI_NETBSD
+#define SANITIZER_INTERCEPT_GETFSENT (SI_FREEBSD || SI_NETBSD || SI_MAC)
+#define SANITIZER_INTERCEPT_ARC4RANDOM (SI_FREEBSD || SI_NETBSD)
+#define SANITIZER_INTERCEPT_FDEVNAME SI_FREEBSD
+#define SANITIZER_INTERCEPT_GETUSERSHELL (SI_POSIX && !SI_ANDROID)
+#define SANITIZER_INTERCEPT_SL_INIT (SI_FREEBSD || SI_NETBSD)
+
+#define SANITIZER_INTERCEPT_GETRANDOM SI_LINUX
#endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H
--- /dev/null
+//===-- sanitizer_platform_limits_freebsd.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific FreeBSD data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD
+
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <fts.h>
+#include <fstab.h>
+#include <grp.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <regex.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/capsicum.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <termios.h>
+#include <time.h>
+
+#include <net/route.h>
+#include <sys/mount.h>
+#include <sys/sockio.h>
+#include <sys/socket.h>
+#include <sys/filio.h>
+#include <sys/signal.h>
+#include <sys/timespec.h>
+#include <sys/timeb.h>
+#include <sys/mqueue.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/statvfs.h>
+#include <sys/soundcard.h>
+#include <sys/mtio.h>
+#include <sys/consio.h>
+#include <sys/kbio.h>
+#include <sys/link_elf.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <net/ppp_defs.h>
+#include <glob.h>
+#include <stdio.h>
+#include <stringlist.h>
+#include <term.h>
+#include <utmpx.h>
+#include <wchar.h>
+#include <vis.h>
+
+#define _KERNEL // to declare 'shminfo' structure
+# include <sys/shm.h>
+#undef _KERNEL
+
+#undef INLINE // to avoid clashes with sanitizers' definitions
+
+#undef IOC_DIRMASK
+
+# include <utime.h>
+# include <sys/ptrace.h>
+# include <semaphore.h>
+
+#include <ifaddrs.h>
+#include <sys/ucontext.h>
+#include <wordexp.h>
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_freebsd.h"
+
+namespace __sanitizer {
+ unsigned struct_cap_rights_sz = sizeof(cap_rights_t);
+ unsigned struct_utsname_sz = sizeof(struct utsname);
+ unsigned struct_stat_sz = sizeof(struct stat);
+ unsigned struct_rusage_sz = sizeof(struct rusage);
+ unsigned struct_tm_sz = sizeof(struct tm);
+ unsigned struct_passwd_sz = sizeof(struct passwd);
+ unsigned struct_group_sz = sizeof(struct group);
+ unsigned siginfo_t_sz = sizeof(siginfo_t);
+ unsigned struct_sigaction_sz = sizeof(struct sigaction);
+ unsigned struct_itimerval_sz = sizeof(struct itimerval);
+ unsigned pthread_t_sz = sizeof(pthread_t);
+ unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
+ unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+ unsigned pid_t_sz = sizeof(pid_t);
+ unsigned timeval_sz = sizeof(timeval);
+ unsigned uid_t_sz = sizeof(uid_t);
+ unsigned gid_t_sz = sizeof(gid_t);
+ unsigned fpos_t_sz = sizeof(fpos_t);
+ unsigned mbstate_t_sz = sizeof(mbstate_t);
+ unsigned sigset_t_sz = sizeof(sigset_t);
+ unsigned struct_timezone_sz = sizeof(struct timezone);
+ unsigned struct_tms_sz = sizeof(struct tms);
+ unsigned struct_sigevent_sz = sizeof(struct sigevent);
+ unsigned struct_sched_param_sz = sizeof(struct sched_param);
+ unsigned struct_statfs_sz = sizeof(struct statfs);
+ unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+ unsigned ucontext_t_sz = sizeof(ucontext_t);
+ unsigned struct_rlimit_sz = sizeof(struct rlimit);
+ unsigned struct_timespec_sz = sizeof(struct timespec);
+ unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+ unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+ unsigned struct_timeb_sz = sizeof(struct timeb);
+ unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
+ unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
+ unsigned struct_statvfs_sz = sizeof(struct statvfs);
+ unsigned struct_shminfo_sz = sizeof(struct shminfo);
+ unsigned struct_shm_info_sz = sizeof(struct shm_info);
+ unsigned struct_regmatch_sz = sizeof(regmatch_t);
+ unsigned struct_regex_sz = sizeof(regex_t);
+ unsigned struct_fstab_sz = sizeof(struct fstab);
+ unsigned struct_FTS_sz = sizeof(FTS);
+ unsigned struct_FTSENT_sz = sizeof(FTSENT);
+ unsigned struct_StringList_sz = sizeof(StringList);
+
+ const uptr sig_ign = (uptr)SIG_IGN;
+ const uptr sig_dfl = (uptr)SIG_DFL;
+ const uptr sig_err = (uptr)SIG_ERR;
+ const uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+ int shmctl_ipc_stat = (int)IPC_STAT;
+ int shmctl_ipc_info = (int)IPC_INFO;
+ int shmctl_shm_info = (int)SHM_INFO;
+ int shmctl_shm_stat = (int)SHM_STAT;
+ unsigned struct_utmpx_sz = sizeof(struct utmpx);
+
+ int map_fixed = MAP_FIXED;
+
+ int af_inet = (int)AF_INET;
+ int af_inet6 = (int)AF_INET6;
+
+ uptr __sanitizer_in_addr_sz(int af) {
+ if (af == AF_INET)
+ return sizeof(struct in_addr);
+ else if (af == AF_INET6)
+ return sizeof(struct in6_addr);
+ else
+ return 0;
+ }
+
+ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+ int glob_nomatch = GLOB_NOMATCH;
+ int glob_altdirfunc = GLOB_ALTDIRFUNC;
+
+ unsigned path_max = PATH_MAX;
+
+ // ioctl arguments
+ unsigned struct_ifreq_sz = sizeof(struct ifreq);
+ unsigned struct_termios_sz = sizeof(struct termios);
+ unsigned struct_winsize_sz = sizeof(struct winsize);
+#if SOUND_VERSION >= 0x040000
+ unsigned struct_copr_buffer_sz = 0;
+ unsigned struct_copr_debug_buf_sz = 0;
+ unsigned struct_copr_msg_sz = 0;
+#else
+ unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
+ unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
+ unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
+#endif
+ unsigned struct_midi_info_sz = sizeof(struct midi_info);
+ unsigned struct_mtget_sz = sizeof(struct mtget);
+ unsigned struct_mtop_sz = sizeof(struct mtop);
+ unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
+ unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
+ unsigned struct_synth_info_sz = sizeof(struct synth_info);
+ unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
+ unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
+ unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
+ unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
+ const unsigned long __sanitizer_bufsiz = BUFSIZ;
+
+ const unsigned IOCTL_NOT_PRESENT = 0;
+
+ unsigned IOCTL_FIOASYNC = FIOASYNC;
+ unsigned IOCTL_FIOCLEX = FIOCLEX;
+ unsigned IOCTL_FIOGETOWN = FIOGETOWN;
+ unsigned IOCTL_FIONBIO = FIONBIO;
+ unsigned IOCTL_FIONCLEX = FIONCLEX;
+ unsigned IOCTL_FIOSETOWN = FIOSETOWN;
+ unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
+ unsigned IOCTL_SIOCATMARK = SIOCATMARK;
+ unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
+ unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
+ unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
+ unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
+ unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
+ unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
+ unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
+ unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
+ unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
+ unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+ unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
+ unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
+ unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
+ unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
+ unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
+ unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
+ unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
+ unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
+ unsigned IOCTL_TIOCCONS = TIOCCONS;
+ unsigned IOCTL_TIOCEXCL = TIOCEXCL;
+ unsigned IOCTL_TIOCGETD = TIOCGETD;
+ unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
+ unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
+ unsigned IOCTL_TIOCMBIC = TIOCMBIC;
+ unsigned IOCTL_TIOCMBIS = TIOCMBIS;
+ unsigned IOCTL_TIOCMGET = TIOCMGET;
+ unsigned IOCTL_TIOCMSET = TIOCMSET;
+ unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
+ unsigned IOCTL_TIOCNXCL = TIOCNXCL;
+ unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
+ unsigned IOCTL_TIOCPKT = TIOCPKT;
+ unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
+ unsigned IOCTL_TIOCSETD = TIOCSETD;
+ unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
+ unsigned IOCTL_TIOCSTI = TIOCSTI;
+ unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
+ unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
+ unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
+ unsigned IOCTL_MTIOCGET = MTIOCGET;
+ unsigned IOCTL_MTIOCTOP = MTIOCTOP;
+ unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
+ unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
+ unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
+ unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
+ unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
+ unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
+ unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
+ unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
+ unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
+ unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
+ unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
+ unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE;
+ unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR;
+ unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO;
+ unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME;
+ unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE;
+ unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT;
+ unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT;
+ unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS;
+ unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS;
+ unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND;
+ unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC;
+ unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE;
+ unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET;
+ unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES;
+ unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC;
+ unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI;
+ unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD;
+ unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO;
+ unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL;
+ unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE;
+ unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME;
+ unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT;
+ unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE;
+ unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START;
+ unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP;
+ unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO;
+ unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE;
+ unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM;
+ unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS;
+ unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS;
+ unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD;
+ unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK;
+ unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE;
+ unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN;
+ unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3;
+ unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD;
+ unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC;
+ unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE;
+ unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN;
+ unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM;
+ unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV;
+ unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK;
+ unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC;
+ unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER;
+ unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS;
+ unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH;
+ unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE;
+ unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME;
+ unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM;
+ unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS;
+ unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD;
+ unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN;
+ unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD;
+ unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC;
+ unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN;
+ unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM;
+ unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV;
+ unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC;
+ unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER;
+ unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
+ unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
+ unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
+ unsigned IOCTL_VT_GETMODE = VT_GETMODE;
+ unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
+ unsigned IOCTL_VT_RELDISP = VT_RELDISP;
+ unsigned IOCTL_VT_SETMODE = VT_SETMODE;
+ unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
+ unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
+ unsigned IOCTL_KDDISABIO = KDDISABIO;
+ unsigned IOCTL_KDENABIO = KDENABIO;
+ unsigned IOCTL_KDGETLED = KDGETLED;
+ unsigned IOCTL_KDGETMODE = KDGETMODE;
+ unsigned IOCTL_KDGKBMODE = KDGKBMODE;
+ unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
+ unsigned IOCTL_KDMKTONE = KDMKTONE;
+ unsigned IOCTL_KDSETLED = KDSETLED;
+ unsigned IOCTL_KDSETMODE = KDSETMODE;
+ unsigned IOCTL_KDSKBMODE = KDSKBMODE;
+ unsigned IOCTL_KIOCSOUND = KIOCSOUND;
+ unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
+ unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
+
+ const int si_SEGV_MAPERR = SEGV_MAPERR;
+ const int si_SEGV_ACCERR = SEGV_ACCERR;
+ const int unvis_valid = UNVIS_VALID;
+ const int unvis_validpush = UNVIS_VALIDPUSH;
+} // namespace __sanitizer
+
+using namespace __sanitizer;
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+// There are more undocumented fields in dl_phdr_info that we are not interested
+// in.
+COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_ino);
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+// Can't write checks for sa_handler and sa_sigaction due to them being
+// preprocessor macros.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
+
+CHECK_TYPE_SIZE(wordexp_t);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
+CHECK_SIZE_AND_OFFSET(tm, tm_zone);
+
+CHECK_TYPE_SIZE(ether_addr);
+
+CHECK_TYPE_SIZE(ipc_perm);
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+
+CHECK_TYPE_SIZE(clock_t);
+
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+#undef ifa_dstaddr
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+#if HAVE_RPC_XDR_H
+CHECK_TYPE_SIZE(XDR);
+CHECK_SIZE_AND_OFFSET(XDR, x_op);
+CHECK_SIZE_AND_OFFSET(XDR, x_ops);
+CHECK_SIZE_AND_OFFSET(XDR, x_public);
+CHECK_SIZE_AND_OFFSET(XDR, x_private);
+CHECK_SIZE_AND_OFFSET(XDR, x_base);
+CHECK_SIZE_AND_OFFSET(XDR, x_handy);
+COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
+COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
+COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
+#endif
+
+CHECK_TYPE_SIZE(sem_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_cap_rights_t) >= sizeof(cap_rights_t));
+#endif // SANITIZER_FREEBSD
--- /dev/null
+//===-- sanitizer_platform_limits_freebsd.h -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific FreeBSD data structures.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_PLATFORM_LIMITS_FREEBSD_H
+#define SANITIZER_PLATFORM_LIMITS_FREEBSD_H
+
+#if SANITIZER_FREEBSD
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform.h"
+
+#include "sanitizer_platform_limits_posix.h"
+
+// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
+// incorporates the map structure.
+# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
+ ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 560)))
+// Get sys/_types.h, because that tells us whether 64-bit inodes are
+// used in struct dirent below.
+#include <sys/_types.h>
+
+namespace __sanitizer {
+ extern unsigned struct_utsname_sz;
+ extern unsigned struct_stat_sz;
+#if defined(__powerpc64__)
+ const unsigned struct___old_kernel_stat_sz = 0;
+#else
+ const unsigned struct___old_kernel_stat_sz = 32;
+#endif
+ extern unsigned struct_rusage_sz;
+ extern unsigned siginfo_t_sz;
+ extern unsigned struct_itimerval_sz;
+ extern unsigned pthread_t_sz;
+ extern unsigned pthread_mutex_t_sz;
+ extern unsigned pthread_cond_t_sz;
+ extern unsigned pid_t_sz;
+ extern unsigned timeval_sz;
+ extern unsigned uid_t_sz;
+ extern unsigned gid_t_sz;
+ extern unsigned fpos_t_sz;
+ extern unsigned mbstate_t_sz;
+ extern unsigned struct_timezone_sz;
+ extern unsigned struct_tms_sz;
+ extern unsigned struct_itimerspec_sz;
+ extern unsigned struct_sigevent_sz;
+ extern unsigned struct_sched_param_sz;
+ extern unsigned struct_statfs64_sz;
+ extern unsigned struct_statfs_sz;
+ extern unsigned struct_sockaddr_sz;
+ extern unsigned ucontext_t_sz;
+ extern unsigned struct_rlimit_sz;
+ extern unsigned struct_utimbuf_sz;
+ extern unsigned struct_timespec_sz;
+ extern unsigned struct_regmatch_sz;
+ extern unsigned struct_regex_sz;
+ extern unsigned struct_FTS_sz;
+ extern unsigned struct_FTSENT_sz;
+ extern const int unvis_valid;
+ extern const int unvis_validpush;
+
+ struct __sanitizer_iocb {
+ u64 aio_data;
+ u32 aio_key_or_aio_reserved1; // Simply crazy.
+ u32 aio_reserved1_or_aio_key; // Luckily, we don't need these.
+ u16 aio_lio_opcode;
+ s16 aio_reqprio;
+ u32 aio_fildes;
+ u64 aio_buf;
+ u64 aio_nbytes;
+ s64 aio_offset;
+ u64 aio_reserved2;
+ u64 aio_reserved3;
+ };
+
+ struct __sanitizer_io_event {
+ u64 data;
+ u64 obj;
+ u64 res;
+ u64 res2;
+ };
+
+ const unsigned iocb_cmd_pread = 0;
+ const unsigned iocb_cmd_pwrite = 1;
+ const unsigned iocb_cmd_preadv = 7;
+ const unsigned iocb_cmd_pwritev = 8;
+
+ struct __sanitizer___sysctl_args {
+ int *name;
+ int nlen;
+ void *oldval;
+ uptr *oldlenp;
+ void *newval;
+ uptr newlen;
+ unsigned long ___unused[4];
+ };
+
+ struct __sanitizer_ipc_perm {
+ unsigned int cuid;
+ unsigned int cgid;
+ unsigned int uid;
+ unsigned int gid;
+ unsigned short mode;
+ unsigned short seq;
+ long key;
+ };
+
+ struct __sanitizer_shmid_ds {
+ __sanitizer_ipc_perm shm_perm;
+ unsigned long shm_segsz;
+ unsigned int shm_lpid;
+ unsigned int shm_cpid;
+ int shm_nattch;
+ unsigned long shm_atime;
+ unsigned long shm_dtime;
+ unsigned long shm_ctime;
+ };
+
+ extern unsigned struct_msqid_ds_sz;
+ extern unsigned struct_mq_attr_sz;
+ extern unsigned struct_timeb_sz;
+ extern unsigned struct_statvfs_sz;
+
+ struct __sanitizer_iovec {
+ void *iov_base;
+ uptr iov_len;
+ };
+
+ struct __sanitizer_ifaddrs {
+ struct __sanitizer_ifaddrs *ifa_next;
+ char *ifa_name;
+ unsigned int ifa_flags;
+ void *ifa_addr; // (struct sockaddr *)
+ void *ifa_netmask; // (struct sockaddr *)
+# undef ifa_dstaddr
+ void *ifa_dstaddr; // (struct sockaddr *)
+ void *ifa_data;
+ };
+
+ typedef unsigned __sanitizer_pthread_key_t;
+
+ struct __sanitizer_passwd {
+ char *pw_name;
+ char *pw_passwd;
+ int pw_uid;
+ int pw_gid;
+ long pw_change;
+ char *pw_class;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+ long pw_expire;
+ int pw_fields;
+ };
+
+ struct __sanitizer_group {
+ char *gr_name;
+ char *gr_passwd;
+ int gr_gid;
+ char **gr_mem;
+ };
+
+#if defined(__LP64___)
+ typedef long long __sanitizer_time_t;
+#else
+ typedef long __sanitizer_time_t;
+#endif
+
+ typedef long __sanitizer_suseconds_t;
+
+ struct __sanitizer_timeval {
+ __sanitizer_time_t tv_sec;
+ __sanitizer_suseconds_t tv_usec;
+ };
+
+ struct __sanitizer_itimerval {
+ struct __sanitizer_timeval it_interval;
+ struct __sanitizer_timeval it_value;
+ };
+
+ struct __sanitizer_timeb {
+ __sanitizer_time_t time;
+ unsigned short millitm;
+ short timezone;
+ short dstflag;
+ };
+
+ struct __sanitizer_ether_addr {
+ u8 octet[6];
+ };
+
+ struct __sanitizer_tm {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+ long int tm_gmtoff;
+ const char *tm_zone;
+ };
+
+ struct __sanitizer_msghdr {
+ void *msg_name;
+ unsigned msg_namelen;
+ struct __sanitizer_iovec *msg_iov;
+ unsigned msg_iovlen;
+ void *msg_control;
+ unsigned msg_controllen;
+ int msg_flags;
+ };
+
+ struct __sanitizer_cmsghdr {
+ unsigned cmsg_len;
+ int cmsg_level;
+ int cmsg_type;
+ };
+
+ struct __sanitizer_dirent {
+#if defined(__INO64)
+ unsigned long long d_fileno;
+ unsigned long long d_off;
+#else
+ unsigned int d_fileno;
+#endif
+ unsigned short d_reclen;
+ // more fields that we don't care about
+ };
+
+// 'clock_t' is 32 bits wide on x64 FreeBSD
+ typedef int __sanitizer_clock_t;
+ typedef int __sanitizer_clockid_t;
+
+#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
+ || defined(__mips__)
+ typedef unsigned __sanitizer___kernel_uid_t;
+ typedef unsigned __sanitizer___kernel_gid_t;
+#else
+ typedef unsigned short __sanitizer___kernel_uid_t;
+ typedef unsigned short __sanitizer___kernel_gid_t;
+#endif
+ typedef long long __sanitizer___kernel_off_t;
+
+#if defined(__powerpc__) || defined(__mips__)
+ typedef unsigned int __sanitizer___kernel_old_uid_t;
+ typedef unsigned int __sanitizer___kernel_old_gid_t;
+#else
+ typedef unsigned short __sanitizer___kernel_old_uid_t;
+ typedef unsigned short __sanitizer___kernel_old_gid_t;
+#endif
+
+ typedef long long __sanitizer___kernel_loff_t;
+ typedef struct {
+ unsigned long fds_bits[1024 / (8 * sizeof(long))];
+ } __sanitizer___kernel_fd_set;
+
+ // This thing depends on the platform. We are only interested in the upper
+ // limit. Verified with a compiler assert in .cpp.
+ const int pthread_attr_t_max_sz = 128;
+ union __sanitizer_pthread_attr_t {
+ char size[pthread_attr_t_max_sz]; // NOLINT
+ void *align;
+ };
+
+ const unsigned old_sigset_t_sz = sizeof(unsigned long);
+
+ struct __sanitizer_sigset_t {
+ // uint32_t * 4
+ unsigned int __bits[4];
+ };
+
+ typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
+
+ struct __sanitizer_siginfo {
+ // The size is determined by looking at sizeof of real siginfo_t on linux.
+ u64 opaque[128 / sizeof(u64)];
+ };
+
+ using __sanitizer_sighandler_ptr = void (*)(int sig);
+ using __sanitizer_sigactionhandler_ptr =
+ void (*)(int sig, __sanitizer_siginfo *siginfo, void *uctx);
+
+ struct __sanitizer_sigaction {
+ union {
+ __sanitizer_sigactionhandler_ptr sigaction;
+ __sanitizer_sighandler_ptr handler;
+ };
+ int sa_flags;
+ __sanitizer_sigset_t sa_mask;
+ };
+
+ struct __sanitizer_sem_t {
+ u32 data[4];
+ };
+
+ extern const uptr sig_ign;
+ extern const uptr sig_dfl;
+ extern const uptr sig_err;
+ extern const uptr sa_siginfo;
+
+ extern int af_inet;
+ extern int af_inet6;
+ uptr __sanitizer_in_addr_sz(int af);
+
+ struct __sanitizer_dl_phdr_info {
+ uptr dlpi_addr;
+ const char *dlpi_name;
+ const void *dlpi_phdr;
+ short dlpi_phnum;
+ };
+
+ extern unsigned struct_ElfW_Phdr_sz;
+
+ struct __sanitizer_addrinfo {
+ int ai_flags;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ unsigned ai_addrlen;
+ char *ai_canonname;
+ void *ai_addr;
+ struct __sanitizer_addrinfo *ai_next;
+ };
+
+ struct __sanitizer_hostent {
+ char *h_name;
+ char **h_aliases;
+ int h_addrtype;
+ int h_length;
+ char **h_addr_list;
+ };
+
+ struct __sanitizer_pollfd {
+ int fd;
+ short events;
+ short revents;
+ };
+
+ typedef unsigned __sanitizer_nfds_t;
+
+ struct __sanitizer_glob_t {
+ uptr gl_pathc;
+ uptr gl_matchc;
+ uptr gl_offs;
+ int gl_flags;
+ char **gl_pathv;
+ int (*gl_errfunc)(const char*, int);
+ void (*gl_closedir)(void *dirp);
+ struct dirent *(*gl_readdir)(void *dirp);
+ void *(*gl_opendir)(const char*);
+ int (*gl_lstat)(const char*, void* /* struct stat* */);
+ int (*gl_stat)(const char*, void* /* struct stat* */);
+ };
+
+ extern int glob_nomatch;
+ extern int glob_altdirfunc;
+
+ extern unsigned path_max;
+
+ struct __sanitizer_wordexp_t {
+ uptr we_wordc;
+ char **we_wordv;
+ uptr we_offs;
+ char *we_strings;
+ uptr we_nbytes;
+ };
+
+ typedef void __sanitizer_FILE;
+
+ extern unsigned struct_shminfo_sz;
+ extern unsigned struct_shm_info_sz;
+ extern int shmctl_ipc_stat;
+ extern int shmctl_ipc_info;
+ extern int shmctl_shm_info;
+ extern int shmctl_shm_stat;
+
+ extern unsigned struct_utmpx_sz;
+
+ extern int map_fixed;
+
+ // ioctl arguments
+ struct __sanitizer_ifconf {
+ int ifc_len;
+ union {
+ void *ifcu_req;
+ } ifc_ifcu;
+ };
+
+#define IOC_NRBITS 8
+#define IOC_TYPEBITS 8
+#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
+#define IOC_SIZEBITS 13
+#define IOC_DIRBITS 3
+#define IOC_NONE 1U
+#define IOC_WRITE 4U
+#define IOC_READ 2U
+#else
+#define IOC_SIZEBITS 14
+#define IOC_DIRBITS 2
+#define IOC_NONE 0U
+#define IOC_WRITE 1U
+#define IOC_READ 2U
+#endif
+#define IOC_NRMASK ((1 << IOC_NRBITS) - 1)
+#define IOC_TYPEMASK ((1 << IOC_TYPEBITS) - 1)
+#define IOC_SIZEMASK ((1 << IOC_SIZEBITS) - 1)
+#if defined(IOC_DIRMASK)
+#undef IOC_DIRMASK
+#endif
+#define IOC_DIRMASK ((1 << IOC_DIRBITS) - 1)
+#define IOC_NRSHIFT 0
+#define IOC_TYPESHIFT (IOC_NRSHIFT + IOC_NRBITS)
+#define IOC_SIZESHIFT (IOC_TYPESHIFT + IOC_TYPEBITS)
+#define IOC_DIRSHIFT (IOC_SIZESHIFT + IOC_SIZEBITS)
+#define EVIOC_EV_MAX 0x1f
+#define EVIOC_ABS_MAX 0x3f
+
+#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
+#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
+#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
+#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
+
+ extern unsigned struct_ifreq_sz;
+ extern unsigned struct_termios_sz;
+ extern unsigned struct_winsize_sz;
+
+ extern unsigned struct_copr_buffer_sz;
+ extern unsigned struct_copr_debug_buf_sz;
+ extern unsigned struct_copr_msg_sz;
+ extern unsigned struct_midi_info_sz;
+ extern unsigned struct_mtget_sz;
+ extern unsigned struct_mtop_sz;
+ extern unsigned struct_rtentry_sz;
+ extern unsigned struct_sbi_instrument_sz;
+ extern unsigned struct_seq_event_rec_sz;
+ extern unsigned struct_synth_info_sz;
+ extern unsigned struct_vt_mode_sz;
+
+ extern const unsigned long __sanitizer_bufsiz;
+ extern unsigned struct_audio_buf_info_sz;
+ extern unsigned struct_ppp_stats_sz;
+ extern unsigned struct_sioc_sg_req_sz;
+ extern unsigned struct_sioc_vif_req_sz;
+
+ // ioctl request identifiers
+
+ // A special value to mark ioctls that are not present on the target platform,
+ // when it can not be determined without including any system headers.
+ extern const unsigned IOCTL_NOT_PRESENT;
+
+ extern unsigned IOCTL_FIOASYNC;
+ extern unsigned IOCTL_FIOCLEX;
+ extern unsigned IOCTL_FIOGETOWN;
+ extern unsigned IOCTL_FIONBIO;
+ extern unsigned IOCTL_FIONCLEX;
+ extern unsigned IOCTL_FIOSETOWN;
+ extern unsigned IOCTL_SIOCADDMULTI;
+ extern unsigned IOCTL_SIOCATMARK;
+ extern unsigned IOCTL_SIOCDELMULTI;
+ extern unsigned IOCTL_SIOCGIFADDR;
+ extern unsigned IOCTL_SIOCGIFBRDADDR;
+ extern unsigned IOCTL_SIOCGIFCONF;
+ extern unsigned IOCTL_SIOCGIFDSTADDR;
+ extern unsigned IOCTL_SIOCGIFFLAGS;
+ extern unsigned IOCTL_SIOCGIFMETRIC;
+ extern unsigned IOCTL_SIOCGIFMTU;
+ extern unsigned IOCTL_SIOCGIFNETMASK;
+ extern unsigned IOCTL_SIOCGPGRP;
+ extern unsigned IOCTL_SIOCSIFADDR;
+ extern unsigned IOCTL_SIOCSIFBRDADDR;
+ extern unsigned IOCTL_SIOCSIFDSTADDR;
+ extern unsigned IOCTL_SIOCSIFFLAGS;
+ extern unsigned IOCTL_SIOCSIFMETRIC;
+ extern unsigned IOCTL_SIOCSIFMTU;
+ extern unsigned IOCTL_SIOCSIFNETMASK;
+ extern unsigned IOCTL_SIOCSPGRP;
+ extern unsigned IOCTL_TIOCCONS;
+ extern unsigned IOCTL_TIOCEXCL;
+ extern unsigned IOCTL_TIOCGETD;
+ extern unsigned IOCTL_TIOCGPGRP;
+ extern unsigned IOCTL_TIOCGWINSZ;
+ extern unsigned IOCTL_TIOCMBIC;
+ extern unsigned IOCTL_TIOCMBIS;
+ extern unsigned IOCTL_TIOCMGET;
+ extern unsigned IOCTL_TIOCMSET;
+ extern unsigned IOCTL_TIOCNOTTY;
+ extern unsigned IOCTL_TIOCNXCL;
+ extern unsigned IOCTL_TIOCOUTQ;
+ extern unsigned IOCTL_TIOCPKT;
+ extern unsigned IOCTL_TIOCSCTTY;
+ extern unsigned IOCTL_TIOCSETD;
+ extern unsigned IOCTL_TIOCSPGRP;
+ extern unsigned IOCTL_TIOCSTI;
+ extern unsigned IOCTL_TIOCSWINSZ;
+ extern unsigned IOCTL_SIOCGETSGCNT;
+ extern unsigned IOCTL_SIOCGETVIFCNT;
+ extern unsigned IOCTL_MTIOCGET;
+ extern unsigned IOCTL_MTIOCTOP;
+ extern unsigned IOCTL_SIOCADDRT;
+ extern unsigned IOCTL_SIOCDELRT;
+ extern unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE;
+ extern unsigned IOCTL_SNDCTL_DSP_GETFMTS;
+ extern unsigned IOCTL_SNDCTL_DSP_NONBLOCK;
+ extern unsigned IOCTL_SNDCTL_DSP_POST;
+ extern unsigned IOCTL_SNDCTL_DSP_RESET;
+ extern unsigned IOCTL_SNDCTL_DSP_SETFMT;
+ extern unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT;
+ extern unsigned IOCTL_SNDCTL_DSP_SPEED;
+ extern unsigned IOCTL_SNDCTL_DSP_STEREO;
+ extern unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE;
+ extern unsigned IOCTL_SNDCTL_DSP_SYNC;
+ extern unsigned IOCTL_SNDCTL_FM_4OP_ENABLE;
+ extern unsigned IOCTL_SNDCTL_FM_LOAD_INSTR;
+ extern unsigned IOCTL_SNDCTL_MIDI_INFO;
+ extern unsigned IOCTL_SNDCTL_MIDI_PRETIME;
+ extern unsigned IOCTL_SNDCTL_SEQ_CTRLRATE;
+ extern unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT;
+ extern unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT;
+ extern unsigned IOCTL_SNDCTL_SEQ_NRMIDIS;
+ extern unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS;
+ extern unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND;
+ extern unsigned IOCTL_SNDCTL_SEQ_PANIC;
+ extern unsigned IOCTL_SNDCTL_SEQ_PERCMODE;
+ extern unsigned IOCTL_SNDCTL_SEQ_RESET;
+ extern unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES;
+ extern unsigned IOCTL_SNDCTL_SEQ_SYNC;
+ extern unsigned IOCTL_SNDCTL_SEQ_TESTMIDI;
+ extern unsigned IOCTL_SNDCTL_SEQ_THRESHOLD;
+ extern unsigned IOCTL_SNDCTL_SYNTH_INFO;
+ extern unsigned IOCTL_SNDCTL_SYNTH_MEMAVL;
+ extern unsigned IOCTL_SNDCTL_TMR_CONTINUE;
+ extern unsigned IOCTL_SNDCTL_TMR_METRONOME;
+ extern unsigned IOCTL_SNDCTL_TMR_SELECT;
+ extern unsigned IOCTL_SNDCTL_TMR_SOURCE;
+ extern unsigned IOCTL_SNDCTL_TMR_START;
+ extern unsigned IOCTL_SNDCTL_TMR_STOP;
+ extern unsigned IOCTL_SNDCTL_TMR_TEMPO;
+ extern unsigned IOCTL_SNDCTL_TMR_TIMEBASE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_ALTPCM;
+ extern unsigned IOCTL_SOUND_MIXER_READ_BASS;
+ extern unsigned IOCTL_SOUND_MIXER_READ_CAPS;
+ extern unsigned IOCTL_SOUND_MIXER_READ_CD;
+ extern unsigned IOCTL_SOUND_MIXER_READ_DEVMASK;
+ extern unsigned IOCTL_SOUND_MIXER_READ_ENHANCE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_IGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_READ_IMIX;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE1;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE2;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE3;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LINE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_LOUD;
+ extern unsigned IOCTL_SOUND_MIXER_READ_MIC;
+ extern unsigned IOCTL_SOUND_MIXER_READ_MUTE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_OGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_READ_PCM;
+ extern unsigned IOCTL_SOUND_MIXER_READ_RECLEV;
+ extern unsigned IOCTL_SOUND_MIXER_READ_RECMASK;
+ extern unsigned IOCTL_SOUND_MIXER_READ_RECSRC;
+ extern unsigned IOCTL_SOUND_MIXER_READ_SPEAKER;
+ extern unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS;
+ extern unsigned IOCTL_SOUND_MIXER_READ_SYNTH;
+ extern unsigned IOCTL_SOUND_MIXER_READ_TREBLE;
+ extern unsigned IOCTL_SOUND_MIXER_READ_VOLUME;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_BASS;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_CD;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_IMIX;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE1;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE2;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE3;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LINE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_LOUD;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_MIC;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_MUTE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_PCM;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE;
+ extern unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME;
+ extern unsigned IOCTL_SOUND_PCM_READ_BITS;
+ extern unsigned IOCTL_SOUND_PCM_READ_CHANNELS;
+ extern unsigned IOCTL_SOUND_PCM_READ_FILTER;
+ extern unsigned IOCTL_SOUND_PCM_READ_RATE;
+ extern unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS;
+ extern unsigned IOCTL_SOUND_PCM_WRITE_FILTER;
+ extern unsigned IOCTL_VT_ACTIVATE;
+ extern unsigned IOCTL_VT_GETMODE;
+ extern unsigned IOCTL_VT_OPENQRY;
+ extern unsigned IOCTL_VT_RELDISP;
+ extern unsigned IOCTL_VT_SETMODE;
+ extern unsigned IOCTL_VT_WAITACTIVE;
+ extern unsigned IOCTL_GIO_SCRNMAP;
+ extern unsigned IOCTL_KDDISABIO;
+ extern unsigned IOCTL_KDENABIO;
+ extern unsigned IOCTL_KDGETLED;
+ extern unsigned IOCTL_KDGETMODE;
+ extern unsigned IOCTL_KDGKBMODE;
+ extern unsigned IOCTL_KDGKBTYPE;
+ extern unsigned IOCTL_KDMKTONE;
+ extern unsigned IOCTL_KDSETLED;
+ extern unsigned IOCTL_KDSETMODE;
+ extern unsigned IOCTL_KDSKBMODE;
+
+ extern const int si_SEGV_MAPERR;
+ extern const int si_SEGV_ACCERR;
+
+ struct __sanitizer_cap_rights {
+ u64 cr_rights[2];
+ };
+
+ typedef struct __sanitizer_cap_rights __sanitizer_cap_rights_t;
+ extern unsigned struct_cap_rights_sz;
+
+ extern unsigned struct_fstab_sz;
+ extern unsigned struct_StringList_sz;
+} // namespace __sanitizer
+
+#define CHECK_TYPE_SIZE(TYPE) \
+ COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE))
+
+#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \
+ COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \
+ sizeof(((CLASS *) NULL)->MEMBER)); \
+ COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \
+ offsetof(CLASS, MEMBER))
+
+// For sigaction, which is a function and struct at the same time,
+// and thus requires explicit "struct" in sizeof() expression.
+#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \
+ COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \
+ sizeof(((struct CLASS *) NULL)->MEMBER)); \
+ COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \
+ offsetof(struct CLASS, MEMBER))
+
+#define SIGACTION_SYMNAME sigaction
+
+#endif
+
+#endif // SANITIZER_FREEBSD
+++ /dev/null
-//===-- sanitizer_platform_limits_linux.cc --------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of Sanitizer common code.
-//
-// Sizes and layouts of linux kernel data structures.
-//===----------------------------------------------------------------------===//
-
-// This is a separate compilation unit for linux headers that conflict with
-// userspace headers.
-// Most "normal" includes go in sanitizer_platform_limits_posix.cc
-
-#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
-
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_posix.h"
-
-// For offsetof -> __builtin_offsetof definition.
-#include <stddef.h>
-
-// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that
-// are not defined anywhere in userspace headers. Fake them. This seems to work
-// fine with newer headers, too. Beware that with <sys/stat.h>, struct stat
-// takes the form of struct stat64 on 32-bit platforms if _FILE_OFFSET_BITS=64.
-// Also, for some platforms (e.g. mips) there are additional members in the
-// <sys/stat.h> struct stat:s.
-#include <linux/posix_types.h>
-#if defined(__x86_64__)
-#include <sys/stat.h>
-#else
-#define ino_t __kernel_ino_t
-#define mode_t __kernel_mode_t
-#define nlink_t __kernel_nlink_t
-#define uid_t __kernel_uid_t
-#define gid_t __kernel_gid_t
-#define off_t __kernel_off_t
-#define time_t __kernel_time_t
-// This header seems to contain the definitions of _kernel_ stat* structs.
-#include <asm/stat.h>
-#undef ino_t
-#undef mode_t
-#undef nlink_t
-#undef uid_t
-#undef gid_t
-#undef off_t
-#endif
-
-#include <linux/aio_abi.h>
-
-#if !SANITIZER_ANDROID
-#include <sys/statfs.h>
-#include <linux/perf_event.h>
-#endif
-
-using namespace __sanitizer;
-
-namespace __sanitizer {
-#if !SANITIZER_ANDROID
- unsigned struct_statfs64_sz = sizeof(struct statfs64);
-#endif
-} // namespace __sanitizer
-
-#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
- && !defined(__mips__) && !defined(__s390__)\
- && !defined(__sparc__)
-COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
-#endif
-
-COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat));
-
-#if defined(__i386__)
-COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64));
-#endif
-
-CHECK_TYPE_SIZE(io_event);
-CHECK_SIZE_AND_OFFSET(io_event, data);
-CHECK_SIZE_AND_OFFSET(io_event, obj);
-CHECK_SIZE_AND_OFFSET(io_event, res);
-CHECK_SIZE_AND_OFFSET(io_event, res2);
-
-#if !SANITIZER_ANDROID
-COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <=
- sizeof(struct perf_event_attr));
-CHECK_SIZE_AND_OFFSET(perf_event_attr, type);
-CHECK_SIZE_AND_OFFSET(perf_event_attr, size);
-#endif
-
-COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD);
-COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE);
-#if !SANITIZER_ANDROID
-COMPILER_CHECK(iocb_cmd_preadv == IOCB_CMD_PREADV);
-COMPILER_CHECK(iocb_cmd_pwritev == IOCB_CMD_PWRITEV);
-#endif
-
-CHECK_TYPE_SIZE(iocb);
-CHECK_SIZE_AND_OFFSET(iocb, aio_data);
-// Skip aio_key, it's weird.
-CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode);
-CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio);
-CHECK_SIZE_AND_OFFSET(iocb, aio_fildes);
-CHECK_SIZE_AND_OFFSET(iocb, aio_buf);
-CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes);
-CHECK_SIZE_AND_OFFSET(iocb, aio_offset);
-
-#endif // SANITIZER_LINUX
--- /dev/null
+//===-- sanitizer_platform_limits_linux.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of linux kernel data structures.
+//===----------------------------------------------------------------------===//
+
+// This is a separate compilation unit for linux headers that conflict with
+// userspace headers.
+// Most "normal" includes go in sanitizer_platform_limits_posix.cpp
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_posix.h"
+
+// For offsetof -> __builtin_offsetof definition.
+#include <stddef.h>
+
+// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that
+// are not defined anywhere in userspace headers. Fake them. This seems to work
+// fine with newer headers, too.
+#include <linux/posix_types.h>
+#if defined(__x86_64__) || defined(__mips__)
+#include <sys/stat.h>
+#else
+#define ino_t __kernel_ino_t
+#define mode_t __kernel_mode_t
+#define nlink_t __kernel_nlink_t
+#define uid_t __kernel_uid_t
+#define gid_t __kernel_gid_t
+#define off_t __kernel_off_t
+#define time_t __kernel_time_t
+// This header seems to contain the definitions of _kernel_ stat* structs.
+#include <asm/stat.h>
+#undef ino_t
+#undef mode_t
+#undef nlink_t
+#undef uid_t
+#undef gid_t
+#undef off_t
+#endif
+
+#include <linux/aio_abi.h>
+
+#if !SANITIZER_ANDROID
+#include <sys/statfs.h>
+#include <linux/perf_event.h>
+#endif
+
+using namespace __sanitizer;
+
+namespace __sanitizer {
+#if !SANITIZER_ANDROID
+ unsigned struct_statfs64_sz = sizeof(struct statfs64);
+#endif
+} // namespace __sanitizer
+
+#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
+ && !defined(__mips__) && !defined(__s390__)\
+ && !defined(__sparc__)
+COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
+#endif
+
+COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat));
+
+#if defined(__i386__)
+COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64));
+#endif
+
+CHECK_TYPE_SIZE(io_event);
+CHECK_SIZE_AND_OFFSET(io_event, data);
+CHECK_SIZE_AND_OFFSET(io_event, obj);
+CHECK_SIZE_AND_OFFSET(io_event, res);
+CHECK_SIZE_AND_OFFSET(io_event, res2);
+
+#if !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <=
+ sizeof(struct perf_event_attr));
+CHECK_SIZE_AND_OFFSET(perf_event_attr, type);
+CHECK_SIZE_AND_OFFSET(perf_event_attr, size);
+#endif
+
+COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD);
+COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE);
+#if !SANITIZER_ANDROID
+COMPILER_CHECK(iocb_cmd_preadv == IOCB_CMD_PREADV);
+COMPILER_CHECK(iocb_cmd_pwritev == IOCB_CMD_PWRITEV);
+#endif
+
+CHECK_TYPE_SIZE(iocb);
+CHECK_SIZE_AND_OFFSET(iocb, aio_data);
+// Skip aio_key, it's weird.
+CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode);
+CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio);
+CHECK_SIZE_AND_OFFSET(iocb, aio_fildes);
+CHECK_SIZE_AND_OFFSET(iocb, aio_buf);
+CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes);
+CHECK_SIZE_AND_OFFSET(iocb, aio_offset);
+
+#endif // SANITIZER_LINUX
+++ /dev/null
-//===-- sanitizer_platform_limits_netbsd.cc -------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of Sanitizer common code.
-//
-// Sizes and layouts of platform-specific NetBSD data structures.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_NETBSD
-#include <sys/param.h>
-#include <sys/types.h>
-
-#include <altq/altq.h>
-#include <altq/altq_afmap.h>
-#include <altq/altq_blue.h>
-#include <altq/altq_cbq.h>
-#include <altq/altq_cdnr.h>
-#include <altq/altq_fifoq.h>
-#include <altq/altq_hfsc.h>
-#include <altq/altq_jobs.h>
-#include <altq/altq_priq.h>
-#include <altq/altq_red.h>
-#include <altq/altq_rio.h>
-#include <altq/altq_wfq.h>
-#include <arpa/inet.h>
-#include <crypto/cryptodev.h>
-#include <dev/apm/apmio.h>
-#include <dev/dm/netbsd-dm.h>
-#include <dev/dmover/dmover_io.h>
-#include <dev/dtv/dtvio_demux.h>
-#include <dev/dtv/dtvio_frontend.h>
-#include <dev/filemon/filemon.h>
-#include <dev/hdaudio/hdaudioio.h>
-#include <dev/hdmicec/hdmicecio.h>
-#include <dev/hpc/hpcfbio.h>
-#include <dev/i2o/iopio.h>
-#include <dev/ic/athioctl.h>
-#include <dev/ic/bt8xx.h>
-#include <dev/ic/icp_ioctl.h>
-#include <dev/ic/isp_ioctl.h>
-#include <dev/ic/mlxio.h>
-#include <dev/ic/nvmeio.h>
-#include <dev/ir/irdaio.h>
-#include <dev/isa/isvio.h>
-#include <dev/isa/satlinkio.h>
-#include <dev/isa/wtreg.h>
-#include <dev/iscsi/iscsi_ioctl.h>
-#include <dev/ofw/openfirmio.h>
-#include <dev/pci/amrio.h>
-
-#include <dev/pci/mlyreg.h>
-#include <dev/pci/mlyio.h>
-
-#include <dev/pci/pciio.h>
-#include <dev/pci/tweio.h>
-#include <dev/pcmcia/if_cnwioctl.h>
-#include <dirent.h>
-#include <glob.h>
-#include <grp.h>
-#include <ifaddrs.h>
-#include <limits.h>
-#include <link_elf.h>
-#include <net/if.h>
-#include <net/if_ether.h>
-#include <net/ppp_defs.h>
-#include <net/route.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/ip_compat.h>
-#include <netinet/ip_fil.h>
-#include <netinet/ip_mroute.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <sys/disk.h>
-#include <sys/disklabel.h>
-#include <sys/mount.h>
-#define RAY_DO_SIGLEV
-#include <dev/biovar.h>
-#include <dev/bluetooth/btdev.h>
-#include <dev/bluetooth/btsco.h>
-#include <dev/ccdvar.h>
-#include <dev/cgdvar.h>
-#include <dev/fssvar.h>
-#include <dev/kttcpio.h>
-#include <dev/lockstat.h>
-#include <dev/md.h>
-#include <dev/pcmcia/if_rayreg.h>
-#include <dev/raidframe/raidframeio.h>
-#include <dev/sbus/mbppio.h>
-#include <dev/scsipi/ses.h>
-#include <dev/spkrio.h>
-#include <dev/sun/disklabel.h>
-#include <dev/sun/fbio.h>
-#include <dev/sun/kbio.h>
-#include <dev/sun/vuid_event.h>
-#include <dev/tc/sticio.h>
-#include <dev/usb/ukyopon.h>
-#include <dev/usb/urio.h>
-#include <dev/usb/usb.h>
-#include <dev/usb/utoppy.h>
-#include <dev/vme/xio.h>
-#include <dev/vndvar.h>
-#include <dev/wscons/wsconsio.h>
-#include <dev/wscons/wsdisplay_usl_io.h>
-#include <net/bpf.h>
-#include <net/if_atm.h>
-#include <net/if_gre.h>
-#include <net/if_ppp.h>
-#include <net/if_pppoe.h>
-#include <net/if_sppp.h>
-#include <net/if_srt.h>
-#include <net/if_tap.h>
-#include <net/if_tun.h>
-#include <net/npf.h>
-#include <net/pfvar.h>
-#include <net/slip.h>
-#include <netbt/hci.h>
-#include <netinet/ip_nat.h>
-#include <netinet/ip_proxy.h>
-#include <netinet6/in6_var.h>
-#include <netinet6/nd6.h>
-#include <netnatm/natm.h>
-#include <netsmb/smb_dev.h>
-#include <soundcard.h>
-#include <sys/agpio.h>
-#include <sys/ataio.h>
-#include <sys/audioio.h>
-#include <sys/cdio.h>
-#include <sys/chio.h>
-#include <sys/clockctl.h>
-#include <sys/cpuio.h>
-#include <sys/dkio.h>
-#include <sys/drvctlio.h>
-#include <sys/dvdio.h>
-#include <sys/envsys.h>
-#include <sys/event.h>
-#include <sys/fdio.h>
-#include <sys/filio.h>
-#include <sys/gpio.h>
-#include <sys/ioctl.h>
-#include <sys/ioctl_compat.h>
-#include <sys/joystick.h>
-#include <sys/ksyms.h>
-#include <sys/lua.h>
-#include <sys/midiio.h>
-#include <sys/mtio.h>
-#include <sys/power.h>
-#include <sys/radioio.h>
-#include <sys/rndio.h>
-#include <sys/scanio.h>
-#include <sys/scsiio.h>
-#include <sys/sockio.h>
-#include <sys/timepps.h>
-#include <sys/ttycom.h>
-#include <sys/verified_exec.h>
-#include <sys/videoio.h>
-#include <sys/wdog.h>
-//#include <xen/xenio.h>
-#include <sys/event.h>
-#include <sys/filio.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/mqueue.h>
-#include <sys/msg.h>
-#include <sys/mtio.h>
-#include <sys/ptrace.h>
-#include <sys/resource.h>
-#include <sys/sem.h>
-#include <sys/shm.h>
-#include <sys/signal.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/soundcard.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/time.h>
-#include <sys/timeb.h>
-#include <sys/times.h>
-#include <sys/timespec.h>
-#include <sys/timex.h>
-#include <sys/types.h>
-#include <sys/ucontext.h>
-#include <sys/utsname.h>
-#include <term.h>
-#include <termios.h>
-#include <time.h>
-#include <ttyent.h>
-#include <utime.h>
-#include <utmp.h>
-#include <utmpx.h>
-#include <wchar.h>
-#include <wordexp.h>
-
-// Include these after system headers to avoid name clashes and ambiguities.
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_netbsd.h"
-
-namespace __sanitizer {
-unsigned struct_utsname_sz = sizeof(struct utsname);
-unsigned struct_stat_sz = sizeof(struct stat);
-unsigned struct_rusage_sz = sizeof(struct rusage);
-unsigned struct_tm_sz = sizeof(struct tm);
-unsigned struct_passwd_sz = sizeof(struct passwd);
-unsigned struct_group_sz = sizeof(struct group);
-unsigned siginfo_t_sz = sizeof(siginfo_t);
-unsigned struct_sigaction_sz = sizeof(struct sigaction);
-unsigned struct_itimerval_sz = sizeof(struct itimerval);
-unsigned pthread_t_sz = sizeof(pthread_t);
-unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
-unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
-unsigned pid_t_sz = sizeof(pid_t);
-unsigned timeval_sz = sizeof(timeval);
-unsigned uid_t_sz = sizeof(uid_t);
-unsigned gid_t_sz = sizeof(gid_t);
-unsigned mbstate_t_sz = sizeof(mbstate_t);
-unsigned sigset_t_sz = sizeof(sigset_t);
-unsigned struct_timezone_sz = sizeof(struct timezone);
-unsigned struct_tms_sz = sizeof(struct tms);
-unsigned struct_sigevent_sz = sizeof(struct sigevent);
-unsigned struct_sched_param_sz = sizeof(struct sched_param);
-unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
-unsigned ucontext_t_sz = sizeof(ucontext_t);
-unsigned struct_rlimit_sz = sizeof(struct rlimit);
-unsigned struct_timespec_sz = sizeof(struct timespec);
-unsigned struct_sembuf_sz = sizeof(struct sembuf);
-unsigned struct_kevent_sz = sizeof(struct kevent);
-unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
-unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
-unsigned struct_timex_sz = sizeof(struct timex);
-unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
-unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
-unsigned struct_statvfs_sz = sizeof(struct statvfs);
-unsigned struct_sigaltstack_sz = sizeof(stack_t);
-
-const uptr sig_ign = (uptr)SIG_IGN;
-const uptr sig_dfl = (uptr)SIG_DFL;
-const uptr sig_err = (uptr)SIG_ERR;
-const uptr sa_siginfo = (uptr)SA_SIGINFO;
-
-int ptrace_pt_io = PT_IO;
-int ptrace_pt_lwpinfo = PT_LWPINFO;
-int ptrace_pt_set_event_mask = PT_SET_EVENT_MASK;
-int ptrace_pt_get_event_mask = PT_GET_EVENT_MASK;
-int ptrace_pt_get_process_state = PT_GET_PROCESS_STATE;
-int ptrace_pt_set_siginfo = PT_SET_SIGINFO;
-int ptrace_pt_get_siginfo = PT_GET_SIGINFO;
-int ptrace_piod_read_d = PIOD_READ_D;
-int ptrace_piod_write_d = PIOD_WRITE_D;
-int ptrace_piod_read_i = PIOD_READ_I;
-int ptrace_piod_write_i = PIOD_WRITE_I;
-int ptrace_piod_read_auxv = PIOD_READ_AUXV;
-
-#if defined(PT_SETREGS) && defined(PT_GETREGS)
-int ptrace_pt_setregs = PT_SETREGS;
-int ptrace_pt_getregs = PT_GETREGS;
-#else
-int ptrace_pt_setregs = -1;
-int ptrace_pt_getregs = -1;
-#endif
-
-#if defined(PT_SETFPREGS) && defined(PT_GETFPREGS)
-int ptrace_pt_setfpregs = PT_SETFPREGS;
-int ptrace_pt_getfpregs = PT_GETFPREGS;
-#else
-int ptrace_pt_setfpregs = -1;
-int ptrace_pt_getfpregs = -1;
-#endif
-
-#if defined(PT_SETDBREGS) && defined(PT_GETDBREGS)
-int ptrace_pt_setdbregs = PT_SETDBREGS;
-int ptrace_pt_getdbregs = PT_GETDBREGS;
-#else
-int ptrace_pt_setdbregs = -1;
-int ptrace_pt_getdbregs = -1;
-#endif
-
-unsigned struct_ptrace_ptrace_io_desc_struct_sz = sizeof(struct ptrace_io_desc);
-unsigned struct_ptrace_ptrace_lwpinfo_struct_sz = sizeof(struct ptrace_lwpinfo);
-unsigned struct_ptrace_ptrace_event_struct_sz = sizeof(ptrace_event_t);
-unsigned struct_ptrace_ptrace_siginfo_struct_sz = sizeof(ptrace_siginfo_t);
-
-#if defined(PT_SETREGS)
-unsigned struct_ptrace_reg_struct_sz = sizeof(struct reg);
-#else
-unsigned struct_ptrace_reg_struct_sz = -1;
-#endif
-
-#if defined(PT_SETFPREGS)
-unsigned struct_ptrace_fpreg_struct_sz = sizeof(struct fpreg);
-#else
-unsigned struct_ptrace_fpreg_struct_sz = -1;
-#endif
-
-#if defined(PT_SETDBREGS)
-unsigned struct_ptrace_dbreg_struct_sz = sizeof(struct dbreg);
-#else
-unsigned struct_ptrace_dbreg_struct_sz = -1;
-#endif
-
-int shmctl_ipc_stat = (int)IPC_STAT;
-
-unsigned struct_utmp_sz = sizeof(struct utmp);
-unsigned struct_utmpx_sz = sizeof(struct utmpx);
-
-int map_fixed = MAP_FIXED;
-
-int af_inet = (int)AF_INET;
-int af_inet6 = (int)AF_INET6;
-
-uptr __sanitizer_in_addr_sz(int af) {
- if (af == AF_INET)
- return sizeof(struct in_addr);
- else if (af == AF_INET6)
- return sizeof(struct in6_addr);
- else
- return 0;
-}
-
-unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
-
-int glob_nomatch = GLOB_NOMATCH;
-int glob_altdirfunc = GLOB_ALTDIRFUNC;
-
-unsigned path_max = PATH_MAX;
-
-int struct_ttyent_sz = sizeof(struct ttyent);
-
-// ioctl arguments
-unsigned struct_altqreq_sz = sizeof(altqreq);
-unsigned struct_amr_user_ioctl_sz = sizeof(amr_user_ioctl);
-unsigned struct_ap_control_sz = sizeof(ap_control);
-unsigned struct_apm_ctl_sz = sizeof(apm_ctl);
-unsigned struct_apm_event_info_sz = sizeof(apm_event_info);
-unsigned struct_apm_power_info_sz = sizeof(apm_power_info);
-unsigned struct_atabusiodetach_args_sz = sizeof(atabusiodetach_args);
-unsigned struct_atabusioscan_args_sz = sizeof(atabusioscan_args);
-unsigned struct_ath_diag_sz = sizeof(ath_diag);
-unsigned struct_atm_flowmap_sz = sizeof(atm_flowmap);
-unsigned struct_atm_pseudoioctl_sz = sizeof(atm_pseudoioctl);
-unsigned struct_audio_buf_info_sz = sizeof(audio_buf_info);
-unsigned struct_audio_device_sz = sizeof(audio_device);
-unsigned struct_audio_encoding_sz = sizeof(audio_encoding);
-unsigned struct_audio_info_sz = sizeof(audio_info);
-unsigned struct_audio_offset_sz = sizeof(audio_offset);
-unsigned struct_bio_locate_sz = sizeof(bio_locate);
-unsigned struct_bioc_alarm_sz = sizeof(bioc_alarm);
-unsigned struct_bioc_blink_sz = sizeof(bioc_blink);
-unsigned struct_bioc_disk_sz = sizeof(bioc_disk);
-unsigned struct_bioc_inq_sz = sizeof(bioc_inq);
-unsigned struct_bioc_setstate_sz = sizeof(bioc_setstate);
-unsigned struct_bioc_vol_sz = sizeof(bioc_vol);
-unsigned struct_bioc_volops_sz = sizeof(bioc_volops);
-unsigned struct_bktr_chnlset_sz = sizeof(bktr_chnlset);
-unsigned struct_bktr_remote_sz = sizeof(bktr_remote);
-unsigned struct_blue_conf_sz = sizeof(blue_conf);
-unsigned struct_blue_interface_sz = sizeof(blue_interface);
-unsigned struct_blue_stats_sz = sizeof(blue_stats);
-unsigned struct_bpf_dltlist_sz = sizeof(bpf_dltlist);
-unsigned struct_bpf_program_sz = sizeof(bpf_program);
-unsigned struct_bpf_stat_old_sz = sizeof(bpf_stat_old);
-unsigned struct_bpf_stat_sz = sizeof(bpf_stat);
-unsigned struct_bpf_version_sz = sizeof(bpf_version);
-unsigned struct_btreq_sz = sizeof(btreq);
-unsigned struct_btsco_info_sz = sizeof(btsco_info);
-unsigned struct_buffmem_desc_sz = sizeof(buffmem_desc);
-unsigned struct_cbq_add_class_sz = sizeof(cbq_add_class);
-unsigned struct_cbq_add_filter_sz = sizeof(cbq_add_filter);
-unsigned struct_cbq_delete_class_sz = sizeof(cbq_delete_class);
-unsigned struct_cbq_delete_filter_sz = sizeof(cbq_delete_filter);
-unsigned struct_cbq_getstats_sz = sizeof(cbq_getstats);
-unsigned struct_cbq_interface_sz = sizeof(cbq_interface);
-unsigned struct_cbq_modify_class_sz = sizeof(cbq_modify_class);
-unsigned struct_ccd_ioctl_sz = sizeof(ccd_ioctl);
-unsigned struct_cdnr_add_element_sz = sizeof(cdnr_add_element);
-unsigned struct_cdnr_add_filter_sz = sizeof(cdnr_add_filter);
-unsigned struct_cdnr_add_tbmeter_sz = sizeof(cdnr_add_tbmeter);
-unsigned struct_cdnr_add_trtcm_sz = sizeof(cdnr_add_trtcm);
-unsigned struct_cdnr_add_tswtcm_sz = sizeof(cdnr_add_tswtcm);
-unsigned struct_cdnr_delete_element_sz = sizeof(cdnr_delete_element);
-unsigned struct_cdnr_delete_filter_sz = sizeof(cdnr_delete_filter);
-unsigned struct_cdnr_get_stats_sz = sizeof(cdnr_get_stats);
-unsigned struct_cdnr_interface_sz = sizeof(cdnr_interface);
-unsigned struct_cdnr_modify_tbmeter_sz = sizeof(cdnr_modify_tbmeter);
-unsigned struct_cdnr_modify_trtcm_sz = sizeof(cdnr_modify_trtcm);
-unsigned struct_cdnr_modify_tswtcm_sz = sizeof(cdnr_modify_tswtcm);
-unsigned struct_cdnr_tbmeter_stats_sz = sizeof(cdnr_tbmeter_stats);
-unsigned struct_cdnr_tcm_stats_sz = sizeof(cdnr_tcm_stats);
-unsigned struct_cgd_ioctl_sz = sizeof(cgd_ioctl);
-unsigned struct_cgd_user_sz = sizeof(cgd_user);
-unsigned struct_changer_element_status_request_sz =
- sizeof(changer_element_status_request);
-unsigned struct_changer_exchange_request_sz = sizeof(changer_exchange_request);
-unsigned struct_changer_move_request_sz = sizeof(changer_move_request);
-unsigned struct_changer_params_sz = sizeof(changer_params);
-unsigned struct_changer_position_request_sz = sizeof(changer_position_request);
-unsigned struct_changer_set_voltag_request_sz =
- sizeof(changer_set_voltag_request);
-unsigned struct_clockctl_adjtime_sz = sizeof(clockctl_adjtime);
-unsigned struct_clockctl_clock_settime_sz = sizeof(clockctl_clock_settime);
-unsigned struct_clockctl_ntp_adjtime_sz = sizeof(clockctl_ntp_adjtime);
-unsigned struct_clockctl_settimeofday_sz = sizeof(clockctl_settimeofday);
-unsigned struct_cnwistats_sz = sizeof(cnwistats);
-unsigned struct_cnwitrail_sz = sizeof(cnwitrail);
-unsigned struct_cnwstatus_sz = sizeof(cnwstatus);
-unsigned struct_count_info_sz = sizeof(count_info);
-unsigned struct_cpu_ucode_sz = sizeof(cpu_ucode);
-unsigned struct_cpu_ucode_version_sz = sizeof(cpu_ucode_version);
-unsigned struct_crypt_kop_sz = sizeof(crypt_kop);
-unsigned struct_crypt_mkop_sz = sizeof(crypt_mkop);
-unsigned struct_crypt_mop_sz = sizeof(crypt_mop);
-unsigned struct_crypt_op_sz = sizeof(crypt_op);
-unsigned struct_crypt_result_sz = sizeof(crypt_result);
-unsigned struct_crypt_sfop_sz = sizeof(crypt_sfop);
-unsigned struct_crypt_sgop_sz = sizeof(crypt_sgop);
-unsigned struct_cryptret_sz = sizeof(cryptret);
-unsigned struct_devdetachargs_sz = sizeof(devdetachargs);
-unsigned struct_devlistargs_sz = sizeof(devlistargs);
-unsigned struct_devpmargs_sz = sizeof(devpmargs);
-unsigned struct_devrescanargs_sz = sizeof(devrescanargs);
-unsigned struct_disk_badsecinfo_sz = sizeof(disk_badsecinfo);
-unsigned struct_disk_strategy_sz = sizeof(disk_strategy);
-unsigned struct_disklabel_sz = sizeof(disklabel);
-unsigned struct_dkbad_sz = sizeof(dkbad);
-unsigned struct_dkwedge_info_sz = sizeof(dkwedge_info);
-unsigned struct_dkwedge_list_sz = sizeof(dkwedge_list);
-unsigned struct_dmio_setfunc_sz = sizeof(dmio_setfunc);
-unsigned struct_dmx_pes_filter_params_sz = sizeof(dmx_pes_filter_params);
-unsigned struct_dmx_sct_filter_params_sz = sizeof(dmx_sct_filter_params);
-unsigned struct_dmx_stc_sz = sizeof(dmx_stc);
-unsigned struct_dvb_diseqc_master_cmd_sz = sizeof(dvb_diseqc_master_cmd);
-unsigned struct_dvb_diseqc_slave_reply_sz = sizeof(dvb_diseqc_slave_reply);
-unsigned struct_dvb_frontend_event_sz = sizeof(dvb_frontend_event);
-unsigned struct_dvb_frontend_info_sz = sizeof(dvb_frontend_info);
-unsigned struct_dvb_frontend_parameters_sz = sizeof(dvb_frontend_parameters);
-unsigned struct_eccapreq_sz = sizeof(eccapreq);
-unsigned struct_fbcmap_sz = sizeof(fbcmap);
-unsigned struct_fbcurpos_sz = sizeof(fbcurpos);
-unsigned struct_fbcursor_sz = sizeof(fbcursor);
-unsigned struct_fbgattr_sz = sizeof(fbgattr);
-unsigned struct_fbsattr_sz = sizeof(fbsattr);
-unsigned struct_fbtype_sz = sizeof(fbtype);
-unsigned struct_fdformat_cmd_sz = sizeof(fdformat_cmd);
-unsigned struct_fdformat_parms_sz = sizeof(fdformat_parms);
-unsigned struct_fifoq_conf_sz = sizeof(fifoq_conf);
-unsigned struct_fifoq_getstats_sz = sizeof(fifoq_getstats);
-unsigned struct_fifoq_interface_sz = sizeof(fifoq_interface);
-unsigned struct_format_op_sz = sizeof(format_op);
-unsigned struct_fss_get_sz = sizeof(fss_get);
-unsigned struct_fss_set_sz = sizeof(fss_set);
-unsigned struct_gpio_attach_sz = sizeof(gpio_attach);
-unsigned struct_gpio_info_sz = sizeof(gpio_info);
-unsigned struct_gpio_req_sz = sizeof(gpio_req);
-unsigned struct_gpio_set_sz = sizeof(gpio_set);
-unsigned struct_hfsc_add_class_sz = sizeof(hfsc_add_class);
-unsigned struct_hfsc_add_filter_sz = sizeof(hfsc_add_filter);
-unsigned struct_hfsc_attach_sz = sizeof(hfsc_attach);
-unsigned struct_hfsc_class_stats_sz = sizeof(hfsc_class_stats);
-unsigned struct_hfsc_delete_class_sz = sizeof(hfsc_delete_class);
-unsigned struct_hfsc_delete_filter_sz = sizeof(hfsc_delete_filter);
-unsigned struct_hfsc_interface_sz = sizeof(hfsc_interface);
-unsigned struct_hfsc_modify_class_sz = sizeof(hfsc_modify_class);
-unsigned struct_hpcfb_dsp_op_sz = sizeof(hpcfb_dsp_op);
-unsigned struct_hpcfb_dspconf_sz = sizeof(hpcfb_dspconf);
-unsigned struct_hpcfb_fbconf_sz = sizeof(hpcfb_fbconf);
-unsigned struct_if_addrprefreq_sz = sizeof(if_addrprefreq);
-unsigned struct_if_clonereq_sz = sizeof(if_clonereq);
-unsigned struct_if_laddrreq_sz = sizeof(if_laddrreq);
-unsigned struct_ifaddr_sz = sizeof(ifaddr);
-unsigned struct_ifaliasreq_sz = sizeof(ifaliasreq);
-unsigned struct_ifcapreq_sz = sizeof(ifcapreq);
-unsigned struct_ifconf_sz = sizeof(ifconf);
-unsigned struct_ifdatareq_sz = sizeof(ifdatareq);
-unsigned struct_ifdrv_sz = sizeof(ifdrv);
-unsigned struct_ifmediareq_sz = sizeof(ifmediareq);
-unsigned struct_ifpppcstatsreq_sz = sizeof(ifpppcstatsreq);
-unsigned struct_ifpppstatsreq_sz = sizeof(ifpppstatsreq);
-unsigned struct_ifreq_sz = sizeof(ifreq);
-unsigned struct_in6_addrpolicy_sz = sizeof(in6_addrpolicy);
-unsigned struct_in6_ndireq_sz = sizeof(in6_ndireq);
-unsigned struct_ioc_load_unload_sz = sizeof(ioc_load_unload);
-unsigned struct_ioc_patch_sz = sizeof(ioc_patch);
-unsigned struct_ioc_play_blocks_sz = sizeof(ioc_play_blocks);
-unsigned struct_ioc_play_msf_sz = sizeof(ioc_play_msf);
-unsigned struct_ioc_play_track_sz = sizeof(ioc_play_track);
-unsigned struct_ioc_read_subchannel_sz = sizeof(ioc_read_subchannel);
-unsigned struct_ioc_read_toc_entry_sz = sizeof(ioc_read_toc_entry);
-unsigned struct_ioc_toc_header_sz = sizeof(ioc_toc_header);
-unsigned struct_ioc_vol_sz = sizeof(ioc_vol);
-unsigned struct_ioctl_pt_sz = sizeof(ioctl_pt);
-unsigned struct_ioppt_sz = sizeof(ioppt);
-unsigned struct_iovec_sz = sizeof(iovec);
-unsigned struct_ipfobj_sz = sizeof(ipfobj);
-unsigned struct_irda_params_sz = sizeof(irda_params);
-unsigned struct_isp_fc_device_sz = sizeof(isp_fc_device);
-unsigned struct_isp_fc_tsk_mgmt_sz = sizeof(isp_fc_tsk_mgmt);
-unsigned struct_isp_hba_device_sz = sizeof(isp_hba_device);
-unsigned struct_isv_cmd_sz = sizeof(isv_cmd);
-unsigned struct_jobs_add_class_sz = sizeof(jobs_add_class);
-unsigned struct_jobs_add_filter_sz = sizeof(jobs_add_filter);
-unsigned struct_jobs_attach_sz = sizeof(jobs_attach);
-unsigned struct_jobs_class_stats_sz = sizeof(jobs_class_stats);
-unsigned struct_jobs_delete_class_sz = sizeof(jobs_delete_class);
-unsigned struct_jobs_delete_filter_sz = sizeof(jobs_delete_filter);
-unsigned struct_jobs_interface_sz = sizeof(jobs_interface);
-unsigned struct_jobs_modify_class_sz = sizeof(jobs_modify_class);
-unsigned struct_kbentry_sz = sizeof(kbentry);
-unsigned struct_kfilter_mapping_sz = sizeof(kfilter_mapping);
-unsigned struct_kiockeymap_sz = sizeof(kiockeymap);
-unsigned struct_ksyms_gsymbol_sz = sizeof(ksyms_gsymbol);
-unsigned struct_ksyms_gvalue_sz = sizeof(ksyms_gvalue);
-unsigned struct_ksyms_ogsymbol_sz = sizeof(ksyms_ogsymbol);
-unsigned struct_kttcp_io_args_sz = sizeof(kttcp_io_args);
-unsigned struct_ltchars_sz = sizeof(ltchars);
-unsigned struct_lua_create_sz = sizeof(struct lua_create);
-unsigned struct_lua_info_sz = sizeof(struct lua_info);
-unsigned struct_lua_load_sz = sizeof(struct lua_load);
-unsigned struct_lua_require_sz = sizeof(lua_require);
-unsigned struct_mbpp_param_sz = sizeof(mbpp_param);
-unsigned struct_md_conf_sz = sizeof(md_conf);
-unsigned struct_meteor_capframe_sz = sizeof(meteor_capframe);
-unsigned struct_meteor_counts_sz = sizeof(meteor_counts);
-unsigned struct_meteor_geomet_sz = sizeof(meteor_geomet);
-unsigned struct_meteor_pixfmt_sz = sizeof(meteor_pixfmt);
-unsigned struct_meteor_video_sz = sizeof(meteor_video);
-unsigned struct_mlx_cinfo_sz = sizeof(mlx_cinfo);
-unsigned struct_mlx_pause_sz = sizeof(mlx_pause);
-unsigned struct_mlx_rebuild_request_sz = sizeof(mlx_rebuild_request);
-unsigned struct_mlx_rebuild_status_sz = sizeof(mlx_rebuild_status);
-unsigned struct_mlx_usercommand_sz = sizeof(mlx_usercommand);
-unsigned struct_mly_user_command_sz = sizeof(mly_user_command);
-unsigned struct_mly_user_health_sz = sizeof(mly_user_health);
-unsigned struct_mtget_sz = sizeof(mtget);
-unsigned struct_mtop_sz = sizeof(mtop);
-unsigned struct_npf_ioctl_table_sz = sizeof(npf_ioctl_table);
-unsigned struct_npioctl_sz = sizeof(npioctl);
-unsigned struct_nvme_pt_command_sz = sizeof(nvme_pt_command);
-unsigned struct_ochanger_element_status_request_sz =
- sizeof(ochanger_element_status_request);
-unsigned struct_ofiocdesc_sz = sizeof(ofiocdesc);
-unsigned struct_okiockey_sz = sizeof(okiockey);
-unsigned struct_ortentry_sz = sizeof(ortentry);
-unsigned struct_oscsi_addr_sz = sizeof(oscsi_addr);
-unsigned struct_oss_audioinfo_sz = sizeof(oss_audioinfo);
-unsigned struct_oss_sysinfo_sz = sizeof(oss_sysinfo);
-unsigned struct_pciio_bdf_cfgreg_sz = sizeof(pciio_bdf_cfgreg);
-unsigned struct_pciio_businfo_sz = sizeof(pciio_businfo);
-unsigned struct_pciio_cfgreg_sz = sizeof(pciio_cfgreg);
-unsigned struct_pciio_drvname_sz = sizeof(pciio_drvname);
-unsigned struct_pciio_drvnameonbus_sz = sizeof(pciio_drvnameonbus);
-unsigned struct_pcvtid_sz = sizeof(pcvtid);
-unsigned struct_pf_osfp_ioctl_sz = sizeof(pf_osfp_ioctl);
-unsigned struct_pf_status_sz = sizeof(pf_status);
-unsigned struct_pfioc_altq_sz = sizeof(pfioc_altq);
-unsigned struct_pfioc_if_sz = sizeof(pfioc_if);
-unsigned struct_pfioc_iface_sz = sizeof(pfioc_iface);
-unsigned struct_pfioc_limit_sz = sizeof(pfioc_limit);
-unsigned struct_pfioc_natlook_sz = sizeof(pfioc_natlook);
-unsigned struct_pfioc_pooladdr_sz = sizeof(pfioc_pooladdr);
-unsigned struct_pfioc_qstats_sz = sizeof(pfioc_qstats);
-unsigned struct_pfioc_rule_sz = sizeof(pfioc_rule);
-unsigned struct_pfioc_ruleset_sz = sizeof(pfioc_ruleset);
-unsigned struct_pfioc_src_node_kill_sz = sizeof(pfioc_src_node_kill);
-unsigned struct_pfioc_src_nodes_sz = sizeof(pfioc_src_nodes);
-unsigned struct_pfioc_state_kill_sz = sizeof(pfioc_state_kill);
-unsigned struct_pfioc_state_sz = sizeof(pfioc_state);
-unsigned struct_pfioc_states_sz = sizeof(pfioc_states);
-unsigned struct_pfioc_table_sz = sizeof(pfioc_table);
-unsigned struct_pfioc_tm_sz = sizeof(pfioc_tm);
-unsigned struct_pfioc_trans_sz = sizeof(pfioc_trans);
-unsigned struct_plistref_sz = sizeof(plistref);
-unsigned struct_power_type_sz = sizeof(power_type);
-unsigned struct_ppp_idle_sz = sizeof(ppp_idle);
-unsigned struct_ppp_option_data_sz = sizeof(ppp_option_data);
-unsigned struct_ppp_rawin_sz = sizeof(ppp_rawin);
-unsigned struct_pppoeconnectionstate_sz = sizeof(pppoeconnectionstate);
-unsigned struct_pppoediscparms_sz = sizeof(pppoediscparms);
-unsigned struct_priq_add_class_sz = sizeof(priq_add_class);
-unsigned struct_priq_add_filter_sz = sizeof(priq_add_filter);
-unsigned struct_priq_class_stats_sz = sizeof(priq_class_stats);
-unsigned struct_priq_delete_class_sz = sizeof(priq_delete_class);
-unsigned struct_priq_delete_filter_sz = sizeof(priq_delete_filter);
-unsigned struct_priq_interface_sz = sizeof(priq_interface);
-unsigned struct_priq_modify_class_sz = sizeof(priq_modify_class);
-unsigned struct_ptmget_sz = sizeof(ptmget);
-unsigned struct_pvctxreq_sz = sizeof(pvctxreq);
-unsigned struct_radio_info_sz = sizeof(radio_info);
-unsigned struct_red_conf_sz = sizeof(red_conf);
-unsigned struct_red_interface_sz = sizeof(red_interface);
-unsigned struct_red_stats_sz = sizeof(red_stats);
-unsigned struct_redparams_sz = sizeof(redparams);
-unsigned struct_rf_pmparams_sz = sizeof(rf_pmparams);
-unsigned struct_rf_pmstat_sz = sizeof(rf_pmstat);
-unsigned struct_rf_recon_req_sz = sizeof(rf_recon_req);
-unsigned struct_rio_conf_sz = sizeof(rio_conf);
-unsigned struct_rio_interface_sz = sizeof(rio_interface);
-unsigned struct_rio_stats_sz = sizeof(rio_stats);
-unsigned struct_satlink_id_sz = sizeof(satlink_id);
-unsigned struct_scan_io_sz = sizeof(scan_io);
-unsigned struct_scbusaccel_args_sz = sizeof(scbusaccel_args);
-unsigned struct_scbusiodetach_args_sz = sizeof(scbusiodetach_args);
-unsigned struct_scbusioscan_args_sz = sizeof(scbusioscan_args);
-unsigned struct_scsi_addr_sz = sizeof(scsi_addr);
-unsigned struct_seq_event_rec_sz = sizeof(seq_event_rec);
-unsigned struct_session_op_sz = sizeof(session_op);
-unsigned struct_sgttyb_sz = sizeof(sgttyb);
-unsigned struct_sioc_sg_req_sz = sizeof(sioc_sg_req);
-unsigned struct_sioc_vif_req_sz = sizeof(sioc_vif_req);
-unsigned struct_smbioc_flags_sz = sizeof(smbioc_flags);
-unsigned struct_smbioc_lookup_sz = sizeof(smbioc_lookup);
-unsigned struct_smbioc_oshare_sz = sizeof(smbioc_oshare);
-unsigned struct_smbioc_ossn_sz = sizeof(smbioc_ossn);
-unsigned struct_smbioc_rq_sz = sizeof(smbioc_rq);
-unsigned struct_smbioc_rw_sz = sizeof(smbioc_rw);
-unsigned struct_spppauthcfg_sz = sizeof(spppauthcfg);
-unsigned struct_spppauthfailuresettings_sz = sizeof(spppauthfailuresettings);
-unsigned struct_spppauthfailurestats_sz = sizeof(spppauthfailurestats);
-unsigned struct_spppdnsaddrs_sz = sizeof(spppdnsaddrs);
-unsigned struct_spppdnssettings_sz = sizeof(spppdnssettings);
-unsigned struct_spppidletimeout_sz = sizeof(spppidletimeout);
-unsigned struct_spppkeepalivesettings_sz = sizeof(spppkeepalivesettings);
-unsigned struct_sppplcpcfg_sz = sizeof(sppplcpcfg);
-unsigned struct_spppstatus_sz = sizeof(spppstatus);
-unsigned struct_spppstatusncp_sz = sizeof(spppstatusncp);
-unsigned struct_srt_rt_sz = sizeof(srt_rt);
-unsigned struct_stic_xinfo_sz = sizeof(stic_xinfo);
-unsigned struct_sun_dkctlr_sz = sizeof(sun_dkctlr);
-unsigned struct_sun_dkgeom_sz = sizeof(sun_dkgeom);
-unsigned struct_sun_dkpart_sz = sizeof(sun_dkpart);
-unsigned struct_synth_info_sz = sizeof(synth_info);
-unsigned struct_tbrreq_sz = sizeof(tbrreq);
-unsigned struct_tchars_sz = sizeof(tchars);
-unsigned struct_termios_sz = sizeof(termios);
-unsigned struct_timeval_sz = sizeof(timeval);
-unsigned struct_twe_drivecommand_sz = sizeof(twe_drivecommand);
-unsigned struct_twe_paramcommand_sz = sizeof(twe_paramcommand);
-unsigned struct_twe_usercommand_sz = sizeof(twe_usercommand);
-unsigned struct_ukyopon_identify_sz = sizeof(ukyopon_identify);
-unsigned struct_urio_command_sz = sizeof(urio_command);
-unsigned struct_usb_alt_interface_sz = sizeof(usb_alt_interface);
-unsigned struct_usb_bulk_ra_wb_opt_sz = sizeof(usb_bulk_ra_wb_opt);
-unsigned struct_usb_config_desc_sz = sizeof(usb_config_desc);
-unsigned struct_usb_ctl_report_desc_sz = sizeof(usb_ctl_report_desc);
-unsigned struct_usb_ctl_report_sz = sizeof(usb_ctl_report);
-unsigned struct_usb_ctl_request_sz = sizeof(usb_ctl_request);
-unsigned struct_usb_device_info_old_sz = sizeof(usb_device_info_old);
-unsigned struct_usb_device_info_sz = sizeof(usb_device_info);
-unsigned struct_usb_device_stats_sz = sizeof(usb_device_stats);
-unsigned struct_usb_endpoint_desc_sz = sizeof(usb_endpoint_desc);
-unsigned struct_usb_full_desc_sz = sizeof(usb_full_desc);
-unsigned struct_usb_interface_desc_sz = sizeof(usb_interface_desc);
-unsigned struct_usb_string_desc_sz = sizeof(usb_string_desc);
-unsigned struct_utoppy_readfile_sz = sizeof(utoppy_readfile);
-unsigned struct_utoppy_rename_sz = sizeof(utoppy_rename);
-unsigned struct_utoppy_stats_sz = sizeof(utoppy_stats);
-unsigned struct_utoppy_writefile_sz = sizeof(utoppy_writefile);
-unsigned struct_v4l2_audio_sz = sizeof(v4l2_audio);
-unsigned struct_v4l2_audioout_sz = sizeof(v4l2_audioout);
-unsigned struct_v4l2_buffer_sz = sizeof(v4l2_buffer);
-unsigned struct_v4l2_capability_sz = sizeof(v4l2_capability);
-unsigned struct_v4l2_control_sz = sizeof(v4l2_control);
-unsigned struct_v4l2_crop_sz = sizeof(v4l2_crop);
-unsigned struct_v4l2_cropcap_sz = sizeof(v4l2_cropcap);
-unsigned struct_v4l2_fmtdesc_sz = sizeof(v4l2_fmtdesc);
-unsigned struct_v4l2_format_sz = sizeof(v4l2_format);
-unsigned struct_v4l2_framebuffer_sz = sizeof(v4l2_framebuffer);
-unsigned struct_v4l2_frequency_sz = sizeof(v4l2_frequency);
-unsigned struct_v4l2_frmivalenum_sz = sizeof(v4l2_frmivalenum);
-unsigned struct_v4l2_frmsizeenum_sz = sizeof(v4l2_frmsizeenum);
-unsigned struct_v4l2_input_sz = sizeof(v4l2_input);
-unsigned struct_v4l2_jpegcompression_sz = sizeof(v4l2_jpegcompression);
-unsigned struct_v4l2_modulator_sz = sizeof(v4l2_modulator);
-unsigned struct_v4l2_output_sz = sizeof(v4l2_output);
-unsigned struct_v4l2_queryctrl_sz = sizeof(v4l2_queryctrl);
-unsigned struct_v4l2_querymenu_sz = sizeof(v4l2_querymenu);
-unsigned struct_v4l2_requestbuffers_sz = sizeof(v4l2_requestbuffers);
-unsigned struct_v4l2_standard_sz = sizeof(v4l2_standard);
-unsigned struct_v4l2_streamparm_sz = sizeof(v4l2_streamparm);
-unsigned struct_v4l2_tuner_sz = sizeof(v4l2_tuner);
-unsigned struct_vnd_ioctl_sz = sizeof(vnd_ioctl);
-unsigned struct_vnd_user_sz = sizeof(vnd_user);
-unsigned struct_vt_stat_sz = sizeof(vt_stat);
-unsigned struct_wdog_conf_sz = sizeof(wdog_conf);
-unsigned struct_wdog_mode_sz = sizeof(wdog_mode);
-unsigned struct_wfq_conf_sz = sizeof(wfq_conf);
-unsigned struct_wfq_getqid_sz = sizeof(wfq_getqid);
-unsigned struct_wfq_getstats_sz = sizeof(wfq_getstats);
-unsigned struct_wfq_interface_sz = sizeof(wfq_interface);
-unsigned struct_wfq_setweight_sz = sizeof(wfq_setweight);
-unsigned struct_winsize_sz = sizeof(winsize);
-unsigned struct_wscons_event_sz = sizeof(wscons_event);
-unsigned struct_wsdisplay_addscreendata_sz = sizeof(wsdisplay_addscreendata);
-unsigned struct_wsdisplay_char_sz = sizeof(wsdisplay_char);
-unsigned struct_wsdisplay_cmap_sz = sizeof(wsdisplay_cmap);
-unsigned struct_wsdisplay_curpos_sz = sizeof(wsdisplay_curpos);
-unsigned struct_wsdisplay_cursor_sz = sizeof(wsdisplay_cursor);
-unsigned struct_wsdisplay_delscreendata_sz = sizeof(wsdisplay_delscreendata);
-unsigned struct_wsdisplay_fbinfo_sz = sizeof(wsdisplay_fbinfo);
-unsigned struct_wsdisplay_font_sz = sizeof(wsdisplay_font);
-unsigned struct_wsdisplay_kbddata_sz = sizeof(wsdisplay_kbddata);
-unsigned struct_wsdisplay_msgattrs_sz = sizeof(wsdisplay_msgattrs);
-unsigned struct_wsdisplay_param_sz = sizeof(wsdisplay_param);
-unsigned struct_wsdisplay_scroll_data_sz = sizeof(wsdisplay_scroll_data);
-unsigned struct_wsdisplay_usefontdata_sz = sizeof(wsdisplay_usefontdata);
-unsigned struct_wsdisplayio_blit_sz = sizeof(wsdisplayio_blit);
-unsigned struct_wsdisplayio_bus_id_sz = sizeof(wsdisplayio_bus_id);
-unsigned struct_wsdisplayio_edid_info_sz = sizeof(wsdisplayio_edid_info);
-unsigned struct_wsdisplayio_fbinfo_sz = sizeof(wsdisplayio_fbinfo);
-unsigned struct_wskbd_bell_data_sz = sizeof(wskbd_bell_data);
-unsigned struct_wskbd_keyrepeat_data_sz = sizeof(wskbd_keyrepeat_data);
-unsigned struct_wskbd_map_data_sz = sizeof(wskbd_map_data);
-unsigned struct_wskbd_scroll_data_sz = sizeof(wskbd_scroll_data);
-unsigned struct_wsmouse_calibcoords_sz = sizeof(wsmouse_calibcoords);
-unsigned struct_wsmouse_id_sz = sizeof(wsmouse_id);
-unsigned struct_wsmouse_repeat_sz = sizeof(wsmouse_repeat);
-unsigned struct_wsmux_device_list_sz = sizeof(wsmux_device_list);
-unsigned struct_wsmux_device_sz = sizeof(wsmux_device);
-unsigned struct_xd_iocmd_sz = sizeof(xd_iocmd);
-
-unsigned struct_scsireq_sz = sizeof(struct scsireq);
-unsigned struct_tone_sz = sizeof(tone_t);
-unsigned union_twe_statrequest_sz = sizeof(union twe_statrequest);
-unsigned struct_usb_device_descriptor_sz = sizeof(usb_device_descriptor_t);
-unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
-unsigned struct__old_mixer_info_sz = sizeof(struct _old_mixer_info);
-unsigned struct__agp_allocate_sz = sizeof(struct _agp_allocate);
-unsigned struct__agp_bind_sz = sizeof(struct _agp_bind);
-unsigned struct__agp_info_sz = sizeof(struct _agp_info);
-unsigned struct__agp_setup_sz = sizeof(struct _agp_setup);
-unsigned struct__agp_unbind_sz = sizeof(struct _agp_unbind);
-unsigned struct_atareq_sz = sizeof(struct atareq);
-unsigned struct_cpustate_sz = sizeof(struct cpustate);
-unsigned struct_dmx_caps_sz = sizeof(struct dmx_caps);
-unsigned enum_dmx_source_sz = sizeof(dmx_source_t);
-unsigned union_dvd_authinfo_sz = sizeof(dvd_authinfo);
-unsigned union_dvd_struct_sz = sizeof(dvd_struct);
-unsigned enum_v4l2_priority_sz = sizeof(enum v4l2_priority);
-unsigned struct_envsys_basic_info_sz = sizeof(struct envsys_basic_info);
-unsigned struct_envsys_tre_data_sz = sizeof(struct envsys_tre_data);
-unsigned enum_fe_sec_mini_cmd_sz = sizeof(enum fe_sec_mini_cmd);
-unsigned enum_fe_sec_tone_mode_sz = sizeof(enum fe_sec_tone_mode);
-unsigned enum_fe_sec_voltage_sz = sizeof(enum fe_sec_voltage);
-unsigned enum_fe_status_sz = sizeof(enum fe_status);
-unsigned struct_gdt_ctrt_sz = sizeof(struct gdt_ctrt);
-unsigned struct_gdt_event_sz = sizeof(struct gdt_event);
-unsigned struct_gdt_osv_sz = sizeof(struct gdt_osv);
-unsigned struct_gdt_rescan_sz = sizeof(struct gdt_rescan);
-unsigned struct_gdt_statist_sz = sizeof(struct gdt_statist);
-unsigned struct_gdt_ucmd_sz = sizeof(struct gdt_ucmd);
-unsigned struct_iscsi_conn_status_parameters_sz =
- sizeof(iscsi_conn_status_parameters_t);
-unsigned struct_iscsi_get_version_parameters_sz =
- sizeof(iscsi_get_version_parameters_t);
-unsigned struct_iscsi_iocommand_parameters_sz =
- sizeof(iscsi_iocommand_parameters_t);
-unsigned struct_iscsi_login_parameters_sz = sizeof(iscsi_login_parameters_t);
-unsigned struct_iscsi_logout_parameters_sz = sizeof(iscsi_logout_parameters_t);
-unsigned struct_iscsi_register_event_parameters_sz =
- sizeof(iscsi_register_event_parameters_t);
-unsigned struct_iscsi_remove_parameters_sz = sizeof(iscsi_remove_parameters_t);
-unsigned struct_iscsi_send_targets_parameters_sz =
- sizeof(iscsi_send_targets_parameters_t);
-unsigned struct_iscsi_set_node_name_parameters_sz =
- sizeof(iscsi_set_node_name_parameters_t);
-unsigned struct_iscsi_wait_event_parameters_sz =
- sizeof(iscsi_wait_event_parameters_t);
-unsigned struct_isp_stats_sz = sizeof(isp_stats_t);
-unsigned struct_lsenable_sz = sizeof(struct lsenable);
-unsigned struct_lsdisable_sz = sizeof(struct lsdisable);
-unsigned struct_mixer_ctrl_sz = sizeof(struct mixer_ctrl);
-unsigned struct_mixer_devinfo_sz = sizeof(struct mixer_devinfo);
-unsigned struct_mpu_command_rec_sz = sizeof(mpu_command_rec);
-unsigned struct_rndstat_sz = sizeof(rndstat_t);
-unsigned struct_rndstat_name_sz = sizeof(rndstat_name_t);
-unsigned struct_rndctl_sz = sizeof(rndctl_t);
-unsigned struct_rnddata_sz = sizeof(rnddata_t);
-unsigned struct_rndpoolstat_sz = sizeof(rndpoolstat_t);
-unsigned struct_rndstat_est_sz = sizeof(rndstat_est_t);
-unsigned struct_rndstat_est_name_sz = sizeof(rndstat_est_name_t);
-unsigned struct_pps_params_sz = sizeof(pps_params_t);
-unsigned struct_pps_info_sz = sizeof(pps_info_t);
-unsigned struct_mixer_info_sz = sizeof(struct mixer_info);
-unsigned struct_RF_SparetWait_sz = sizeof(RF_SparetWait_t);
-unsigned struct_RF_ComponentLabel_sz = sizeof(RF_ComponentLabel_t);
-unsigned struct_RF_SingleComponent_sz = sizeof(RF_SingleComponent_t);
-unsigned struct_RF_ProgressInfo_sz = sizeof(RF_ProgressInfo_t);
-
-const unsigned IOCTL_NOT_PRESENT = 0;
-
-unsigned IOCTL_AFM_ADDFMAP = AFM_ADDFMAP;
-unsigned IOCTL_AFM_DELFMAP = AFM_DELFMAP;
-unsigned IOCTL_AFM_CLEANFMAP = AFM_CLEANFMAP;
-unsigned IOCTL_AFM_GETFMAP = AFM_GETFMAP;
-unsigned IOCTL_ALTQGTYPE = ALTQGTYPE;
-unsigned IOCTL_ALTQTBRSET = ALTQTBRSET;
-unsigned IOCTL_ALTQTBRGET = ALTQTBRGET;
-unsigned IOCTL_BLUE_IF_ATTACH = BLUE_IF_ATTACH;
-unsigned IOCTL_BLUE_IF_DETACH = BLUE_IF_DETACH;
-unsigned IOCTL_BLUE_ENABLE = BLUE_ENABLE;
-unsigned IOCTL_BLUE_DISABLE = BLUE_DISABLE;
-unsigned IOCTL_BLUE_CONFIG = BLUE_CONFIG;
-unsigned IOCTL_BLUE_GETSTATS = BLUE_GETSTATS;
-unsigned IOCTL_CBQ_IF_ATTACH = CBQ_IF_ATTACH;
-unsigned IOCTL_CBQ_IF_DETACH = CBQ_IF_DETACH;
-unsigned IOCTL_CBQ_ENABLE = CBQ_ENABLE;
-unsigned IOCTL_CBQ_DISABLE = CBQ_DISABLE;
-unsigned IOCTL_CBQ_CLEAR_HIERARCHY = CBQ_CLEAR_HIERARCHY;
-unsigned IOCTL_CBQ_ADD_CLASS = CBQ_ADD_CLASS;
-unsigned IOCTL_CBQ_DEL_CLASS = CBQ_DEL_CLASS;
-unsigned IOCTL_CBQ_MODIFY_CLASS = CBQ_MODIFY_CLASS;
-unsigned IOCTL_CBQ_ADD_FILTER = CBQ_ADD_FILTER;
-unsigned IOCTL_CBQ_DEL_FILTER = CBQ_DEL_FILTER;
-unsigned IOCTL_CBQ_GETSTATS = CBQ_GETSTATS;
-unsigned IOCTL_CDNR_IF_ATTACH = CDNR_IF_ATTACH;
-unsigned IOCTL_CDNR_IF_DETACH = CDNR_IF_DETACH;
-unsigned IOCTL_CDNR_ENABLE = CDNR_ENABLE;
-unsigned IOCTL_CDNR_DISABLE = CDNR_DISABLE;
-unsigned IOCTL_CDNR_ADD_FILTER = CDNR_ADD_FILTER;
-unsigned IOCTL_CDNR_DEL_FILTER = CDNR_DEL_FILTER;
-unsigned IOCTL_CDNR_GETSTATS = CDNR_GETSTATS;
-unsigned IOCTL_CDNR_ADD_ELEM = CDNR_ADD_ELEM;
-unsigned IOCTL_CDNR_DEL_ELEM = CDNR_DEL_ELEM;
-unsigned IOCTL_CDNR_ADD_TBM = CDNR_ADD_TBM;
-unsigned IOCTL_CDNR_MOD_TBM = CDNR_MOD_TBM;
-unsigned IOCTL_CDNR_TBM_STATS = CDNR_TBM_STATS;
-unsigned IOCTL_CDNR_ADD_TCM = CDNR_ADD_TCM;
-unsigned IOCTL_CDNR_MOD_TCM = CDNR_MOD_TCM;
-unsigned IOCTL_CDNR_TCM_STATS = CDNR_TCM_STATS;
-unsigned IOCTL_CDNR_ADD_TSW = CDNR_ADD_TSW;
-unsigned IOCTL_CDNR_MOD_TSW = CDNR_MOD_TSW;
-unsigned IOCTL_FIFOQ_IF_ATTACH = FIFOQ_IF_ATTACH;
-unsigned IOCTL_FIFOQ_IF_DETACH = FIFOQ_IF_DETACH;
-unsigned IOCTL_FIFOQ_ENABLE = FIFOQ_ENABLE;
-unsigned IOCTL_FIFOQ_DISABLE = FIFOQ_DISABLE;
-unsigned IOCTL_FIFOQ_CONFIG = FIFOQ_CONFIG;
-unsigned IOCTL_FIFOQ_GETSTATS = FIFOQ_GETSTATS;
-unsigned IOCTL_HFSC_IF_ATTACH = HFSC_IF_ATTACH;
-unsigned IOCTL_HFSC_IF_DETACH = HFSC_IF_DETACH;
-unsigned IOCTL_HFSC_ENABLE = HFSC_ENABLE;
-unsigned IOCTL_HFSC_DISABLE = HFSC_DISABLE;
-unsigned IOCTL_HFSC_CLEAR_HIERARCHY = HFSC_CLEAR_HIERARCHY;
-unsigned IOCTL_HFSC_ADD_CLASS = HFSC_ADD_CLASS;
-unsigned IOCTL_HFSC_DEL_CLASS = HFSC_DEL_CLASS;
-unsigned IOCTL_HFSC_MOD_CLASS = HFSC_MOD_CLASS;
-unsigned IOCTL_HFSC_ADD_FILTER = HFSC_ADD_FILTER;
-unsigned IOCTL_HFSC_DEL_FILTER = HFSC_DEL_FILTER;
-unsigned IOCTL_HFSC_GETSTATS = HFSC_GETSTATS;
-unsigned IOCTL_JOBS_IF_ATTACH = JOBS_IF_ATTACH;
-unsigned IOCTL_JOBS_IF_DETACH = JOBS_IF_DETACH;
-unsigned IOCTL_JOBS_ENABLE = JOBS_ENABLE;
-unsigned IOCTL_JOBS_DISABLE = JOBS_DISABLE;
-unsigned IOCTL_JOBS_CLEAR = JOBS_CLEAR;
-unsigned IOCTL_JOBS_ADD_CLASS = JOBS_ADD_CLASS;
-unsigned IOCTL_JOBS_DEL_CLASS = JOBS_DEL_CLASS;
-unsigned IOCTL_JOBS_MOD_CLASS = JOBS_MOD_CLASS;
-unsigned IOCTL_JOBS_ADD_FILTER = JOBS_ADD_FILTER;
-unsigned IOCTL_JOBS_DEL_FILTER = JOBS_DEL_FILTER;
-unsigned IOCTL_JOBS_GETSTATS = JOBS_GETSTATS;
-unsigned IOCTL_PRIQ_IF_ATTACH = PRIQ_IF_ATTACH;
-unsigned IOCTL_PRIQ_IF_DETACH = PRIQ_IF_DETACH;
-unsigned IOCTL_PRIQ_ENABLE = PRIQ_ENABLE;
-unsigned IOCTL_PRIQ_DISABLE = PRIQ_DISABLE;
-unsigned IOCTL_PRIQ_CLEAR = PRIQ_CLEAR;
-unsigned IOCTL_PRIQ_ADD_CLASS = PRIQ_ADD_CLASS;
-unsigned IOCTL_PRIQ_DEL_CLASS = PRIQ_DEL_CLASS;
-unsigned IOCTL_PRIQ_MOD_CLASS = PRIQ_MOD_CLASS;
-unsigned IOCTL_PRIQ_ADD_FILTER = PRIQ_ADD_FILTER;
-unsigned IOCTL_PRIQ_DEL_FILTER = PRIQ_DEL_FILTER;
-unsigned IOCTL_PRIQ_GETSTATS = PRIQ_GETSTATS;
-unsigned IOCTL_RED_IF_ATTACH = RED_IF_ATTACH;
-unsigned IOCTL_RED_IF_DETACH = RED_IF_DETACH;
-unsigned IOCTL_RED_ENABLE = RED_ENABLE;
-unsigned IOCTL_RED_DISABLE = RED_DISABLE;
-unsigned IOCTL_RED_CONFIG = RED_CONFIG;
-unsigned IOCTL_RED_GETSTATS = RED_GETSTATS;
-unsigned IOCTL_RED_SETDEFAULTS = RED_SETDEFAULTS;
-unsigned IOCTL_RIO_IF_ATTACH = RIO_IF_ATTACH;
-unsigned IOCTL_RIO_IF_DETACH = RIO_IF_DETACH;
-unsigned IOCTL_RIO_ENABLE = RIO_ENABLE;
-unsigned IOCTL_RIO_DISABLE = RIO_DISABLE;
-unsigned IOCTL_RIO_CONFIG = RIO_CONFIG;
-unsigned IOCTL_RIO_GETSTATS = RIO_GETSTATS;
-unsigned IOCTL_RIO_SETDEFAULTS = RIO_SETDEFAULTS;
-unsigned IOCTL_WFQ_IF_ATTACH = WFQ_IF_ATTACH;
-unsigned IOCTL_WFQ_IF_DETACH = WFQ_IF_DETACH;
-unsigned IOCTL_WFQ_ENABLE = WFQ_ENABLE;
-unsigned IOCTL_WFQ_DISABLE = WFQ_DISABLE;
-unsigned IOCTL_WFQ_CONFIG = WFQ_CONFIG;
-unsigned IOCTL_WFQ_GET_STATS = WFQ_GET_STATS;
-unsigned IOCTL_WFQ_GET_QID = WFQ_GET_QID;
-unsigned IOCTL_WFQ_SET_WEIGHT = WFQ_SET_WEIGHT;
-unsigned IOCTL_CRIOGET = CRIOGET;
-unsigned IOCTL_CIOCFSESSION = CIOCFSESSION;
-unsigned IOCTL_CIOCKEY = CIOCKEY;
-unsigned IOCTL_CIOCNFKEYM = CIOCNFKEYM;
-unsigned IOCTL_CIOCNFSESSION = CIOCNFSESSION;
-unsigned IOCTL_CIOCNCRYPTRETM = CIOCNCRYPTRETM;
-unsigned IOCTL_CIOCNCRYPTRET = CIOCNCRYPTRET;
-unsigned IOCTL_CIOCGSESSION = CIOCGSESSION;
-unsigned IOCTL_CIOCNGSESSION = CIOCNGSESSION;
-unsigned IOCTL_CIOCCRYPT = CIOCCRYPT;
-unsigned IOCTL_CIOCNCRYPTM = CIOCNCRYPTM;
-unsigned IOCTL_CIOCASYMFEAT = CIOCASYMFEAT;
-unsigned IOCTL_APM_IOC_REJECT = APM_IOC_REJECT;
-unsigned IOCTL_APM_IOC_STANDBY = APM_IOC_STANDBY;
-unsigned IOCTL_APM_IOC_SUSPEND = APM_IOC_SUSPEND;
-unsigned IOCTL_OAPM_IOC_GETPOWER = OAPM_IOC_GETPOWER;
-unsigned IOCTL_APM_IOC_GETPOWER = APM_IOC_GETPOWER;
-unsigned IOCTL_APM_IOC_NEXTEVENT = APM_IOC_NEXTEVENT;
-unsigned IOCTL_APM_IOC_DEV_CTL = APM_IOC_DEV_CTL;
-unsigned IOCTL_NETBSD_DM_IOCTL = NETBSD_DM_IOCTL;
-unsigned IOCTL_DMIO_SETFUNC = DMIO_SETFUNC;
-unsigned IOCTL_DMX_START = DMX_START;
-unsigned IOCTL_DMX_STOP = DMX_STOP;
-unsigned IOCTL_DMX_SET_FILTER = DMX_SET_FILTER;
-unsigned IOCTL_DMX_SET_PES_FILTER = DMX_SET_PES_FILTER;
-unsigned IOCTL_DMX_SET_BUFFER_SIZE = DMX_SET_BUFFER_SIZE;
-unsigned IOCTL_DMX_GET_STC = DMX_GET_STC;
-unsigned IOCTL_DMX_ADD_PID = DMX_ADD_PID;
-unsigned IOCTL_DMX_REMOVE_PID = DMX_REMOVE_PID;
-unsigned IOCTL_DMX_GET_CAPS = DMX_GET_CAPS;
-unsigned IOCTL_DMX_SET_SOURCE = DMX_SET_SOURCE;
-unsigned IOCTL_FE_READ_STATUS = FE_READ_STATUS;
-unsigned IOCTL_FE_READ_BER = FE_READ_BER;
-unsigned IOCTL_FE_READ_SNR = FE_READ_SNR;
-unsigned IOCTL_FE_READ_SIGNAL_STRENGTH = FE_READ_SIGNAL_STRENGTH;
-unsigned IOCTL_FE_READ_UNCORRECTED_BLOCKS = FE_READ_UNCORRECTED_BLOCKS;
-unsigned IOCTL_FE_SET_FRONTEND = FE_SET_FRONTEND;
-unsigned IOCTL_FE_GET_FRONTEND = FE_GET_FRONTEND;
-unsigned IOCTL_FE_GET_EVENT = FE_GET_EVENT;
-unsigned IOCTL_FE_GET_INFO = FE_GET_INFO;
-unsigned IOCTL_FE_DISEQC_RESET_OVERLOAD = FE_DISEQC_RESET_OVERLOAD;
-unsigned IOCTL_FE_DISEQC_SEND_MASTER_CMD = FE_DISEQC_SEND_MASTER_CMD;
-unsigned IOCTL_FE_DISEQC_RECV_SLAVE_REPLY = FE_DISEQC_RECV_SLAVE_REPLY;
-unsigned IOCTL_FE_DISEQC_SEND_BURST = FE_DISEQC_SEND_BURST;
-unsigned IOCTL_FE_SET_TONE = FE_SET_TONE;
-unsigned IOCTL_FE_SET_VOLTAGE = FE_SET_VOLTAGE;
-unsigned IOCTL_FE_ENABLE_HIGH_LNB_VOLTAGE = FE_ENABLE_HIGH_LNB_VOLTAGE;
-unsigned IOCTL_FE_SET_FRONTEND_TUNE_MODE = FE_SET_FRONTEND_TUNE_MODE;
-unsigned IOCTL_FE_DISHNETWORK_SEND_LEGACY_CMD = FE_DISHNETWORK_SEND_LEGACY_CMD;
-unsigned IOCTL_FILEMON_SET_FD = FILEMON_SET_FD;
-unsigned IOCTL_FILEMON_SET_PID = FILEMON_SET_PID;
-unsigned IOCTL_HDAUDIO_FGRP_INFO = HDAUDIO_FGRP_INFO;
-unsigned IOCTL_HDAUDIO_FGRP_GETCONFIG = HDAUDIO_FGRP_GETCONFIG;
-unsigned IOCTL_HDAUDIO_FGRP_SETCONFIG = HDAUDIO_FGRP_SETCONFIG;
-unsigned IOCTL_HDAUDIO_FGRP_WIDGET_INFO = HDAUDIO_FGRP_WIDGET_INFO;
-unsigned IOCTL_HDAUDIO_FGRP_CODEC_INFO = HDAUDIO_FGRP_CODEC_INFO;
-unsigned IOCTL_HDAUDIO_AFG_WIDGET_INFO = HDAUDIO_AFG_WIDGET_INFO;
-unsigned IOCTL_HDAUDIO_AFG_CODEC_INFO = HDAUDIO_AFG_CODEC_INFO;
-unsigned IOCTL_CEC_GET_PHYS_ADDR = CEC_GET_PHYS_ADDR;
-unsigned IOCTL_CEC_GET_LOG_ADDRS = CEC_GET_LOG_ADDRS;
-unsigned IOCTL_CEC_SET_LOG_ADDRS = CEC_SET_LOG_ADDRS;
-unsigned IOCTL_CEC_GET_VENDOR_ID = CEC_GET_VENDOR_ID;
-unsigned IOCTL_HPCFBIO_GCONF = HPCFBIO_GCONF;
-unsigned IOCTL_HPCFBIO_SCONF = HPCFBIO_SCONF;
-unsigned IOCTL_HPCFBIO_GDSPCONF = HPCFBIO_GDSPCONF;
-unsigned IOCTL_HPCFBIO_SDSPCONF = HPCFBIO_SDSPCONF;
-unsigned IOCTL_HPCFBIO_GOP = HPCFBIO_GOP;
-unsigned IOCTL_HPCFBIO_SOP = HPCFBIO_SOP;
-unsigned IOCTL_IOPIOCPT = IOPIOCPT;
-unsigned IOCTL_IOPIOCGLCT = IOPIOCGLCT;
-unsigned IOCTL_IOPIOCGSTATUS = IOPIOCGSTATUS;
-unsigned IOCTL_IOPIOCRECONFIG = IOPIOCRECONFIG;
-unsigned IOCTL_IOPIOCGTIDMAP = IOPIOCGTIDMAP;
-unsigned IOCTL_SIOCGATHSTATS = SIOCGATHSTATS;
-unsigned IOCTL_SIOCGATHDIAG = SIOCGATHDIAG;
-unsigned IOCTL_METEORCAPTUR = METEORCAPTUR;
-unsigned IOCTL_METEORCAPFRM = METEORCAPFRM;
-unsigned IOCTL_METEORSETGEO = METEORSETGEO;
-unsigned IOCTL_METEORGETGEO = METEORGETGEO;
-unsigned IOCTL_METEORSTATUS = METEORSTATUS;
-unsigned IOCTL_METEORSHUE = METEORSHUE;
-unsigned IOCTL_METEORGHUE = METEORGHUE;
-unsigned IOCTL_METEORSFMT = METEORSFMT;
-unsigned IOCTL_METEORGFMT = METEORGFMT;
-unsigned IOCTL_METEORSINPUT = METEORSINPUT;
-unsigned IOCTL_METEORGINPUT = METEORGINPUT;
-unsigned IOCTL_METEORSCHCV = METEORSCHCV;
-unsigned IOCTL_METEORGCHCV = METEORGCHCV;
-unsigned IOCTL_METEORSCOUNT = METEORSCOUNT;
-unsigned IOCTL_METEORGCOUNT = METEORGCOUNT;
-unsigned IOCTL_METEORSFPS = METEORSFPS;
-unsigned IOCTL_METEORGFPS = METEORGFPS;
-unsigned IOCTL_METEORSSIGNAL = METEORSSIGNAL;
-unsigned IOCTL_METEORGSIGNAL = METEORGSIGNAL;
-unsigned IOCTL_METEORSVIDEO = METEORSVIDEO;
-unsigned IOCTL_METEORGVIDEO = METEORGVIDEO;
-unsigned IOCTL_METEORSBRIG = METEORSBRIG;
-unsigned IOCTL_METEORGBRIG = METEORGBRIG;
-unsigned IOCTL_METEORSCSAT = METEORSCSAT;
-unsigned IOCTL_METEORGCSAT = METEORGCSAT;
-unsigned IOCTL_METEORSCONT = METEORSCONT;
-unsigned IOCTL_METEORGCONT = METEORGCONT;
-unsigned IOCTL_METEORSHWS = METEORSHWS;
-unsigned IOCTL_METEORGHWS = METEORGHWS;
-unsigned IOCTL_METEORSVWS = METEORSVWS;
-unsigned IOCTL_METEORGVWS = METEORGVWS;
-unsigned IOCTL_METEORSTS = METEORSTS;
-unsigned IOCTL_METEORGTS = METEORGTS;
-unsigned IOCTL_TVTUNER_SETCHNL = TVTUNER_SETCHNL;
-unsigned IOCTL_TVTUNER_GETCHNL = TVTUNER_GETCHNL;
-unsigned IOCTL_TVTUNER_SETTYPE = TVTUNER_SETTYPE;
-unsigned IOCTL_TVTUNER_GETTYPE = TVTUNER_GETTYPE;
-unsigned IOCTL_TVTUNER_GETSTATUS = TVTUNER_GETSTATUS;
-unsigned IOCTL_TVTUNER_SETFREQ = TVTUNER_SETFREQ;
-unsigned IOCTL_TVTUNER_GETFREQ = TVTUNER_GETFREQ;
-unsigned IOCTL_TVTUNER_SETAFC = TVTUNER_SETAFC;
-unsigned IOCTL_TVTUNER_GETAFC = TVTUNER_GETAFC;
-unsigned IOCTL_RADIO_SETMODE = RADIO_SETMODE;
-unsigned IOCTL_RADIO_GETMODE = RADIO_GETMODE;
-unsigned IOCTL_RADIO_SETFREQ = RADIO_SETFREQ;
-unsigned IOCTL_RADIO_GETFREQ = RADIO_GETFREQ;
-unsigned IOCTL_METEORSACTPIXFMT = METEORSACTPIXFMT;
-unsigned IOCTL_METEORGACTPIXFMT = METEORGACTPIXFMT;
-unsigned IOCTL_METEORGSUPPIXFMT = METEORGSUPPIXFMT;
-unsigned IOCTL_TVTUNER_GETCHNLSET = TVTUNER_GETCHNLSET;
-unsigned IOCTL_REMOTE_GETKEY = REMOTE_GETKEY;
-unsigned IOCTL_GDT_IOCTL_GENERAL = GDT_IOCTL_GENERAL;
-unsigned IOCTL_GDT_IOCTL_DRVERS = GDT_IOCTL_DRVERS;
-unsigned IOCTL_GDT_IOCTL_CTRTYPE = GDT_IOCTL_CTRTYPE;
-unsigned IOCTL_GDT_IOCTL_OSVERS = GDT_IOCTL_OSVERS;
-unsigned IOCTL_GDT_IOCTL_CTRCNT = GDT_IOCTL_CTRCNT;
-unsigned IOCTL_GDT_IOCTL_EVENT = GDT_IOCTL_EVENT;
-unsigned IOCTL_GDT_IOCTL_STATIST = GDT_IOCTL_STATIST;
-unsigned IOCTL_GDT_IOCTL_RESCAN = GDT_IOCTL_RESCAN;
-unsigned IOCTL_ISP_SDBLEV = ISP_SDBLEV;
-unsigned IOCTL_ISP_RESETHBA = ISP_RESETHBA;
-unsigned IOCTL_ISP_RESCAN = ISP_RESCAN;
-unsigned IOCTL_ISP_SETROLE = ISP_SETROLE;
-unsigned IOCTL_ISP_GETROLE = ISP_GETROLE;
-unsigned IOCTL_ISP_GET_STATS = ISP_GET_STATS;
-unsigned IOCTL_ISP_CLR_STATS = ISP_CLR_STATS;
-unsigned IOCTL_ISP_FC_LIP = ISP_FC_LIP;
-unsigned IOCTL_ISP_FC_GETDINFO = ISP_FC_GETDINFO;
-unsigned IOCTL_ISP_GET_FW_CRASH_DUMP = ISP_GET_FW_CRASH_DUMP;
-unsigned IOCTL_ISP_FORCE_CRASH_DUMP = ISP_FORCE_CRASH_DUMP;
-unsigned IOCTL_ISP_FC_GETHINFO = ISP_FC_GETHINFO;
-unsigned IOCTL_ISP_TSK_MGMT = ISP_TSK_MGMT;
-unsigned IOCTL_ISP_FC_GETDLIST = ISP_FC_GETDLIST;
-unsigned IOCTL_MLXD_STATUS = MLXD_STATUS;
-unsigned IOCTL_MLXD_CHECKASYNC = MLXD_CHECKASYNC;
-unsigned IOCTL_MLXD_DETACH = MLXD_DETACH;
-unsigned IOCTL_MLX_RESCAN_DRIVES = MLX_RESCAN_DRIVES;
-unsigned IOCTL_MLX_PAUSE_CHANNEL = MLX_PAUSE_CHANNEL;
-unsigned IOCTL_MLX_COMMAND = MLX_COMMAND;
-unsigned IOCTL_MLX_REBUILDASYNC = MLX_REBUILDASYNC;
-unsigned IOCTL_MLX_REBUILDSTAT = MLX_REBUILDSTAT;
-unsigned IOCTL_MLX_GET_SYSDRIVE = MLX_GET_SYSDRIVE;
-unsigned IOCTL_MLX_GET_CINFO = MLX_GET_CINFO;
-unsigned IOCTL_NVME_PASSTHROUGH_CMD = NVME_PASSTHROUGH_CMD;
-unsigned IOCTL_IRDA_RESET_PARAMS = IRDA_RESET_PARAMS;
-unsigned IOCTL_IRDA_SET_PARAMS = IRDA_SET_PARAMS;
-unsigned IOCTL_IRDA_GET_SPEEDMASK = IRDA_GET_SPEEDMASK;
-unsigned IOCTL_IRDA_GET_TURNAROUNDMASK = IRDA_GET_TURNAROUNDMASK;
-unsigned IOCTL_IRFRAMETTY_GET_DEVICE = IRFRAMETTY_GET_DEVICE;
-unsigned IOCTL_IRFRAMETTY_GET_DONGLE = IRFRAMETTY_GET_DONGLE;
-unsigned IOCTL_IRFRAMETTY_SET_DONGLE = IRFRAMETTY_SET_DONGLE;
-unsigned IOCTL_SATIORESET = SATIORESET;
-unsigned IOCTL_SATIOGID = SATIOGID;
-unsigned IOCTL_SATIOSBUFSIZE = SATIOSBUFSIZE;
-unsigned IOCTL_ISV_CMD = ISV_CMD;
-unsigned IOCTL_WTQICMD = WTQICMD;
-unsigned IOCTL_ISCSI_GET_VERSION = ISCSI_GET_VERSION;
-unsigned IOCTL_ISCSI_LOGIN = ISCSI_LOGIN;
-unsigned IOCTL_ISCSI_LOGOUT = ISCSI_LOGOUT;
-unsigned IOCTL_ISCSI_ADD_CONNECTION = ISCSI_ADD_CONNECTION;
-unsigned IOCTL_ISCSI_RESTORE_CONNECTION = ISCSI_RESTORE_CONNECTION;
-unsigned IOCTL_ISCSI_REMOVE_CONNECTION = ISCSI_REMOVE_CONNECTION;
-unsigned IOCTL_ISCSI_CONNECTION_STATUS = ISCSI_CONNECTION_STATUS;
-unsigned IOCTL_ISCSI_SEND_TARGETS = ISCSI_SEND_TARGETS;
-unsigned IOCTL_ISCSI_SET_NODE_NAME = ISCSI_SET_NODE_NAME;
-unsigned IOCTL_ISCSI_IO_COMMAND = ISCSI_IO_COMMAND;
-unsigned IOCTL_ISCSI_REGISTER_EVENT = ISCSI_REGISTER_EVENT;
-unsigned IOCTL_ISCSI_DEREGISTER_EVENT = ISCSI_DEREGISTER_EVENT;
-unsigned IOCTL_ISCSI_WAIT_EVENT = ISCSI_WAIT_EVENT;
-unsigned IOCTL_ISCSI_POLL_EVENT = ISCSI_POLL_EVENT;
-unsigned IOCTL_OFIOCGET = OFIOCGET;
-unsigned IOCTL_OFIOCSET = OFIOCSET;
-unsigned IOCTL_OFIOCNEXTPROP = OFIOCNEXTPROP;
-unsigned IOCTL_OFIOCGETOPTNODE = OFIOCGETOPTNODE;
-unsigned IOCTL_OFIOCGETNEXT = OFIOCGETNEXT;
-unsigned IOCTL_OFIOCGETCHILD = OFIOCGETCHILD;
-unsigned IOCTL_OFIOCFINDDEVICE = OFIOCFINDDEVICE;
-unsigned IOCTL_AMR_IO_VERSION = AMR_IO_VERSION;
-unsigned IOCTL_AMR_IO_COMMAND = AMR_IO_COMMAND;
-unsigned IOCTL_MLYIO_COMMAND = MLYIO_COMMAND;
-unsigned IOCTL_MLYIO_HEALTH = MLYIO_HEALTH;
-unsigned IOCTL_PCI_IOC_CFGREAD = PCI_IOC_CFGREAD;
-unsigned IOCTL_PCI_IOC_CFGWRITE = PCI_IOC_CFGWRITE;
-unsigned IOCTL_PCI_IOC_BDF_CFGREAD = PCI_IOC_BDF_CFGREAD;
-unsigned IOCTL_PCI_IOC_BDF_CFGWRITE = PCI_IOC_BDF_CFGWRITE;
-unsigned IOCTL_PCI_IOC_BUSINFO = PCI_IOC_BUSINFO;
-unsigned IOCTL_PCI_IOC_DRVNAME = PCI_IOC_DRVNAME;
-unsigned IOCTL_PCI_IOC_DRVNAMEONBUS = PCI_IOC_DRVNAMEONBUS;
-unsigned IOCTL_TWEIO_COMMAND = TWEIO_COMMAND;
-unsigned IOCTL_TWEIO_STATS = TWEIO_STATS;
-unsigned IOCTL_TWEIO_AEN_POLL = TWEIO_AEN_POLL;
-unsigned IOCTL_TWEIO_AEN_WAIT = TWEIO_AEN_WAIT;
-unsigned IOCTL_TWEIO_SET_PARAM = TWEIO_SET_PARAM;
-unsigned IOCTL_TWEIO_GET_PARAM = TWEIO_GET_PARAM;
-unsigned IOCTL_TWEIO_RESET = TWEIO_RESET;
-unsigned IOCTL_TWEIO_ADD_UNIT = TWEIO_ADD_UNIT;
-unsigned IOCTL_TWEIO_DEL_UNIT = TWEIO_DEL_UNIT;
-unsigned IOCTL_SIOCSCNWDOMAIN = SIOCSCNWDOMAIN;
-unsigned IOCTL_SIOCGCNWDOMAIN = SIOCGCNWDOMAIN;
-unsigned IOCTL_SIOCSCNWKEY = SIOCSCNWKEY;
-unsigned IOCTL_SIOCGCNWSTATUS = SIOCGCNWSTATUS;
-unsigned IOCTL_SIOCGCNWSTATS = SIOCGCNWSTATS;
-unsigned IOCTL_SIOCGCNWTRAIL = SIOCGCNWTRAIL;
-unsigned IOCTL_SIOCGRAYSIGLEV = SIOCGRAYSIGLEV;
-unsigned IOCTL_RAIDFRAME_SHUTDOWN = RAIDFRAME_SHUTDOWN;
-unsigned IOCTL_RAIDFRAME_TUR = RAIDFRAME_TUR;
-unsigned IOCTL_RAIDFRAME_FAIL_DISK = RAIDFRAME_FAIL_DISK;
-unsigned IOCTL_RAIDFRAME_CHECK_RECON_STATUS = RAIDFRAME_CHECK_RECON_STATUS;
-unsigned IOCTL_RAIDFRAME_REWRITEPARITY = RAIDFRAME_REWRITEPARITY;
-unsigned IOCTL_RAIDFRAME_COPYBACK = RAIDFRAME_COPYBACK;
-unsigned IOCTL_RAIDFRAME_SPARET_WAIT = RAIDFRAME_SPARET_WAIT;
-unsigned IOCTL_RAIDFRAME_SEND_SPARET = RAIDFRAME_SEND_SPARET;
-unsigned IOCTL_RAIDFRAME_ABORT_SPARET_WAIT = RAIDFRAME_ABORT_SPARET_WAIT;
-unsigned IOCTL_RAIDFRAME_START_ATRACE = RAIDFRAME_START_ATRACE;
-unsigned IOCTL_RAIDFRAME_STOP_ATRACE = RAIDFRAME_STOP_ATRACE;
-unsigned IOCTL_RAIDFRAME_GET_SIZE = RAIDFRAME_GET_SIZE;
-unsigned IOCTL_RAIDFRAME_RESET_ACCTOTALS = RAIDFRAME_RESET_ACCTOTALS;
-unsigned IOCTL_RAIDFRAME_KEEP_ACCTOTALS = RAIDFRAME_KEEP_ACCTOTALS;
-unsigned IOCTL_RAIDFRAME_GET_COMPONENT_LABEL = RAIDFRAME_GET_COMPONENT_LABEL;
-unsigned IOCTL_RAIDFRAME_SET_COMPONENT_LABEL = RAIDFRAME_SET_COMPONENT_LABEL;
-unsigned IOCTL_RAIDFRAME_INIT_LABELS = RAIDFRAME_INIT_LABELS;
-unsigned IOCTL_RAIDFRAME_ADD_HOT_SPARE = RAIDFRAME_ADD_HOT_SPARE;
-unsigned IOCTL_RAIDFRAME_REMOVE_HOT_SPARE = RAIDFRAME_REMOVE_HOT_SPARE;
-unsigned IOCTL_RAIDFRAME_REBUILD_IN_PLACE = RAIDFRAME_REBUILD_IN_PLACE;
-unsigned IOCTL_RAIDFRAME_CHECK_PARITY = RAIDFRAME_CHECK_PARITY;
-unsigned IOCTL_RAIDFRAME_CHECK_PARITYREWRITE_STATUS =
- RAIDFRAME_CHECK_PARITYREWRITE_STATUS;
-unsigned IOCTL_RAIDFRAME_CHECK_COPYBACK_STATUS =
- RAIDFRAME_CHECK_COPYBACK_STATUS;
-unsigned IOCTL_RAIDFRAME_SET_AUTOCONFIG = RAIDFRAME_SET_AUTOCONFIG;
-unsigned IOCTL_RAIDFRAME_SET_ROOT = RAIDFRAME_SET_ROOT;
-unsigned IOCTL_RAIDFRAME_DELETE_COMPONENT = RAIDFRAME_DELETE_COMPONENT;
-unsigned IOCTL_RAIDFRAME_INCORPORATE_HOT_SPARE =
- RAIDFRAME_INCORPORATE_HOT_SPARE;
-unsigned IOCTL_RAIDFRAME_CHECK_RECON_STATUS_EXT =
- RAIDFRAME_CHECK_RECON_STATUS_EXT;
-unsigned IOCTL_RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT =
- RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT;
-unsigned IOCTL_RAIDFRAME_CHECK_COPYBACK_STATUS_EXT =
- RAIDFRAME_CHECK_COPYBACK_STATUS_EXT;
-unsigned IOCTL_RAIDFRAME_CONFIGURE = RAIDFRAME_CONFIGURE;
-unsigned IOCTL_RAIDFRAME_GET_INFO = RAIDFRAME_GET_INFO;
-unsigned IOCTL_RAIDFRAME_PARITYMAP_STATUS = RAIDFRAME_PARITYMAP_STATUS;
-unsigned IOCTL_RAIDFRAME_PARITYMAP_GET_DISABLE =
- RAIDFRAME_PARITYMAP_GET_DISABLE;
-unsigned IOCTL_RAIDFRAME_PARITYMAP_SET_DISABLE =
- RAIDFRAME_PARITYMAP_SET_DISABLE;
-unsigned IOCTL_RAIDFRAME_PARITYMAP_SET_PARAMS = RAIDFRAME_PARITYMAP_SET_PARAMS;
-unsigned IOCTL_RAIDFRAME_SET_LAST_UNIT = RAIDFRAME_SET_LAST_UNIT;
-unsigned IOCTL_MBPPIOCSPARAM = MBPPIOCSPARAM;
-unsigned IOCTL_MBPPIOCGPARAM = MBPPIOCGPARAM;
-unsigned IOCTL_MBPPIOCGSTAT = MBPPIOCGSTAT;
-unsigned IOCTL_SESIOC_GETNOBJ = SESIOC_GETNOBJ;
-unsigned IOCTL_SESIOC_GETOBJMAP = SESIOC_GETOBJMAP;
-unsigned IOCTL_SESIOC_GETENCSTAT = SESIOC_GETENCSTAT;
-unsigned IOCTL_SESIOC_SETENCSTAT = SESIOC_SETENCSTAT;
-unsigned IOCTL_SESIOC_GETOBJSTAT = SESIOC_GETOBJSTAT;
-unsigned IOCTL_SESIOC_SETOBJSTAT = SESIOC_SETOBJSTAT;
-unsigned IOCTL_SESIOC_GETTEXT = SESIOC_GETTEXT;
-unsigned IOCTL_SESIOC_INIT = SESIOC_INIT;
-unsigned IOCTL_SUN_DKIOCGGEOM = SUN_DKIOCGGEOM;
-unsigned IOCTL_SUN_DKIOCINFO = SUN_DKIOCINFO;
-unsigned IOCTL_SUN_DKIOCGPART = SUN_DKIOCGPART;
-unsigned IOCTL_FBIOGTYPE = FBIOGTYPE;
-unsigned IOCTL_FBIOPUTCMAP = FBIOPUTCMAP;
-unsigned IOCTL_FBIOGETCMAP = FBIOGETCMAP;
-unsigned IOCTL_FBIOGATTR = FBIOGATTR;
-unsigned IOCTL_FBIOSVIDEO = FBIOSVIDEO;
-unsigned IOCTL_FBIOGVIDEO = FBIOGVIDEO;
-unsigned IOCTL_FBIOSCURSOR = FBIOSCURSOR;
-unsigned IOCTL_FBIOGCURSOR = FBIOGCURSOR;
-unsigned IOCTL_FBIOSCURPOS = FBIOSCURPOS;
-unsigned IOCTL_FBIOGCURPOS = FBIOGCURPOS;
-unsigned IOCTL_FBIOGCURMAX = FBIOGCURMAX;
-unsigned IOCTL_KIOCTRANS = KIOCTRANS;
-unsigned IOCTL_KIOCSETKEY = KIOCSETKEY;
-unsigned IOCTL_KIOCGETKEY = KIOCGETKEY;
-unsigned IOCTL_KIOCGTRANS = KIOCGTRANS;
-unsigned IOCTL_KIOCCMD = KIOCCMD;
-unsigned IOCTL_KIOCTYPE = KIOCTYPE;
-unsigned IOCTL_KIOCSDIRECT = KIOCSDIRECT;
-unsigned IOCTL_KIOCSKEY = KIOCSKEY;
-unsigned IOCTL_KIOCGKEY = KIOCGKEY;
-unsigned IOCTL_KIOCSLED = KIOCSLED;
-unsigned IOCTL_KIOCGLED = KIOCGLED;
-unsigned IOCTL_KIOCLAYOUT = KIOCLAYOUT;
-unsigned IOCTL_VUIDSFORMAT = VUIDSFORMAT;
-unsigned IOCTL_VUIDGFORMAT = VUIDGFORMAT;
-unsigned IOCTL_STICIO_GXINFO = STICIO_GXINFO;
-unsigned IOCTL_STICIO_RESET = STICIO_RESET;
-unsigned IOCTL_STICIO_STARTQ = STICIO_STARTQ;
-unsigned IOCTL_STICIO_STOPQ = STICIO_STOPQ;
-unsigned IOCTL_UKYOPON_IDENTIFY = UKYOPON_IDENTIFY;
-unsigned IOCTL_URIO_SEND_COMMAND = URIO_SEND_COMMAND;
-unsigned IOCTL_URIO_RECV_COMMAND = URIO_RECV_COMMAND;
-unsigned IOCTL_USB_REQUEST = USB_REQUEST;
-unsigned IOCTL_USB_SETDEBUG = USB_SETDEBUG;
-unsigned IOCTL_USB_DISCOVER = USB_DISCOVER;
-unsigned IOCTL_USB_DEVICEINFO = USB_DEVICEINFO;
-unsigned IOCTL_USB_DEVICEINFO_OLD = USB_DEVICEINFO_OLD;
-unsigned IOCTL_USB_DEVICESTATS = USB_DEVICESTATS;
-unsigned IOCTL_USB_GET_REPORT_DESC = USB_GET_REPORT_DESC;
-unsigned IOCTL_USB_SET_IMMED = USB_SET_IMMED;
-unsigned IOCTL_USB_GET_REPORT = USB_GET_REPORT;
-unsigned IOCTL_USB_SET_REPORT = USB_SET_REPORT;
-unsigned IOCTL_USB_GET_REPORT_ID = USB_GET_REPORT_ID;
-unsigned IOCTL_USB_GET_CONFIG = USB_GET_CONFIG;
-unsigned IOCTL_USB_SET_CONFIG = USB_SET_CONFIG;
-unsigned IOCTL_USB_GET_ALTINTERFACE = USB_GET_ALTINTERFACE;
-unsigned IOCTL_USB_SET_ALTINTERFACE = USB_SET_ALTINTERFACE;
-unsigned IOCTL_USB_GET_NO_ALT = USB_GET_NO_ALT;
-unsigned IOCTL_USB_GET_DEVICE_DESC = USB_GET_DEVICE_DESC;
-unsigned IOCTL_USB_GET_CONFIG_DESC = USB_GET_CONFIG_DESC;
-unsigned IOCTL_USB_GET_INTERFACE_DESC = USB_GET_INTERFACE_DESC;
-unsigned IOCTL_USB_GET_ENDPOINT_DESC = USB_GET_ENDPOINT_DESC;
-unsigned IOCTL_USB_GET_FULL_DESC = USB_GET_FULL_DESC;
-unsigned IOCTL_USB_GET_STRING_DESC = USB_GET_STRING_DESC;
-unsigned IOCTL_USB_DO_REQUEST = USB_DO_REQUEST;
-unsigned IOCTL_USB_GET_DEVICEINFO = USB_GET_DEVICEINFO;
-unsigned IOCTL_USB_GET_DEVICEINFO_OLD = USB_GET_DEVICEINFO_OLD;
-unsigned IOCTL_USB_SET_SHORT_XFER = USB_SET_SHORT_XFER;
-unsigned IOCTL_USB_SET_TIMEOUT = USB_SET_TIMEOUT;
-unsigned IOCTL_USB_SET_BULK_RA = USB_SET_BULK_RA;
-unsigned IOCTL_USB_SET_BULK_WB = USB_SET_BULK_WB;
-unsigned IOCTL_USB_SET_BULK_RA_OPT = USB_SET_BULK_RA_OPT;
-unsigned IOCTL_USB_SET_BULK_WB_OPT = USB_SET_BULK_WB_OPT;
-unsigned IOCTL_USB_GET_CM_OVER_DATA = USB_GET_CM_OVER_DATA;
-unsigned IOCTL_USB_SET_CM_OVER_DATA = USB_SET_CM_OVER_DATA;
-unsigned IOCTL_UTOPPYIOTURBO = UTOPPYIOTURBO;
-unsigned IOCTL_UTOPPYIOCANCEL = UTOPPYIOCANCEL;
-unsigned IOCTL_UTOPPYIOREBOOT = UTOPPYIOREBOOT;
-unsigned IOCTL_UTOPPYIOSTATS = UTOPPYIOSTATS;
-unsigned IOCTL_UTOPPYIORENAME = UTOPPYIORENAME;
-unsigned IOCTL_UTOPPYIOMKDIR = UTOPPYIOMKDIR;
-unsigned IOCTL_UTOPPYIODELETE = UTOPPYIODELETE;
-unsigned IOCTL_UTOPPYIOREADDIR = UTOPPYIOREADDIR;
-unsigned IOCTL_UTOPPYIOREADFILE = UTOPPYIOREADFILE;
-unsigned IOCTL_UTOPPYIOWRITEFILE = UTOPPYIOWRITEFILE;
-unsigned IOCTL_DIOSXDCMD = DIOSXDCMD;
-unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
-unsigned IOCTL_VT_SETMODE = VT_SETMODE;
-unsigned IOCTL_VT_GETMODE = VT_GETMODE;
-unsigned IOCTL_VT_RELDISP = VT_RELDISP;
-unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
-unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
-unsigned IOCTL_VT_GETACTIVE = VT_GETACTIVE;
-unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
-unsigned IOCTL_KDGETKBENT = KDGETKBENT;
-unsigned IOCTL_KDGKBMODE = KDGKBMODE;
-unsigned IOCTL_KDSKBMODE = KDSKBMODE;
-unsigned IOCTL_KDMKTONE = KDMKTONE;
-unsigned IOCTL_KDSETMODE = KDSETMODE;
-unsigned IOCTL_KDENABIO = KDENABIO;
-unsigned IOCTL_KDDISABIO = KDDISABIO;
-unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
-unsigned IOCTL_KDGETLED = KDGETLED;
-unsigned IOCTL_KDSETLED = KDSETLED;
-unsigned IOCTL_KDSETRAD = KDSETRAD;
-unsigned IOCTL_VGAPCVTID = VGAPCVTID;
-unsigned IOCTL_CONS_GETVERS = CONS_GETVERS;
-unsigned IOCTL_WSKBDIO_GTYPE = WSKBDIO_GTYPE;
-unsigned IOCTL_WSKBDIO_BELL = WSKBDIO_BELL;
-unsigned IOCTL_WSKBDIO_COMPLEXBELL = WSKBDIO_COMPLEXBELL;
-unsigned IOCTL_WSKBDIO_SETBELL = WSKBDIO_SETBELL;
-unsigned IOCTL_WSKBDIO_GETBELL = WSKBDIO_GETBELL;
-unsigned IOCTL_WSKBDIO_SETDEFAULTBELL = WSKBDIO_SETDEFAULTBELL;
-unsigned IOCTL_WSKBDIO_GETDEFAULTBELL = WSKBDIO_GETDEFAULTBELL;
-unsigned IOCTL_WSKBDIO_SETKEYREPEAT = WSKBDIO_SETKEYREPEAT;
-unsigned IOCTL_WSKBDIO_GETKEYREPEAT = WSKBDIO_GETKEYREPEAT;
-unsigned IOCTL_WSKBDIO_SETDEFAULTKEYREPEAT = WSKBDIO_SETDEFAULTKEYREPEAT;
-unsigned IOCTL_WSKBDIO_GETDEFAULTKEYREPEAT = WSKBDIO_GETDEFAULTKEYREPEAT;
-unsigned IOCTL_WSKBDIO_SETLEDS = WSKBDIO_SETLEDS;
-unsigned IOCTL_WSKBDIO_GETLEDS = WSKBDIO_GETLEDS;
-unsigned IOCTL_WSKBDIO_GETMAP = WSKBDIO_GETMAP;
-unsigned IOCTL_WSKBDIO_SETMAP = WSKBDIO_SETMAP;
-unsigned IOCTL_WSKBDIO_GETENCODING = WSKBDIO_GETENCODING;
-unsigned IOCTL_WSKBDIO_SETENCODING = WSKBDIO_SETENCODING;
-unsigned IOCTL_WSKBDIO_SETMODE = WSKBDIO_SETMODE;
-unsigned IOCTL_WSKBDIO_GETMODE = WSKBDIO_GETMODE;
-unsigned IOCTL_WSKBDIO_SETKEYCLICK = WSKBDIO_SETKEYCLICK;
-unsigned IOCTL_WSKBDIO_GETKEYCLICK = WSKBDIO_GETKEYCLICK;
-unsigned IOCTL_WSKBDIO_GETSCROLL = WSKBDIO_GETSCROLL;
-unsigned IOCTL_WSKBDIO_SETSCROLL = WSKBDIO_SETSCROLL;
-unsigned IOCTL_WSKBDIO_SETVERSION = WSKBDIO_SETVERSION;
-unsigned IOCTL_WSMOUSEIO_GTYPE = WSMOUSEIO_GTYPE;
-unsigned IOCTL_WSMOUSEIO_SRES = WSMOUSEIO_SRES;
-unsigned IOCTL_WSMOUSEIO_SSCALE = WSMOUSEIO_SSCALE;
-unsigned IOCTL_WSMOUSEIO_SRATE = WSMOUSEIO_SRATE;
-unsigned IOCTL_WSMOUSEIO_SCALIBCOORDS = WSMOUSEIO_SCALIBCOORDS;
-unsigned IOCTL_WSMOUSEIO_GCALIBCOORDS = WSMOUSEIO_GCALIBCOORDS;
-unsigned IOCTL_WSMOUSEIO_GETID = WSMOUSEIO_GETID;
-unsigned IOCTL_WSMOUSEIO_GETREPEAT = WSMOUSEIO_GETREPEAT;
-unsigned IOCTL_WSMOUSEIO_SETREPEAT = WSMOUSEIO_SETREPEAT;
-unsigned IOCTL_WSMOUSEIO_SETVERSION = WSMOUSEIO_SETVERSION;
-unsigned IOCTL_WSDISPLAYIO_GTYPE = WSDISPLAYIO_GTYPE;
-unsigned IOCTL_WSDISPLAYIO_GINFO = WSDISPLAYIO_GINFO;
-unsigned IOCTL_WSDISPLAYIO_GETCMAP = WSDISPLAYIO_GETCMAP;
-unsigned IOCTL_WSDISPLAYIO_PUTCMAP = WSDISPLAYIO_PUTCMAP;
-unsigned IOCTL_WSDISPLAYIO_GVIDEO = WSDISPLAYIO_GVIDEO;
-unsigned IOCTL_WSDISPLAYIO_SVIDEO = WSDISPLAYIO_SVIDEO;
-unsigned IOCTL_WSDISPLAYIO_GCURPOS = WSDISPLAYIO_GCURPOS;
-unsigned IOCTL_WSDISPLAYIO_SCURPOS = WSDISPLAYIO_SCURPOS;
-unsigned IOCTL_WSDISPLAYIO_GCURMAX = WSDISPLAYIO_GCURMAX;
-unsigned IOCTL_WSDISPLAYIO_GCURSOR = WSDISPLAYIO_GCURSOR;
-unsigned IOCTL_WSDISPLAYIO_SCURSOR = WSDISPLAYIO_SCURSOR;
-unsigned IOCTL_WSDISPLAYIO_GMODE = WSDISPLAYIO_GMODE;
-unsigned IOCTL_WSDISPLAYIO_SMODE = WSDISPLAYIO_SMODE;
-unsigned IOCTL_WSDISPLAYIO_LDFONT = WSDISPLAYIO_LDFONT;
-unsigned IOCTL_WSDISPLAYIO_ADDSCREEN = WSDISPLAYIO_ADDSCREEN;
-unsigned IOCTL_WSDISPLAYIO_DELSCREEN = WSDISPLAYIO_DELSCREEN;
-unsigned IOCTL_WSDISPLAYIO_SFONT = WSDISPLAYIO_SFONT;
-unsigned IOCTL__O_WSDISPLAYIO_SETKEYBOARD = _O_WSDISPLAYIO_SETKEYBOARD;
-unsigned IOCTL_WSDISPLAYIO_GETPARAM = WSDISPLAYIO_GETPARAM;
-unsigned IOCTL_WSDISPLAYIO_SETPARAM = WSDISPLAYIO_SETPARAM;
-unsigned IOCTL_WSDISPLAYIO_GETACTIVESCREEN = WSDISPLAYIO_GETACTIVESCREEN;
-unsigned IOCTL_WSDISPLAYIO_GETWSCHAR = WSDISPLAYIO_GETWSCHAR;
-unsigned IOCTL_WSDISPLAYIO_PUTWSCHAR = WSDISPLAYIO_PUTWSCHAR;
-unsigned IOCTL_WSDISPLAYIO_DGSCROLL = WSDISPLAYIO_DGSCROLL;
-unsigned IOCTL_WSDISPLAYIO_DSSCROLL = WSDISPLAYIO_DSSCROLL;
-unsigned IOCTL_WSDISPLAYIO_GMSGATTRS = WSDISPLAYIO_GMSGATTRS;
-unsigned IOCTL_WSDISPLAYIO_SMSGATTRS = WSDISPLAYIO_SMSGATTRS;
-unsigned IOCTL_WSDISPLAYIO_GBORDER = WSDISPLAYIO_GBORDER;
-unsigned IOCTL_WSDISPLAYIO_SBORDER = WSDISPLAYIO_SBORDER;
-unsigned IOCTL_WSDISPLAYIO_SSPLASH = WSDISPLAYIO_SSPLASH;
-unsigned IOCTL_WSDISPLAYIO_SPROGRESS = WSDISPLAYIO_SPROGRESS;
-unsigned IOCTL_WSDISPLAYIO_LINEBYTES = WSDISPLAYIO_LINEBYTES;
-unsigned IOCTL_WSDISPLAYIO_SETVERSION = WSDISPLAYIO_SETVERSION;
-unsigned IOCTL_WSMUXIO_ADD_DEVICE = WSMUXIO_ADD_DEVICE;
-unsigned IOCTL_WSMUXIO_REMOVE_DEVICE = WSMUXIO_REMOVE_DEVICE;
-unsigned IOCTL_WSMUXIO_LIST_DEVICES = WSMUXIO_LIST_DEVICES;
-unsigned IOCTL_WSMUXIO_INJECTEVENT = WSMUXIO_INJECTEVENT;
-unsigned IOCTL_WSDISPLAYIO_GET_BUSID = WSDISPLAYIO_GET_BUSID;
-unsigned IOCTL_WSDISPLAYIO_GET_EDID = WSDISPLAYIO_GET_EDID;
-unsigned IOCTL_WSDISPLAYIO_SET_POLLING = WSDISPLAYIO_SET_POLLING;
-unsigned IOCTL_WSDISPLAYIO_GET_FBINFO = WSDISPLAYIO_GET_FBINFO;
-unsigned IOCTL_WSDISPLAYIO_DOBLIT = WSDISPLAYIO_DOBLIT;
-unsigned IOCTL_WSDISPLAYIO_WAITBLIT = WSDISPLAYIO_WAITBLIT;
-unsigned IOCTL_BIOCLOCATE = BIOCLOCATE;
-unsigned IOCTL_BIOCINQ = BIOCINQ;
-unsigned IOCTL_BIOCDISK_NOVOL = BIOCDISK_NOVOL;
-unsigned IOCTL_BIOCDISK = BIOCDISK;
-unsigned IOCTL_BIOCVOL = BIOCVOL;
-unsigned IOCTL_BIOCALARM = BIOCALARM;
-unsigned IOCTL_BIOCBLINK = BIOCBLINK;
-unsigned IOCTL_BIOCSETSTATE = BIOCSETSTATE;
-unsigned IOCTL_BIOCVOLOPS = BIOCVOLOPS;
-unsigned IOCTL_MD_GETCONF = MD_GETCONF;
-unsigned IOCTL_MD_SETCONF = MD_SETCONF;
-unsigned IOCTL_CCDIOCSET = CCDIOCSET;
-unsigned IOCTL_CCDIOCCLR = CCDIOCCLR;
-unsigned IOCTL_CGDIOCSET = CGDIOCSET;
-unsigned IOCTL_CGDIOCCLR = CGDIOCCLR;
-unsigned IOCTL_CGDIOCGET = CGDIOCGET;
-unsigned IOCTL_FSSIOCSET = FSSIOCSET;
-unsigned IOCTL_FSSIOCGET = FSSIOCGET;
-unsigned IOCTL_FSSIOCCLR = FSSIOCCLR;
-unsigned IOCTL_FSSIOFSET = FSSIOFSET;
-unsigned IOCTL_FSSIOFGET = FSSIOFGET;
-unsigned IOCTL_BTDEV_ATTACH = BTDEV_ATTACH;
-unsigned IOCTL_BTDEV_DETACH = BTDEV_DETACH;
-unsigned IOCTL_BTSCO_GETINFO = BTSCO_GETINFO;
-unsigned IOCTL_KTTCP_IO_SEND = KTTCP_IO_SEND;
-unsigned IOCTL_KTTCP_IO_RECV = KTTCP_IO_RECV;
-unsigned IOCTL_IOC_LOCKSTAT_GVERSION = IOC_LOCKSTAT_GVERSION;
-unsigned IOCTL_IOC_LOCKSTAT_ENABLE = IOC_LOCKSTAT_ENABLE;
-unsigned IOCTL_IOC_LOCKSTAT_DISABLE = IOC_LOCKSTAT_DISABLE;
-unsigned IOCTL_VNDIOCSET = VNDIOCSET;
-unsigned IOCTL_VNDIOCCLR = VNDIOCCLR;
-unsigned IOCTL_VNDIOCGET = VNDIOCGET;
-unsigned IOCTL_SPKRTONE = SPKRTONE;
-unsigned IOCTL_SPKRTUNE = SPKRTUNE;
-unsigned IOCTL_SPKRGETVOL = SPKRGETVOL;
-unsigned IOCTL_SPKRSETVOL = SPKRSETVOL;
-unsigned IOCTL_BIOCGBLEN = BIOCGBLEN;
-unsigned IOCTL_BIOCSBLEN = BIOCSBLEN;
-unsigned IOCTL_BIOCSETF = BIOCSETF;
-unsigned IOCTL_BIOCFLUSH = BIOCFLUSH;
-unsigned IOCTL_BIOCPROMISC = BIOCPROMISC;
-unsigned IOCTL_BIOCGDLT = BIOCGDLT;
-unsigned IOCTL_BIOCGETIF = BIOCGETIF;
-unsigned IOCTL_BIOCSETIF = BIOCSETIF;
-unsigned IOCTL_BIOCGSTATS = BIOCGSTATS;
-unsigned IOCTL_BIOCGSTATSOLD = BIOCGSTATSOLD;
-unsigned IOCTL_BIOCIMMEDIATE = BIOCIMMEDIATE;
-unsigned IOCTL_BIOCVERSION = BIOCVERSION;
-unsigned IOCTL_BIOCSTCPF = BIOCSTCPF;
-unsigned IOCTL_BIOCSUDPF = BIOCSUDPF;
-unsigned IOCTL_BIOCGHDRCMPLT = BIOCGHDRCMPLT;
-unsigned IOCTL_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
-unsigned IOCTL_BIOCSDLT = BIOCSDLT;
-unsigned IOCTL_BIOCGDLTLIST = BIOCGDLTLIST;
-unsigned IOCTL_BIOCGSEESENT = BIOCGSEESENT;
-unsigned IOCTL_BIOCSSEESENT = BIOCSSEESENT;
-unsigned IOCTL_BIOCSRTIMEOUT = BIOCSRTIMEOUT;
-unsigned IOCTL_BIOCGRTIMEOUT = BIOCGRTIMEOUT;
-unsigned IOCTL_BIOCGFEEDBACK = BIOCGFEEDBACK;
-unsigned IOCTL_BIOCSFEEDBACK = BIOCSFEEDBACK;
-unsigned IOCTL_SIOCRAWATM = SIOCRAWATM;
-unsigned IOCTL_SIOCATMENA = SIOCATMENA;
-unsigned IOCTL_SIOCATMDIS = SIOCATMDIS;
-unsigned IOCTL_SIOCSPVCTX = SIOCSPVCTX;
-unsigned IOCTL_SIOCGPVCTX = SIOCGPVCTX;
-unsigned IOCTL_SIOCSPVCSIF = SIOCSPVCSIF;
-unsigned IOCTL_SIOCGPVCSIF = SIOCGPVCSIF;
-unsigned IOCTL_GRESADDRS = GRESADDRS;
-unsigned IOCTL_GRESADDRD = GRESADDRD;
-unsigned IOCTL_GREGADDRS = GREGADDRS;
-unsigned IOCTL_GREGADDRD = GREGADDRD;
-unsigned IOCTL_GRESPROTO = GRESPROTO;
-unsigned IOCTL_GREGPROTO = GREGPROTO;
-unsigned IOCTL_GRESSOCK = GRESSOCK;
-unsigned IOCTL_GREDSOCK = GREDSOCK;
-unsigned IOCTL_PPPIOCGRAWIN = PPPIOCGRAWIN;
-unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS;
-unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS;
-unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP;
-unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP;
-unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT;
-unsigned IOCTL_PPPIOCGRASYNCMAP = PPPIOCGRASYNCMAP;
-unsigned IOCTL_PPPIOCSRASYNCMAP = PPPIOCSRASYNCMAP;
-unsigned IOCTL_PPPIOCGMRU = PPPIOCGMRU;
-unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU;
-unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID;
-unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP;
-unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP;
-unsigned IOCTL_PPPIOCXFERUNIT = PPPIOCXFERUNIT;
-unsigned IOCTL_PPPIOCSCOMPRESS = PPPIOCSCOMPRESS;
-unsigned IOCTL_PPPIOCGNPMODE = PPPIOCGNPMODE;
-unsigned IOCTL_PPPIOCSNPMODE = PPPIOCSNPMODE;
-unsigned IOCTL_PPPIOCGIDLE = PPPIOCGIDLE;
-unsigned IOCTL_PPPIOCGMTU = PPPIOCGMTU;
-unsigned IOCTL_PPPIOCSMTU = PPPIOCSMTU;
-unsigned IOCTL_SIOCGPPPSTATS = SIOCGPPPSTATS;
-unsigned IOCTL_SIOCGPPPCSTATS = SIOCGPPPCSTATS;
-unsigned IOCTL_IOC_NPF_VERSION = IOC_NPF_VERSION;
-unsigned IOCTL_IOC_NPF_SWITCH = IOC_NPF_SWITCH;
-unsigned IOCTL_IOC_NPF_LOAD = IOC_NPF_LOAD;
-unsigned IOCTL_IOC_NPF_TABLE = IOC_NPF_TABLE;
-unsigned IOCTL_IOC_NPF_STATS = IOC_NPF_STATS;
-unsigned IOCTL_IOC_NPF_SAVE = IOC_NPF_SAVE;
-unsigned IOCTL_IOC_NPF_RULE = IOC_NPF_RULE;
-unsigned IOCTL_IOC_NPF_CONN_LOOKUP = IOC_NPF_CONN_LOOKUP;
-unsigned IOCTL_PPPOESETPARMS = PPPOESETPARMS;
-unsigned IOCTL_PPPOEGETPARMS = PPPOEGETPARMS;
-unsigned IOCTL_PPPOEGETSESSION = PPPOEGETSESSION;
-unsigned IOCTL_SPPPGETAUTHCFG = SPPPGETAUTHCFG;
-unsigned IOCTL_SPPPSETAUTHCFG = SPPPSETAUTHCFG;
-unsigned IOCTL_SPPPGETLCPCFG = SPPPGETLCPCFG;
-unsigned IOCTL_SPPPSETLCPCFG = SPPPSETLCPCFG;
-unsigned IOCTL_SPPPGETSTATUS = SPPPGETSTATUS;
-unsigned IOCTL_SPPPGETSTATUSNCP = SPPPGETSTATUSNCP;
-unsigned IOCTL_SPPPGETIDLETO = SPPPGETIDLETO;
-unsigned IOCTL_SPPPSETIDLETO = SPPPSETIDLETO;
-unsigned IOCTL_SPPPGETAUTHFAILURES = SPPPGETAUTHFAILURES;
-unsigned IOCTL_SPPPSETAUTHFAILURE = SPPPSETAUTHFAILURE;
-unsigned IOCTL_SPPPSETDNSOPTS = SPPPSETDNSOPTS;
-unsigned IOCTL_SPPPGETDNSOPTS = SPPPGETDNSOPTS;
-unsigned IOCTL_SPPPGETDNSADDRS = SPPPGETDNSADDRS;
-unsigned IOCTL_SPPPSETKEEPALIVE = SPPPSETKEEPALIVE;
-unsigned IOCTL_SPPPGETKEEPALIVE = SPPPGETKEEPALIVE;
-unsigned IOCTL_SRT_GETNRT = SRT_GETNRT;
-unsigned IOCTL_SRT_GETRT = SRT_GETRT;
-unsigned IOCTL_SRT_SETRT = SRT_SETRT;
-unsigned IOCTL_SRT_DELRT = SRT_DELRT;
-unsigned IOCTL_SRT_SFLAGS = SRT_SFLAGS;
-unsigned IOCTL_SRT_GFLAGS = SRT_GFLAGS;
-unsigned IOCTL_SRT_SGFLAGS = SRT_SGFLAGS;
-unsigned IOCTL_SRT_DEBUG = SRT_DEBUG;
-unsigned IOCTL_TAPGIFNAME = TAPGIFNAME;
-unsigned IOCTL_TUNSDEBUG = TUNSDEBUG;
-unsigned IOCTL_TUNGDEBUG = TUNGDEBUG;
-unsigned IOCTL_TUNSIFMODE = TUNSIFMODE;
-unsigned IOCTL_TUNSLMODE = TUNSLMODE;
-unsigned IOCTL_TUNSIFHEAD = TUNSIFHEAD;
-unsigned IOCTL_TUNGIFHEAD = TUNGIFHEAD;
-unsigned IOCTL_DIOCSTART = DIOCSTART;
-unsigned IOCTL_DIOCSTOP = DIOCSTOP;
-unsigned IOCTL_DIOCADDRULE = DIOCADDRULE;
-unsigned IOCTL_DIOCGETRULES = DIOCGETRULES;
-unsigned IOCTL_DIOCGETRULE = DIOCGETRULE;
-unsigned IOCTL_DIOCSETLCK = DIOCSETLCK;
-unsigned IOCTL_DIOCCLRSTATES = DIOCCLRSTATES;
-unsigned IOCTL_DIOCGETSTATE = DIOCGETSTATE;
-unsigned IOCTL_DIOCSETSTATUSIF = DIOCSETSTATUSIF;
-unsigned IOCTL_DIOCGETSTATUS = DIOCGETSTATUS;
-unsigned IOCTL_DIOCCLRSTATUS = DIOCCLRSTATUS;
-unsigned IOCTL_DIOCNATLOOK = DIOCNATLOOK;
-unsigned IOCTL_DIOCSETDEBUG = DIOCSETDEBUG;
-unsigned IOCTL_DIOCGETSTATES = DIOCGETSTATES;
-unsigned IOCTL_DIOCCHANGERULE = DIOCCHANGERULE;
-unsigned IOCTL_DIOCSETTIMEOUT = DIOCSETTIMEOUT;
-unsigned IOCTL_DIOCGETTIMEOUT = DIOCGETTIMEOUT;
-unsigned IOCTL_DIOCADDSTATE = DIOCADDSTATE;
-unsigned IOCTL_DIOCCLRRULECTRS = DIOCCLRRULECTRS;
-unsigned IOCTL_DIOCGETLIMIT = DIOCGETLIMIT;
-unsigned IOCTL_DIOCSETLIMIT = DIOCSETLIMIT;
-unsigned IOCTL_DIOCKILLSTATES = DIOCKILLSTATES;
-unsigned IOCTL_DIOCSTARTALTQ = DIOCSTARTALTQ;
-unsigned IOCTL_DIOCSTOPALTQ = DIOCSTOPALTQ;
-unsigned IOCTL_DIOCADDALTQ = DIOCADDALTQ;
-unsigned IOCTL_DIOCGETALTQS = DIOCGETALTQS;
-unsigned IOCTL_DIOCGETALTQ = DIOCGETALTQ;
-unsigned IOCTL_DIOCCHANGEALTQ = DIOCCHANGEALTQ;
-unsigned IOCTL_DIOCGETQSTATS = DIOCGETQSTATS;
-unsigned IOCTL_DIOCBEGINADDRS = DIOCBEGINADDRS;
-unsigned IOCTL_DIOCADDADDR = DIOCADDADDR;
-unsigned IOCTL_DIOCGETADDRS = DIOCGETADDRS;
-unsigned IOCTL_DIOCGETADDR = DIOCGETADDR;
-unsigned IOCTL_DIOCCHANGEADDR = DIOCCHANGEADDR;
-unsigned IOCTL_DIOCADDSTATES = DIOCADDSTATES;
-unsigned IOCTL_DIOCGETRULESETS = DIOCGETRULESETS;
-unsigned IOCTL_DIOCGETRULESET = DIOCGETRULESET;
-unsigned IOCTL_DIOCRCLRTABLES = DIOCRCLRTABLES;
-unsigned IOCTL_DIOCRADDTABLES = DIOCRADDTABLES;
-unsigned IOCTL_DIOCRDELTABLES = DIOCRDELTABLES;
-unsigned IOCTL_DIOCRGETTABLES = DIOCRGETTABLES;
-unsigned IOCTL_DIOCRGETTSTATS = DIOCRGETTSTATS;
-unsigned IOCTL_DIOCRCLRTSTATS = DIOCRCLRTSTATS;
-unsigned IOCTL_DIOCRCLRADDRS = DIOCRCLRADDRS;
-unsigned IOCTL_DIOCRADDADDRS = DIOCRADDADDRS;
-unsigned IOCTL_DIOCRDELADDRS = DIOCRDELADDRS;
-unsigned IOCTL_DIOCRSETADDRS = DIOCRSETADDRS;
-unsigned IOCTL_DIOCRGETADDRS = DIOCRGETADDRS;
-unsigned IOCTL_DIOCRGETASTATS = DIOCRGETASTATS;
-unsigned IOCTL_DIOCRCLRASTATS = DIOCRCLRASTATS;
-unsigned IOCTL_DIOCRTSTADDRS = DIOCRTSTADDRS;
-unsigned IOCTL_DIOCRSETTFLAGS = DIOCRSETTFLAGS;
-unsigned IOCTL_DIOCRINADEFINE = DIOCRINADEFINE;
-unsigned IOCTL_DIOCOSFPFLUSH = DIOCOSFPFLUSH;
-unsigned IOCTL_DIOCOSFPADD = DIOCOSFPADD;
-unsigned IOCTL_DIOCOSFPGET = DIOCOSFPGET;
-unsigned IOCTL_DIOCXBEGIN = DIOCXBEGIN;
-unsigned IOCTL_DIOCXCOMMIT = DIOCXCOMMIT;
-unsigned IOCTL_DIOCXROLLBACK = DIOCXROLLBACK;
-unsigned IOCTL_DIOCGETSRCNODES = DIOCGETSRCNODES;
-unsigned IOCTL_DIOCCLRSRCNODES = DIOCCLRSRCNODES;
-unsigned IOCTL_DIOCSETHOSTID = DIOCSETHOSTID;
-unsigned IOCTL_DIOCIGETIFACES = DIOCIGETIFACES;
-unsigned IOCTL_DIOCSETIFFLAG = DIOCSETIFFLAG;
-unsigned IOCTL_DIOCCLRIFFLAG = DIOCCLRIFFLAG;
-unsigned IOCTL_DIOCKILLSRCNODES = DIOCKILLSRCNODES;
-unsigned IOCTL_SLIOCGUNIT = SLIOCGUNIT;
-unsigned IOCTL_SIOCGBTINFO = SIOCGBTINFO;
-unsigned IOCTL_SIOCGBTINFOA = SIOCGBTINFOA;
-unsigned IOCTL_SIOCNBTINFO = SIOCNBTINFO;
-unsigned IOCTL_SIOCSBTFLAGS = SIOCSBTFLAGS;
-unsigned IOCTL_SIOCSBTPOLICY = SIOCSBTPOLICY;
-unsigned IOCTL_SIOCSBTPTYPE = SIOCSBTPTYPE;
-unsigned IOCTL_SIOCGBTSTATS = SIOCGBTSTATS;
-unsigned IOCTL_SIOCZBTSTATS = SIOCZBTSTATS;
-unsigned IOCTL_SIOCBTDUMP = SIOCBTDUMP;
-unsigned IOCTL_SIOCSBTSCOMTU = SIOCSBTSCOMTU;
-unsigned IOCTL_SIOCGBTFEAT = SIOCGBTFEAT;
-unsigned IOCTL_SIOCADNAT = SIOCADNAT;
-unsigned IOCTL_SIOCRMNAT = SIOCRMNAT;
-unsigned IOCTL_SIOCGNATS = SIOCGNATS;
-unsigned IOCTL_SIOCGNATL = SIOCGNATL;
-unsigned IOCTL_SIOCPURGENAT = SIOCPURGENAT;
-unsigned IOCTL_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
-unsigned IOCTL_SIOCAADDRCTL_POLICY = SIOCAADDRCTL_POLICY;
-unsigned IOCTL_SIOCDADDRCTL_POLICY = SIOCDADDRCTL_POLICY;
-unsigned IOCTL_SMBIOC_OPENSESSION = SMBIOC_OPENSESSION;
-unsigned IOCTL_SMBIOC_OPENSHARE = SMBIOC_OPENSHARE;
-unsigned IOCTL_SMBIOC_REQUEST = SMBIOC_REQUEST;
-unsigned IOCTL_SMBIOC_SETFLAGS = SMBIOC_SETFLAGS;
-unsigned IOCTL_SMBIOC_LOOKUP = SMBIOC_LOOKUP;
-unsigned IOCTL_SMBIOC_READ = SMBIOC_READ;
-unsigned IOCTL_SMBIOC_WRITE = SMBIOC_WRITE;
-unsigned IOCTL_AGPIOC_INFO = AGPIOC_INFO;
-unsigned IOCTL_AGPIOC_ACQUIRE = AGPIOC_ACQUIRE;
-unsigned IOCTL_AGPIOC_RELEASE = AGPIOC_RELEASE;
-unsigned IOCTL_AGPIOC_SETUP = AGPIOC_SETUP;
-unsigned IOCTL_AGPIOC_ALLOCATE = AGPIOC_ALLOCATE;
-unsigned IOCTL_AGPIOC_DEALLOCATE = AGPIOC_DEALLOCATE;
-unsigned IOCTL_AGPIOC_BIND = AGPIOC_BIND;
-unsigned IOCTL_AGPIOC_UNBIND = AGPIOC_UNBIND;
-unsigned IOCTL_AUDIO_GETINFO = AUDIO_GETINFO;
-unsigned IOCTL_AUDIO_SETINFO = AUDIO_SETINFO;
-unsigned IOCTL_AUDIO_DRAIN = AUDIO_DRAIN;
-unsigned IOCTL_AUDIO_FLUSH = AUDIO_FLUSH;
-unsigned IOCTL_AUDIO_WSEEK = AUDIO_WSEEK;
-unsigned IOCTL_AUDIO_RERROR = AUDIO_RERROR;
-unsigned IOCTL_AUDIO_GETDEV = AUDIO_GETDEV;
-unsigned IOCTL_AUDIO_GETENC = AUDIO_GETENC;
-unsigned IOCTL_AUDIO_GETFD = AUDIO_GETFD;
-unsigned IOCTL_AUDIO_SETFD = AUDIO_SETFD;
-unsigned IOCTL_AUDIO_PERROR = AUDIO_PERROR;
-unsigned IOCTL_AUDIO_GETIOFFS = AUDIO_GETIOFFS;
-unsigned IOCTL_AUDIO_GETOOFFS = AUDIO_GETOOFFS;
-unsigned IOCTL_AUDIO_GETPROPS = AUDIO_GETPROPS;
-unsigned IOCTL_AUDIO_GETBUFINFO = AUDIO_GETBUFINFO;
-unsigned IOCTL_AUDIO_SETCHAN = AUDIO_SETCHAN;
-unsigned IOCTL_AUDIO_GETCHAN = AUDIO_GETCHAN;
-unsigned IOCTL_AUDIO_MIXER_READ = AUDIO_MIXER_READ;
-unsigned IOCTL_AUDIO_MIXER_WRITE = AUDIO_MIXER_WRITE;
-unsigned IOCTL_AUDIO_MIXER_DEVINFO = AUDIO_MIXER_DEVINFO;
-unsigned IOCTL_ATAIOCCOMMAND = ATAIOCCOMMAND;
-unsigned IOCTL_ATABUSIOSCAN = ATABUSIOSCAN;
-unsigned IOCTL_ATABUSIORESET = ATABUSIORESET;
-unsigned IOCTL_ATABUSIODETACH = ATABUSIODETACH;
-unsigned IOCTL_CDIOCPLAYTRACKS = CDIOCPLAYTRACKS;
-unsigned IOCTL_CDIOCPLAYBLOCKS = CDIOCPLAYBLOCKS;
-unsigned IOCTL_CDIOCREADSUBCHANNEL = CDIOCREADSUBCHANNEL;
-unsigned IOCTL_CDIOREADTOCHEADER = CDIOREADTOCHEADER;
-unsigned IOCTL_CDIOREADTOCENTRIES = CDIOREADTOCENTRIES;
-unsigned IOCTL_CDIOREADMSADDR = CDIOREADMSADDR;
-unsigned IOCTL_CDIOCSETPATCH = CDIOCSETPATCH;
-unsigned IOCTL_CDIOCGETVOL = CDIOCGETVOL;
-unsigned IOCTL_CDIOCSETVOL = CDIOCSETVOL;
-unsigned IOCTL_CDIOCSETMONO = CDIOCSETMONO;
-unsigned IOCTL_CDIOCSETSTEREO = CDIOCSETSTEREO;
-unsigned IOCTL_CDIOCSETMUTE = CDIOCSETMUTE;
-unsigned IOCTL_CDIOCSETLEFT = CDIOCSETLEFT;
-unsigned IOCTL_CDIOCSETRIGHT = CDIOCSETRIGHT;
-unsigned IOCTL_CDIOCSETDEBUG = CDIOCSETDEBUG;
-unsigned IOCTL_CDIOCCLRDEBUG = CDIOCCLRDEBUG;
-unsigned IOCTL_CDIOCPAUSE = CDIOCPAUSE;
-unsigned IOCTL_CDIOCRESUME = CDIOCRESUME;
-unsigned IOCTL_CDIOCRESET = CDIOCRESET;
-unsigned IOCTL_CDIOCSTART = CDIOCSTART;
-unsigned IOCTL_CDIOCSTOP = CDIOCSTOP;
-unsigned IOCTL_CDIOCEJECT = CDIOCEJECT;
-unsigned IOCTL_CDIOCALLOW = CDIOCALLOW;
-unsigned IOCTL_CDIOCPREVENT = CDIOCPREVENT;
-unsigned IOCTL_CDIOCCLOSE = CDIOCCLOSE;
-unsigned IOCTL_CDIOCPLAYMSF = CDIOCPLAYMSF;
-unsigned IOCTL_CDIOCLOADUNLOAD = CDIOCLOADUNLOAD;
-unsigned IOCTL_CHIOMOVE = CHIOMOVE;
-unsigned IOCTL_CHIOEXCHANGE = CHIOEXCHANGE;
-unsigned IOCTL_CHIOPOSITION = CHIOPOSITION;
-unsigned IOCTL_CHIOGPICKER = CHIOGPICKER;
-unsigned IOCTL_CHIOSPICKER = CHIOSPICKER;
-unsigned IOCTL_CHIOGPARAMS = CHIOGPARAMS;
-unsigned IOCTL_CHIOIELEM = CHIOIELEM;
-unsigned IOCTL_OCHIOGSTATUS = OCHIOGSTATUS;
-unsigned IOCTL_CHIOGSTATUS = CHIOGSTATUS;
-unsigned IOCTL_CHIOSVOLTAG = CHIOSVOLTAG;
-unsigned IOCTL_CLOCKCTL_SETTIMEOFDAY = CLOCKCTL_SETTIMEOFDAY;
-unsigned IOCTL_CLOCKCTL_ADJTIME = CLOCKCTL_ADJTIME;
-unsigned IOCTL_CLOCKCTL_CLOCK_SETTIME = CLOCKCTL_CLOCK_SETTIME;
-unsigned IOCTL_CLOCKCTL_NTP_ADJTIME = CLOCKCTL_NTP_ADJTIME;
-unsigned IOCTL_IOC_CPU_SETSTATE = IOC_CPU_SETSTATE;
-unsigned IOCTL_IOC_CPU_GETSTATE = IOC_CPU_GETSTATE;
-unsigned IOCTL_IOC_CPU_GETCOUNT = IOC_CPU_GETCOUNT;
-unsigned IOCTL_IOC_CPU_MAPID = IOC_CPU_MAPID;
-unsigned IOCTL_IOC_CPU_UCODE_GET_VERSION = IOC_CPU_UCODE_GET_VERSION;
-unsigned IOCTL_IOC_CPU_UCODE_APPLY = IOC_CPU_UCODE_APPLY;
-unsigned IOCTL_DIOCGDINFO = DIOCGDINFO;
-unsigned IOCTL_DIOCSDINFO = DIOCSDINFO;
-unsigned IOCTL_DIOCWDINFO = DIOCWDINFO;
-unsigned IOCTL_DIOCRFORMAT = DIOCRFORMAT;
-unsigned IOCTL_DIOCWFORMAT = DIOCWFORMAT;
-unsigned IOCTL_DIOCSSTEP = DIOCSSTEP;
-unsigned IOCTL_DIOCSRETRIES = DIOCSRETRIES;
-unsigned IOCTL_DIOCKLABEL = DIOCKLABEL;
-unsigned IOCTL_DIOCWLABEL = DIOCWLABEL;
-unsigned IOCTL_DIOCSBAD = DIOCSBAD;
-unsigned IOCTL_DIOCEJECT = DIOCEJECT;
-unsigned IOCTL_ODIOCEJECT = ODIOCEJECT;
-unsigned IOCTL_DIOCLOCK = DIOCLOCK;
-unsigned IOCTL_DIOCGDEFLABEL = DIOCGDEFLABEL;
-unsigned IOCTL_DIOCCLRLABEL = DIOCCLRLABEL;
-unsigned IOCTL_DIOCGCACHE = DIOCGCACHE;
-unsigned IOCTL_DIOCSCACHE = DIOCSCACHE;
-unsigned IOCTL_DIOCCACHESYNC = DIOCCACHESYNC;
-unsigned IOCTL_DIOCBSLIST = DIOCBSLIST;
-unsigned IOCTL_DIOCBSFLUSH = DIOCBSFLUSH;
-unsigned IOCTL_DIOCAWEDGE = DIOCAWEDGE;
-unsigned IOCTL_DIOCGWEDGEINFO = DIOCGWEDGEINFO;
-unsigned IOCTL_DIOCDWEDGE = DIOCDWEDGE;
-unsigned IOCTL_DIOCLWEDGES = DIOCLWEDGES;
-unsigned IOCTL_DIOCGSTRATEGY = DIOCGSTRATEGY;
-unsigned IOCTL_DIOCSSTRATEGY = DIOCSSTRATEGY;
-unsigned IOCTL_DIOCGDISKINFO = DIOCGDISKINFO;
-unsigned IOCTL_DIOCTUR = DIOCTUR;
-unsigned IOCTL_DIOCMWEDGES = DIOCMWEDGES;
-unsigned IOCTL_DIOCGSECTORSIZE = DIOCGSECTORSIZE;
-unsigned IOCTL_DIOCGMEDIASIZE = DIOCGMEDIASIZE;
-unsigned IOCTL_DRVDETACHDEV = DRVDETACHDEV;
-unsigned IOCTL_DRVRESCANBUS = DRVRESCANBUS;
-unsigned IOCTL_DRVCTLCOMMAND = DRVCTLCOMMAND;
-unsigned IOCTL_DRVRESUMEDEV = DRVRESUMEDEV;
-unsigned IOCTL_DRVLISTDEV = DRVLISTDEV;
-unsigned IOCTL_DRVGETEVENT = DRVGETEVENT;
-unsigned IOCTL_DRVSUSPENDDEV = DRVSUSPENDDEV;
-unsigned IOCTL_DVD_READ_STRUCT = DVD_READ_STRUCT;
-unsigned IOCTL_DVD_WRITE_STRUCT = DVD_WRITE_STRUCT;
-unsigned IOCTL_DVD_AUTH = DVD_AUTH;
-unsigned IOCTL_ENVSYS_GETDICTIONARY = ENVSYS_GETDICTIONARY;
-unsigned IOCTL_ENVSYS_SETDICTIONARY = ENVSYS_SETDICTIONARY;
-unsigned IOCTL_ENVSYS_REMOVEPROPS = ENVSYS_REMOVEPROPS;
-unsigned IOCTL_ENVSYS_GTREDATA = ENVSYS_GTREDATA;
-unsigned IOCTL_ENVSYS_GTREINFO = ENVSYS_GTREINFO;
-unsigned IOCTL_KFILTER_BYFILTER = KFILTER_BYFILTER;
-unsigned IOCTL_KFILTER_BYNAME = KFILTER_BYNAME;
-unsigned IOCTL_FDIOCGETOPTS = FDIOCGETOPTS;
-unsigned IOCTL_FDIOCSETOPTS = FDIOCSETOPTS;
-unsigned IOCTL_FDIOCSETFORMAT = FDIOCSETFORMAT;
-unsigned IOCTL_FDIOCGETFORMAT = FDIOCGETFORMAT;
-unsigned IOCTL_FDIOCFORMAT_TRACK = FDIOCFORMAT_TRACK;
-unsigned IOCTL_FIOCLEX = FIOCLEX;
-unsigned IOCTL_FIONCLEX = FIONCLEX;
-unsigned IOCTL_FIONREAD = FIONREAD;
-unsigned IOCTL_FIONBIO = FIONBIO;
-unsigned IOCTL_FIOASYNC = FIOASYNC;
-unsigned IOCTL_FIOSETOWN = FIOSETOWN;
-unsigned IOCTL_FIOGETOWN = FIOGETOWN;
-unsigned IOCTL_OFIOGETBMAP = OFIOGETBMAP;
-unsigned IOCTL_FIOGETBMAP = FIOGETBMAP;
-unsigned IOCTL_FIONWRITE = FIONWRITE;
-unsigned IOCTL_FIONSPACE = FIONSPACE;
-unsigned IOCTL_GPIOINFO = GPIOINFO;
-unsigned IOCTL_GPIOSET = GPIOSET;
-unsigned IOCTL_GPIOUNSET = GPIOUNSET;
-unsigned IOCTL_GPIOREAD = GPIOREAD;
-unsigned IOCTL_GPIOWRITE = GPIOWRITE;
-unsigned IOCTL_GPIOTOGGLE = GPIOTOGGLE;
-unsigned IOCTL_GPIOATTACH = GPIOATTACH;
-unsigned IOCTL_PTIOCNETBSD = PTIOCNETBSD;
-unsigned IOCTL_PTIOCSUNOS = PTIOCSUNOS;
-unsigned IOCTL_PTIOCLINUX = PTIOCLINUX;
-unsigned IOCTL_PTIOCFREEBSD = PTIOCFREEBSD;
-unsigned IOCTL_PTIOCULTRIX = PTIOCULTRIX;
-unsigned IOCTL_TIOCHPCL = TIOCHPCL;
-unsigned IOCTL_TIOCGETP = TIOCGETP;
-unsigned IOCTL_TIOCSETP = TIOCSETP;
-unsigned IOCTL_TIOCSETN = TIOCSETN;
-unsigned IOCTL_TIOCSETC = TIOCSETC;
-unsigned IOCTL_TIOCGETC = TIOCGETC;
-unsigned IOCTL_TIOCLBIS = TIOCLBIS;
-unsigned IOCTL_TIOCLBIC = TIOCLBIC;
-unsigned IOCTL_TIOCLSET = TIOCLSET;
-unsigned IOCTL_TIOCLGET = TIOCLGET;
-unsigned IOCTL_TIOCSLTC = TIOCSLTC;
-unsigned IOCTL_TIOCGLTC = TIOCGLTC;
-unsigned IOCTL_OTIOCCONS = OTIOCCONS;
-unsigned IOCTL_JOY_SETTIMEOUT = JOY_SETTIMEOUT;
-unsigned IOCTL_JOY_GETTIMEOUT = JOY_GETTIMEOUT;
-unsigned IOCTL_JOY_SET_X_OFFSET = JOY_SET_X_OFFSET;
-unsigned IOCTL_JOY_SET_Y_OFFSET = JOY_SET_Y_OFFSET;
-unsigned IOCTL_JOY_GET_X_OFFSET = JOY_GET_X_OFFSET;
-unsigned IOCTL_JOY_GET_Y_OFFSET = JOY_GET_Y_OFFSET;
-unsigned IOCTL_OKIOCGSYMBOL = OKIOCGSYMBOL;
-unsigned IOCTL_OKIOCGVALUE = OKIOCGVALUE;
-unsigned IOCTL_KIOCGSIZE = KIOCGSIZE;
-unsigned IOCTL_KIOCGVALUE = KIOCGVALUE;
-unsigned IOCTL_KIOCGSYMBOL = KIOCGSYMBOL;
-unsigned IOCTL_LUAINFO = LUAINFO;
-unsigned IOCTL_LUACREATE = LUACREATE;
-unsigned IOCTL_LUADESTROY = LUADESTROY;
-unsigned IOCTL_LUAREQUIRE = LUAREQUIRE;
-unsigned IOCTL_LUALOAD = LUALOAD;
-unsigned IOCTL_MIDI_PRETIME = MIDI_PRETIME;
-unsigned IOCTL_MIDI_MPUMODE = MIDI_MPUMODE;
-unsigned IOCTL_MIDI_MPUCMD = MIDI_MPUCMD;
-unsigned IOCTL_SEQUENCER_RESET = SEQUENCER_RESET;
-unsigned IOCTL_SEQUENCER_SYNC = SEQUENCER_SYNC;
-unsigned IOCTL_SEQUENCER_INFO = SEQUENCER_INFO;
-unsigned IOCTL_SEQUENCER_CTRLRATE = SEQUENCER_CTRLRATE;
-unsigned IOCTL_SEQUENCER_GETOUTCOUNT = SEQUENCER_GETOUTCOUNT;
-unsigned IOCTL_SEQUENCER_GETINCOUNT = SEQUENCER_GETINCOUNT;
-unsigned IOCTL_SEQUENCER_RESETSAMPLES = SEQUENCER_RESETSAMPLES;
-unsigned IOCTL_SEQUENCER_NRSYNTHS = SEQUENCER_NRSYNTHS;
-unsigned IOCTL_SEQUENCER_NRMIDIS = SEQUENCER_NRMIDIS;
-unsigned IOCTL_SEQUENCER_THRESHOLD = SEQUENCER_THRESHOLD;
-unsigned IOCTL_SEQUENCER_MEMAVL = SEQUENCER_MEMAVL;
-unsigned IOCTL_SEQUENCER_PANIC = SEQUENCER_PANIC;
-unsigned IOCTL_SEQUENCER_OUTOFBAND = SEQUENCER_OUTOFBAND;
-unsigned IOCTL_SEQUENCER_GETTIME = SEQUENCER_GETTIME;
-unsigned IOCTL_SEQUENCER_TMR_TIMEBASE = SEQUENCER_TMR_TIMEBASE;
-unsigned IOCTL_SEQUENCER_TMR_START = SEQUENCER_TMR_START;
-unsigned IOCTL_SEQUENCER_TMR_STOP = SEQUENCER_TMR_STOP;
-unsigned IOCTL_SEQUENCER_TMR_CONTINUE = SEQUENCER_TMR_CONTINUE;
-unsigned IOCTL_SEQUENCER_TMR_TEMPO = SEQUENCER_TMR_TEMPO;
-unsigned IOCTL_SEQUENCER_TMR_SOURCE = SEQUENCER_TMR_SOURCE;
-unsigned IOCTL_SEQUENCER_TMR_METRONOME = SEQUENCER_TMR_METRONOME;
-unsigned IOCTL_SEQUENCER_TMR_SELECT = SEQUENCER_TMR_SELECT;
-unsigned IOCTL_MTIOCTOP = MTIOCTOP;
-unsigned IOCTL_MTIOCGET = MTIOCGET;
-unsigned IOCTL_MTIOCIEOT = MTIOCIEOT;
-unsigned IOCTL_MTIOCEEOT = MTIOCEEOT;
-unsigned IOCTL_MTIOCRDSPOS = MTIOCRDSPOS;
-unsigned IOCTL_MTIOCRDHPOS = MTIOCRDHPOS;
-unsigned IOCTL_MTIOCSLOCATE = MTIOCSLOCATE;
-unsigned IOCTL_MTIOCHLOCATE = MTIOCHLOCATE;
-unsigned IOCTL_POWER_EVENT_RECVDICT = POWER_EVENT_RECVDICT;
-unsigned IOCTL_POWER_IOC_GET_TYPE = POWER_IOC_GET_TYPE;
-unsigned IOCTL_POWER_IOC_GET_TYPE_WITH_LOSSAGE =
- POWER_IOC_GET_TYPE_WITH_LOSSAGE;
-unsigned IOCTL_RIOCGINFO = RIOCGINFO;
-unsigned IOCTL_RIOCSINFO = RIOCSINFO;
-unsigned IOCTL_RIOCSSRCH = RIOCSSRCH;
-unsigned IOCTL_RNDGETENTCNT = RNDGETENTCNT;
-unsigned IOCTL_RNDGETSRCNUM = RNDGETSRCNUM;
-unsigned IOCTL_RNDGETSRCNAME = RNDGETSRCNAME;
-unsigned IOCTL_RNDCTL = RNDCTL;
-unsigned IOCTL_RNDADDDATA = RNDADDDATA;
-unsigned IOCTL_RNDGETPOOLSTAT = RNDGETPOOLSTAT;
-unsigned IOCTL_RNDGETESTNUM = RNDGETESTNUM;
-unsigned IOCTL_RNDGETESTNAME = RNDGETESTNAME;
-unsigned IOCTL_SCIOCGET = SCIOCGET;
-unsigned IOCTL_SCIOCSET = SCIOCSET;
-unsigned IOCTL_SCIOCRESTART = SCIOCRESTART;
-unsigned IOCTL_SCIOC_USE_ADF = SCIOC_USE_ADF;
-unsigned IOCTL_SCIOCCOMMAND = SCIOCCOMMAND;
-unsigned IOCTL_SCIOCDEBUG = SCIOCDEBUG;
-unsigned IOCTL_SCIOCIDENTIFY = SCIOCIDENTIFY;
-unsigned IOCTL_OSCIOCIDENTIFY = OSCIOCIDENTIFY;
-unsigned IOCTL_SCIOCDECONFIG = SCIOCDECONFIG;
-unsigned IOCTL_SCIOCRECONFIG = SCIOCRECONFIG;
-unsigned IOCTL_SCIOCRESET = SCIOCRESET;
-unsigned IOCTL_SCBUSIOSCAN = SCBUSIOSCAN;
-unsigned IOCTL_SCBUSIORESET = SCBUSIORESET;
-unsigned IOCTL_SCBUSIODETACH = SCBUSIODETACH;
-unsigned IOCTL_SCBUSACCEL = SCBUSACCEL;
-unsigned IOCTL_SCBUSIOLLSCAN = SCBUSIOLLSCAN;
-unsigned IOCTL_SIOCSHIWAT = SIOCSHIWAT;
-unsigned IOCTL_SIOCGHIWAT = SIOCGHIWAT;
-unsigned IOCTL_SIOCSLOWAT = SIOCSLOWAT;
-unsigned IOCTL_SIOCGLOWAT = SIOCGLOWAT;
-unsigned IOCTL_SIOCATMARK = SIOCATMARK;
-unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
-unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
-unsigned IOCTL_SIOCADDRT = SIOCADDRT;
-unsigned IOCTL_SIOCDELRT = SIOCDELRT;
-unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
-unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
-unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
-unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
-unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
-unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
-unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
-unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
-unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
-unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
-unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
-unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
-unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
-unsigned IOCTL_SIOCDIFADDR = SIOCDIFADDR;
-unsigned IOCTL_SIOCAIFADDR = SIOCAIFADDR;
-unsigned IOCTL_SIOCGIFALIAS = SIOCGIFALIAS;
-unsigned IOCTL_SIOCGIFAFLAG_IN = SIOCGIFAFLAG_IN;
-unsigned IOCTL_SIOCALIFADDR = SIOCALIFADDR;
-unsigned IOCTL_SIOCGLIFADDR = SIOCGLIFADDR;
-unsigned IOCTL_SIOCDLIFADDR = SIOCDLIFADDR;
-unsigned IOCTL_SIOCSIFADDRPREF = SIOCSIFADDRPREF;
-unsigned IOCTL_SIOCGIFADDRPREF = SIOCGIFADDRPREF;
-unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
-unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
-unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
-unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
-unsigned IOCTL_SIOCSIFMEDIA = SIOCSIFMEDIA;
-unsigned IOCTL_SIOCGIFMEDIA = SIOCGIFMEDIA;
-unsigned IOCTL_SIOCSIFGENERIC = SIOCSIFGENERIC;
-unsigned IOCTL_SIOCGIFGENERIC = SIOCGIFGENERIC;
-unsigned IOCTL_SIOCSIFPHYADDR = SIOCSIFPHYADDR;
-unsigned IOCTL_SIOCGIFPSRCADDR = SIOCGIFPSRCADDR;
-unsigned IOCTL_SIOCGIFPDSTADDR = SIOCGIFPDSTADDR;
-unsigned IOCTL_SIOCDIFPHYADDR = SIOCDIFPHYADDR;
-unsigned IOCTL_SIOCSLIFPHYADDR = SIOCSLIFPHYADDR;
-unsigned IOCTL_SIOCGLIFPHYADDR = SIOCGLIFPHYADDR;
-unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
-unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
-unsigned IOCTL_SIOCSDRVSPEC = SIOCSDRVSPEC;
-unsigned IOCTL_SIOCGDRVSPEC = SIOCGDRVSPEC;
-unsigned IOCTL_SIOCIFCREATE = SIOCIFCREATE;
-unsigned IOCTL_SIOCIFDESTROY = SIOCIFDESTROY;
-unsigned IOCTL_SIOCIFGCLONERS = SIOCIFGCLONERS;
-unsigned IOCTL_SIOCGIFDLT = SIOCGIFDLT;
-unsigned IOCTL_SIOCGIFCAP = SIOCGIFCAP;
-unsigned IOCTL_SIOCSIFCAP = SIOCSIFCAP;
-unsigned IOCTL_SIOCSVH = SIOCSVH;
-unsigned IOCTL_SIOCGVH = SIOCGVH;
-unsigned IOCTL_SIOCINITIFADDR = SIOCINITIFADDR;
-unsigned IOCTL_SIOCGIFDATA = SIOCGIFDATA;
-unsigned IOCTL_SIOCZIFDATA = SIOCZIFDATA;
-unsigned IOCTL_SIOCGLINKSTR = SIOCGLINKSTR;
-unsigned IOCTL_SIOCSLINKSTR = SIOCSLINKSTR;
-unsigned IOCTL_SIOCGETHERCAP = SIOCGETHERCAP;
-unsigned IOCTL_SIOCGIFINDEX = SIOCGIFINDEX;
-unsigned IOCTL_SIOCSETPFSYNC = SIOCSETPFSYNC;
-unsigned IOCTL_SIOCGETPFSYNC = SIOCGETPFSYNC;
-unsigned IOCTL_PPS_IOC_CREATE = PPS_IOC_CREATE;
-unsigned IOCTL_PPS_IOC_DESTROY = PPS_IOC_DESTROY;
-unsigned IOCTL_PPS_IOC_SETPARAMS = PPS_IOC_SETPARAMS;
-unsigned IOCTL_PPS_IOC_GETPARAMS = PPS_IOC_GETPARAMS;
-unsigned IOCTL_PPS_IOC_GETCAP = PPS_IOC_GETCAP;
-unsigned IOCTL_PPS_IOC_FETCH = PPS_IOC_FETCH;
-unsigned IOCTL_PPS_IOC_KCBIND = PPS_IOC_KCBIND;
-unsigned IOCTL_TIOCEXCL = TIOCEXCL;
-unsigned IOCTL_TIOCNXCL = TIOCNXCL;
-unsigned IOCTL_TIOCFLUSH = TIOCFLUSH;
-unsigned IOCTL_TIOCGETA = TIOCGETA;
-unsigned IOCTL_TIOCSETA = TIOCSETA;
-unsigned IOCTL_TIOCSETAW = TIOCSETAW;
-unsigned IOCTL_TIOCSETAF = TIOCSETAF;
-unsigned IOCTL_TIOCGETD = TIOCGETD;
-unsigned IOCTL_TIOCSETD = TIOCSETD;
-unsigned IOCTL_TIOCGLINED = TIOCGLINED;
-unsigned IOCTL_TIOCSLINED = TIOCSLINED;
-unsigned IOCTL_TIOCSBRK = TIOCSBRK;
-unsigned IOCTL_TIOCCBRK = TIOCCBRK;
-unsigned IOCTL_TIOCSDTR = TIOCSDTR;
-unsigned IOCTL_TIOCCDTR = TIOCCDTR;
-unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
-unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
-unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
-unsigned IOCTL_TIOCSTI = TIOCSTI;
-unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
-unsigned IOCTL_TIOCPKT = TIOCPKT;
-unsigned IOCTL_TIOCSTOP = TIOCSTOP;
-unsigned IOCTL_TIOCSTART = TIOCSTART;
-unsigned IOCTL_TIOCMSET = TIOCMSET;
-unsigned IOCTL_TIOCMBIS = TIOCMBIS;
-unsigned IOCTL_TIOCMBIC = TIOCMBIC;
-unsigned IOCTL_TIOCMGET = TIOCMGET;
-unsigned IOCTL_TIOCREMOTE = TIOCREMOTE;
-unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
-unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
-unsigned IOCTL_TIOCUCNTL = TIOCUCNTL;
-unsigned IOCTL_TIOCSTAT = TIOCSTAT;
-unsigned IOCTL_TIOCGSID = TIOCGSID;
-unsigned IOCTL_TIOCCONS = TIOCCONS;
-unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
-unsigned IOCTL_TIOCEXT = TIOCEXT;
-unsigned IOCTL_TIOCSIG = TIOCSIG;
-unsigned IOCTL_TIOCDRAIN = TIOCDRAIN;
-unsigned IOCTL_TIOCGFLAGS = TIOCGFLAGS;
-unsigned IOCTL_TIOCSFLAGS = TIOCSFLAGS;
-unsigned IOCTL_TIOCDCDTIMESTAMP = TIOCDCDTIMESTAMP;
-unsigned IOCTL_TIOCRCVFRAME = TIOCRCVFRAME;
-unsigned IOCTL_TIOCXMTFRAME = TIOCXMTFRAME;
-unsigned IOCTL_TIOCPTMGET = TIOCPTMGET;
-unsigned IOCTL_TIOCGRANTPT = TIOCGRANTPT;
-unsigned IOCTL_TIOCPTSNAME = TIOCPTSNAME;
-unsigned IOCTL_TIOCSQSIZE = TIOCSQSIZE;
-unsigned IOCTL_TIOCGQSIZE = TIOCGQSIZE;
-unsigned IOCTL_VERIEXEC_LOAD = VERIEXEC_LOAD;
-unsigned IOCTL_VERIEXEC_TABLESIZE = VERIEXEC_TABLESIZE;
-unsigned IOCTL_VERIEXEC_DELETE = VERIEXEC_DELETE;
-unsigned IOCTL_VERIEXEC_QUERY = VERIEXEC_QUERY;
-unsigned IOCTL_VERIEXEC_DUMP = VERIEXEC_DUMP;
-unsigned IOCTL_VERIEXEC_FLUSH = VERIEXEC_FLUSH;
-unsigned IOCTL_VIDIOC_QUERYCAP = VIDIOC_QUERYCAP;
-unsigned IOCTL_VIDIOC_RESERVED = VIDIOC_RESERVED;
-unsigned IOCTL_VIDIOC_ENUM_FMT = VIDIOC_ENUM_FMT;
-unsigned IOCTL_VIDIOC_G_FMT = VIDIOC_G_FMT;
-unsigned IOCTL_VIDIOC_S_FMT = VIDIOC_S_FMT;
-unsigned IOCTL_VIDIOC_REQBUFS = VIDIOC_REQBUFS;
-unsigned IOCTL_VIDIOC_QUERYBUF = VIDIOC_QUERYBUF;
-unsigned IOCTL_VIDIOC_G_FBUF = VIDIOC_G_FBUF;
-unsigned IOCTL_VIDIOC_S_FBUF = VIDIOC_S_FBUF;
-unsigned IOCTL_VIDIOC_OVERLAY = VIDIOC_OVERLAY;
-unsigned IOCTL_VIDIOC_QBUF = VIDIOC_QBUF;
-unsigned IOCTL_VIDIOC_DQBUF = VIDIOC_DQBUF;
-unsigned IOCTL_VIDIOC_STREAMON = VIDIOC_STREAMON;
-unsigned IOCTL_VIDIOC_STREAMOFF = VIDIOC_STREAMOFF;
-unsigned IOCTL_VIDIOC_G_PARM = VIDIOC_G_PARM;
-unsigned IOCTL_VIDIOC_S_PARM = VIDIOC_S_PARM;
-unsigned IOCTL_VIDIOC_G_STD = VIDIOC_G_STD;
-unsigned IOCTL_VIDIOC_S_STD = VIDIOC_S_STD;
-unsigned IOCTL_VIDIOC_ENUMSTD = VIDIOC_ENUMSTD;
-unsigned IOCTL_VIDIOC_ENUMINPUT = VIDIOC_ENUMINPUT;
-unsigned IOCTL_VIDIOC_G_CTRL = VIDIOC_G_CTRL;
-unsigned IOCTL_VIDIOC_S_CTRL = VIDIOC_S_CTRL;
-unsigned IOCTL_VIDIOC_G_TUNER = VIDIOC_G_TUNER;
-unsigned IOCTL_VIDIOC_S_TUNER = VIDIOC_S_TUNER;
-unsigned IOCTL_VIDIOC_G_AUDIO = VIDIOC_G_AUDIO;
-unsigned IOCTL_VIDIOC_S_AUDIO = VIDIOC_S_AUDIO;
-unsigned IOCTL_VIDIOC_QUERYCTRL = VIDIOC_QUERYCTRL;
-unsigned IOCTL_VIDIOC_QUERYMENU = VIDIOC_QUERYMENU;
-unsigned IOCTL_VIDIOC_G_INPUT = VIDIOC_G_INPUT;
-unsigned IOCTL_VIDIOC_S_INPUT = VIDIOC_S_INPUT;
-unsigned IOCTL_VIDIOC_G_OUTPUT = VIDIOC_G_OUTPUT;
-unsigned IOCTL_VIDIOC_S_OUTPUT = VIDIOC_S_OUTPUT;
-unsigned IOCTL_VIDIOC_ENUMOUTPUT = VIDIOC_ENUMOUTPUT;
-unsigned IOCTL_VIDIOC_G_AUDOUT = VIDIOC_G_AUDOUT;
-unsigned IOCTL_VIDIOC_S_AUDOUT = VIDIOC_S_AUDOUT;
-unsigned IOCTL_VIDIOC_G_MODULATOR = VIDIOC_G_MODULATOR;
-unsigned IOCTL_VIDIOC_S_MODULATOR = VIDIOC_S_MODULATOR;
-unsigned IOCTL_VIDIOC_G_FREQUENCY = VIDIOC_G_FREQUENCY;
-unsigned IOCTL_VIDIOC_S_FREQUENCY = VIDIOC_S_FREQUENCY;
-unsigned IOCTL_VIDIOC_CROPCAP = VIDIOC_CROPCAP;
-unsigned IOCTL_VIDIOC_G_CROP = VIDIOC_G_CROP;
-unsigned IOCTL_VIDIOC_S_CROP = VIDIOC_S_CROP;
-unsigned IOCTL_VIDIOC_G_JPEGCOMP = VIDIOC_G_JPEGCOMP;
-unsigned IOCTL_VIDIOC_S_JPEGCOMP = VIDIOC_S_JPEGCOMP;
-unsigned IOCTL_VIDIOC_QUERYSTD = VIDIOC_QUERYSTD;
-unsigned IOCTL_VIDIOC_TRY_FMT = VIDIOC_TRY_FMT;
-unsigned IOCTL_VIDIOC_ENUMAUDIO = VIDIOC_ENUMAUDIO;
-unsigned IOCTL_VIDIOC_ENUMAUDOUT = VIDIOC_ENUMAUDOUT;
-unsigned IOCTL_VIDIOC_G_PRIORITY = VIDIOC_G_PRIORITY;
-unsigned IOCTL_VIDIOC_S_PRIORITY = VIDIOC_S_PRIORITY;
-unsigned IOCTL_VIDIOC_ENUM_FRAMESIZES = VIDIOC_ENUM_FRAMESIZES;
-unsigned IOCTL_VIDIOC_ENUM_FRAMEINTERVALS = VIDIOC_ENUM_FRAMEINTERVALS;
-unsigned IOCTL_WDOGIOC_GMODE = WDOGIOC_GMODE;
-unsigned IOCTL_WDOGIOC_SMODE = WDOGIOC_SMODE;
-unsigned IOCTL_WDOGIOC_WHICH = WDOGIOC_WHICH;
-unsigned IOCTL_WDOGIOC_TICKLE = WDOGIOC_TICKLE;
-unsigned IOCTL_WDOGIOC_GTICKLER = WDOGIOC_GTICKLER;
-unsigned IOCTL_WDOGIOC_GWDOGS = WDOGIOC_GWDOGS;
-unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
-unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
-unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
-unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
-unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
-unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
-unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
-unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS;
-unsigned IOCTL_SNDCTL_DSP_CHANNELS = SNDCTL_DSP_CHANNELS;
-unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS;
-unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
-unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER;
-unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
-unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
-unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
-unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
-unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
-unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
-unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
-unsigned IOCTL_SNDCTL_DSP_GETCAPS = SNDCTL_DSP_GETCAPS;
-unsigned IOCTL_SNDCTL_DSP_GETTRIGGER = SNDCTL_DSP_GETTRIGGER;
-unsigned IOCTL_SNDCTL_DSP_SETTRIGGER = SNDCTL_DSP_SETTRIGGER;
-unsigned IOCTL_SNDCTL_DSP_GETIPTR = SNDCTL_DSP_GETIPTR;
-unsigned IOCTL_SNDCTL_DSP_GETOPTR = SNDCTL_DSP_GETOPTR;
-unsigned IOCTL_SNDCTL_DSP_MAPINBUF = SNDCTL_DSP_MAPINBUF;
-unsigned IOCTL_SNDCTL_DSP_MAPOUTBUF = SNDCTL_DSP_MAPOUTBUF;
-unsigned IOCTL_SNDCTL_DSP_SETSYNCRO = SNDCTL_DSP_SETSYNCRO;
-unsigned IOCTL_SNDCTL_DSP_SETDUPLEX = SNDCTL_DSP_SETDUPLEX;
-unsigned IOCTL_SNDCTL_DSP_PROFILE = SNDCTL_DSP_PROFILE;
-unsigned IOCTL_SNDCTL_DSP_GETODELAY = SNDCTL_DSP_GETODELAY;
-unsigned IOCTL_SOUND_MIXER_INFO = SOUND_MIXER_INFO;
-unsigned IOCTL_SOUND_OLD_MIXER_INFO = SOUND_OLD_MIXER_INFO;
-unsigned IOCTL_OSS_GETVERSION = OSS_GETVERSION;
-unsigned IOCTL_SNDCTL_SYSINFO = SNDCTL_SYSINFO;
-unsigned IOCTL_SNDCTL_AUDIOINFO = SNDCTL_AUDIOINFO;
-unsigned IOCTL_SNDCTL_ENGINEINFO = SNDCTL_ENGINEINFO;
-unsigned IOCTL_SNDCTL_DSP_GETPLAYVOL = SNDCTL_DSP_GETPLAYVOL;
-unsigned IOCTL_SNDCTL_DSP_SETPLAYVOL = SNDCTL_DSP_SETPLAYVOL;
-unsigned IOCTL_SNDCTL_DSP_GETRECVOL = SNDCTL_DSP_GETRECVOL;
-unsigned IOCTL_SNDCTL_DSP_SETRECVOL = SNDCTL_DSP_SETRECVOL;
-unsigned IOCTL_SNDCTL_DSP_SKIP = SNDCTL_DSP_SKIP;
-unsigned IOCTL_SNDCTL_DSP_SILENCE = SNDCTL_DSP_SILENCE;
-
-const int si_SEGV_MAPERR = SEGV_MAPERR;
-const int si_SEGV_ACCERR = SEGV_ACCERR;
-} // namespace __sanitizer
-
-using namespace __sanitizer;
-
-COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
-
-COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
-CHECK_TYPE_SIZE(pthread_key_t);
-
-// There are more undocumented fields in dl_phdr_info that we are not interested
-// in.
-COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
-
-CHECK_TYPE_SIZE(glob_t);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
-
-CHECK_TYPE_SIZE(addrinfo);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
-
-CHECK_TYPE_SIZE(hostent);
-CHECK_SIZE_AND_OFFSET(hostent, h_name);
-CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
-CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
-CHECK_SIZE_AND_OFFSET(hostent, h_length);
-CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
-
-CHECK_TYPE_SIZE(iovec);
-CHECK_SIZE_AND_OFFSET(iovec, iov_base);
-CHECK_SIZE_AND_OFFSET(iovec, iov_len);
-
-CHECK_TYPE_SIZE(msghdr);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
-
-CHECK_TYPE_SIZE(cmsghdr);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
-
-COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
-CHECK_SIZE_AND_OFFSET(dirent, d_fileno);
-CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
-
-CHECK_TYPE_SIZE(ifconf);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
-
-CHECK_TYPE_SIZE(pollfd);
-CHECK_SIZE_AND_OFFSET(pollfd, fd);
-CHECK_SIZE_AND_OFFSET(pollfd, events);
-CHECK_SIZE_AND_OFFSET(pollfd, revents);
-
-CHECK_TYPE_SIZE(nfds_t);
-
-CHECK_TYPE_SIZE(sigset_t);
-
-COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
-// Can't write checks for sa_handler and sa_sigaction due to them being
-// preprocessor macros.
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
-
-CHECK_TYPE_SIZE(wordexp_t);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
-
-CHECK_TYPE_SIZE(tm);
-CHECK_SIZE_AND_OFFSET(tm, tm_sec);
-CHECK_SIZE_AND_OFFSET(tm, tm_min);
-CHECK_SIZE_AND_OFFSET(tm, tm_hour);
-CHECK_SIZE_AND_OFFSET(tm, tm_mday);
-CHECK_SIZE_AND_OFFSET(tm, tm_mon);
-CHECK_SIZE_AND_OFFSET(tm, tm_year);
-CHECK_SIZE_AND_OFFSET(tm, tm_wday);
-CHECK_SIZE_AND_OFFSET(tm, tm_yday);
-CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
-CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
-CHECK_SIZE_AND_OFFSET(tm, tm_zone);
-
-CHECK_TYPE_SIZE(ether_addr);
-
-CHECK_TYPE_SIZE(ipc_perm);
-CHECK_SIZE_AND_OFFSET(ipc_perm, _key);
-CHECK_SIZE_AND_OFFSET(ipc_perm, _seq);
-CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
-
-CHECK_TYPE_SIZE(shmid_ds);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
-
-CHECK_TYPE_SIZE(clock_t);
-
-CHECK_TYPE_SIZE(ifaddrs);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
-// Compare against the union, because we can't reach into the union in a
-// compliant way.
-#ifdef ifa_dstaddr
-#undef ifa_dstaddr
-#endif
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
-
-CHECK_TYPE_SIZE(timeb);
-CHECK_SIZE_AND_OFFSET(timeb, time);
-CHECK_SIZE_AND_OFFSET(timeb, millitm);
-CHECK_SIZE_AND_OFFSET(timeb, timezone);
-CHECK_SIZE_AND_OFFSET(timeb, dstflag);
-
-CHECK_TYPE_SIZE(passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_name);
-CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
-CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
-
-CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
-
-CHECK_TYPE_SIZE(group);
-CHECK_SIZE_AND_OFFSET(group, gr_name);
-CHECK_SIZE_AND_OFFSET(group, gr_passwd);
-CHECK_SIZE_AND_OFFSET(group, gr_gid);
-CHECK_SIZE_AND_OFFSET(group, gr_mem);
-
-#endif // SANITIZER_NETBSD
--- /dev/null
+//===-- sanitizer_platform_limits_netbsd.cpp ------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific NetBSD data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_NETBSD
+
+#define _KMEMUSER
+#define RAY_DO_SIGLEV
+
+// clang-format off
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/disk.h>
+#include <sys/disklabel.h>
+#include <sys/mount.h>
+#include <sys/agpio.h>
+#include <sys/ataio.h>
+#include <sys/audioio.h>
+#include <sys/cdbr.h>
+#include <sys/cdio.h>
+#include <sys/chio.h>
+#include <sys/clockctl.h>
+#include <sys/cpuio.h>
+#include <sys/dkio.h>
+#include <sys/drvctlio.h>
+#include <sys/dvdio.h>
+#include <sys/envsys.h>
+#include <sys/event.h>
+#include <sys/fdio.h>
+#include <sys/filio.h>
+#include <sys/gpio.h>
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h>
+#include <sys/joystick.h>
+#include <sys/ksyms.h>
+#include <sys/lua.h>
+#include <sys/midiio.h>
+#include <sys/mtio.h>
+#include <sys/power.h>
+#include <sys/radioio.h>
+#include <sys/rndio.h>
+#include <sys/scanio.h>
+#include <sys/scsiio.h>
+#include <sys/sockio.h>
+#include <sys/timepps.h>
+#include <sys/ttycom.h>
+#include <sys/verified_exec.h>
+#include <sys/videoio.h>
+#include <sys/wdog.h>
+#include <sys/event.h>
+#include <sys/filio.h>
+#include <sys/ipc.h>
+#include <sys/ipmi.h>
+#include <sys/kcov.h>
+#include <sys/mman.h>
+#include <sys/module.h>
+#include <sys/mount.h>
+#include <sys/mqueue.h>
+#include <sys/msg.h>
+#include <sys/mtio.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/sem.h>
+#include <sys/sha1.h>
+#include <sys/sha2.h>
+#include <sys/shm.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/soundcard.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/times.h>
+#include <sys/timespec.h>
+#include <sys/timex.h>
+#include <sys/types.h>
+#include <sys/ucontext.h>
+#include <sys/utsname.h>
+#include <altq/altq.h>
+#include <altq/altq_afmap.h>
+#include <altq/altq_blue.h>
+#include <altq/altq_cbq.h>
+#include <altq/altq_cdnr.h>
+#include <altq/altq_fifoq.h>
+#include <altq/altq_hfsc.h>
+#include <altq/altq_jobs.h>
+#include <altq/altq_priq.h>
+#include <altq/altq_red.h>
+#include <altq/altq_rio.h>
+#include <altq/altq_wfq.h>
+#include <arpa/inet.h>
+#include <crypto/cryptodev.h>
+#include <dev/apm/apmio.h>
+#include <dev/dm/netbsd-dm.h>
+#include <dev/dmover/dmover_io.h>
+#include <dev/dtv/dtvio_demux.h>
+#include <dev/dtv/dtvio_frontend.h>
+#include <dev/filemon/filemon.h>
+#include <dev/hdaudio/hdaudioio.h>
+#include <dev/hdmicec/hdmicecio.h>
+#include <dev/hpc/hpcfbio.h>
+#include <dev/i2o/iopio.h>
+#include <dev/ic/athioctl.h>
+#include <dev/ic/bt8xx.h>
+#include <dev/ic/icp_ioctl.h>
+#include <dev/ic/isp_ioctl.h>
+#include <dev/ic/mlxio.h>
+#include <dev/ic/qemufwcfgio.h>
+#include <dev/ic/nvmeio.h>
+#include <dev/ir/irdaio.h>
+#include <dev/isa/isvio.h>
+#include <dev/isa/wtreg.h>
+#include <dev/iscsi/iscsi_ioctl.h>
+#include <dev/ofw/openfirmio.h>
+#include <dev/pci/amrio.h>
+#include <dev/pci/mlyreg.h>
+#include <dev/pci/mlyio.h>
+#include <dev/pci/pciio.h>
+#include <dev/pci/tweio.h>
+#include <dev/pcmcia/if_cnwioctl.h>
+#include <net/bpf.h>
+#include <net/if_gre.h>
+#include <net/ppp_defs.h>
+#include <net/if_ppp.h>
+#include <net/if_pppoe.h>
+#include <net/if_sppp.h>
+#include <net/if_srt.h>
+#include <net/if_tap.h>
+#include <net/if_tun.h>
+#include <net/npf.h>
+#include <net/pfvar.h>
+#include <net/slip.h>
+#include <netbt/hci.h>
+#include <netinet/ip_compat.h>
+#include <netinet/ip_fil.h>
+#include <netinet/ip_nat.h>
+#include <netinet/ip_proxy.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+#include <netsmb/smb_dev.h>
+#include <dev/biovar.h>
+#include <dev/bluetooth/btdev.h>
+#include <dev/bluetooth/btsco.h>
+#include <dev/ccdvar.h>
+#include <dev/cgdvar.h>
+#include <dev/fssvar.h>
+#include <dev/kttcpio.h>
+#include <dev/lockstat.h>
+#include <dev/md.h>
+#include <net/if_ether.h>
+#include <dev/pcmcia/if_rayreg.h>
+#include <stdio.h>
+#include <dev/raidframe/raidframeio.h>
+#include <dev/sbus/mbppio.h>
+#include <dev/scsipi/ses.h>
+#include <dev/spi/spi_io.h>
+#include <dev/spkrio.h>
+#include <dev/sun/disklabel.h>
+#include <dev/sun/fbio.h>
+#include <dev/sun/kbio.h>
+#include <dev/sun/vuid_event.h>
+#include <dev/tc/sticio.h>
+#include <dev/usb/ukyopon.h>
+#include <dev/usb/urio.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/utoppy.h>
+#include <dev/vme/xio.h>
+#include <dev/vndvar.h>
+#include <dev/wscons/wsconsio.h>
+#include <dev/wscons/wsdisplay_usl_io.h>
+#include <fs/autofs/autofs_ioctl.h>
+#include <dirent.h>
+#include <glob.h>
+#include <grp.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <link_elf.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <netinet/sctp_uio.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stddef.h>
+#include <md2.h>
+#include <md4.h>
+#include <md5.h>
+#include <rmd160.h>
+#include <soundcard.h>
+#include <term.h>
+#include <termios.h>
+#include <time.h>
+#include <ttyent.h>
+#include <utime.h>
+#include <utmp.h>
+#include <utmpx.h>
+#include <vis.h>
+#include <wchar.h>
+#include <wordexp.h>
+#include <ttyent.h>
+#include <fts.h>
+#include <regex.h>
+#include <fstab.h>
+#include <stringlist.h>
+
+#if defined(__x86_64__)
+#include <nvmm.h>
+#endif
+// clang-format on
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_netbsd.h"
+
+namespace __sanitizer {
+unsigned struct_utsname_sz = sizeof(struct utsname);
+unsigned struct_stat_sz = sizeof(struct stat);
+unsigned struct_rusage_sz = sizeof(struct rusage);
+unsigned struct_tm_sz = sizeof(struct tm);
+unsigned struct_passwd_sz = sizeof(struct passwd);
+unsigned struct_group_sz = sizeof(struct group);
+unsigned siginfo_t_sz = sizeof(siginfo_t);
+unsigned struct_sigaction_sz = sizeof(struct sigaction);
+unsigned struct_itimerval_sz = sizeof(struct itimerval);
+unsigned pthread_t_sz = sizeof(pthread_t);
+unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
+unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+unsigned pid_t_sz = sizeof(pid_t);
+unsigned timeval_sz = sizeof(timeval);
+unsigned uid_t_sz = sizeof(uid_t);
+unsigned gid_t_sz = sizeof(gid_t);
+unsigned mbstate_t_sz = sizeof(mbstate_t);
+unsigned sigset_t_sz = sizeof(sigset_t);
+unsigned struct_timezone_sz = sizeof(struct timezone);
+unsigned struct_tms_sz = sizeof(struct tms);
+unsigned struct_sigevent_sz = sizeof(struct sigevent);
+unsigned struct_sched_param_sz = sizeof(struct sched_param);
+unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+unsigned ucontext_t_sz = sizeof(ucontext_t);
+unsigned struct_rlimit_sz = sizeof(struct rlimit);
+unsigned struct_timespec_sz = sizeof(struct timespec);
+unsigned struct_sembuf_sz = sizeof(struct sembuf);
+unsigned struct_kevent_sz = sizeof(struct kevent);
+unsigned struct_FTS_sz = sizeof(FTS);
+unsigned struct_FTSENT_sz = sizeof(FTSENT);
+unsigned struct_regex_sz = sizeof(regex_t);
+unsigned struct_regmatch_sz = sizeof(regmatch_t);
+unsigned struct_fstab_sz = sizeof(struct fstab);
+unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+unsigned struct_timex_sz = sizeof(struct timex);
+unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
+unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
+unsigned struct_statvfs_sz = sizeof(struct statvfs);
+unsigned struct_sigaltstack_sz = sizeof(stack_t);
+
+const uptr sig_ign = (uptr)SIG_IGN;
+const uptr sig_dfl = (uptr)SIG_DFL;
+const uptr sig_err = (uptr)SIG_ERR;
+const uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+const unsigned long __sanitizer_bufsiz = BUFSIZ;
+
+int ptrace_pt_io = PT_IO;
+int ptrace_pt_lwpinfo = PT_LWPINFO;
+int ptrace_pt_set_event_mask = PT_SET_EVENT_MASK;
+int ptrace_pt_get_event_mask = PT_GET_EVENT_MASK;
+int ptrace_pt_get_process_state = PT_GET_PROCESS_STATE;
+int ptrace_pt_set_siginfo = PT_SET_SIGINFO;
+int ptrace_pt_get_siginfo = PT_GET_SIGINFO;
+int ptrace_piod_read_d = PIOD_READ_D;
+int ptrace_piod_write_d = PIOD_WRITE_D;
+int ptrace_piod_read_i = PIOD_READ_I;
+int ptrace_piod_write_i = PIOD_WRITE_I;
+int ptrace_piod_read_auxv = PIOD_READ_AUXV;
+
+#if defined(PT_SETREGS) && defined(PT_GETREGS)
+int ptrace_pt_setregs = PT_SETREGS;
+int ptrace_pt_getregs = PT_GETREGS;
+#else
+int ptrace_pt_setregs = -1;
+int ptrace_pt_getregs = -1;
+#endif
+
+#if defined(PT_SETFPREGS) && defined(PT_GETFPREGS)
+int ptrace_pt_setfpregs = PT_SETFPREGS;
+int ptrace_pt_getfpregs = PT_GETFPREGS;
+#else
+int ptrace_pt_setfpregs = -1;
+int ptrace_pt_getfpregs = -1;
+#endif
+
+#if defined(PT_SETDBREGS) && defined(PT_GETDBREGS)
+int ptrace_pt_setdbregs = PT_SETDBREGS;
+int ptrace_pt_getdbregs = PT_GETDBREGS;
+#else
+int ptrace_pt_setdbregs = -1;
+int ptrace_pt_getdbregs = -1;
+#endif
+
+unsigned struct_ptrace_ptrace_io_desc_struct_sz = sizeof(struct ptrace_io_desc);
+unsigned struct_ptrace_ptrace_lwpinfo_struct_sz = sizeof(struct ptrace_lwpinfo);
+unsigned struct_ptrace_ptrace_event_struct_sz = sizeof(ptrace_event_t);
+unsigned struct_ptrace_ptrace_siginfo_struct_sz = sizeof(ptrace_siginfo_t);
+
+#if defined(PT_SETREGS)
+unsigned struct_ptrace_reg_struct_sz = sizeof(struct reg);
+#else
+unsigned struct_ptrace_reg_struct_sz = -1;
+#endif
+
+#if defined(PT_SETFPREGS)
+unsigned struct_ptrace_fpreg_struct_sz = sizeof(struct fpreg);
+#else
+unsigned struct_ptrace_fpreg_struct_sz = -1;
+#endif
+
+#if defined(PT_SETDBREGS)
+unsigned struct_ptrace_dbreg_struct_sz = sizeof(struct dbreg);
+#else
+unsigned struct_ptrace_dbreg_struct_sz = -1;
+#endif
+
+int shmctl_ipc_stat = (int)IPC_STAT;
+
+unsigned struct_utmp_sz = sizeof(struct utmp);
+unsigned struct_utmpx_sz = sizeof(struct utmpx);
+
+int map_fixed = MAP_FIXED;
+
+int af_inet = (int)AF_INET;
+int af_inet6 = (int)AF_INET6;
+
+uptr __sanitizer_in_addr_sz(int af) {
+ if (af == AF_INET)
+ return sizeof(struct in_addr);
+ else if (af == AF_INET6)
+ return sizeof(struct in6_addr);
+ else
+ return 0;
+}
+
+unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+
+int glob_nomatch = GLOB_NOMATCH;
+int glob_altdirfunc = GLOB_ALTDIRFUNC;
+
+unsigned path_max = PATH_MAX;
+
+int struct_ttyent_sz = sizeof(struct ttyent);
+
+struct __sanitizer_nvlist_ref_t {
+ void *buf;
+ uptr len;
+ int flags;
+};
+
+typedef __sanitizer_nvlist_ref_t nvlist_ref_t;
+
+// ioctl arguments
+unsigned struct_altqreq_sz = sizeof(altqreq);
+unsigned struct_amr_user_ioctl_sz = sizeof(amr_user_ioctl);
+unsigned struct_ap_control_sz = sizeof(ap_control);
+unsigned struct_apm_ctl_sz = sizeof(apm_ctl);
+unsigned struct_apm_event_info_sz = sizeof(apm_event_info);
+unsigned struct_apm_power_info_sz = sizeof(apm_power_info);
+unsigned struct_atabusiodetach_args_sz = sizeof(atabusiodetach_args);
+unsigned struct_atabusioscan_args_sz = sizeof(atabusioscan_args);
+unsigned struct_ath_diag_sz = sizeof(ath_diag);
+unsigned struct_atm_flowmap_sz = sizeof(atm_flowmap);
+unsigned struct_audio_buf_info_sz = sizeof(audio_buf_info);
+unsigned struct_audio_device_sz = sizeof(audio_device);
+unsigned struct_audio_encoding_sz = sizeof(audio_encoding);
+unsigned struct_audio_info_sz = sizeof(audio_info);
+unsigned struct_audio_offset_sz = sizeof(audio_offset);
+unsigned struct_bio_locate_sz = sizeof(bio_locate);
+unsigned struct_bioc_alarm_sz = sizeof(bioc_alarm);
+unsigned struct_bioc_blink_sz = sizeof(bioc_blink);
+unsigned struct_bioc_disk_sz = sizeof(bioc_disk);
+unsigned struct_bioc_inq_sz = sizeof(bioc_inq);
+unsigned struct_bioc_setstate_sz = sizeof(bioc_setstate);
+unsigned struct_bioc_vol_sz = sizeof(bioc_vol);
+unsigned struct_bioc_volops_sz = sizeof(bioc_volops);
+unsigned struct_bktr_chnlset_sz = sizeof(bktr_chnlset);
+unsigned struct_bktr_remote_sz = sizeof(bktr_remote);
+unsigned struct_blue_conf_sz = sizeof(blue_conf);
+unsigned struct_blue_interface_sz = sizeof(blue_interface);
+unsigned struct_blue_stats_sz = sizeof(blue_stats);
+unsigned struct_bpf_dltlist_sz = sizeof(bpf_dltlist);
+unsigned struct_bpf_program_sz = sizeof(bpf_program);
+unsigned struct_bpf_stat_old_sz = sizeof(bpf_stat_old);
+unsigned struct_bpf_stat_sz = sizeof(bpf_stat);
+unsigned struct_bpf_version_sz = sizeof(bpf_version);
+unsigned struct_btreq_sz = sizeof(btreq);
+unsigned struct_btsco_info_sz = sizeof(btsco_info);
+unsigned struct_buffmem_desc_sz = sizeof(buffmem_desc);
+unsigned struct_cbq_add_class_sz = sizeof(cbq_add_class);
+unsigned struct_cbq_add_filter_sz = sizeof(cbq_add_filter);
+unsigned struct_cbq_delete_class_sz = sizeof(cbq_delete_class);
+unsigned struct_cbq_delete_filter_sz = sizeof(cbq_delete_filter);
+unsigned struct_cbq_getstats_sz = sizeof(cbq_getstats);
+unsigned struct_cbq_interface_sz = sizeof(cbq_interface);
+unsigned struct_cbq_modify_class_sz = sizeof(cbq_modify_class);
+unsigned struct_ccd_ioctl_sz = sizeof(ccd_ioctl);
+unsigned struct_cdnr_add_element_sz = sizeof(cdnr_add_element);
+unsigned struct_cdnr_add_filter_sz = sizeof(cdnr_add_filter);
+unsigned struct_cdnr_add_tbmeter_sz = sizeof(cdnr_add_tbmeter);
+unsigned struct_cdnr_add_trtcm_sz = sizeof(cdnr_add_trtcm);
+unsigned struct_cdnr_add_tswtcm_sz = sizeof(cdnr_add_tswtcm);
+unsigned struct_cdnr_delete_element_sz = sizeof(cdnr_delete_element);
+unsigned struct_cdnr_delete_filter_sz = sizeof(cdnr_delete_filter);
+unsigned struct_cdnr_get_stats_sz = sizeof(cdnr_get_stats);
+unsigned struct_cdnr_interface_sz = sizeof(cdnr_interface);
+unsigned struct_cdnr_modify_tbmeter_sz = sizeof(cdnr_modify_tbmeter);
+unsigned struct_cdnr_modify_trtcm_sz = sizeof(cdnr_modify_trtcm);
+unsigned struct_cdnr_modify_tswtcm_sz = sizeof(cdnr_modify_tswtcm);
+unsigned struct_cdnr_tbmeter_stats_sz = sizeof(cdnr_tbmeter_stats);
+unsigned struct_cdnr_tcm_stats_sz = sizeof(cdnr_tcm_stats);
+unsigned struct_cgd_ioctl_sz = sizeof(cgd_ioctl);
+unsigned struct_cgd_user_sz = sizeof(cgd_user);
+unsigned struct_changer_element_status_request_sz =
+ sizeof(changer_element_status_request);
+unsigned struct_changer_exchange_request_sz = sizeof(changer_exchange_request);
+unsigned struct_changer_move_request_sz = sizeof(changer_move_request);
+unsigned struct_changer_params_sz = sizeof(changer_params);
+unsigned struct_changer_position_request_sz = sizeof(changer_position_request);
+unsigned struct_changer_set_voltag_request_sz =
+ sizeof(changer_set_voltag_request);
+unsigned struct_clockctl_adjtime_sz = sizeof(clockctl_adjtime);
+unsigned struct_clockctl_clock_settime_sz = sizeof(clockctl_clock_settime);
+unsigned struct_clockctl_ntp_adjtime_sz = sizeof(clockctl_ntp_adjtime);
+unsigned struct_clockctl_settimeofday_sz = sizeof(clockctl_settimeofday);
+unsigned struct_cnwistats_sz = sizeof(cnwistats);
+unsigned struct_cnwitrail_sz = sizeof(cnwitrail);
+unsigned struct_cnwstatus_sz = sizeof(cnwstatus);
+unsigned struct_count_info_sz = sizeof(count_info);
+unsigned struct_cpu_ucode_sz = sizeof(cpu_ucode);
+unsigned struct_cpu_ucode_version_sz = sizeof(cpu_ucode_version);
+unsigned struct_crypt_kop_sz = sizeof(crypt_kop);
+unsigned struct_crypt_mkop_sz = sizeof(crypt_mkop);
+unsigned struct_crypt_mop_sz = sizeof(crypt_mop);
+unsigned struct_crypt_op_sz = sizeof(crypt_op);
+unsigned struct_crypt_result_sz = sizeof(crypt_result);
+unsigned struct_crypt_sfop_sz = sizeof(crypt_sfop);
+unsigned struct_crypt_sgop_sz = sizeof(crypt_sgop);
+unsigned struct_cryptret_sz = sizeof(cryptret);
+unsigned struct_devdetachargs_sz = sizeof(devdetachargs);
+unsigned struct_devlistargs_sz = sizeof(devlistargs);
+unsigned struct_devpmargs_sz = sizeof(devpmargs);
+unsigned struct_devrescanargs_sz = sizeof(devrescanargs);
+unsigned struct_disk_badsecinfo_sz = sizeof(disk_badsecinfo);
+unsigned struct_disk_strategy_sz = sizeof(disk_strategy);
+unsigned struct_disklabel_sz = sizeof(disklabel);
+unsigned struct_dkbad_sz = sizeof(dkbad);
+unsigned struct_dkwedge_info_sz = sizeof(dkwedge_info);
+unsigned struct_dkwedge_list_sz = sizeof(dkwedge_list);
+unsigned struct_dmio_setfunc_sz = sizeof(dmio_setfunc);
+unsigned struct_dmx_pes_filter_params_sz = sizeof(dmx_pes_filter_params);
+unsigned struct_dmx_sct_filter_params_sz = sizeof(dmx_sct_filter_params);
+unsigned struct_dmx_stc_sz = sizeof(dmx_stc);
+unsigned struct_dvb_diseqc_master_cmd_sz = sizeof(dvb_diseqc_master_cmd);
+unsigned struct_dvb_diseqc_slave_reply_sz = sizeof(dvb_diseqc_slave_reply);
+unsigned struct_dvb_frontend_event_sz = sizeof(dvb_frontend_event);
+unsigned struct_dvb_frontend_info_sz = sizeof(dvb_frontend_info);
+unsigned struct_dvb_frontend_parameters_sz = sizeof(dvb_frontend_parameters);
+unsigned struct_eccapreq_sz = sizeof(eccapreq);
+unsigned struct_fbcmap_sz = sizeof(fbcmap);
+unsigned struct_fbcurpos_sz = sizeof(fbcurpos);
+unsigned struct_fbcursor_sz = sizeof(fbcursor);
+unsigned struct_fbgattr_sz = sizeof(fbgattr);
+unsigned struct_fbsattr_sz = sizeof(fbsattr);
+unsigned struct_fbtype_sz = sizeof(fbtype);
+unsigned struct_fdformat_cmd_sz = sizeof(fdformat_cmd);
+unsigned struct_fdformat_parms_sz = sizeof(fdformat_parms);
+unsigned struct_fifoq_conf_sz = sizeof(fifoq_conf);
+unsigned struct_fifoq_getstats_sz = sizeof(fifoq_getstats);
+unsigned struct_fifoq_interface_sz = sizeof(fifoq_interface);
+unsigned struct_format_op_sz = sizeof(format_op);
+unsigned struct_fss_get_sz = sizeof(fss_get);
+unsigned struct_fss_set_sz = sizeof(fss_set);
+unsigned struct_gpio_attach_sz = sizeof(gpio_attach);
+unsigned struct_gpio_info_sz = sizeof(gpio_info);
+unsigned struct_gpio_req_sz = sizeof(gpio_req);
+unsigned struct_gpio_set_sz = sizeof(gpio_set);
+unsigned struct_hfsc_add_class_sz = sizeof(hfsc_add_class);
+unsigned struct_hfsc_add_filter_sz = sizeof(hfsc_add_filter);
+unsigned struct_hfsc_attach_sz = sizeof(hfsc_attach);
+unsigned struct_hfsc_class_stats_sz = sizeof(hfsc_class_stats);
+unsigned struct_hfsc_delete_class_sz = sizeof(hfsc_delete_class);
+unsigned struct_hfsc_delete_filter_sz = sizeof(hfsc_delete_filter);
+unsigned struct_hfsc_interface_sz = sizeof(hfsc_interface);
+unsigned struct_hfsc_modify_class_sz = sizeof(hfsc_modify_class);
+unsigned struct_hpcfb_dsp_op_sz = sizeof(hpcfb_dsp_op);
+unsigned struct_hpcfb_dspconf_sz = sizeof(hpcfb_dspconf);
+unsigned struct_hpcfb_fbconf_sz = sizeof(hpcfb_fbconf);
+unsigned struct_if_addrprefreq_sz = sizeof(if_addrprefreq);
+unsigned struct_if_clonereq_sz = sizeof(if_clonereq);
+unsigned struct_if_laddrreq_sz = sizeof(if_laddrreq);
+unsigned struct_ifaddr_sz = sizeof(ifaddr);
+unsigned struct_ifaliasreq_sz = sizeof(ifaliasreq);
+unsigned struct_ifcapreq_sz = sizeof(ifcapreq);
+unsigned struct_ifconf_sz = sizeof(ifconf);
+unsigned struct_ifdatareq_sz = sizeof(ifdatareq);
+unsigned struct_ifdrv_sz = sizeof(ifdrv);
+unsigned struct_ifmediareq_sz = sizeof(ifmediareq);
+unsigned struct_ifpppcstatsreq_sz = sizeof(ifpppcstatsreq);
+unsigned struct_ifpppstatsreq_sz = sizeof(ifpppstatsreq);
+unsigned struct_ifreq_sz = sizeof(ifreq);
+unsigned struct_in6_addrpolicy_sz = sizeof(in6_addrpolicy);
+unsigned struct_in6_ndireq_sz = sizeof(in6_ndireq);
+unsigned struct_ioc_load_unload_sz = sizeof(ioc_load_unload);
+unsigned struct_ioc_patch_sz = sizeof(ioc_patch);
+unsigned struct_ioc_play_blocks_sz = sizeof(ioc_play_blocks);
+unsigned struct_ioc_play_msf_sz = sizeof(ioc_play_msf);
+unsigned struct_ioc_play_track_sz = sizeof(ioc_play_track);
+unsigned struct_ioc_read_subchannel_sz = sizeof(ioc_read_subchannel);
+unsigned struct_ioc_read_toc_entry_sz = sizeof(ioc_read_toc_entry);
+unsigned struct_ioc_toc_header_sz = sizeof(ioc_toc_header);
+unsigned struct_ioc_vol_sz = sizeof(ioc_vol);
+unsigned struct_ioctl_pt_sz = sizeof(ioctl_pt);
+unsigned struct_ioppt_sz = sizeof(ioppt);
+unsigned struct_iovec_sz = sizeof(iovec);
+unsigned struct_ipfobj_sz = sizeof(ipfobj);
+unsigned struct_irda_params_sz = sizeof(irda_params);
+unsigned struct_isp_fc_device_sz = sizeof(isp_fc_device);
+unsigned struct_isp_fc_tsk_mgmt_sz = sizeof(isp_fc_tsk_mgmt);
+unsigned struct_isp_hba_device_sz = sizeof(isp_hba_device);
+unsigned struct_isv_cmd_sz = sizeof(isv_cmd);
+unsigned struct_jobs_add_class_sz = sizeof(jobs_add_class);
+unsigned struct_jobs_add_filter_sz = sizeof(jobs_add_filter);
+unsigned struct_jobs_attach_sz = sizeof(jobs_attach);
+unsigned struct_jobs_class_stats_sz = sizeof(jobs_class_stats);
+unsigned struct_jobs_delete_class_sz = sizeof(jobs_delete_class);
+unsigned struct_jobs_delete_filter_sz = sizeof(jobs_delete_filter);
+unsigned struct_jobs_interface_sz = sizeof(jobs_interface);
+unsigned struct_jobs_modify_class_sz = sizeof(jobs_modify_class);
+unsigned struct_kbentry_sz = sizeof(kbentry);
+unsigned struct_kfilter_mapping_sz = sizeof(kfilter_mapping);
+unsigned struct_kiockeymap_sz = sizeof(kiockeymap);
+unsigned struct_ksyms_gsymbol_sz = sizeof(ksyms_gsymbol);
+unsigned struct_ksyms_gvalue_sz = sizeof(ksyms_gvalue);
+unsigned struct_ksyms_ogsymbol_sz = sizeof(ksyms_ogsymbol);
+unsigned struct_kttcp_io_args_sz = sizeof(kttcp_io_args);
+unsigned struct_ltchars_sz = sizeof(ltchars);
+unsigned struct_lua_create_sz = sizeof(struct lua_create);
+unsigned struct_lua_info_sz = sizeof(struct lua_info);
+unsigned struct_lua_load_sz = sizeof(struct lua_load);
+unsigned struct_lua_require_sz = sizeof(lua_require);
+unsigned struct_mbpp_param_sz = sizeof(mbpp_param);
+unsigned struct_md_conf_sz = sizeof(md_conf);
+unsigned struct_meteor_capframe_sz = sizeof(meteor_capframe);
+unsigned struct_meteor_counts_sz = sizeof(meteor_counts);
+unsigned struct_meteor_geomet_sz = sizeof(meteor_geomet);
+unsigned struct_meteor_pixfmt_sz = sizeof(meteor_pixfmt);
+unsigned struct_meteor_video_sz = sizeof(meteor_video);
+unsigned struct_mlx_cinfo_sz = sizeof(mlx_cinfo);
+unsigned struct_mlx_pause_sz = sizeof(mlx_pause);
+unsigned struct_mlx_rebuild_request_sz = sizeof(mlx_rebuild_request);
+unsigned struct_mlx_rebuild_status_sz = sizeof(mlx_rebuild_status);
+unsigned struct_mlx_usercommand_sz = sizeof(mlx_usercommand);
+unsigned struct_mly_user_command_sz = sizeof(mly_user_command);
+unsigned struct_mly_user_health_sz = sizeof(mly_user_health);
+unsigned struct_mtget_sz = sizeof(mtget);
+unsigned struct_mtop_sz = sizeof(mtop);
+unsigned struct_npf_ioctl_table_sz = sizeof(npf_ioctl_table);
+unsigned struct_npioctl_sz = sizeof(npioctl);
+unsigned struct_nvme_pt_command_sz = sizeof(nvme_pt_command);
+unsigned struct_ochanger_element_status_request_sz =
+ sizeof(ochanger_element_status_request);
+unsigned struct_ofiocdesc_sz = sizeof(ofiocdesc);
+unsigned struct_okiockey_sz = sizeof(okiockey);
+unsigned struct_ortentry_sz = sizeof(ortentry);
+unsigned struct_oscsi_addr_sz = sizeof(oscsi_addr);
+unsigned struct_oss_audioinfo_sz = sizeof(oss_audioinfo);
+unsigned struct_oss_sysinfo_sz = sizeof(oss_sysinfo);
+unsigned struct_pciio_bdf_cfgreg_sz = sizeof(pciio_bdf_cfgreg);
+unsigned struct_pciio_businfo_sz = sizeof(pciio_businfo);
+unsigned struct_pciio_cfgreg_sz = sizeof(pciio_cfgreg);
+unsigned struct_pciio_drvname_sz = sizeof(pciio_drvname);
+unsigned struct_pciio_drvnameonbus_sz = sizeof(pciio_drvnameonbus);
+unsigned struct_pcvtid_sz = sizeof(pcvtid);
+unsigned struct_pf_osfp_ioctl_sz = sizeof(pf_osfp_ioctl);
+unsigned struct_pf_status_sz = sizeof(pf_status);
+unsigned struct_pfioc_altq_sz = sizeof(pfioc_altq);
+unsigned struct_pfioc_if_sz = sizeof(pfioc_if);
+unsigned struct_pfioc_iface_sz = sizeof(pfioc_iface);
+unsigned struct_pfioc_limit_sz = sizeof(pfioc_limit);
+unsigned struct_pfioc_natlook_sz = sizeof(pfioc_natlook);
+unsigned struct_pfioc_pooladdr_sz = sizeof(pfioc_pooladdr);
+unsigned struct_pfioc_qstats_sz = sizeof(pfioc_qstats);
+unsigned struct_pfioc_rule_sz = sizeof(pfioc_rule);
+unsigned struct_pfioc_ruleset_sz = sizeof(pfioc_ruleset);
+unsigned struct_pfioc_src_node_kill_sz = sizeof(pfioc_src_node_kill);
+unsigned struct_pfioc_src_nodes_sz = sizeof(pfioc_src_nodes);
+unsigned struct_pfioc_state_kill_sz = sizeof(pfioc_state_kill);
+unsigned struct_pfioc_state_sz = sizeof(pfioc_state);
+unsigned struct_pfioc_states_sz = sizeof(pfioc_states);
+unsigned struct_pfioc_table_sz = sizeof(pfioc_table);
+unsigned struct_pfioc_tm_sz = sizeof(pfioc_tm);
+unsigned struct_pfioc_trans_sz = sizeof(pfioc_trans);
+unsigned struct_plistref_sz = sizeof(plistref);
+unsigned struct_power_type_sz = sizeof(power_type);
+unsigned struct_ppp_idle_sz = sizeof(ppp_idle);
+unsigned struct_ppp_option_data_sz = sizeof(ppp_option_data);
+unsigned struct_ppp_rawin_sz = sizeof(ppp_rawin);
+unsigned struct_pppoeconnectionstate_sz = sizeof(pppoeconnectionstate);
+unsigned struct_pppoediscparms_sz = sizeof(pppoediscparms);
+unsigned struct_priq_add_class_sz = sizeof(priq_add_class);
+unsigned struct_priq_add_filter_sz = sizeof(priq_add_filter);
+unsigned struct_priq_class_stats_sz = sizeof(priq_class_stats);
+unsigned struct_priq_delete_class_sz = sizeof(priq_delete_class);
+unsigned struct_priq_delete_filter_sz = sizeof(priq_delete_filter);
+unsigned struct_priq_interface_sz = sizeof(priq_interface);
+unsigned struct_priq_modify_class_sz = sizeof(priq_modify_class);
+unsigned struct_ptmget_sz = sizeof(ptmget);
+unsigned struct_radio_info_sz = sizeof(radio_info);
+unsigned struct_red_conf_sz = sizeof(red_conf);
+unsigned struct_red_interface_sz = sizeof(red_interface);
+unsigned struct_red_stats_sz = sizeof(red_stats);
+unsigned struct_redparams_sz = sizeof(redparams);
+unsigned struct_rf_pmparams_sz = sizeof(rf_pmparams);
+unsigned struct_rf_pmstat_sz = sizeof(rf_pmstat);
+unsigned struct_rf_recon_req_sz = sizeof(rf_recon_req);
+unsigned struct_rio_conf_sz = sizeof(rio_conf);
+unsigned struct_rio_interface_sz = sizeof(rio_interface);
+unsigned struct_rio_stats_sz = sizeof(rio_stats);
+unsigned struct_scan_io_sz = sizeof(scan_io);
+unsigned struct_scbusaccel_args_sz = sizeof(scbusaccel_args);
+unsigned struct_scbusiodetach_args_sz = sizeof(scbusiodetach_args);
+unsigned struct_scbusioscan_args_sz = sizeof(scbusioscan_args);
+unsigned struct_scsi_addr_sz = sizeof(scsi_addr);
+unsigned struct_seq_event_rec_sz = sizeof(seq_event_rec);
+unsigned struct_session_op_sz = sizeof(session_op);
+unsigned struct_sgttyb_sz = sizeof(sgttyb);
+unsigned struct_sioc_sg_req_sz = sizeof(sioc_sg_req);
+unsigned struct_sioc_vif_req_sz = sizeof(sioc_vif_req);
+unsigned struct_smbioc_flags_sz = sizeof(smbioc_flags);
+unsigned struct_smbioc_lookup_sz = sizeof(smbioc_lookup);
+unsigned struct_smbioc_oshare_sz = sizeof(smbioc_oshare);
+unsigned struct_smbioc_ossn_sz = sizeof(smbioc_ossn);
+unsigned struct_smbioc_rq_sz = sizeof(smbioc_rq);
+unsigned struct_smbioc_rw_sz = sizeof(smbioc_rw);
+unsigned struct_spppauthcfg_sz = sizeof(spppauthcfg);
+unsigned struct_spppauthfailuresettings_sz = sizeof(spppauthfailuresettings);
+unsigned struct_spppauthfailurestats_sz = sizeof(spppauthfailurestats);
+unsigned struct_spppdnsaddrs_sz = sizeof(spppdnsaddrs);
+unsigned struct_spppdnssettings_sz = sizeof(spppdnssettings);
+unsigned struct_spppidletimeout_sz = sizeof(spppidletimeout);
+unsigned struct_spppkeepalivesettings_sz = sizeof(spppkeepalivesettings);
+unsigned struct_sppplcpcfg_sz = sizeof(sppplcpcfg);
+unsigned struct_spppstatus_sz = sizeof(spppstatus);
+unsigned struct_spppstatusncp_sz = sizeof(spppstatusncp);
+unsigned struct_srt_rt_sz = sizeof(srt_rt);
+unsigned struct_stic_xinfo_sz = sizeof(stic_xinfo);
+unsigned struct_sun_dkctlr_sz = sizeof(sun_dkctlr);
+unsigned struct_sun_dkgeom_sz = sizeof(sun_dkgeom);
+unsigned struct_sun_dkpart_sz = sizeof(sun_dkpart);
+unsigned struct_synth_info_sz = sizeof(synth_info);
+unsigned struct_tbrreq_sz = sizeof(tbrreq);
+unsigned struct_tchars_sz = sizeof(tchars);
+unsigned struct_termios_sz = sizeof(termios);
+unsigned struct_timeval_sz = sizeof(timeval);
+unsigned struct_twe_drivecommand_sz = sizeof(twe_drivecommand);
+unsigned struct_twe_paramcommand_sz = sizeof(twe_paramcommand);
+unsigned struct_twe_usercommand_sz = sizeof(twe_usercommand);
+unsigned struct_ukyopon_identify_sz = sizeof(ukyopon_identify);
+unsigned struct_urio_command_sz = sizeof(urio_command);
+unsigned struct_usb_alt_interface_sz = sizeof(usb_alt_interface);
+unsigned struct_usb_bulk_ra_wb_opt_sz = sizeof(usb_bulk_ra_wb_opt);
+unsigned struct_usb_config_desc_sz = sizeof(usb_config_desc);
+unsigned struct_usb_ctl_report_desc_sz = sizeof(usb_ctl_report_desc);
+unsigned struct_usb_ctl_report_sz = sizeof(usb_ctl_report);
+unsigned struct_usb_ctl_request_sz = sizeof(usb_ctl_request);
+#if defined(__x86_64__)
+unsigned struct_nvmm_ioc_capability_sz = sizeof(nvmm_ioc_capability);
+unsigned struct_nvmm_ioc_machine_create_sz = sizeof(nvmm_ioc_machine_create);
+unsigned struct_nvmm_ioc_machine_destroy_sz = sizeof(nvmm_ioc_machine_destroy);
+unsigned struct_nvmm_ioc_machine_configure_sz =
+ sizeof(nvmm_ioc_machine_configure);
+unsigned struct_nvmm_ioc_vcpu_create_sz = sizeof(nvmm_ioc_vcpu_create);
+unsigned struct_nvmm_ioc_vcpu_destroy_sz = sizeof(nvmm_ioc_vcpu_destroy);
+unsigned struct_nvmm_ioc_vcpu_setstate_sz = sizeof(nvmm_ioc_vcpu_destroy);
+unsigned struct_nvmm_ioc_vcpu_getstate_sz = sizeof(nvmm_ioc_vcpu_getstate);
+unsigned struct_nvmm_ioc_vcpu_inject_sz = sizeof(nvmm_ioc_vcpu_inject);
+unsigned struct_nvmm_ioc_vcpu_run_sz = sizeof(nvmm_ioc_vcpu_run);
+unsigned struct_nvmm_ioc_gpa_map_sz = sizeof(nvmm_ioc_gpa_map);
+unsigned struct_nvmm_ioc_gpa_unmap_sz = sizeof(nvmm_ioc_gpa_unmap);
+unsigned struct_nvmm_ioc_hva_map_sz = sizeof(nvmm_ioc_hva_map);
+unsigned struct_nvmm_ioc_hva_unmap_sz = sizeof(nvmm_ioc_hva_unmap);
+unsigned struct_nvmm_ioc_ctl_sz = sizeof(nvmm_ioc_ctl);
+#endif
+unsigned struct_spi_ioctl_configure_sz = sizeof(spi_ioctl_configure);
+unsigned struct_spi_ioctl_transfer_sz = sizeof(spi_ioctl_transfer);
+unsigned struct_autofs_daemon_request_sz = sizeof(autofs_daemon_request);
+unsigned struct_autofs_daemon_done_sz = sizeof(autofs_daemon_done);
+unsigned struct_sctp_connectx_addrs_sz = sizeof(sctp_connectx_addrs);
+unsigned struct_usb_device_info_old_sz = sizeof(usb_device_info_old);
+unsigned struct_usb_device_info_sz = sizeof(usb_device_info);
+unsigned struct_usb_device_stats_sz = sizeof(usb_device_stats);
+unsigned struct_usb_endpoint_desc_sz = sizeof(usb_endpoint_desc);
+unsigned struct_usb_full_desc_sz = sizeof(usb_full_desc);
+unsigned struct_usb_interface_desc_sz = sizeof(usb_interface_desc);
+unsigned struct_usb_string_desc_sz = sizeof(usb_string_desc);
+unsigned struct_utoppy_readfile_sz = sizeof(utoppy_readfile);
+unsigned struct_utoppy_rename_sz = sizeof(utoppy_rename);
+unsigned struct_utoppy_stats_sz = sizeof(utoppy_stats);
+unsigned struct_utoppy_writefile_sz = sizeof(utoppy_writefile);
+unsigned struct_v4l2_audio_sz = sizeof(v4l2_audio);
+unsigned struct_v4l2_audioout_sz = sizeof(v4l2_audioout);
+unsigned struct_v4l2_buffer_sz = sizeof(v4l2_buffer);
+unsigned struct_v4l2_capability_sz = sizeof(v4l2_capability);
+unsigned struct_v4l2_control_sz = sizeof(v4l2_control);
+unsigned struct_v4l2_crop_sz = sizeof(v4l2_crop);
+unsigned struct_v4l2_cropcap_sz = sizeof(v4l2_cropcap);
+unsigned struct_v4l2_fmtdesc_sz = sizeof(v4l2_fmtdesc);
+unsigned struct_v4l2_format_sz = sizeof(v4l2_format);
+unsigned struct_v4l2_framebuffer_sz = sizeof(v4l2_framebuffer);
+unsigned struct_v4l2_frequency_sz = sizeof(v4l2_frequency);
+unsigned struct_v4l2_frmivalenum_sz = sizeof(v4l2_frmivalenum);
+unsigned struct_v4l2_frmsizeenum_sz = sizeof(v4l2_frmsizeenum);
+unsigned struct_v4l2_input_sz = sizeof(v4l2_input);
+unsigned struct_v4l2_jpegcompression_sz = sizeof(v4l2_jpegcompression);
+unsigned struct_v4l2_modulator_sz = sizeof(v4l2_modulator);
+unsigned struct_v4l2_output_sz = sizeof(v4l2_output);
+unsigned struct_v4l2_queryctrl_sz = sizeof(v4l2_queryctrl);
+unsigned struct_v4l2_querymenu_sz = sizeof(v4l2_querymenu);
+unsigned struct_v4l2_requestbuffers_sz = sizeof(v4l2_requestbuffers);
+unsigned struct_v4l2_standard_sz = sizeof(v4l2_standard);
+unsigned struct_v4l2_streamparm_sz = sizeof(v4l2_streamparm);
+unsigned struct_v4l2_tuner_sz = sizeof(v4l2_tuner);
+unsigned struct_vnd_ioctl_sz = sizeof(vnd_ioctl);
+unsigned struct_vnd_user_sz = sizeof(vnd_user);
+unsigned struct_vt_stat_sz = sizeof(vt_stat);
+unsigned struct_wdog_conf_sz = sizeof(wdog_conf);
+unsigned struct_wdog_mode_sz = sizeof(wdog_mode);
+unsigned struct_ipmi_recv_sz = sizeof(ipmi_recv);
+unsigned struct_ipmi_req_sz = sizeof(ipmi_req);
+unsigned struct_ipmi_cmdspec_sz = sizeof(ipmi_cmdspec);
+unsigned struct_wfq_conf_sz = sizeof(wfq_conf);
+unsigned struct_wfq_getqid_sz = sizeof(wfq_getqid);
+unsigned struct_wfq_getstats_sz = sizeof(wfq_getstats);
+unsigned struct_wfq_interface_sz = sizeof(wfq_interface);
+unsigned struct_wfq_setweight_sz = sizeof(wfq_setweight);
+unsigned struct_winsize_sz = sizeof(winsize);
+unsigned struct_wscons_event_sz = sizeof(wscons_event);
+unsigned struct_wsdisplay_addscreendata_sz = sizeof(wsdisplay_addscreendata);
+unsigned struct_wsdisplay_char_sz = sizeof(wsdisplay_char);
+unsigned struct_wsdisplay_cmap_sz = sizeof(wsdisplay_cmap);
+unsigned struct_wsdisplay_curpos_sz = sizeof(wsdisplay_curpos);
+unsigned struct_wsdisplay_cursor_sz = sizeof(wsdisplay_cursor);
+unsigned struct_wsdisplay_delscreendata_sz = sizeof(wsdisplay_delscreendata);
+unsigned struct_wsdisplay_fbinfo_sz = sizeof(wsdisplay_fbinfo);
+unsigned struct_wsdisplay_font_sz = sizeof(wsdisplay_font);
+unsigned struct_wsdisplay_kbddata_sz = sizeof(wsdisplay_kbddata);
+unsigned struct_wsdisplay_msgattrs_sz = sizeof(wsdisplay_msgattrs);
+unsigned struct_wsdisplay_param_sz = sizeof(wsdisplay_param);
+unsigned struct_wsdisplay_scroll_data_sz = sizeof(wsdisplay_scroll_data);
+unsigned struct_wsdisplay_usefontdata_sz = sizeof(wsdisplay_usefontdata);
+unsigned struct_wsdisplayio_blit_sz = sizeof(wsdisplayio_blit);
+unsigned struct_wsdisplayio_bus_id_sz = sizeof(wsdisplayio_bus_id);
+unsigned struct_wsdisplayio_edid_info_sz = sizeof(wsdisplayio_edid_info);
+unsigned struct_wsdisplayio_fbinfo_sz = sizeof(wsdisplayio_fbinfo);
+unsigned struct_wskbd_bell_data_sz = sizeof(wskbd_bell_data);
+unsigned struct_wskbd_keyrepeat_data_sz = sizeof(wskbd_keyrepeat_data);
+unsigned struct_wskbd_map_data_sz = sizeof(wskbd_map_data);
+unsigned struct_wskbd_scroll_data_sz = sizeof(wskbd_scroll_data);
+unsigned struct_wsmouse_calibcoords_sz = sizeof(wsmouse_calibcoords);
+unsigned struct_wsmouse_id_sz = sizeof(wsmouse_id);
+unsigned struct_wsmouse_repeat_sz = sizeof(wsmouse_repeat);
+unsigned struct_wsmux_device_list_sz = sizeof(wsmux_device_list);
+unsigned struct_wsmux_device_sz = sizeof(wsmux_device);
+unsigned struct_xd_iocmd_sz = sizeof(xd_iocmd);
+
+unsigned struct_scsireq_sz = sizeof(struct scsireq);
+unsigned struct_tone_sz = sizeof(tone_t);
+unsigned union_twe_statrequest_sz = sizeof(union twe_statrequest);
+unsigned struct_usb_device_descriptor_sz = sizeof(usb_device_descriptor_t);
+unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
+unsigned struct__old_mixer_info_sz = sizeof(struct _old_mixer_info);
+unsigned struct__agp_allocate_sz = sizeof(struct _agp_allocate);
+unsigned struct__agp_bind_sz = sizeof(struct _agp_bind);
+unsigned struct__agp_info_sz = sizeof(struct _agp_info);
+unsigned struct__agp_setup_sz = sizeof(struct _agp_setup);
+unsigned struct__agp_unbind_sz = sizeof(struct _agp_unbind);
+unsigned struct_atareq_sz = sizeof(struct atareq);
+unsigned struct_cpustate_sz = sizeof(struct cpustate);
+unsigned struct_dmx_caps_sz = sizeof(struct dmx_caps);
+unsigned enum_dmx_source_sz = sizeof(dmx_source_t);
+unsigned union_dvd_authinfo_sz = sizeof(dvd_authinfo);
+unsigned union_dvd_struct_sz = sizeof(dvd_struct);
+unsigned enum_v4l2_priority_sz = sizeof(enum v4l2_priority);
+unsigned struct_envsys_basic_info_sz = sizeof(struct envsys_basic_info);
+unsigned struct_envsys_tre_data_sz = sizeof(struct envsys_tre_data);
+unsigned enum_fe_sec_mini_cmd_sz = sizeof(enum fe_sec_mini_cmd);
+unsigned enum_fe_sec_tone_mode_sz = sizeof(enum fe_sec_tone_mode);
+unsigned enum_fe_sec_voltage_sz = sizeof(enum fe_sec_voltage);
+unsigned enum_fe_status_sz = sizeof(enum fe_status);
+unsigned struct_gdt_ctrt_sz = sizeof(struct gdt_ctrt);
+unsigned struct_gdt_event_sz = sizeof(struct gdt_event);
+unsigned struct_gdt_osv_sz = sizeof(struct gdt_osv);
+unsigned struct_gdt_rescan_sz = sizeof(struct gdt_rescan);
+unsigned struct_gdt_statist_sz = sizeof(struct gdt_statist);
+unsigned struct_gdt_ucmd_sz = sizeof(struct gdt_ucmd);
+unsigned struct_iscsi_conn_status_parameters_sz =
+ sizeof(iscsi_conn_status_parameters_t);
+unsigned struct_iscsi_get_version_parameters_sz =
+ sizeof(iscsi_get_version_parameters_t);
+unsigned struct_iscsi_iocommand_parameters_sz =
+ sizeof(iscsi_iocommand_parameters_t);
+unsigned struct_iscsi_login_parameters_sz = sizeof(iscsi_login_parameters_t);
+unsigned struct_iscsi_logout_parameters_sz = sizeof(iscsi_logout_parameters_t);
+unsigned struct_iscsi_register_event_parameters_sz =
+ sizeof(iscsi_register_event_parameters_t);
+unsigned struct_iscsi_remove_parameters_sz = sizeof(iscsi_remove_parameters_t);
+unsigned struct_iscsi_send_targets_parameters_sz =
+ sizeof(iscsi_send_targets_parameters_t);
+unsigned struct_iscsi_set_node_name_parameters_sz =
+ sizeof(iscsi_set_node_name_parameters_t);
+unsigned struct_iscsi_wait_event_parameters_sz =
+ sizeof(iscsi_wait_event_parameters_t);
+unsigned struct_isp_stats_sz = sizeof(isp_stats_t);
+unsigned struct_lsenable_sz = sizeof(struct lsenable);
+unsigned struct_lsdisable_sz = sizeof(struct lsdisable);
+unsigned struct_audio_format_query_sz = sizeof(audio_format_query);
+unsigned struct_mixer_ctrl_sz = sizeof(struct mixer_ctrl);
+unsigned struct_mixer_devinfo_sz = sizeof(struct mixer_devinfo);
+unsigned struct_mpu_command_rec_sz = sizeof(mpu_command_rec);
+unsigned struct_rndstat_sz = sizeof(rndstat_t);
+unsigned struct_rndstat_name_sz = sizeof(rndstat_name_t);
+unsigned struct_rndctl_sz = sizeof(rndctl_t);
+unsigned struct_rnddata_sz = sizeof(rnddata_t);
+unsigned struct_rndpoolstat_sz = sizeof(rndpoolstat_t);
+unsigned struct_rndstat_est_sz = sizeof(rndstat_est_t);
+unsigned struct_rndstat_est_name_sz = sizeof(rndstat_est_name_t);
+unsigned struct_pps_params_sz = sizeof(pps_params_t);
+unsigned struct_pps_info_sz = sizeof(pps_info_t);
+unsigned struct_mixer_info_sz = sizeof(struct mixer_info);
+unsigned struct_RF_SparetWait_sz = sizeof(RF_SparetWait_t);
+unsigned struct_RF_ComponentLabel_sz = sizeof(RF_ComponentLabel_t);
+unsigned struct_RF_SingleComponent_sz = sizeof(RF_SingleComponent_t);
+unsigned struct_RF_ProgressInfo_sz = sizeof(RF_ProgressInfo_t);
+unsigned struct_nvlist_ref_sz = sizeof(struct __sanitizer_nvlist_ref_t);
+unsigned struct_StringList_sz = sizeof(StringList);
+
+const unsigned IOCTL_NOT_PRESENT = 0;
+
+unsigned IOCTL_AFM_ADDFMAP = AFM_ADDFMAP;
+unsigned IOCTL_AFM_DELFMAP = AFM_DELFMAP;
+unsigned IOCTL_AFM_CLEANFMAP = AFM_CLEANFMAP;
+unsigned IOCTL_AFM_GETFMAP = AFM_GETFMAP;
+unsigned IOCTL_ALTQGTYPE = ALTQGTYPE;
+unsigned IOCTL_ALTQTBRSET = ALTQTBRSET;
+unsigned IOCTL_ALTQTBRGET = ALTQTBRGET;
+unsigned IOCTL_BLUE_IF_ATTACH = BLUE_IF_ATTACH;
+unsigned IOCTL_BLUE_IF_DETACH = BLUE_IF_DETACH;
+unsigned IOCTL_BLUE_ENABLE = BLUE_ENABLE;
+unsigned IOCTL_BLUE_DISABLE = BLUE_DISABLE;
+unsigned IOCTL_BLUE_CONFIG = BLUE_CONFIG;
+unsigned IOCTL_BLUE_GETSTATS = BLUE_GETSTATS;
+unsigned IOCTL_CBQ_IF_ATTACH = CBQ_IF_ATTACH;
+unsigned IOCTL_CBQ_IF_DETACH = CBQ_IF_DETACH;
+unsigned IOCTL_CBQ_ENABLE = CBQ_ENABLE;
+unsigned IOCTL_CBQ_DISABLE = CBQ_DISABLE;
+unsigned IOCTL_CBQ_CLEAR_HIERARCHY = CBQ_CLEAR_HIERARCHY;
+unsigned IOCTL_CBQ_ADD_CLASS = CBQ_ADD_CLASS;
+unsigned IOCTL_CBQ_DEL_CLASS = CBQ_DEL_CLASS;
+unsigned IOCTL_CBQ_MODIFY_CLASS = CBQ_MODIFY_CLASS;
+unsigned IOCTL_CBQ_ADD_FILTER = CBQ_ADD_FILTER;
+unsigned IOCTL_CBQ_DEL_FILTER = CBQ_DEL_FILTER;
+unsigned IOCTL_CBQ_GETSTATS = CBQ_GETSTATS;
+unsigned IOCTL_CDNR_IF_ATTACH = CDNR_IF_ATTACH;
+unsigned IOCTL_CDNR_IF_DETACH = CDNR_IF_DETACH;
+unsigned IOCTL_CDNR_ENABLE = CDNR_ENABLE;
+unsigned IOCTL_CDNR_DISABLE = CDNR_DISABLE;
+unsigned IOCTL_CDNR_ADD_FILTER = CDNR_ADD_FILTER;
+unsigned IOCTL_CDNR_DEL_FILTER = CDNR_DEL_FILTER;
+unsigned IOCTL_CDNR_GETSTATS = CDNR_GETSTATS;
+unsigned IOCTL_CDNR_ADD_ELEM = CDNR_ADD_ELEM;
+unsigned IOCTL_CDNR_DEL_ELEM = CDNR_DEL_ELEM;
+unsigned IOCTL_CDNR_ADD_TBM = CDNR_ADD_TBM;
+unsigned IOCTL_CDNR_MOD_TBM = CDNR_MOD_TBM;
+unsigned IOCTL_CDNR_TBM_STATS = CDNR_TBM_STATS;
+unsigned IOCTL_CDNR_ADD_TCM = CDNR_ADD_TCM;
+unsigned IOCTL_CDNR_MOD_TCM = CDNR_MOD_TCM;
+unsigned IOCTL_CDNR_TCM_STATS = CDNR_TCM_STATS;
+unsigned IOCTL_CDNR_ADD_TSW = CDNR_ADD_TSW;
+unsigned IOCTL_CDNR_MOD_TSW = CDNR_MOD_TSW;
+unsigned IOCTL_FIFOQ_IF_ATTACH = FIFOQ_IF_ATTACH;
+unsigned IOCTL_FIFOQ_IF_DETACH = FIFOQ_IF_DETACH;
+unsigned IOCTL_FIFOQ_ENABLE = FIFOQ_ENABLE;
+unsigned IOCTL_FIFOQ_DISABLE = FIFOQ_DISABLE;
+unsigned IOCTL_FIFOQ_CONFIG = FIFOQ_CONFIG;
+unsigned IOCTL_FIFOQ_GETSTATS = FIFOQ_GETSTATS;
+unsigned IOCTL_HFSC_IF_ATTACH = HFSC_IF_ATTACH;
+unsigned IOCTL_HFSC_IF_DETACH = HFSC_IF_DETACH;
+unsigned IOCTL_HFSC_ENABLE = HFSC_ENABLE;
+unsigned IOCTL_HFSC_DISABLE = HFSC_DISABLE;
+unsigned IOCTL_HFSC_CLEAR_HIERARCHY = HFSC_CLEAR_HIERARCHY;
+unsigned IOCTL_HFSC_ADD_CLASS = HFSC_ADD_CLASS;
+unsigned IOCTL_HFSC_DEL_CLASS = HFSC_DEL_CLASS;
+unsigned IOCTL_HFSC_MOD_CLASS = HFSC_MOD_CLASS;
+unsigned IOCTL_HFSC_ADD_FILTER = HFSC_ADD_FILTER;
+unsigned IOCTL_HFSC_DEL_FILTER = HFSC_DEL_FILTER;
+unsigned IOCTL_HFSC_GETSTATS = HFSC_GETSTATS;
+unsigned IOCTL_JOBS_IF_ATTACH = JOBS_IF_ATTACH;
+unsigned IOCTL_JOBS_IF_DETACH = JOBS_IF_DETACH;
+unsigned IOCTL_JOBS_ENABLE = JOBS_ENABLE;
+unsigned IOCTL_JOBS_DISABLE = JOBS_DISABLE;
+unsigned IOCTL_JOBS_CLEAR = JOBS_CLEAR;
+unsigned IOCTL_JOBS_ADD_CLASS = JOBS_ADD_CLASS;
+unsigned IOCTL_JOBS_DEL_CLASS = JOBS_DEL_CLASS;
+unsigned IOCTL_JOBS_MOD_CLASS = JOBS_MOD_CLASS;
+unsigned IOCTL_JOBS_ADD_FILTER = JOBS_ADD_FILTER;
+unsigned IOCTL_JOBS_DEL_FILTER = JOBS_DEL_FILTER;
+unsigned IOCTL_JOBS_GETSTATS = JOBS_GETSTATS;
+unsigned IOCTL_PRIQ_IF_ATTACH = PRIQ_IF_ATTACH;
+unsigned IOCTL_PRIQ_IF_DETACH = PRIQ_IF_DETACH;
+unsigned IOCTL_PRIQ_ENABLE = PRIQ_ENABLE;
+unsigned IOCTL_PRIQ_DISABLE = PRIQ_DISABLE;
+unsigned IOCTL_PRIQ_CLEAR = PRIQ_CLEAR;
+unsigned IOCTL_PRIQ_ADD_CLASS = PRIQ_ADD_CLASS;
+unsigned IOCTL_PRIQ_DEL_CLASS = PRIQ_DEL_CLASS;
+unsigned IOCTL_PRIQ_MOD_CLASS = PRIQ_MOD_CLASS;
+unsigned IOCTL_PRIQ_ADD_FILTER = PRIQ_ADD_FILTER;
+unsigned IOCTL_PRIQ_DEL_FILTER = PRIQ_DEL_FILTER;
+unsigned IOCTL_PRIQ_GETSTATS = PRIQ_GETSTATS;
+unsigned IOCTL_RED_IF_ATTACH = RED_IF_ATTACH;
+unsigned IOCTL_RED_IF_DETACH = RED_IF_DETACH;
+unsigned IOCTL_RED_ENABLE = RED_ENABLE;
+unsigned IOCTL_RED_DISABLE = RED_DISABLE;
+unsigned IOCTL_RED_CONFIG = RED_CONFIG;
+unsigned IOCTL_RED_GETSTATS = RED_GETSTATS;
+unsigned IOCTL_RED_SETDEFAULTS = RED_SETDEFAULTS;
+unsigned IOCTL_RIO_IF_ATTACH = RIO_IF_ATTACH;
+unsigned IOCTL_RIO_IF_DETACH = RIO_IF_DETACH;
+unsigned IOCTL_RIO_ENABLE = RIO_ENABLE;
+unsigned IOCTL_RIO_DISABLE = RIO_DISABLE;
+unsigned IOCTL_RIO_CONFIG = RIO_CONFIG;
+unsigned IOCTL_RIO_GETSTATS = RIO_GETSTATS;
+unsigned IOCTL_RIO_SETDEFAULTS = RIO_SETDEFAULTS;
+unsigned IOCTL_WFQ_IF_ATTACH = WFQ_IF_ATTACH;
+unsigned IOCTL_WFQ_IF_DETACH = WFQ_IF_DETACH;
+unsigned IOCTL_WFQ_ENABLE = WFQ_ENABLE;
+unsigned IOCTL_WFQ_DISABLE = WFQ_DISABLE;
+unsigned IOCTL_WFQ_CONFIG = WFQ_CONFIG;
+unsigned IOCTL_WFQ_GET_STATS = WFQ_GET_STATS;
+unsigned IOCTL_WFQ_GET_QID = WFQ_GET_QID;
+unsigned IOCTL_WFQ_SET_WEIGHT = WFQ_SET_WEIGHT;
+unsigned IOCTL_CRIOGET = CRIOGET;
+unsigned IOCTL_CIOCFSESSION = CIOCFSESSION;
+unsigned IOCTL_CIOCKEY = CIOCKEY;
+unsigned IOCTL_CIOCNFKEYM = CIOCNFKEYM;
+unsigned IOCTL_CIOCNFSESSION = CIOCNFSESSION;
+unsigned IOCTL_CIOCNCRYPTRETM = CIOCNCRYPTRETM;
+unsigned IOCTL_CIOCNCRYPTRET = CIOCNCRYPTRET;
+unsigned IOCTL_CIOCGSESSION = CIOCGSESSION;
+unsigned IOCTL_CIOCNGSESSION = CIOCNGSESSION;
+unsigned IOCTL_CIOCCRYPT = CIOCCRYPT;
+unsigned IOCTL_CIOCNCRYPTM = CIOCNCRYPTM;
+unsigned IOCTL_CIOCASYMFEAT = CIOCASYMFEAT;
+unsigned IOCTL_APM_IOC_REJECT = APM_IOC_REJECT;
+unsigned IOCTL_APM_IOC_STANDBY = APM_IOC_STANDBY;
+unsigned IOCTL_APM_IOC_SUSPEND = APM_IOC_SUSPEND;
+unsigned IOCTL_OAPM_IOC_GETPOWER = OAPM_IOC_GETPOWER;
+unsigned IOCTL_APM_IOC_GETPOWER = APM_IOC_GETPOWER;
+unsigned IOCTL_APM_IOC_NEXTEVENT = APM_IOC_NEXTEVENT;
+unsigned IOCTL_APM_IOC_DEV_CTL = APM_IOC_DEV_CTL;
+unsigned IOCTL_NETBSD_DM_IOCTL = NETBSD_DM_IOCTL;
+unsigned IOCTL_DMIO_SETFUNC = DMIO_SETFUNC;
+unsigned IOCTL_DMX_START = DMX_START;
+unsigned IOCTL_DMX_STOP = DMX_STOP;
+unsigned IOCTL_DMX_SET_FILTER = DMX_SET_FILTER;
+unsigned IOCTL_DMX_SET_PES_FILTER = DMX_SET_PES_FILTER;
+unsigned IOCTL_DMX_SET_BUFFER_SIZE = DMX_SET_BUFFER_SIZE;
+unsigned IOCTL_DMX_GET_STC = DMX_GET_STC;
+unsigned IOCTL_DMX_ADD_PID = DMX_ADD_PID;
+unsigned IOCTL_DMX_REMOVE_PID = DMX_REMOVE_PID;
+unsigned IOCTL_DMX_GET_CAPS = DMX_GET_CAPS;
+unsigned IOCTL_DMX_SET_SOURCE = DMX_SET_SOURCE;
+unsigned IOCTL_FE_READ_STATUS = FE_READ_STATUS;
+unsigned IOCTL_FE_READ_BER = FE_READ_BER;
+unsigned IOCTL_FE_READ_SNR = FE_READ_SNR;
+unsigned IOCTL_FE_READ_SIGNAL_STRENGTH = FE_READ_SIGNAL_STRENGTH;
+unsigned IOCTL_FE_READ_UNCORRECTED_BLOCKS = FE_READ_UNCORRECTED_BLOCKS;
+unsigned IOCTL_FE_SET_FRONTEND = FE_SET_FRONTEND;
+unsigned IOCTL_FE_GET_FRONTEND = FE_GET_FRONTEND;
+unsigned IOCTL_FE_GET_EVENT = FE_GET_EVENT;
+unsigned IOCTL_FE_GET_INFO = FE_GET_INFO;
+unsigned IOCTL_FE_DISEQC_RESET_OVERLOAD = FE_DISEQC_RESET_OVERLOAD;
+unsigned IOCTL_FE_DISEQC_SEND_MASTER_CMD = FE_DISEQC_SEND_MASTER_CMD;
+unsigned IOCTL_FE_DISEQC_RECV_SLAVE_REPLY = FE_DISEQC_RECV_SLAVE_REPLY;
+unsigned IOCTL_FE_DISEQC_SEND_BURST = FE_DISEQC_SEND_BURST;
+unsigned IOCTL_FE_SET_TONE = FE_SET_TONE;
+unsigned IOCTL_FE_SET_VOLTAGE = FE_SET_VOLTAGE;
+unsigned IOCTL_FE_ENABLE_HIGH_LNB_VOLTAGE = FE_ENABLE_HIGH_LNB_VOLTAGE;
+unsigned IOCTL_FE_SET_FRONTEND_TUNE_MODE = FE_SET_FRONTEND_TUNE_MODE;
+unsigned IOCTL_FE_DISHNETWORK_SEND_LEGACY_CMD = FE_DISHNETWORK_SEND_LEGACY_CMD;
+unsigned IOCTL_FILEMON_SET_FD = FILEMON_SET_FD;
+unsigned IOCTL_FILEMON_SET_PID = FILEMON_SET_PID;
+unsigned IOCTL_HDAUDIO_FGRP_INFO = HDAUDIO_FGRP_INFO;
+unsigned IOCTL_HDAUDIO_FGRP_GETCONFIG = HDAUDIO_FGRP_GETCONFIG;
+unsigned IOCTL_HDAUDIO_FGRP_SETCONFIG = HDAUDIO_FGRP_SETCONFIG;
+unsigned IOCTL_HDAUDIO_FGRP_WIDGET_INFO = HDAUDIO_FGRP_WIDGET_INFO;
+unsigned IOCTL_HDAUDIO_FGRP_CODEC_INFO = HDAUDIO_FGRP_CODEC_INFO;
+unsigned IOCTL_HDAUDIO_AFG_WIDGET_INFO = HDAUDIO_AFG_WIDGET_INFO;
+unsigned IOCTL_HDAUDIO_AFG_CODEC_INFO = HDAUDIO_AFG_CODEC_INFO;
+unsigned IOCTL_CEC_GET_PHYS_ADDR = CEC_GET_PHYS_ADDR;
+unsigned IOCTL_CEC_GET_LOG_ADDRS = CEC_GET_LOG_ADDRS;
+unsigned IOCTL_CEC_SET_LOG_ADDRS = CEC_SET_LOG_ADDRS;
+unsigned IOCTL_CEC_GET_VENDOR_ID = CEC_GET_VENDOR_ID;
+unsigned IOCTL_HPCFBIO_GCONF = HPCFBIO_GCONF;
+unsigned IOCTL_HPCFBIO_SCONF = HPCFBIO_SCONF;
+unsigned IOCTL_HPCFBIO_GDSPCONF = HPCFBIO_GDSPCONF;
+unsigned IOCTL_HPCFBIO_SDSPCONF = HPCFBIO_SDSPCONF;
+unsigned IOCTL_HPCFBIO_GOP = HPCFBIO_GOP;
+unsigned IOCTL_HPCFBIO_SOP = HPCFBIO_SOP;
+unsigned IOCTL_IOPIOCPT = IOPIOCPT;
+unsigned IOCTL_IOPIOCGLCT = IOPIOCGLCT;
+unsigned IOCTL_IOPIOCGSTATUS = IOPIOCGSTATUS;
+unsigned IOCTL_IOPIOCRECONFIG = IOPIOCRECONFIG;
+unsigned IOCTL_IOPIOCGTIDMAP = IOPIOCGTIDMAP;
+unsigned IOCTL_SIOCGATHSTATS = SIOCGATHSTATS;
+unsigned IOCTL_SIOCGATHDIAG = SIOCGATHDIAG;
+unsigned IOCTL_METEORCAPTUR = METEORCAPTUR;
+unsigned IOCTL_METEORCAPFRM = METEORCAPFRM;
+unsigned IOCTL_METEORSETGEO = METEORSETGEO;
+unsigned IOCTL_METEORGETGEO = METEORGETGEO;
+unsigned IOCTL_METEORSTATUS = METEORSTATUS;
+unsigned IOCTL_METEORSHUE = METEORSHUE;
+unsigned IOCTL_METEORGHUE = METEORGHUE;
+unsigned IOCTL_METEORSFMT = METEORSFMT;
+unsigned IOCTL_METEORGFMT = METEORGFMT;
+unsigned IOCTL_METEORSINPUT = METEORSINPUT;
+unsigned IOCTL_METEORGINPUT = METEORGINPUT;
+unsigned IOCTL_METEORSCHCV = METEORSCHCV;
+unsigned IOCTL_METEORGCHCV = METEORGCHCV;
+unsigned IOCTL_METEORSCOUNT = METEORSCOUNT;
+unsigned IOCTL_METEORGCOUNT = METEORGCOUNT;
+unsigned IOCTL_METEORSFPS = METEORSFPS;
+unsigned IOCTL_METEORGFPS = METEORGFPS;
+unsigned IOCTL_METEORSSIGNAL = METEORSSIGNAL;
+unsigned IOCTL_METEORGSIGNAL = METEORGSIGNAL;
+unsigned IOCTL_METEORSVIDEO = METEORSVIDEO;
+unsigned IOCTL_METEORGVIDEO = METEORGVIDEO;
+unsigned IOCTL_METEORSBRIG = METEORSBRIG;
+unsigned IOCTL_METEORGBRIG = METEORGBRIG;
+unsigned IOCTL_METEORSCSAT = METEORSCSAT;
+unsigned IOCTL_METEORGCSAT = METEORGCSAT;
+unsigned IOCTL_METEORSCONT = METEORSCONT;
+unsigned IOCTL_METEORGCONT = METEORGCONT;
+unsigned IOCTL_METEORSHWS = METEORSHWS;
+unsigned IOCTL_METEORGHWS = METEORGHWS;
+unsigned IOCTL_METEORSVWS = METEORSVWS;
+unsigned IOCTL_METEORGVWS = METEORGVWS;
+unsigned IOCTL_METEORSTS = METEORSTS;
+unsigned IOCTL_METEORGTS = METEORGTS;
+unsigned IOCTL_TVTUNER_SETCHNL = TVTUNER_SETCHNL;
+unsigned IOCTL_TVTUNER_GETCHNL = TVTUNER_GETCHNL;
+unsigned IOCTL_TVTUNER_SETTYPE = TVTUNER_SETTYPE;
+unsigned IOCTL_TVTUNER_GETTYPE = TVTUNER_GETTYPE;
+unsigned IOCTL_TVTUNER_GETSTATUS = TVTUNER_GETSTATUS;
+unsigned IOCTL_TVTUNER_SETFREQ = TVTUNER_SETFREQ;
+unsigned IOCTL_TVTUNER_GETFREQ = TVTUNER_GETFREQ;
+unsigned IOCTL_TVTUNER_SETAFC = TVTUNER_SETAFC;
+unsigned IOCTL_TVTUNER_GETAFC = TVTUNER_GETAFC;
+unsigned IOCTL_RADIO_SETMODE = RADIO_SETMODE;
+unsigned IOCTL_RADIO_GETMODE = RADIO_GETMODE;
+unsigned IOCTL_RADIO_SETFREQ = RADIO_SETFREQ;
+unsigned IOCTL_RADIO_GETFREQ = RADIO_GETFREQ;
+unsigned IOCTL_METEORSACTPIXFMT = METEORSACTPIXFMT;
+unsigned IOCTL_METEORGACTPIXFMT = METEORGACTPIXFMT;
+unsigned IOCTL_METEORGSUPPIXFMT = METEORGSUPPIXFMT;
+unsigned IOCTL_TVTUNER_GETCHNLSET = TVTUNER_GETCHNLSET;
+unsigned IOCTL_REMOTE_GETKEY = REMOTE_GETKEY;
+unsigned IOCTL_GDT_IOCTL_GENERAL = GDT_IOCTL_GENERAL;
+unsigned IOCTL_GDT_IOCTL_DRVERS = GDT_IOCTL_DRVERS;
+unsigned IOCTL_GDT_IOCTL_CTRTYPE = GDT_IOCTL_CTRTYPE;
+unsigned IOCTL_GDT_IOCTL_OSVERS = GDT_IOCTL_OSVERS;
+unsigned IOCTL_GDT_IOCTL_CTRCNT = GDT_IOCTL_CTRCNT;
+unsigned IOCTL_GDT_IOCTL_EVENT = GDT_IOCTL_EVENT;
+unsigned IOCTL_GDT_IOCTL_STATIST = GDT_IOCTL_STATIST;
+unsigned IOCTL_GDT_IOCTL_RESCAN = GDT_IOCTL_RESCAN;
+unsigned IOCTL_ISP_SDBLEV = ISP_SDBLEV;
+unsigned IOCTL_ISP_RESETHBA = ISP_RESETHBA;
+unsigned IOCTL_ISP_RESCAN = ISP_RESCAN;
+unsigned IOCTL_ISP_SETROLE = ISP_SETROLE;
+unsigned IOCTL_ISP_GETROLE = ISP_GETROLE;
+unsigned IOCTL_ISP_GET_STATS = ISP_GET_STATS;
+unsigned IOCTL_ISP_CLR_STATS = ISP_CLR_STATS;
+unsigned IOCTL_ISP_FC_LIP = ISP_FC_LIP;
+unsigned IOCTL_ISP_FC_GETDINFO = ISP_FC_GETDINFO;
+unsigned IOCTL_ISP_GET_FW_CRASH_DUMP = ISP_GET_FW_CRASH_DUMP;
+unsigned IOCTL_ISP_FORCE_CRASH_DUMP = ISP_FORCE_CRASH_DUMP;
+unsigned IOCTL_ISP_FC_GETHINFO = ISP_FC_GETHINFO;
+unsigned IOCTL_ISP_TSK_MGMT = ISP_TSK_MGMT;
+unsigned IOCTL_ISP_FC_GETDLIST = ISP_FC_GETDLIST;
+unsigned IOCTL_MLXD_STATUS = MLXD_STATUS;
+unsigned IOCTL_MLXD_CHECKASYNC = MLXD_CHECKASYNC;
+unsigned IOCTL_MLXD_DETACH = MLXD_DETACH;
+unsigned IOCTL_MLX_RESCAN_DRIVES = MLX_RESCAN_DRIVES;
+unsigned IOCTL_MLX_PAUSE_CHANNEL = MLX_PAUSE_CHANNEL;
+unsigned IOCTL_MLX_COMMAND = MLX_COMMAND;
+unsigned IOCTL_MLX_REBUILDASYNC = MLX_REBUILDASYNC;
+unsigned IOCTL_MLX_REBUILDSTAT = MLX_REBUILDSTAT;
+unsigned IOCTL_MLX_GET_SYSDRIVE = MLX_GET_SYSDRIVE;
+unsigned IOCTL_MLX_GET_CINFO = MLX_GET_CINFO;
+unsigned IOCTL_NVME_PASSTHROUGH_CMD = NVME_PASSTHROUGH_CMD;
+unsigned IOCTL_FWCFGIO_SET_INDEX = FWCFGIO_SET_INDEX;
+unsigned IOCTL_IRDA_RESET_PARAMS = IRDA_RESET_PARAMS;
+unsigned IOCTL_IRDA_SET_PARAMS = IRDA_SET_PARAMS;
+unsigned IOCTL_IRDA_GET_SPEEDMASK = IRDA_GET_SPEEDMASK;
+unsigned IOCTL_IRDA_GET_TURNAROUNDMASK = IRDA_GET_TURNAROUNDMASK;
+unsigned IOCTL_IRFRAMETTY_GET_DEVICE = IRFRAMETTY_GET_DEVICE;
+unsigned IOCTL_IRFRAMETTY_GET_DONGLE = IRFRAMETTY_GET_DONGLE;
+unsigned IOCTL_IRFRAMETTY_SET_DONGLE = IRFRAMETTY_SET_DONGLE;
+unsigned IOCTL_ISV_CMD = ISV_CMD;
+unsigned IOCTL_WTQICMD = WTQICMD;
+unsigned IOCTL_ISCSI_GET_VERSION = ISCSI_GET_VERSION;
+unsigned IOCTL_ISCSI_LOGIN = ISCSI_LOGIN;
+unsigned IOCTL_ISCSI_LOGOUT = ISCSI_LOGOUT;
+unsigned IOCTL_ISCSI_ADD_CONNECTION = ISCSI_ADD_CONNECTION;
+unsigned IOCTL_ISCSI_RESTORE_CONNECTION = ISCSI_RESTORE_CONNECTION;
+unsigned IOCTL_ISCSI_REMOVE_CONNECTION = ISCSI_REMOVE_CONNECTION;
+unsigned IOCTL_ISCSI_CONNECTION_STATUS = ISCSI_CONNECTION_STATUS;
+unsigned IOCTL_ISCSI_SEND_TARGETS = ISCSI_SEND_TARGETS;
+unsigned IOCTL_ISCSI_SET_NODE_NAME = ISCSI_SET_NODE_NAME;
+unsigned IOCTL_ISCSI_IO_COMMAND = ISCSI_IO_COMMAND;
+unsigned IOCTL_ISCSI_REGISTER_EVENT = ISCSI_REGISTER_EVENT;
+unsigned IOCTL_ISCSI_DEREGISTER_EVENT = ISCSI_DEREGISTER_EVENT;
+unsigned IOCTL_ISCSI_WAIT_EVENT = ISCSI_WAIT_EVENT;
+unsigned IOCTL_ISCSI_POLL_EVENT = ISCSI_POLL_EVENT;
+unsigned IOCTL_OFIOCGET = OFIOCGET;
+unsigned IOCTL_OFIOCSET = OFIOCSET;
+unsigned IOCTL_OFIOCNEXTPROP = OFIOCNEXTPROP;
+unsigned IOCTL_OFIOCGETOPTNODE = OFIOCGETOPTNODE;
+unsigned IOCTL_OFIOCGETNEXT = OFIOCGETNEXT;
+unsigned IOCTL_OFIOCGETCHILD = OFIOCGETCHILD;
+unsigned IOCTL_OFIOCFINDDEVICE = OFIOCFINDDEVICE;
+unsigned IOCTL_AMR_IO_VERSION = AMR_IO_VERSION;
+unsigned IOCTL_AMR_IO_COMMAND = AMR_IO_COMMAND;
+unsigned IOCTL_MLYIO_COMMAND = MLYIO_COMMAND;
+unsigned IOCTL_MLYIO_HEALTH = MLYIO_HEALTH;
+unsigned IOCTL_PCI_IOC_CFGREAD = PCI_IOC_CFGREAD;
+unsigned IOCTL_PCI_IOC_CFGWRITE = PCI_IOC_CFGWRITE;
+unsigned IOCTL_PCI_IOC_BDF_CFGREAD = PCI_IOC_BDF_CFGREAD;
+unsigned IOCTL_PCI_IOC_BDF_CFGWRITE = PCI_IOC_BDF_CFGWRITE;
+unsigned IOCTL_PCI_IOC_BUSINFO = PCI_IOC_BUSINFO;
+unsigned IOCTL_PCI_IOC_DRVNAME = PCI_IOC_DRVNAME;
+unsigned IOCTL_PCI_IOC_DRVNAMEONBUS = PCI_IOC_DRVNAMEONBUS;
+unsigned IOCTL_TWEIO_COMMAND = TWEIO_COMMAND;
+unsigned IOCTL_TWEIO_STATS = TWEIO_STATS;
+unsigned IOCTL_TWEIO_AEN_POLL = TWEIO_AEN_POLL;
+unsigned IOCTL_TWEIO_AEN_WAIT = TWEIO_AEN_WAIT;
+unsigned IOCTL_TWEIO_SET_PARAM = TWEIO_SET_PARAM;
+unsigned IOCTL_TWEIO_GET_PARAM = TWEIO_GET_PARAM;
+unsigned IOCTL_TWEIO_RESET = TWEIO_RESET;
+unsigned IOCTL_TWEIO_ADD_UNIT = TWEIO_ADD_UNIT;
+unsigned IOCTL_TWEIO_DEL_UNIT = TWEIO_DEL_UNIT;
+unsigned IOCTL_SIOCSCNWDOMAIN = SIOCSCNWDOMAIN;
+unsigned IOCTL_SIOCGCNWDOMAIN = SIOCGCNWDOMAIN;
+unsigned IOCTL_SIOCSCNWKEY = SIOCSCNWKEY;
+unsigned IOCTL_SIOCGCNWSTATUS = SIOCGCNWSTATUS;
+unsigned IOCTL_SIOCGCNWSTATS = SIOCGCNWSTATS;
+unsigned IOCTL_SIOCGCNWTRAIL = SIOCGCNWTRAIL;
+unsigned IOCTL_SIOCGRAYSIGLEV = SIOCGRAYSIGLEV;
+unsigned IOCTL_RAIDFRAME_SHUTDOWN = RAIDFRAME_SHUTDOWN;
+unsigned IOCTL_RAIDFRAME_TUR = RAIDFRAME_TUR;
+unsigned IOCTL_RAIDFRAME_FAIL_DISK = RAIDFRAME_FAIL_DISK;
+unsigned IOCTL_RAIDFRAME_CHECK_RECON_STATUS = RAIDFRAME_CHECK_RECON_STATUS;
+unsigned IOCTL_RAIDFRAME_REWRITEPARITY = RAIDFRAME_REWRITEPARITY;
+unsigned IOCTL_RAIDFRAME_COPYBACK = RAIDFRAME_COPYBACK;
+unsigned IOCTL_RAIDFRAME_SPARET_WAIT = RAIDFRAME_SPARET_WAIT;
+unsigned IOCTL_RAIDFRAME_SEND_SPARET = RAIDFRAME_SEND_SPARET;
+unsigned IOCTL_RAIDFRAME_ABORT_SPARET_WAIT = RAIDFRAME_ABORT_SPARET_WAIT;
+unsigned IOCTL_RAIDFRAME_START_ATRACE = RAIDFRAME_START_ATRACE;
+unsigned IOCTL_RAIDFRAME_STOP_ATRACE = RAIDFRAME_STOP_ATRACE;
+unsigned IOCTL_RAIDFRAME_GET_SIZE = RAIDFRAME_GET_SIZE;
+unsigned IOCTL_RAIDFRAME_RESET_ACCTOTALS = RAIDFRAME_RESET_ACCTOTALS;
+unsigned IOCTL_RAIDFRAME_KEEP_ACCTOTALS = RAIDFRAME_KEEP_ACCTOTALS;
+unsigned IOCTL_RAIDFRAME_GET_COMPONENT_LABEL = RAIDFRAME_GET_COMPONENT_LABEL;
+unsigned IOCTL_RAIDFRAME_SET_COMPONENT_LABEL = RAIDFRAME_SET_COMPONENT_LABEL;
+unsigned IOCTL_RAIDFRAME_INIT_LABELS = RAIDFRAME_INIT_LABELS;
+unsigned IOCTL_RAIDFRAME_ADD_HOT_SPARE = RAIDFRAME_ADD_HOT_SPARE;
+unsigned IOCTL_RAIDFRAME_REMOVE_HOT_SPARE = RAIDFRAME_REMOVE_HOT_SPARE;
+unsigned IOCTL_RAIDFRAME_REBUILD_IN_PLACE = RAIDFRAME_REBUILD_IN_PLACE;
+unsigned IOCTL_RAIDFRAME_CHECK_PARITY = RAIDFRAME_CHECK_PARITY;
+unsigned IOCTL_RAIDFRAME_CHECK_PARITYREWRITE_STATUS =
+ RAIDFRAME_CHECK_PARITYREWRITE_STATUS;
+unsigned IOCTL_RAIDFRAME_CHECK_COPYBACK_STATUS =
+ RAIDFRAME_CHECK_COPYBACK_STATUS;
+unsigned IOCTL_RAIDFRAME_SET_AUTOCONFIG = RAIDFRAME_SET_AUTOCONFIG;
+unsigned IOCTL_RAIDFRAME_SET_ROOT = RAIDFRAME_SET_ROOT;
+unsigned IOCTL_RAIDFRAME_DELETE_COMPONENT = RAIDFRAME_DELETE_COMPONENT;
+unsigned IOCTL_RAIDFRAME_INCORPORATE_HOT_SPARE =
+ RAIDFRAME_INCORPORATE_HOT_SPARE;
+unsigned IOCTL_RAIDFRAME_CHECK_RECON_STATUS_EXT =
+ RAIDFRAME_CHECK_RECON_STATUS_EXT;
+unsigned IOCTL_RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT =
+ RAIDFRAME_CHECK_PARITYREWRITE_STATUS_EXT;
+unsigned IOCTL_RAIDFRAME_CHECK_COPYBACK_STATUS_EXT =
+ RAIDFRAME_CHECK_COPYBACK_STATUS_EXT;
+unsigned IOCTL_RAIDFRAME_CONFIGURE = RAIDFRAME_CONFIGURE;
+unsigned IOCTL_RAIDFRAME_GET_INFO = RAIDFRAME_GET_INFO;
+unsigned IOCTL_RAIDFRAME_PARITYMAP_STATUS = RAIDFRAME_PARITYMAP_STATUS;
+unsigned IOCTL_RAIDFRAME_PARITYMAP_GET_DISABLE =
+ RAIDFRAME_PARITYMAP_GET_DISABLE;
+unsigned IOCTL_RAIDFRAME_PARITYMAP_SET_DISABLE =
+ RAIDFRAME_PARITYMAP_SET_DISABLE;
+unsigned IOCTL_RAIDFRAME_PARITYMAP_SET_PARAMS = RAIDFRAME_PARITYMAP_SET_PARAMS;
+unsigned IOCTL_RAIDFRAME_SET_LAST_UNIT = RAIDFRAME_SET_LAST_UNIT;
+unsigned IOCTL_MBPPIOCSPARAM = MBPPIOCSPARAM;
+unsigned IOCTL_MBPPIOCGPARAM = MBPPIOCGPARAM;
+unsigned IOCTL_MBPPIOCGSTAT = MBPPIOCGSTAT;
+unsigned IOCTL_SESIOC_GETNOBJ = SESIOC_GETNOBJ;
+unsigned IOCTL_SESIOC_GETOBJMAP = SESIOC_GETOBJMAP;
+unsigned IOCTL_SESIOC_GETENCSTAT = SESIOC_GETENCSTAT;
+unsigned IOCTL_SESIOC_SETENCSTAT = SESIOC_SETENCSTAT;
+unsigned IOCTL_SESIOC_GETOBJSTAT = SESIOC_GETOBJSTAT;
+unsigned IOCTL_SESIOC_SETOBJSTAT = SESIOC_SETOBJSTAT;
+unsigned IOCTL_SESIOC_GETTEXT = SESIOC_GETTEXT;
+unsigned IOCTL_SESIOC_INIT = SESIOC_INIT;
+unsigned IOCTL_SUN_DKIOCGGEOM = SUN_DKIOCGGEOM;
+unsigned IOCTL_SUN_DKIOCINFO = SUN_DKIOCINFO;
+unsigned IOCTL_SUN_DKIOCGPART = SUN_DKIOCGPART;
+unsigned IOCTL_FBIOGTYPE = FBIOGTYPE;
+unsigned IOCTL_FBIOPUTCMAP = FBIOPUTCMAP;
+unsigned IOCTL_FBIOGETCMAP = FBIOGETCMAP;
+unsigned IOCTL_FBIOGATTR = FBIOGATTR;
+unsigned IOCTL_FBIOSVIDEO = FBIOSVIDEO;
+unsigned IOCTL_FBIOGVIDEO = FBIOGVIDEO;
+unsigned IOCTL_FBIOSCURSOR = FBIOSCURSOR;
+unsigned IOCTL_FBIOGCURSOR = FBIOGCURSOR;
+unsigned IOCTL_FBIOSCURPOS = FBIOSCURPOS;
+unsigned IOCTL_FBIOGCURPOS = FBIOGCURPOS;
+unsigned IOCTL_FBIOGCURMAX = FBIOGCURMAX;
+unsigned IOCTL_KIOCTRANS = KIOCTRANS;
+unsigned IOCTL_KIOCSETKEY = KIOCSETKEY;
+unsigned IOCTL_KIOCGETKEY = KIOCGETKEY;
+unsigned IOCTL_KIOCGTRANS = KIOCGTRANS;
+unsigned IOCTL_KIOCCMD = KIOCCMD;
+unsigned IOCTL_KIOCTYPE = KIOCTYPE;
+unsigned IOCTL_KIOCSDIRECT = KIOCSDIRECT;
+unsigned IOCTL_KIOCSKEY = KIOCSKEY;
+unsigned IOCTL_KIOCGKEY = KIOCGKEY;
+unsigned IOCTL_KIOCSLED = KIOCSLED;
+unsigned IOCTL_KIOCGLED = KIOCGLED;
+unsigned IOCTL_KIOCLAYOUT = KIOCLAYOUT;
+unsigned IOCTL_VUIDSFORMAT = VUIDSFORMAT;
+unsigned IOCTL_VUIDGFORMAT = VUIDGFORMAT;
+unsigned IOCTL_STICIO_GXINFO = STICIO_GXINFO;
+unsigned IOCTL_STICIO_RESET = STICIO_RESET;
+unsigned IOCTL_STICIO_STARTQ = STICIO_STARTQ;
+unsigned IOCTL_STICIO_STOPQ = STICIO_STOPQ;
+unsigned IOCTL_UKYOPON_IDENTIFY = UKYOPON_IDENTIFY;
+unsigned IOCTL_URIO_SEND_COMMAND = URIO_SEND_COMMAND;
+unsigned IOCTL_URIO_RECV_COMMAND = URIO_RECV_COMMAND;
+unsigned IOCTL_USB_REQUEST = USB_REQUEST;
+unsigned IOCTL_USB_SETDEBUG = USB_SETDEBUG;
+unsigned IOCTL_USB_DISCOVER = USB_DISCOVER;
+unsigned IOCTL_USB_DEVICEINFO = USB_DEVICEINFO;
+unsigned IOCTL_USB_DEVICEINFO_OLD = USB_DEVICEINFO_OLD;
+unsigned IOCTL_USB_DEVICESTATS = USB_DEVICESTATS;
+unsigned IOCTL_USB_GET_REPORT_DESC = USB_GET_REPORT_DESC;
+unsigned IOCTL_USB_SET_IMMED = USB_SET_IMMED;
+unsigned IOCTL_USB_GET_REPORT = USB_GET_REPORT;
+unsigned IOCTL_USB_SET_REPORT = USB_SET_REPORT;
+unsigned IOCTL_USB_GET_REPORT_ID = USB_GET_REPORT_ID;
+unsigned IOCTL_USB_GET_CONFIG = USB_GET_CONFIG;
+unsigned IOCTL_USB_SET_CONFIG = USB_SET_CONFIG;
+unsigned IOCTL_USB_GET_ALTINTERFACE = USB_GET_ALTINTERFACE;
+unsigned IOCTL_USB_SET_ALTINTERFACE = USB_SET_ALTINTERFACE;
+unsigned IOCTL_USB_GET_NO_ALT = USB_GET_NO_ALT;
+unsigned IOCTL_USB_GET_DEVICE_DESC = USB_GET_DEVICE_DESC;
+unsigned IOCTL_USB_GET_CONFIG_DESC = USB_GET_CONFIG_DESC;
+unsigned IOCTL_USB_GET_INTERFACE_DESC = USB_GET_INTERFACE_DESC;
+unsigned IOCTL_USB_GET_ENDPOINT_DESC = USB_GET_ENDPOINT_DESC;
+unsigned IOCTL_USB_GET_FULL_DESC = USB_GET_FULL_DESC;
+unsigned IOCTL_USB_GET_STRING_DESC = USB_GET_STRING_DESC;
+unsigned IOCTL_USB_DO_REQUEST = USB_DO_REQUEST;
+unsigned IOCTL_USB_GET_DEVICEINFO = USB_GET_DEVICEINFO;
+unsigned IOCTL_USB_GET_DEVICEINFO_OLD = USB_GET_DEVICEINFO_OLD;
+unsigned IOCTL_USB_SET_SHORT_XFER = USB_SET_SHORT_XFER;
+unsigned IOCTL_USB_SET_TIMEOUT = USB_SET_TIMEOUT;
+unsigned IOCTL_USB_SET_BULK_RA = USB_SET_BULK_RA;
+unsigned IOCTL_USB_SET_BULK_WB = USB_SET_BULK_WB;
+unsigned IOCTL_USB_SET_BULK_RA_OPT = USB_SET_BULK_RA_OPT;
+unsigned IOCTL_USB_SET_BULK_WB_OPT = USB_SET_BULK_WB_OPT;
+unsigned IOCTL_USB_GET_CM_OVER_DATA = USB_GET_CM_OVER_DATA;
+unsigned IOCTL_USB_SET_CM_OVER_DATA = USB_SET_CM_OVER_DATA;
+unsigned IOCTL_UTOPPYIOTURBO = UTOPPYIOTURBO;
+unsigned IOCTL_UTOPPYIOCANCEL = UTOPPYIOCANCEL;
+unsigned IOCTL_UTOPPYIOREBOOT = UTOPPYIOREBOOT;
+unsigned IOCTL_UTOPPYIOSTATS = UTOPPYIOSTATS;
+unsigned IOCTL_UTOPPYIORENAME = UTOPPYIORENAME;
+unsigned IOCTL_UTOPPYIOMKDIR = UTOPPYIOMKDIR;
+unsigned IOCTL_UTOPPYIODELETE = UTOPPYIODELETE;
+unsigned IOCTL_UTOPPYIOREADDIR = UTOPPYIOREADDIR;
+unsigned IOCTL_UTOPPYIOREADFILE = UTOPPYIOREADFILE;
+unsigned IOCTL_UTOPPYIOWRITEFILE = UTOPPYIOWRITEFILE;
+unsigned IOCTL_DIOSXDCMD = DIOSXDCMD;
+unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
+unsigned IOCTL_VT_SETMODE = VT_SETMODE;
+unsigned IOCTL_VT_GETMODE = VT_GETMODE;
+unsigned IOCTL_VT_RELDISP = VT_RELDISP;
+unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
+unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
+unsigned IOCTL_VT_GETACTIVE = VT_GETACTIVE;
+unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
+unsigned IOCTL_KDGETKBENT = KDGETKBENT;
+unsigned IOCTL_KDGKBMODE = KDGKBMODE;
+unsigned IOCTL_KDSKBMODE = KDSKBMODE;
+unsigned IOCTL_KDMKTONE = KDMKTONE;
+unsigned IOCTL_KDSETMODE = KDSETMODE;
+unsigned IOCTL_KDENABIO = KDENABIO;
+unsigned IOCTL_KDDISABIO = KDDISABIO;
+unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
+unsigned IOCTL_KDGETLED = KDGETLED;
+unsigned IOCTL_KDSETLED = KDSETLED;
+unsigned IOCTL_KDSETRAD = KDSETRAD;
+unsigned IOCTL_VGAPCVTID = VGAPCVTID;
+unsigned IOCTL_CONS_GETVERS = CONS_GETVERS;
+unsigned IOCTL_WSKBDIO_GTYPE = WSKBDIO_GTYPE;
+unsigned IOCTL_WSKBDIO_BELL = WSKBDIO_BELL;
+unsigned IOCTL_WSKBDIO_COMPLEXBELL = WSKBDIO_COMPLEXBELL;
+unsigned IOCTL_WSKBDIO_SETBELL = WSKBDIO_SETBELL;
+unsigned IOCTL_WSKBDIO_GETBELL = WSKBDIO_GETBELL;
+unsigned IOCTL_WSKBDIO_SETDEFAULTBELL = WSKBDIO_SETDEFAULTBELL;
+unsigned IOCTL_WSKBDIO_GETDEFAULTBELL = WSKBDIO_GETDEFAULTBELL;
+unsigned IOCTL_WSKBDIO_SETKEYREPEAT = WSKBDIO_SETKEYREPEAT;
+unsigned IOCTL_WSKBDIO_GETKEYREPEAT = WSKBDIO_GETKEYREPEAT;
+unsigned IOCTL_WSKBDIO_SETDEFAULTKEYREPEAT = WSKBDIO_SETDEFAULTKEYREPEAT;
+unsigned IOCTL_WSKBDIO_GETDEFAULTKEYREPEAT = WSKBDIO_GETDEFAULTKEYREPEAT;
+unsigned IOCTL_WSKBDIO_SETLEDS = WSKBDIO_SETLEDS;
+unsigned IOCTL_WSKBDIO_GETLEDS = WSKBDIO_GETLEDS;
+unsigned IOCTL_WSKBDIO_GETMAP = WSKBDIO_GETMAP;
+unsigned IOCTL_WSKBDIO_SETMAP = WSKBDIO_SETMAP;
+unsigned IOCTL_WSKBDIO_GETENCODING = WSKBDIO_GETENCODING;
+unsigned IOCTL_WSKBDIO_SETENCODING = WSKBDIO_SETENCODING;
+unsigned IOCTL_WSKBDIO_SETMODE = WSKBDIO_SETMODE;
+unsigned IOCTL_WSKBDIO_GETMODE = WSKBDIO_GETMODE;
+unsigned IOCTL_WSKBDIO_SETKEYCLICK = WSKBDIO_SETKEYCLICK;
+unsigned IOCTL_WSKBDIO_GETKEYCLICK = WSKBDIO_GETKEYCLICK;
+unsigned IOCTL_WSKBDIO_GETSCROLL = WSKBDIO_GETSCROLL;
+unsigned IOCTL_WSKBDIO_SETSCROLL = WSKBDIO_SETSCROLL;
+unsigned IOCTL_WSKBDIO_SETVERSION = WSKBDIO_SETVERSION;
+unsigned IOCTL_WSMOUSEIO_GTYPE = WSMOUSEIO_GTYPE;
+unsigned IOCTL_WSMOUSEIO_SRES = WSMOUSEIO_SRES;
+unsigned IOCTL_WSMOUSEIO_SSCALE = WSMOUSEIO_SSCALE;
+unsigned IOCTL_WSMOUSEIO_SRATE = WSMOUSEIO_SRATE;
+unsigned IOCTL_WSMOUSEIO_SCALIBCOORDS = WSMOUSEIO_SCALIBCOORDS;
+unsigned IOCTL_WSMOUSEIO_GCALIBCOORDS = WSMOUSEIO_GCALIBCOORDS;
+unsigned IOCTL_WSMOUSEIO_GETID = WSMOUSEIO_GETID;
+unsigned IOCTL_WSMOUSEIO_GETREPEAT = WSMOUSEIO_GETREPEAT;
+unsigned IOCTL_WSMOUSEIO_SETREPEAT = WSMOUSEIO_SETREPEAT;
+unsigned IOCTL_WSMOUSEIO_SETVERSION = WSMOUSEIO_SETVERSION;
+unsigned IOCTL_WSDISPLAYIO_GTYPE = WSDISPLAYIO_GTYPE;
+unsigned IOCTL_WSDISPLAYIO_GINFO = WSDISPLAYIO_GINFO;
+unsigned IOCTL_WSDISPLAYIO_GETCMAP = WSDISPLAYIO_GETCMAP;
+unsigned IOCTL_WSDISPLAYIO_PUTCMAP = WSDISPLAYIO_PUTCMAP;
+unsigned IOCTL_WSDISPLAYIO_GVIDEO = WSDISPLAYIO_GVIDEO;
+unsigned IOCTL_WSDISPLAYIO_SVIDEO = WSDISPLAYIO_SVIDEO;
+unsigned IOCTL_WSDISPLAYIO_GCURPOS = WSDISPLAYIO_GCURPOS;
+unsigned IOCTL_WSDISPLAYIO_SCURPOS = WSDISPLAYIO_SCURPOS;
+unsigned IOCTL_WSDISPLAYIO_GCURMAX = WSDISPLAYIO_GCURMAX;
+unsigned IOCTL_WSDISPLAYIO_GCURSOR = WSDISPLAYIO_GCURSOR;
+unsigned IOCTL_WSDISPLAYIO_SCURSOR = WSDISPLAYIO_SCURSOR;
+unsigned IOCTL_WSDISPLAYIO_GMODE = WSDISPLAYIO_GMODE;
+unsigned IOCTL_WSDISPLAYIO_SMODE = WSDISPLAYIO_SMODE;
+unsigned IOCTL_WSDISPLAYIO_LDFONT = WSDISPLAYIO_LDFONT;
+unsigned IOCTL_WSDISPLAYIO_ADDSCREEN = WSDISPLAYIO_ADDSCREEN;
+unsigned IOCTL_WSDISPLAYIO_DELSCREEN = WSDISPLAYIO_DELSCREEN;
+unsigned IOCTL_WSDISPLAYIO_SFONT = WSDISPLAYIO_SFONT;
+unsigned IOCTL__O_WSDISPLAYIO_SETKEYBOARD = _O_WSDISPLAYIO_SETKEYBOARD;
+unsigned IOCTL_WSDISPLAYIO_GETPARAM = WSDISPLAYIO_GETPARAM;
+unsigned IOCTL_WSDISPLAYIO_SETPARAM = WSDISPLAYIO_SETPARAM;
+unsigned IOCTL_WSDISPLAYIO_GETACTIVESCREEN = WSDISPLAYIO_GETACTIVESCREEN;
+unsigned IOCTL_WSDISPLAYIO_GETWSCHAR = WSDISPLAYIO_GETWSCHAR;
+unsigned IOCTL_WSDISPLAYIO_PUTWSCHAR = WSDISPLAYIO_PUTWSCHAR;
+unsigned IOCTL_WSDISPLAYIO_DGSCROLL = WSDISPLAYIO_DGSCROLL;
+unsigned IOCTL_WSDISPLAYIO_DSSCROLL = WSDISPLAYIO_DSSCROLL;
+unsigned IOCTL_WSDISPLAYIO_GMSGATTRS = WSDISPLAYIO_GMSGATTRS;
+unsigned IOCTL_WSDISPLAYIO_SMSGATTRS = WSDISPLAYIO_SMSGATTRS;
+unsigned IOCTL_WSDISPLAYIO_GBORDER = WSDISPLAYIO_GBORDER;
+unsigned IOCTL_WSDISPLAYIO_SBORDER = WSDISPLAYIO_SBORDER;
+unsigned IOCTL_WSDISPLAYIO_SSPLASH = WSDISPLAYIO_SSPLASH;
+unsigned IOCTL_WSDISPLAYIO_SPROGRESS = WSDISPLAYIO_SPROGRESS;
+unsigned IOCTL_WSDISPLAYIO_LINEBYTES = WSDISPLAYIO_LINEBYTES;
+unsigned IOCTL_WSDISPLAYIO_SETVERSION = WSDISPLAYIO_SETVERSION;
+unsigned IOCTL_WSMUXIO_ADD_DEVICE = WSMUXIO_ADD_DEVICE;
+unsigned IOCTL_WSMUXIO_REMOVE_DEVICE = WSMUXIO_REMOVE_DEVICE;
+unsigned IOCTL_WSMUXIO_LIST_DEVICES = WSMUXIO_LIST_DEVICES;
+unsigned IOCTL_WSMUXIO_INJECTEVENT = WSMUXIO_INJECTEVENT;
+unsigned IOCTL_WSDISPLAYIO_GET_BUSID = WSDISPLAYIO_GET_BUSID;
+unsigned IOCTL_WSDISPLAYIO_GET_EDID = WSDISPLAYIO_GET_EDID;
+unsigned IOCTL_WSDISPLAYIO_SET_POLLING = WSDISPLAYIO_SET_POLLING;
+unsigned IOCTL_WSDISPLAYIO_GET_FBINFO = WSDISPLAYIO_GET_FBINFO;
+unsigned IOCTL_WSDISPLAYIO_DOBLIT = WSDISPLAYIO_DOBLIT;
+unsigned IOCTL_WSDISPLAYIO_WAITBLIT = WSDISPLAYIO_WAITBLIT;
+unsigned IOCTL_BIOCLOCATE = BIOCLOCATE;
+unsigned IOCTL_BIOCINQ = BIOCINQ;
+unsigned IOCTL_BIOCDISK_NOVOL = BIOCDISK_NOVOL;
+unsigned IOCTL_BIOCDISK = BIOCDISK;
+unsigned IOCTL_BIOCVOL = BIOCVOL;
+unsigned IOCTL_BIOCALARM = BIOCALARM;
+unsigned IOCTL_BIOCBLINK = BIOCBLINK;
+unsigned IOCTL_BIOCSETSTATE = BIOCSETSTATE;
+unsigned IOCTL_BIOCVOLOPS = BIOCVOLOPS;
+unsigned IOCTL_MD_GETCONF = MD_GETCONF;
+unsigned IOCTL_MD_SETCONF = MD_SETCONF;
+unsigned IOCTL_CCDIOCSET = CCDIOCSET;
+unsigned IOCTL_CCDIOCCLR = CCDIOCCLR;
+unsigned IOCTL_CGDIOCSET = CGDIOCSET;
+unsigned IOCTL_CGDIOCCLR = CGDIOCCLR;
+unsigned IOCTL_CGDIOCGET = CGDIOCGET;
+unsigned IOCTL_FSSIOCSET = FSSIOCSET;
+unsigned IOCTL_FSSIOCGET = FSSIOCGET;
+unsigned IOCTL_FSSIOCCLR = FSSIOCCLR;
+unsigned IOCTL_FSSIOFSET = FSSIOFSET;
+unsigned IOCTL_FSSIOFGET = FSSIOFGET;
+unsigned IOCTL_BTDEV_ATTACH = BTDEV_ATTACH;
+unsigned IOCTL_BTDEV_DETACH = BTDEV_DETACH;
+unsigned IOCTL_BTSCO_GETINFO = BTSCO_GETINFO;
+unsigned IOCTL_KTTCP_IO_SEND = KTTCP_IO_SEND;
+unsigned IOCTL_KTTCP_IO_RECV = KTTCP_IO_RECV;
+unsigned IOCTL_IOC_LOCKSTAT_GVERSION = IOC_LOCKSTAT_GVERSION;
+unsigned IOCTL_IOC_LOCKSTAT_ENABLE = IOC_LOCKSTAT_ENABLE;
+unsigned IOCTL_IOC_LOCKSTAT_DISABLE = IOC_LOCKSTAT_DISABLE;
+unsigned IOCTL_VNDIOCSET = VNDIOCSET;
+unsigned IOCTL_VNDIOCCLR = VNDIOCCLR;
+unsigned IOCTL_VNDIOCGET = VNDIOCGET;
+unsigned IOCTL_SPKRTONE = SPKRTONE;
+unsigned IOCTL_SPKRTUNE = SPKRTUNE;
+unsigned IOCTL_SPKRGETVOL = SPKRGETVOL;
+unsigned IOCTL_SPKRSETVOL = SPKRSETVOL;
+#if defined(__x86_64__)
+unsigned IOCTL_NVMM_IOC_CAPABILITY = NVMM_IOC_CAPABILITY;
+unsigned IOCTL_NVMM_IOC_MACHINE_CREATE = NVMM_IOC_MACHINE_CREATE;
+unsigned IOCTL_NVMM_IOC_MACHINE_DESTROY = NVMM_IOC_MACHINE_DESTROY;
+unsigned IOCTL_NVMM_IOC_MACHINE_CONFIGURE = NVMM_IOC_MACHINE_CONFIGURE;
+unsigned IOCTL_NVMM_IOC_VCPU_CREATE = NVMM_IOC_VCPU_CREATE;
+unsigned IOCTL_NVMM_IOC_VCPU_DESTROY = NVMM_IOC_VCPU_DESTROY;
+unsigned IOCTL_NVMM_IOC_VCPU_SETSTATE = NVMM_IOC_VCPU_SETSTATE;
+unsigned IOCTL_NVMM_IOC_VCPU_GETSTATE = NVMM_IOC_VCPU_GETSTATE;
+unsigned IOCTL_NVMM_IOC_VCPU_INJECT = NVMM_IOC_VCPU_INJECT;
+unsigned IOCTL_NVMM_IOC_VCPU_RUN = NVMM_IOC_VCPU_RUN;
+unsigned IOCTL_NVMM_IOC_GPA_MAP = NVMM_IOC_GPA_MAP;
+unsigned IOCTL_NVMM_IOC_GPA_UNMAP = NVMM_IOC_GPA_UNMAP;
+unsigned IOCTL_NVMM_IOC_HVA_MAP = NVMM_IOC_HVA_MAP;
+unsigned IOCTL_NVMM_IOC_HVA_UNMAP = NVMM_IOC_HVA_UNMAP;
+unsigned IOCTL_NVMM_IOC_CTL = NVMM_IOC_CTL;
+#endif
+unsigned IOCTL_SPI_IOCTL_CONFIGURE = SPI_IOCTL_CONFIGURE;
+unsigned IOCTL_SPI_IOCTL_TRANSFER = SPI_IOCTL_TRANSFER;
+unsigned IOCTL_AUTOFSREQUEST = AUTOFSREQUEST;
+unsigned IOCTL_AUTOFSDONE = AUTOFSDONE;
+unsigned IOCTL_BIOCGBLEN = BIOCGBLEN;
+unsigned IOCTL_BIOCSBLEN = BIOCSBLEN;
+unsigned IOCTL_BIOCSETF = BIOCSETF;
+unsigned IOCTL_BIOCFLUSH = BIOCFLUSH;
+unsigned IOCTL_BIOCPROMISC = BIOCPROMISC;
+unsigned IOCTL_BIOCGDLT = BIOCGDLT;
+unsigned IOCTL_BIOCGETIF = BIOCGETIF;
+unsigned IOCTL_BIOCSETIF = BIOCSETIF;
+unsigned IOCTL_BIOCGSTATS = BIOCGSTATS;
+unsigned IOCTL_BIOCGSTATSOLD = BIOCGSTATSOLD;
+unsigned IOCTL_BIOCIMMEDIATE = BIOCIMMEDIATE;
+unsigned IOCTL_BIOCVERSION = BIOCVERSION;
+unsigned IOCTL_BIOCSTCPF = BIOCSTCPF;
+unsigned IOCTL_BIOCSUDPF = BIOCSUDPF;
+unsigned IOCTL_BIOCGHDRCMPLT = BIOCGHDRCMPLT;
+unsigned IOCTL_BIOCSHDRCMPLT = BIOCSHDRCMPLT;
+unsigned IOCTL_BIOCSDLT = BIOCSDLT;
+unsigned IOCTL_BIOCGDLTLIST = BIOCGDLTLIST;
+unsigned IOCTL_BIOCGDIRECTION = BIOCGDIRECTION;
+unsigned IOCTL_BIOCSDIRECTION = BIOCSDIRECTION;
+unsigned IOCTL_BIOCSRTIMEOUT = BIOCSRTIMEOUT;
+unsigned IOCTL_BIOCGRTIMEOUT = BIOCGRTIMEOUT;
+unsigned IOCTL_BIOCGFEEDBACK = BIOCGFEEDBACK;
+unsigned IOCTL_BIOCSFEEDBACK = BIOCSFEEDBACK;
+unsigned IOCTL_GRESADDRS = GRESADDRS;
+unsigned IOCTL_GRESADDRD = GRESADDRD;
+unsigned IOCTL_GREGADDRS = GREGADDRS;
+unsigned IOCTL_GREGADDRD = GREGADDRD;
+unsigned IOCTL_GRESPROTO = GRESPROTO;
+unsigned IOCTL_GREGPROTO = GREGPROTO;
+unsigned IOCTL_GRESSOCK = GRESSOCK;
+unsigned IOCTL_GREDSOCK = GREDSOCK;
+unsigned IOCTL_PPPIOCGRAWIN = PPPIOCGRAWIN;
+unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS;
+unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS;
+unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP;
+unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP;
+unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT;
+unsigned IOCTL_PPPIOCGRASYNCMAP = PPPIOCGRASYNCMAP;
+unsigned IOCTL_PPPIOCSRASYNCMAP = PPPIOCSRASYNCMAP;
+unsigned IOCTL_PPPIOCGMRU = PPPIOCGMRU;
+unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU;
+unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID;
+unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP;
+unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP;
+unsigned IOCTL_PPPIOCXFERUNIT = PPPIOCXFERUNIT;
+unsigned IOCTL_PPPIOCSCOMPRESS = PPPIOCSCOMPRESS;
+unsigned IOCTL_PPPIOCGNPMODE = PPPIOCGNPMODE;
+unsigned IOCTL_PPPIOCSNPMODE = PPPIOCSNPMODE;
+unsigned IOCTL_PPPIOCGIDLE = PPPIOCGIDLE;
+unsigned IOCTL_PPPIOCGMTU = PPPIOCGMTU;
+unsigned IOCTL_PPPIOCSMTU = PPPIOCSMTU;
+unsigned IOCTL_SIOCGPPPSTATS = SIOCGPPPSTATS;
+unsigned IOCTL_SIOCGPPPCSTATS = SIOCGPPPCSTATS;
+unsigned IOCTL_IOC_NPF_VERSION = IOC_NPF_VERSION;
+unsigned IOCTL_IOC_NPF_SWITCH = IOC_NPF_SWITCH;
+unsigned IOCTL_IOC_NPF_LOAD = IOC_NPF_LOAD;
+unsigned IOCTL_IOC_NPF_TABLE = IOC_NPF_TABLE;
+unsigned IOCTL_IOC_NPF_STATS = IOC_NPF_STATS;
+unsigned IOCTL_IOC_NPF_SAVE = IOC_NPF_SAVE;
+unsigned IOCTL_IOC_NPF_RULE = IOC_NPF_RULE;
+unsigned IOCTL_IOC_NPF_CONN_LOOKUP = IOC_NPF_CONN_LOOKUP;
+unsigned IOCTL_PPPOESETPARMS = PPPOESETPARMS;
+unsigned IOCTL_PPPOEGETPARMS = PPPOEGETPARMS;
+unsigned IOCTL_PPPOEGETSESSION = PPPOEGETSESSION;
+unsigned IOCTL_SPPPGETAUTHCFG = SPPPGETAUTHCFG;
+unsigned IOCTL_SPPPSETAUTHCFG = SPPPSETAUTHCFG;
+unsigned IOCTL_SPPPGETLCPCFG = SPPPGETLCPCFG;
+unsigned IOCTL_SPPPSETLCPCFG = SPPPSETLCPCFG;
+unsigned IOCTL_SPPPGETSTATUS = SPPPGETSTATUS;
+unsigned IOCTL_SPPPGETSTATUSNCP = SPPPGETSTATUSNCP;
+unsigned IOCTL_SPPPGETIDLETO = SPPPGETIDLETO;
+unsigned IOCTL_SPPPSETIDLETO = SPPPSETIDLETO;
+unsigned IOCTL_SPPPGETAUTHFAILURES = SPPPGETAUTHFAILURES;
+unsigned IOCTL_SPPPSETAUTHFAILURE = SPPPSETAUTHFAILURE;
+unsigned IOCTL_SPPPSETDNSOPTS = SPPPSETDNSOPTS;
+unsigned IOCTL_SPPPGETDNSOPTS = SPPPGETDNSOPTS;
+unsigned IOCTL_SPPPGETDNSADDRS = SPPPGETDNSADDRS;
+unsigned IOCTL_SPPPSETKEEPALIVE = SPPPSETKEEPALIVE;
+unsigned IOCTL_SPPPGETKEEPALIVE = SPPPGETKEEPALIVE;
+unsigned IOCTL_SRT_GETNRT = SRT_GETNRT;
+unsigned IOCTL_SRT_GETRT = SRT_GETRT;
+unsigned IOCTL_SRT_SETRT = SRT_SETRT;
+unsigned IOCTL_SRT_DELRT = SRT_DELRT;
+unsigned IOCTL_SRT_SFLAGS = SRT_SFLAGS;
+unsigned IOCTL_SRT_GFLAGS = SRT_GFLAGS;
+unsigned IOCTL_SRT_SGFLAGS = SRT_SGFLAGS;
+unsigned IOCTL_SRT_DEBUG = SRT_DEBUG;
+unsigned IOCTL_TAPGIFNAME = TAPGIFNAME;
+unsigned IOCTL_TUNSDEBUG = TUNSDEBUG;
+unsigned IOCTL_TUNGDEBUG = TUNGDEBUG;
+unsigned IOCTL_TUNSIFMODE = TUNSIFMODE;
+unsigned IOCTL_TUNSLMODE = TUNSLMODE;
+unsigned IOCTL_TUNSIFHEAD = TUNSIFHEAD;
+unsigned IOCTL_TUNGIFHEAD = TUNGIFHEAD;
+unsigned IOCTL_DIOCSTART = DIOCSTART;
+unsigned IOCTL_DIOCSTOP = DIOCSTOP;
+unsigned IOCTL_DIOCADDRULE = DIOCADDRULE;
+unsigned IOCTL_DIOCGETRULES = DIOCGETRULES;
+unsigned IOCTL_DIOCGETRULE = DIOCGETRULE;
+unsigned IOCTL_DIOCSETLCK = DIOCSETLCK;
+unsigned IOCTL_DIOCCLRSTATES = DIOCCLRSTATES;
+unsigned IOCTL_DIOCGETSTATE = DIOCGETSTATE;
+unsigned IOCTL_DIOCSETSTATUSIF = DIOCSETSTATUSIF;
+unsigned IOCTL_DIOCGETSTATUS = DIOCGETSTATUS;
+unsigned IOCTL_DIOCCLRSTATUS = DIOCCLRSTATUS;
+unsigned IOCTL_DIOCNATLOOK = DIOCNATLOOK;
+unsigned IOCTL_DIOCSETDEBUG = DIOCSETDEBUG;
+unsigned IOCTL_DIOCGETSTATES = DIOCGETSTATES;
+unsigned IOCTL_DIOCCHANGERULE = DIOCCHANGERULE;
+unsigned IOCTL_DIOCSETTIMEOUT = DIOCSETTIMEOUT;
+unsigned IOCTL_DIOCGETTIMEOUT = DIOCGETTIMEOUT;
+unsigned IOCTL_DIOCADDSTATE = DIOCADDSTATE;
+unsigned IOCTL_DIOCCLRRULECTRS = DIOCCLRRULECTRS;
+unsigned IOCTL_DIOCGETLIMIT = DIOCGETLIMIT;
+unsigned IOCTL_DIOCSETLIMIT = DIOCSETLIMIT;
+unsigned IOCTL_DIOCKILLSTATES = DIOCKILLSTATES;
+unsigned IOCTL_DIOCSTARTALTQ = DIOCSTARTALTQ;
+unsigned IOCTL_DIOCSTOPALTQ = DIOCSTOPALTQ;
+unsigned IOCTL_DIOCADDALTQ = DIOCADDALTQ;
+unsigned IOCTL_DIOCGETALTQS = DIOCGETALTQS;
+unsigned IOCTL_DIOCGETALTQ = DIOCGETALTQ;
+unsigned IOCTL_DIOCCHANGEALTQ = DIOCCHANGEALTQ;
+unsigned IOCTL_DIOCGETQSTATS = DIOCGETQSTATS;
+unsigned IOCTL_DIOCBEGINADDRS = DIOCBEGINADDRS;
+unsigned IOCTL_DIOCADDADDR = DIOCADDADDR;
+unsigned IOCTL_DIOCGETADDRS = DIOCGETADDRS;
+unsigned IOCTL_DIOCGETADDR = DIOCGETADDR;
+unsigned IOCTL_DIOCCHANGEADDR = DIOCCHANGEADDR;
+unsigned IOCTL_DIOCADDSTATES = DIOCADDSTATES;
+unsigned IOCTL_DIOCGETRULESETS = DIOCGETRULESETS;
+unsigned IOCTL_DIOCGETRULESET = DIOCGETRULESET;
+unsigned IOCTL_DIOCRCLRTABLES = DIOCRCLRTABLES;
+unsigned IOCTL_DIOCRADDTABLES = DIOCRADDTABLES;
+unsigned IOCTL_DIOCRDELTABLES = DIOCRDELTABLES;
+unsigned IOCTL_DIOCRGETTABLES = DIOCRGETTABLES;
+unsigned IOCTL_DIOCRGETTSTATS = DIOCRGETTSTATS;
+unsigned IOCTL_DIOCRCLRTSTATS = DIOCRCLRTSTATS;
+unsigned IOCTL_DIOCRCLRADDRS = DIOCRCLRADDRS;
+unsigned IOCTL_DIOCRADDADDRS = DIOCRADDADDRS;
+unsigned IOCTL_DIOCRDELADDRS = DIOCRDELADDRS;
+unsigned IOCTL_DIOCRSETADDRS = DIOCRSETADDRS;
+unsigned IOCTL_DIOCRGETADDRS = DIOCRGETADDRS;
+unsigned IOCTL_DIOCRGETASTATS = DIOCRGETASTATS;
+unsigned IOCTL_DIOCRCLRASTATS = DIOCRCLRASTATS;
+unsigned IOCTL_DIOCRTSTADDRS = DIOCRTSTADDRS;
+unsigned IOCTL_DIOCRSETTFLAGS = DIOCRSETTFLAGS;
+unsigned IOCTL_DIOCRINADEFINE = DIOCRINADEFINE;
+unsigned IOCTL_DIOCOSFPFLUSH = DIOCOSFPFLUSH;
+unsigned IOCTL_DIOCOSFPADD = DIOCOSFPADD;
+unsigned IOCTL_DIOCOSFPGET = DIOCOSFPGET;
+unsigned IOCTL_DIOCXBEGIN = DIOCXBEGIN;
+unsigned IOCTL_DIOCXCOMMIT = DIOCXCOMMIT;
+unsigned IOCTL_DIOCXROLLBACK = DIOCXROLLBACK;
+unsigned IOCTL_DIOCGETSRCNODES = DIOCGETSRCNODES;
+unsigned IOCTL_DIOCCLRSRCNODES = DIOCCLRSRCNODES;
+unsigned IOCTL_DIOCSETHOSTID = DIOCSETHOSTID;
+unsigned IOCTL_DIOCIGETIFACES = DIOCIGETIFACES;
+unsigned IOCTL_DIOCSETIFFLAG = DIOCSETIFFLAG;
+unsigned IOCTL_DIOCCLRIFFLAG = DIOCCLRIFFLAG;
+unsigned IOCTL_DIOCKILLSRCNODES = DIOCKILLSRCNODES;
+unsigned IOCTL_SLIOCGUNIT = SLIOCGUNIT;
+unsigned IOCTL_SIOCGBTINFO = SIOCGBTINFO;
+unsigned IOCTL_SIOCGBTINFOA = SIOCGBTINFOA;
+unsigned IOCTL_SIOCNBTINFO = SIOCNBTINFO;
+unsigned IOCTL_SIOCSBTFLAGS = SIOCSBTFLAGS;
+unsigned IOCTL_SIOCSBTPOLICY = SIOCSBTPOLICY;
+unsigned IOCTL_SIOCSBTPTYPE = SIOCSBTPTYPE;
+unsigned IOCTL_SIOCGBTSTATS = SIOCGBTSTATS;
+unsigned IOCTL_SIOCZBTSTATS = SIOCZBTSTATS;
+unsigned IOCTL_SIOCBTDUMP = SIOCBTDUMP;
+unsigned IOCTL_SIOCSBTSCOMTU = SIOCSBTSCOMTU;
+unsigned IOCTL_SIOCGBTFEAT = SIOCGBTFEAT;
+unsigned IOCTL_SIOCADNAT = SIOCADNAT;
+unsigned IOCTL_SIOCRMNAT = SIOCRMNAT;
+unsigned IOCTL_SIOCGNATS = SIOCGNATS;
+unsigned IOCTL_SIOCGNATL = SIOCGNATL;
+unsigned IOCTL_SIOCPURGENAT = SIOCPURGENAT;
+unsigned IOCTL_SIOCCONNECTX = SIOCCONNECTX;
+unsigned IOCTL_SIOCCONNECTXDEL = SIOCCONNECTXDEL;
+unsigned IOCTL_SIOCSIFINFO_FLAGS = SIOCSIFINFO_FLAGS;
+unsigned IOCTL_SIOCAADDRCTL_POLICY = SIOCAADDRCTL_POLICY;
+unsigned IOCTL_SIOCDADDRCTL_POLICY = SIOCDADDRCTL_POLICY;
+unsigned IOCTL_SMBIOC_OPENSESSION = SMBIOC_OPENSESSION;
+unsigned IOCTL_SMBIOC_OPENSHARE = SMBIOC_OPENSHARE;
+unsigned IOCTL_SMBIOC_REQUEST = SMBIOC_REQUEST;
+unsigned IOCTL_SMBIOC_SETFLAGS = SMBIOC_SETFLAGS;
+unsigned IOCTL_SMBIOC_LOOKUP = SMBIOC_LOOKUP;
+unsigned IOCTL_SMBIOC_READ = SMBIOC_READ;
+unsigned IOCTL_SMBIOC_WRITE = SMBIOC_WRITE;
+unsigned IOCTL_AGPIOC_INFO = AGPIOC_INFO;
+unsigned IOCTL_AGPIOC_ACQUIRE = AGPIOC_ACQUIRE;
+unsigned IOCTL_AGPIOC_RELEASE = AGPIOC_RELEASE;
+unsigned IOCTL_AGPIOC_SETUP = AGPIOC_SETUP;
+unsigned IOCTL_AGPIOC_ALLOCATE = AGPIOC_ALLOCATE;
+unsigned IOCTL_AGPIOC_DEALLOCATE = AGPIOC_DEALLOCATE;
+unsigned IOCTL_AGPIOC_BIND = AGPIOC_BIND;
+unsigned IOCTL_AGPIOC_UNBIND = AGPIOC_UNBIND;
+unsigned IOCTL_AUDIO_GETINFO = AUDIO_GETINFO;
+unsigned IOCTL_AUDIO_SETINFO = AUDIO_SETINFO;
+unsigned IOCTL_AUDIO_DRAIN = AUDIO_DRAIN;
+unsigned IOCTL_AUDIO_FLUSH = AUDIO_FLUSH;
+unsigned IOCTL_AUDIO_WSEEK = AUDIO_WSEEK;
+unsigned IOCTL_AUDIO_RERROR = AUDIO_RERROR;
+unsigned IOCTL_AUDIO_GETDEV = AUDIO_GETDEV;
+unsigned IOCTL_AUDIO_GETENC = AUDIO_GETENC;
+unsigned IOCTL_AUDIO_GETFD = AUDIO_GETFD;
+unsigned IOCTL_AUDIO_SETFD = AUDIO_SETFD;
+unsigned IOCTL_AUDIO_PERROR = AUDIO_PERROR;
+unsigned IOCTL_AUDIO_GETIOFFS = AUDIO_GETIOFFS;
+unsigned IOCTL_AUDIO_GETOOFFS = AUDIO_GETOOFFS;
+unsigned IOCTL_AUDIO_GETPROPS = AUDIO_GETPROPS;
+unsigned IOCTL_AUDIO_GETBUFINFO = AUDIO_GETBUFINFO;
+unsigned IOCTL_AUDIO_SETCHAN = AUDIO_SETCHAN;
+unsigned IOCTL_AUDIO_GETCHAN = AUDIO_GETCHAN;
+unsigned IOCTL_AUDIO_QUERYFORMAT = AUDIO_QUERYFORMAT;
+unsigned IOCTL_AUDIO_GETFORMAT = AUDIO_GETFORMAT;
+unsigned IOCTL_AUDIO_SETFORMAT = AUDIO_SETFORMAT;
+unsigned IOCTL_AUDIO_MIXER_READ = AUDIO_MIXER_READ;
+unsigned IOCTL_AUDIO_MIXER_WRITE = AUDIO_MIXER_WRITE;
+unsigned IOCTL_AUDIO_MIXER_DEVINFO = AUDIO_MIXER_DEVINFO;
+unsigned IOCTL_ATAIOCCOMMAND = ATAIOCCOMMAND;
+unsigned IOCTL_ATABUSIOSCAN = ATABUSIOSCAN;
+unsigned IOCTL_ATABUSIORESET = ATABUSIORESET;
+unsigned IOCTL_ATABUSIODETACH = ATABUSIODETACH;
+unsigned IOCTL_CDIOCPLAYTRACKS = CDIOCPLAYTRACKS;
+unsigned IOCTL_CDIOCPLAYBLOCKS = CDIOCPLAYBLOCKS;
+unsigned IOCTL_CDIOCREADSUBCHANNEL = CDIOCREADSUBCHANNEL;
+unsigned IOCTL_CDIOREADTOCHEADER = CDIOREADTOCHEADER;
+unsigned IOCTL_CDIOREADTOCENTRIES = CDIOREADTOCENTRIES;
+unsigned IOCTL_CDIOREADMSADDR = CDIOREADMSADDR;
+unsigned IOCTL_CDIOCSETPATCH = CDIOCSETPATCH;
+unsigned IOCTL_CDIOCGETVOL = CDIOCGETVOL;
+unsigned IOCTL_CDIOCSETVOL = CDIOCSETVOL;
+unsigned IOCTL_CDIOCSETMONO = CDIOCSETMONO;
+unsigned IOCTL_CDIOCSETSTEREO = CDIOCSETSTEREO;
+unsigned IOCTL_CDIOCSETMUTE = CDIOCSETMUTE;
+unsigned IOCTL_CDIOCSETLEFT = CDIOCSETLEFT;
+unsigned IOCTL_CDIOCSETRIGHT = CDIOCSETRIGHT;
+unsigned IOCTL_CDIOCSETDEBUG = CDIOCSETDEBUG;
+unsigned IOCTL_CDIOCCLRDEBUG = CDIOCCLRDEBUG;
+unsigned IOCTL_CDIOCPAUSE = CDIOCPAUSE;
+unsigned IOCTL_CDIOCRESUME = CDIOCRESUME;
+unsigned IOCTL_CDIOCRESET = CDIOCRESET;
+unsigned IOCTL_CDIOCSTART = CDIOCSTART;
+unsigned IOCTL_CDIOCSTOP = CDIOCSTOP;
+unsigned IOCTL_CDIOCEJECT = CDIOCEJECT;
+unsigned IOCTL_CDIOCALLOW = CDIOCALLOW;
+unsigned IOCTL_CDIOCPREVENT = CDIOCPREVENT;
+unsigned IOCTL_CDIOCCLOSE = CDIOCCLOSE;
+unsigned IOCTL_CDIOCPLAYMSF = CDIOCPLAYMSF;
+unsigned IOCTL_CDIOCLOADUNLOAD = CDIOCLOADUNLOAD;
+unsigned IOCTL_CHIOMOVE = CHIOMOVE;
+unsigned IOCTL_CHIOEXCHANGE = CHIOEXCHANGE;
+unsigned IOCTL_CHIOPOSITION = CHIOPOSITION;
+unsigned IOCTL_CHIOGPICKER = CHIOGPICKER;
+unsigned IOCTL_CHIOSPICKER = CHIOSPICKER;
+unsigned IOCTL_CHIOGPARAMS = CHIOGPARAMS;
+unsigned IOCTL_CHIOIELEM = CHIOIELEM;
+unsigned IOCTL_OCHIOGSTATUS = OCHIOGSTATUS;
+unsigned IOCTL_CHIOGSTATUS = CHIOGSTATUS;
+unsigned IOCTL_CHIOSVOLTAG = CHIOSVOLTAG;
+unsigned IOCTL_CLOCKCTL_SETTIMEOFDAY = CLOCKCTL_SETTIMEOFDAY;
+unsigned IOCTL_CLOCKCTL_ADJTIME = CLOCKCTL_ADJTIME;
+unsigned IOCTL_CLOCKCTL_CLOCK_SETTIME = CLOCKCTL_CLOCK_SETTIME;
+unsigned IOCTL_CLOCKCTL_NTP_ADJTIME = CLOCKCTL_NTP_ADJTIME;
+unsigned IOCTL_IOC_CPU_SETSTATE = IOC_CPU_SETSTATE;
+unsigned IOCTL_IOC_CPU_GETSTATE = IOC_CPU_GETSTATE;
+unsigned IOCTL_IOC_CPU_GETCOUNT = IOC_CPU_GETCOUNT;
+unsigned IOCTL_IOC_CPU_MAPID = IOC_CPU_MAPID;
+unsigned IOCTL_IOC_CPU_UCODE_GET_VERSION = IOC_CPU_UCODE_GET_VERSION;
+unsigned IOCTL_IOC_CPU_UCODE_APPLY = IOC_CPU_UCODE_APPLY;
+unsigned IOCTL_DIOCGDINFO = DIOCGDINFO;
+unsigned IOCTL_DIOCSDINFO = DIOCSDINFO;
+unsigned IOCTL_DIOCWDINFO = DIOCWDINFO;
+unsigned IOCTL_DIOCRFORMAT = DIOCRFORMAT;
+unsigned IOCTL_DIOCWFORMAT = DIOCWFORMAT;
+unsigned IOCTL_DIOCSSTEP = DIOCSSTEP;
+unsigned IOCTL_DIOCSRETRIES = DIOCSRETRIES;
+unsigned IOCTL_DIOCKLABEL = DIOCKLABEL;
+unsigned IOCTL_DIOCWLABEL = DIOCWLABEL;
+unsigned IOCTL_DIOCSBAD = DIOCSBAD;
+unsigned IOCTL_DIOCEJECT = DIOCEJECT;
+unsigned IOCTL_ODIOCEJECT = ODIOCEJECT;
+unsigned IOCTL_DIOCLOCK = DIOCLOCK;
+unsigned IOCTL_DIOCGDEFLABEL = DIOCGDEFLABEL;
+unsigned IOCTL_DIOCCLRLABEL = DIOCCLRLABEL;
+unsigned IOCTL_DIOCGCACHE = DIOCGCACHE;
+unsigned IOCTL_DIOCSCACHE = DIOCSCACHE;
+unsigned IOCTL_DIOCCACHESYNC = DIOCCACHESYNC;
+unsigned IOCTL_DIOCBSLIST = DIOCBSLIST;
+unsigned IOCTL_DIOCBSFLUSH = DIOCBSFLUSH;
+unsigned IOCTL_DIOCAWEDGE = DIOCAWEDGE;
+unsigned IOCTL_DIOCGWEDGEINFO = DIOCGWEDGEINFO;
+unsigned IOCTL_DIOCDWEDGE = DIOCDWEDGE;
+unsigned IOCTL_DIOCLWEDGES = DIOCLWEDGES;
+unsigned IOCTL_DIOCGSTRATEGY = DIOCGSTRATEGY;
+unsigned IOCTL_DIOCSSTRATEGY = DIOCSSTRATEGY;
+unsigned IOCTL_DIOCGDISKINFO = DIOCGDISKINFO;
+unsigned IOCTL_DIOCTUR = DIOCTUR;
+unsigned IOCTL_DIOCMWEDGES = DIOCMWEDGES;
+unsigned IOCTL_DIOCGSECTORSIZE = DIOCGSECTORSIZE;
+unsigned IOCTL_DIOCGMEDIASIZE = DIOCGMEDIASIZE;
+unsigned IOCTL_DIOCRMWEDGES = DIOCRMWEDGES;
+unsigned IOCTL_DRVDETACHDEV = DRVDETACHDEV;
+unsigned IOCTL_DRVRESCANBUS = DRVRESCANBUS;
+unsigned IOCTL_DRVCTLCOMMAND = DRVCTLCOMMAND;
+unsigned IOCTL_DRVRESUMEDEV = DRVRESUMEDEV;
+unsigned IOCTL_DRVLISTDEV = DRVLISTDEV;
+unsigned IOCTL_DRVGETEVENT = DRVGETEVENT;
+unsigned IOCTL_DRVSUSPENDDEV = DRVSUSPENDDEV;
+unsigned IOCTL_DVD_READ_STRUCT = DVD_READ_STRUCT;
+unsigned IOCTL_DVD_WRITE_STRUCT = DVD_WRITE_STRUCT;
+unsigned IOCTL_DVD_AUTH = DVD_AUTH;
+unsigned IOCTL_ENVSYS_GETDICTIONARY = ENVSYS_GETDICTIONARY;
+unsigned IOCTL_ENVSYS_SETDICTIONARY = ENVSYS_SETDICTIONARY;
+unsigned IOCTL_ENVSYS_REMOVEPROPS = ENVSYS_REMOVEPROPS;
+unsigned IOCTL_ENVSYS_GTREDATA = ENVSYS_GTREDATA;
+unsigned IOCTL_ENVSYS_GTREINFO = ENVSYS_GTREINFO;
+unsigned IOCTL_KFILTER_BYFILTER = KFILTER_BYFILTER;
+unsigned IOCTL_KFILTER_BYNAME = KFILTER_BYNAME;
+unsigned IOCTL_FDIOCGETOPTS = FDIOCGETOPTS;
+unsigned IOCTL_FDIOCSETOPTS = FDIOCSETOPTS;
+unsigned IOCTL_FDIOCSETFORMAT = FDIOCSETFORMAT;
+unsigned IOCTL_FDIOCGETFORMAT = FDIOCGETFORMAT;
+unsigned IOCTL_FDIOCFORMAT_TRACK = FDIOCFORMAT_TRACK;
+unsigned IOCTL_FIOCLEX = FIOCLEX;
+unsigned IOCTL_FIONCLEX = FIONCLEX;
+unsigned IOCTL_FIOSEEKDATA = FIOSEEKDATA;
+unsigned IOCTL_FIOSEEKHOLE = FIOSEEKHOLE;
+unsigned IOCTL_FIONREAD = FIONREAD;
+unsigned IOCTL_FIONBIO = FIONBIO;
+unsigned IOCTL_FIOASYNC = FIOASYNC;
+unsigned IOCTL_FIOSETOWN = FIOSETOWN;
+unsigned IOCTL_FIOGETOWN = FIOGETOWN;
+unsigned IOCTL_OFIOGETBMAP = OFIOGETBMAP;
+unsigned IOCTL_FIOGETBMAP = FIOGETBMAP;
+unsigned IOCTL_FIONWRITE = FIONWRITE;
+unsigned IOCTL_FIONSPACE = FIONSPACE;
+unsigned IOCTL_GPIOINFO = GPIOINFO;
+unsigned IOCTL_GPIOSET = GPIOSET;
+unsigned IOCTL_GPIOUNSET = GPIOUNSET;
+unsigned IOCTL_GPIOREAD = GPIOREAD;
+unsigned IOCTL_GPIOWRITE = GPIOWRITE;
+unsigned IOCTL_GPIOTOGGLE = GPIOTOGGLE;
+unsigned IOCTL_GPIOATTACH = GPIOATTACH;
+unsigned IOCTL_PTIOCNETBSD = PTIOCNETBSD;
+unsigned IOCTL_PTIOCSUNOS = PTIOCSUNOS;
+unsigned IOCTL_PTIOCLINUX = PTIOCLINUX;
+unsigned IOCTL_PTIOCFREEBSD = PTIOCFREEBSD;
+unsigned IOCTL_PTIOCULTRIX = PTIOCULTRIX;
+unsigned IOCTL_TIOCHPCL = TIOCHPCL;
+unsigned IOCTL_TIOCGETP = TIOCGETP;
+unsigned IOCTL_TIOCSETP = TIOCSETP;
+unsigned IOCTL_TIOCSETN = TIOCSETN;
+unsigned IOCTL_TIOCSETC = TIOCSETC;
+unsigned IOCTL_TIOCGETC = TIOCGETC;
+unsigned IOCTL_TIOCLBIS = TIOCLBIS;
+unsigned IOCTL_TIOCLBIC = TIOCLBIC;
+unsigned IOCTL_TIOCLSET = TIOCLSET;
+unsigned IOCTL_TIOCLGET = TIOCLGET;
+unsigned IOCTL_TIOCSLTC = TIOCSLTC;
+unsigned IOCTL_TIOCGLTC = TIOCGLTC;
+unsigned IOCTL_OTIOCCONS = OTIOCCONS;
+unsigned IOCTL_JOY_SETTIMEOUT = JOY_SETTIMEOUT;
+unsigned IOCTL_JOY_GETTIMEOUT = JOY_GETTIMEOUT;
+unsigned IOCTL_JOY_SET_X_OFFSET = JOY_SET_X_OFFSET;
+unsigned IOCTL_JOY_SET_Y_OFFSET = JOY_SET_Y_OFFSET;
+unsigned IOCTL_JOY_GET_X_OFFSET = JOY_GET_X_OFFSET;
+unsigned IOCTL_JOY_GET_Y_OFFSET = JOY_GET_Y_OFFSET;
+unsigned IOCTL_OKIOCGSYMBOL = OKIOCGSYMBOL;
+unsigned IOCTL_OKIOCGVALUE = OKIOCGVALUE;
+unsigned IOCTL_KIOCGSIZE = KIOCGSIZE;
+unsigned IOCTL_KIOCGVALUE = KIOCGVALUE;
+unsigned IOCTL_KIOCGSYMBOL = KIOCGSYMBOL;
+unsigned IOCTL_LUAINFO = LUAINFO;
+unsigned IOCTL_LUACREATE = LUACREATE;
+unsigned IOCTL_LUADESTROY = LUADESTROY;
+unsigned IOCTL_LUAREQUIRE = LUAREQUIRE;
+unsigned IOCTL_LUALOAD = LUALOAD;
+unsigned IOCTL_MIDI_PRETIME = MIDI_PRETIME;
+unsigned IOCTL_MIDI_MPUMODE = MIDI_MPUMODE;
+unsigned IOCTL_MIDI_MPUCMD = MIDI_MPUCMD;
+unsigned IOCTL_SEQUENCER_RESET = SEQUENCER_RESET;
+unsigned IOCTL_SEQUENCER_SYNC = SEQUENCER_SYNC;
+unsigned IOCTL_SEQUENCER_INFO = SEQUENCER_INFO;
+unsigned IOCTL_SEQUENCER_CTRLRATE = SEQUENCER_CTRLRATE;
+unsigned IOCTL_SEQUENCER_GETOUTCOUNT = SEQUENCER_GETOUTCOUNT;
+unsigned IOCTL_SEQUENCER_GETINCOUNT = SEQUENCER_GETINCOUNT;
+unsigned IOCTL_SEQUENCER_RESETSAMPLES = SEQUENCER_RESETSAMPLES;
+unsigned IOCTL_SEQUENCER_NRSYNTHS = SEQUENCER_NRSYNTHS;
+unsigned IOCTL_SEQUENCER_NRMIDIS = SEQUENCER_NRMIDIS;
+unsigned IOCTL_SEQUENCER_THRESHOLD = SEQUENCER_THRESHOLD;
+unsigned IOCTL_SEQUENCER_MEMAVL = SEQUENCER_MEMAVL;
+unsigned IOCTL_SEQUENCER_PANIC = SEQUENCER_PANIC;
+unsigned IOCTL_SEQUENCER_OUTOFBAND = SEQUENCER_OUTOFBAND;
+unsigned IOCTL_SEQUENCER_GETTIME = SEQUENCER_GETTIME;
+unsigned IOCTL_SEQUENCER_TMR_TIMEBASE = SEQUENCER_TMR_TIMEBASE;
+unsigned IOCTL_SEQUENCER_TMR_START = SEQUENCER_TMR_START;
+unsigned IOCTL_SEQUENCER_TMR_STOP = SEQUENCER_TMR_STOP;
+unsigned IOCTL_SEQUENCER_TMR_CONTINUE = SEQUENCER_TMR_CONTINUE;
+unsigned IOCTL_SEQUENCER_TMR_TEMPO = SEQUENCER_TMR_TEMPO;
+unsigned IOCTL_SEQUENCER_TMR_SOURCE = SEQUENCER_TMR_SOURCE;
+unsigned IOCTL_SEQUENCER_TMR_METRONOME = SEQUENCER_TMR_METRONOME;
+unsigned IOCTL_SEQUENCER_TMR_SELECT = SEQUENCER_TMR_SELECT;
+unsigned IOCTL_MTIOCTOP = MTIOCTOP;
+unsigned IOCTL_MTIOCGET = MTIOCGET;
+unsigned IOCTL_MTIOCIEOT = MTIOCIEOT;
+unsigned IOCTL_MTIOCEEOT = MTIOCEEOT;
+unsigned IOCTL_MTIOCRDSPOS = MTIOCRDSPOS;
+unsigned IOCTL_MTIOCRDHPOS = MTIOCRDHPOS;
+unsigned IOCTL_MTIOCSLOCATE = MTIOCSLOCATE;
+unsigned IOCTL_MTIOCHLOCATE = MTIOCHLOCATE;
+unsigned IOCTL_POWER_EVENT_RECVDICT = POWER_EVENT_RECVDICT;
+unsigned IOCTL_POWER_IOC_GET_TYPE = POWER_IOC_GET_TYPE;
+unsigned IOCTL_RIOCGINFO = RIOCGINFO;
+unsigned IOCTL_RIOCSINFO = RIOCSINFO;
+unsigned IOCTL_RIOCSSRCH = RIOCSSRCH;
+unsigned IOCTL_RNDGETENTCNT = RNDGETENTCNT;
+unsigned IOCTL_RNDGETSRCNUM = RNDGETSRCNUM;
+unsigned IOCTL_RNDGETSRCNAME = RNDGETSRCNAME;
+unsigned IOCTL_RNDCTL = RNDCTL;
+unsigned IOCTL_RNDADDDATA = RNDADDDATA;
+unsigned IOCTL_RNDGETPOOLSTAT = RNDGETPOOLSTAT;
+unsigned IOCTL_RNDGETESTNUM = RNDGETESTNUM;
+unsigned IOCTL_RNDGETESTNAME = RNDGETESTNAME;
+unsigned IOCTL_SCIOCGET = SCIOCGET;
+unsigned IOCTL_SCIOCSET = SCIOCSET;
+unsigned IOCTL_SCIOCRESTART = SCIOCRESTART;
+unsigned IOCTL_SCIOC_USE_ADF = SCIOC_USE_ADF;
+unsigned IOCTL_SCIOCCOMMAND = SCIOCCOMMAND;
+unsigned IOCTL_SCIOCDEBUG = SCIOCDEBUG;
+unsigned IOCTL_SCIOCIDENTIFY = SCIOCIDENTIFY;
+unsigned IOCTL_OSCIOCIDENTIFY = OSCIOCIDENTIFY;
+unsigned IOCTL_SCIOCDECONFIG = SCIOCDECONFIG;
+unsigned IOCTL_SCIOCRECONFIG = SCIOCRECONFIG;
+unsigned IOCTL_SCIOCRESET = SCIOCRESET;
+unsigned IOCTL_SCBUSIOSCAN = SCBUSIOSCAN;
+unsigned IOCTL_SCBUSIORESET = SCBUSIORESET;
+unsigned IOCTL_SCBUSIODETACH = SCBUSIODETACH;
+unsigned IOCTL_SCBUSACCEL = SCBUSACCEL;
+unsigned IOCTL_SCBUSIOLLSCAN = SCBUSIOLLSCAN;
+unsigned IOCTL_SIOCSHIWAT = SIOCSHIWAT;
+unsigned IOCTL_SIOCGHIWAT = SIOCGHIWAT;
+unsigned IOCTL_SIOCSLOWAT = SIOCSLOWAT;
+unsigned IOCTL_SIOCGLOWAT = SIOCGLOWAT;
+unsigned IOCTL_SIOCATMARK = SIOCATMARK;
+unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
+unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+unsigned IOCTL_SIOCPEELOFF = SIOCPEELOFF;
+unsigned IOCTL_SIOCADDRT = SIOCADDRT;
+unsigned IOCTL_SIOCDELRT = SIOCDELRT;
+unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
+unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
+unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
+unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
+unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
+unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
+unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
+unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
+unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
+unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
+unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
+unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
+unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
+unsigned IOCTL_SIOCDIFADDR = SIOCDIFADDR;
+unsigned IOCTL_SIOCAIFADDR = SIOCAIFADDR;
+unsigned IOCTL_SIOCGIFALIAS = SIOCGIFALIAS;
+unsigned IOCTL_SIOCGIFAFLAG_IN = SIOCGIFAFLAG_IN;
+unsigned IOCTL_SIOCALIFADDR = SIOCALIFADDR;
+unsigned IOCTL_SIOCGLIFADDR = SIOCGLIFADDR;
+unsigned IOCTL_SIOCDLIFADDR = SIOCDLIFADDR;
+unsigned IOCTL_SIOCSIFADDRPREF = SIOCSIFADDRPREF;
+unsigned IOCTL_SIOCGIFADDRPREF = SIOCGIFADDRPREF;
+unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
+unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
+unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
+unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
+unsigned IOCTL_SIOCSIFMEDIA = SIOCSIFMEDIA;
+unsigned IOCTL_SIOCGIFMEDIA = SIOCGIFMEDIA;
+unsigned IOCTL_SIOCSIFGENERIC = SIOCSIFGENERIC;
+unsigned IOCTL_SIOCGIFGENERIC = SIOCGIFGENERIC;
+unsigned IOCTL_SIOCSIFPHYADDR = SIOCSIFPHYADDR;
+unsigned IOCTL_SIOCGIFPSRCADDR = SIOCGIFPSRCADDR;
+unsigned IOCTL_SIOCGIFPDSTADDR = SIOCGIFPDSTADDR;
+unsigned IOCTL_SIOCDIFPHYADDR = SIOCDIFPHYADDR;
+unsigned IOCTL_SIOCSLIFPHYADDR = SIOCSLIFPHYADDR;
+unsigned IOCTL_SIOCGLIFPHYADDR = SIOCGLIFPHYADDR;
+unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
+unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
+unsigned IOCTL_SIOCSDRVSPEC = SIOCSDRVSPEC;
+unsigned IOCTL_SIOCGDRVSPEC = SIOCGDRVSPEC;
+unsigned IOCTL_SIOCIFCREATE = SIOCIFCREATE;
+unsigned IOCTL_SIOCIFDESTROY = SIOCIFDESTROY;
+unsigned IOCTL_SIOCIFGCLONERS = SIOCIFGCLONERS;
+unsigned IOCTL_SIOCGIFDLT = SIOCGIFDLT;
+unsigned IOCTL_SIOCGIFCAP = SIOCGIFCAP;
+unsigned IOCTL_SIOCSIFCAP = SIOCSIFCAP;
+unsigned IOCTL_SIOCSVH = SIOCSVH;
+unsigned IOCTL_SIOCGVH = SIOCGVH;
+unsigned IOCTL_SIOCINITIFADDR = SIOCINITIFADDR;
+unsigned IOCTL_SIOCGIFDATA = SIOCGIFDATA;
+unsigned IOCTL_SIOCZIFDATA = SIOCZIFDATA;
+unsigned IOCTL_SIOCGLINKSTR = SIOCGLINKSTR;
+unsigned IOCTL_SIOCSLINKSTR = SIOCSLINKSTR;
+unsigned IOCTL_SIOCGETHERCAP = SIOCGETHERCAP;
+unsigned IOCTL_SIOCGIFINDEX = SIOCGIFINDEX;
+unsigned IOCTL_SIOCSETHERCAP = SIOCSETHERCAP;
+unsigned IOCTL_SIOCSIFDESCR = SIOCSIFDESCR;
+unsigned IOCTL_SIOCGIFDESCR = SIOCGIFDESCR;
+unsigned IOCTL_SIOCGUMBINFO = SIOCGUMBINFO;
+unsigned IOCTL_SIOCSUMBPARAM = SIOCSUMBPARAM;
+unsigned IOCTL_SIOCGUMBPARAM = SIOCGUMBPARAM;
+unsigned IOCTL_SIOCSETPFSYNC = SIOCSETPFSYNC;
+unsigned IOCTL_SIOCGETPFSYNC = SIOCGETPFSYNC;
+unsigned IOCTL_PPS_IOC_CREATE = PPS_IOC_CREATE;
+unsigned IOCTL_PPS_IOC_DESTROY = PPS_IOC_DESTROY;
+unsigned IOCTL_PPS_IOC_SETPARAMS = PPS_IOC_SETPARAMS;
+unsigned IOCTL_PPS_IOC_GETPARAMS = PPS_IOC_GETPARAMS;
+unsigned IOCTL_PPS_IOC_GETCAP = PPS_IOC_GETCAP;
+unsigned IOCTL_PPS_IOC_FETCH = PPS_IOC_FETCH;
+unsigned IOCTL_PPS_IOC_KCBIND = PPS_IOC_KCBIND;
+unsigned IOCTL_TIOCEXCL = TIOCEXCL;
+unsigned IOCTL_TIOCNXCL = TIOCNXCL;
+unsigned IOCTL_TIOCFLUSH = TIOCFLUSH;
+unsigned IOCTL_TIOCGETA = TIOCGETA;
+unsigned IOCTL_TIOCSETA = TIOCSETA;
+unsigned IOCTL_TIOCSETAW = TIOCSETAW;
+unsigned IOCTL_TIOCSETAF = TIOCSETAF;
+unsigned IOCTL_TIOCGETD = TIOCGETD;
+unsigned IOCTL_TIOCSETD = TIOCSETD;
+unsigned IOCTL_TIOCGLINED = TIOCGLINED;
+unsigned IOCTL_TIOCSLINED = TIOCSLINED;
+unsigned IOCTL_TIOCSBRK = TIOCSBRK;
+unsigned IOCTL_TIOCCBRK = TIOCCBRK;
+unsigned IOCTL_TIOCSDTR = TIOCSDTR;
+unsigned IOCTL_TIOCCDTR = TIOCCDTR;
+unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
+unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
+unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
+unsigned IOCTL_TIOCSTI = TIOCSTI;
+unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
+unsigned IOCTL_TIOCPKT = TIOCPKT;
+unsigned IOCTL_TIOCSTOP = TIOCSTOP;
+unsigned IOCTL_TIOCSTART = TIOCSTART;
+unsigned IOCTL_TIOCMSET = TIOCMSET;
+unsigned IOCTL_TIOCMBIS = TIOCMBIS;
+unsigned IOCTL_TIOCMBIC = TIOCMBIC;
+unsigned IOCTL_TIOCMGET = TIOCMGET;
+unsigned IOCTL_TIOCREMOTE = TIOCREMOTE;
+unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
+unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
+unsigned IOCTL_TIOCUCNTL = TIOCUCNTL;
+unsigned IOCTL_TIOCSTAT = TIOCSTAT;
+unsigned IOCTL_TIOCGSID = TIOCGSID;
+unsigned IOCTL_TIOCCONS = TIOCCONS;
+unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
+unsigned IOCTL_TIOCEXT = TIOCEXT;
+unsigned IOCTL_TIOCSIG = TIOCSIG;
+unsigned IOCTL_TIOCDRAIN = TIOCDRAIN;
+unsigned IOCTL_TIOCGFLAGS = TIOCGFLAGS;
+unsigned IOCTL_TIOCSFLAGS = TIOCSFLAGS;
+unsigned IOCTL_TIOCDCDTIMESTAMP = TIOCDCDTIMESTAMP;
+unsigned IOCTL_TIOCRCVFRAME = TIOCRCVFRAME;
+unsigned IOCTL_TIOCXMTFRAME = TIOCXMTFRAME;
+unsigned IOCTL_TIOCPTMGET = TIOCPTMGET;
+unsigned IOCTL_TIOCGRANTPT = TIOCGRANTPT;
+unsigned IOCTL_TIOCPTSNAME = TIOCPTSNAME;
+unsigned IOCTL_TIOCSQSIZE = TIOCSQSIZE;
+unsigned IOCTL_TIOCGQSIZE = TIOCGQSIZE;
+unsigned IOCTL_VERIEXEC_LOAD = VERIEXEC_LOAD;
+unsigned IOCTL_VERIEXEC_TABLESIZE = VERIEXEC_TABLESIZE;
+unsigned IOCTL_VERIEXEC_DELETE = VERIEXEC_DELETE;
+unsigned IOCTL_VERIEXEC_QUERY = VERIEXEC_QUERY;
+unsigned IOCTL_VERIEXEC_DUMP = VERIEXEC_DUMP;
+unsigned IOCTL_VERIEXEC_FLUSH = VERIEXEC_FLUSH;
+unsigned IOCTL_VIDIOC_QUERYCAP = VIDIOC_QUERYCAP;
+unsigned IOCTL_VIDIOC_RESERVED = VIDIOC_RESERVED;
+unsigned IOCTL_VIDIOC_ENUM_FMT = VIDIOC_ENUM_FMT;
+unsigned IOCTL_VIDIOC_G_FMT = VIDIOC_G_FMT;
+unsigned IOCTL_VIDIOC_S_FMT = VIDIOC_S_FMT;
+unsigned IOCTL_VIDIOC_REQBUFS = VIDIOC_REQBUFS;
+unsigned IOCTL_VIDIOC_QUERYBUF = VIDIOC_QUERYBUF;
+unsigned IOCTL_VIDIOC_G_FBUF = VIDIOC_G_FBUF;
+unsigned IOCTL_VIDIOC_S_FBUF = VIDIOC_S_FBUF;
+unsigned IOCTL_VIDIOC_OVERLAY = VIDIOC_OVERLAY;
+unsigned IOCTL_VIDIOC_QBUF = VIDIOC_QBUF;
+unsigned IOCTL_VIDIOC_DQBUF = VIDIOC_DQBUF;
+unsigned IOCTL_VIDIOC_STREAMON = VIDIOC_STREAMON;
+unsigned IOCTL_VIDIOC_STREAMOFF = VIDIOC_STREAMOFF;
+unsigned IOCTL_VIDIOC_G_PARM = VIDIOC_G_PARM;
+unsigned IOCTL_VIDIOC_S_PARM = VIDIOC_S_PARM;
+unsigned IOCTL_VIDIOC_G_STD = VIDIOC_G_STD;
+unsigned IOCTL_VIDIOC_S_STD = VIDIOC_S_STD;
+unsigned IOCTL_VIDIOC_ENUMSTD = VIDIOC_ENUMSTD;
+unsigned IOCTL_VIDIOC_ENUMINPUT = VIDIOC_ENUMINPUT;
+unsigned IOCTL_VIDIOC_G_CTRL = VIDIOC_G_CTRL;
+unsigned IOCTL_VIDIOC_S_CTRL = VIDIOC_S_CTRL;
+unsigned IOCTL_VIDIOC_G_TUNER = VIDIOC_G_TUNER;
+unsigned IOCTL_VIDIOC_S_TUNER = VIDIOC_S_TUNER;
+unsigned IOCTL_VIDIOC_G_AUDIO = VIDIOC_G_AUDIO;
+unsigned IOCTL_VIDIOC_S_AUDIO = VIDIOC_S_AUDIO;
+unsigned IOCTL_VIDIOC_QUERYCTRL = VIDIOC_QUERYCTRL;
+unsigned IOCTL_VIDIOC_QUERYMENU = VIDIOC_QUERYMENU;
+unsigned IOCTL_VIDIOC_G_INPUT = VIDIOC_G_INPUT;
+unsigned IOCTL_VIDIOC_S_INPUT = VIDIOC_S_INPUT;
+unsigned IOCTL_VIDIOC_G_OUTPUT = VIDIOC_G_OUTPUT;
+unsigned IOCTL_VIDIOC_S_OUTPUT = VIDIOC_S_OUTPUT;
+unsigned IOCTL_VIDIOC_ENUMOUTPUT = VIDIOC_ENUMOUTPUT;
+unsigned IOCTL_VIDIOC_G_AUDOUT = VIDIOC_G_AUDOUT;
+unsigned IOCTL_VIDIOC_S_AUDOUT = VIDIOC_S_AUDOUT;
+unsigned IOCTL_VIDIOC_G_MODULATOR = VIDIOC_G_MODULATOR;
+unsigned IOCTL_VIDIOC_S_MODULATOR = VIDIOC_S_MODULATOR;
+unsigned IOCTL_VIDIOC_G_FREQUENCY = VIDIOC_G_FREQUENCY;
+unsigned IOCTL_VIDIOC_S_FREQUENCY = VIDIOC_S_FREQUENCY;
+unsigned IOCTL_VIDIOC_CROPCAP = VIDIOC_CROPCAP;
+unsigned IOCTL_VIDIOC_G_CROP = VIDIOC_G_CROP;
+unsigned IOCTL_VIDIOC_S_CROP = VIDIOC_S_CROP;
+unsigned IOCTL_VIDIOC_G_JPEGCOMP = VIDIOC_G_JPEGCOMP;
+unsigned IOCTL_VIDIOC_S_JPEGCOMP = VIDIOC_S_JPEGCOMP;
+unsigned IOCTL_VIDIOC_QUERYSTD = VIDIOC_QUERYSTD;
+unsigned IOCTL_VIDIOC_TRY_FMT = VIDIOC_TRY_FMT;
+unsigned IOCTL_VIDIOC_ENUMAUDIO = VIDIOC_ENUMAUDIO;
+unsigned IOCTL_VIDIOC_ENUMAUDOUT = VIDIOC_ENUMAUDOUT;
+unsigned IOCTL_VIDIOC_G_PRIORITY = VIDIOC_G_PRIORITY;
+unsigned IOCTL_VIDIOC_S_PRIORITY = VIDIOC_S_PRIORITY;
+unsigned IOCTL_VIDIOC_ENUM_FRAMESIZES = VIDIOC_ENUM_FRAMESIZES;
+unsigned IOCTL_VIDIOC_ENUM_FRAMEINTERVALS = VIDIOC_ENUM_FRAMEINTERVALS;
+unsigned IOCTL_WDOGIOC_GMODE = WDOGIOC_GMODE;
+unsigned IOCTL_WDOGIOC_SMODE = WDOGIOC_SMODE;
+unsigned IOCTL_WDOGIOC_WHICH = WDOGIOC_WHICH;
+unsigned IOCTL_WDOGIOC_TICKLE = WDOGIOC_TICKLE;
+unsigned IOCTL_WDOGIOC_GTICKLER = WDOGIOC_GTICKLER;
+unsigned IOCTL_WDOGIOC_GWDOGS = WDOGIOC_GWDOGS;
+unsigned IOCTL_KCOV_IOC_SETBUFSIZE = KCOV_IOC_SETBUFSIZE;
+unsigned IOCTL_KCOV_IOC_ENABLE = KCOV_IOC_ENABLE;
+unsigned IOCTL_KCOV_IOC_DISABLE = KCOV_IOC_DISABLE;
+unsigned IOCTL_IPMICTL_RECEIVE_MSG_TRUNC = IPMICTL_RECEIVE_MSG_TRUNC;
+unsigned IOCTL_IPMICTL_RECEIVE_MSG = IPMICTL_RECEIVE_MSG;
+unsigned IOCTL_IPMICTL_SEND_COMMAND = IPMICTL_SEND_COMMAND;
+unsigned IOCTL_IPMICTL_REGISTER_FOR_CMD = IPMICTL_REGISTER_FOR_CMD;
+unsigned IOCTL_IPMICTL_UNREGISTER_FOR_CMD = IPMICTL_UNREGISTER_FOR_CMD;
+unsigned IOCTL_IPMICTL_SET_GETS_EVENTS_CMD = IPMICTL_SET_GETS_EVENTS_CMD;
+unsigned IOCTL_IPMICTL_SET_MY_ADDRESS_CMD = IPMICTL_SET_MY_ADDRESS_CMD;
+unsigned IOCTL_IPMICTL_GET_MY_ADDRESS_CMD = IPMICTL_GET_MY_ADDRESS_CMD;
+unsigned IOCTL_IPMICTL_SET_MY_LUN_CMD = IPMICTL_SET_MY_LUN_CMD;
+unsigned IOCTL_IPMICTL_GET_MY_LUN_CMD = IPMICTL_GET_MY_LUN_CMD;
+unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
+unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
+unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
+unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
+unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
+unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
+unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
+unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS;
+unsigned IOCTL_SNDCTL_DSP_CHANNELS = SNDCTL_DSP_CHANNELS;
+unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS;
+unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
+unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER;
+unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
+unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
+unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
+unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
+unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
+unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
+unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
+unsigned IOCTL_SNDCTL_DSP_GETCAPS = SNDCTL_DSP_GETCAPS;
+unsigned IOCTL_SNDCTL_DSP_GETTRIGGER = SNDCTL_DSP_GETTRIGGER;
+unsigned IOCTL_SNDCTL_DSP_SETTRIGGER = SNDCTL_DSP_SETTRIGGER;
+unsigned IOCTL_SNDCTL_DSP_GETIPTR = SNDCTL_DSP_GETIPTR;
+unsigned IOCTL_SNDCTL_DSP_GETOPTR = SNDCTL_DSP_GETOPTR;
+unsigned IOCTL_SNDCTL_DSP_MAPINBUF = SNDCTL_DSP_MAPINBUF;
+unsigned IOCTL_SNDCTL_DSP_MAPOUTBUF = SNDCTL_DSP_MAPOUTBUF;
+unsigned IOCTL_SNDCTL_DSP_SETSYNCRO = SNDCTL_DSP_SETSYNCRO;
+unsigned IOCTL_SNDCTL_DSP_SETDUPLEX = SNDCTL_DSP_SETDUPLEX;
+unsigned IOCTL_SNDCTL_DSP_PROFILE = SNDCTL_DSP_PROFILE;
+unsigned IOCTL_SNDCTL_DSP_GETODELAY = SNDCTL_DSP_GETODELAY;
+unsigned IOCTL_SOUND_MIXER_INFO = SOUND_MIXER_INFO;
+unsigned IOCTL_SOUND_OLD_MIXER_INFO = SOUND_OLD_MIXER_INFO;
+unsigned IOCTL_OSS_GETVERSION = OSS_GETVERSION;
+unsigned IOCTL_SNDCTL_SYSINFO = SNDCTL_SYSINFO;
+unsigned IOCTL_SNDCTL_AUDIOINFO = SNDCTL_AUDIOINFO;
+unsigned IOCTL_SNDCTL_ENGINEINFO = SNDCTL_ENGINEINFO;
+unsigned IOCTL_SNDCTL_DSP_GETPLAYVOL = SNDCTL_DSP_GETPLAYVOL;
+unsigned IOCTL_SNDCTL_DSP_SETPLAYVOL = SNDCTL_DSP_SETPLAYVOL;
+unsigned IOCTL_SNDCTL_DSP_GETRECVOL = SNDCTL_DSP_GETRECVOL;
+unsigned IOCTL_SNDCTL_DSP_SETRECVOL = SNDCTL_DSP_SETRECVOL;
+unsigned IOCTL_SNDCTL_DSP_SKIP = SNDCTL_DSP_SKIP;
+unsigned IOCTL_SNDCTL_DSP_SILENCE = SNDCTL_DSP_SILENCE;
+
+const int si_SEGV_MAPERR = SEGV_MAPERR;
+const int si_SEGV_ACCERR = SEGV_ACCERR;
+
+const int modctl_load = MODCTL_LOAD;
+const int modctl_unload = MODCTL_UNLOAD;
+const int modctl_stat = MODCTL_STAT;
+const int modctl_exists = MODCTL_EXISTS;
+
+const unsigned SHA1_CTX_sz = sizeof(SHA1_CTX);
+const unsigned SHA1_return_length = SHA1_DIGEST_STRING_LENGTH;
+
+const unsigned MD4_CTX_sz = sizeof(MD4_CTX);
+const unsigned MD4_return_length = MD4_DIGEST_STRING_LENGTH;
+
+const unsigned RMD160_CTX_sz = sizeof(RMD160_CTX);
+const unsigned RMD160_return_length = RMD160_DIGEST_STRING_LENGTH;
+
+const unsigned MD5_CTX_sz = sizeof(MD5_CTX);
+const unsigned MD5_return_length = MD5_DIGEST_STRING_LENGTH;
+
+const unsigned fpos_t_sz = sizeof(fpos_t);
+
+const unsigned MD2_CTX_sz = sizeof(MD2_CTX);
+const unsigned MD2_return_length = MD2_DIGEST_STRING_LENGTH;
+
+#define SHA2_CONST(LEN) \
+ const unsigned SHA##LEN##_CTX_sz = sizeof(SHA##LEN##_CTX); \
+ const unsigned SHA##LEN##_return_length = SHA##LEN##_DIGEST_STRING_LENGTH; \
+ const unsigned SHA##LEN##_block_length = SHA##LEN##_BLOCK_LENGTH; \
+ const unsigned SHA##LEN##_digest_length = SHA##LEN##_DIGEST_LENGTH
+
+SHA2_CONST(224);
+SHA2_CONST(256);
+SHA2_CONST(384);
+SHA2_CONST(512);
+
+#undef SHA2_CONST
+
+const int unvis_valid = UNVIS_VALID;
+const int unvis_validpush = UNVIS_VALIDPUSH;
+} // namespace __sanitizer
+
+using namespace __sanitizer;
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+// There are more undocumented fields in dl_phdr_info that we are not interested
+// in.
+COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_fileno);
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+// Can't write checks for sa_handler and sa_sigaction due to them being
+// preprocessor macros.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
+
+CHECK_TYPE_SIZE(wordexp_t);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+
+COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE));
+CHECK_SIZE_AND_OFFSET(FILE, _p);
+CHECK_SIZE_AND_OFFSET(FILE, _r);
+CHECK_SIZE_AND_OFFSET(FILE, _w);
+CHECK_SIZE_AND_OFFSET(FILE, _flags);
+CHECK_SIZE_AND_OFFSET(FILE, _file);
+CHECK_SIZE_AND_OFFSET(FILE, _bf);
+CHECK_SIZE_AND_OFFSET(FILE, _lbfsize);
+CHECK_SIZE_AND_OFFSET(FILE, _cookie);
+CHECK_SIZE_AND_OFFSET(FILE, _close);
+CHECK_SIZE_AND_OFFSET(FILE, _read);
+CHECK_SIZE_AND_OFFSET(FILE, _seek);
+CHECK_SIZE_AND_OFFSET(FILE, _write);
+CHECK_SIZE_AND_OFFSET(FILE, _ext);
+CHECK_SIZE_AND_OFFSET(FILE, _up);
+CHECK_SIZE_AND_OFFSET(FILE, _ur);
+CHECK_SIZE_AND_OFFSET(FILE, _ubuf);
+CHECK_SIZE_AND_OFFSET(FILE, _nbuf);
+CHECK_SIZE_AND_OFFSET(FILE, _flush);
+CHECK_SIZE_AND_OFFSET(FILE, _lb_unused);
+CHECK_SIZE_AND_OFFSET(FILE, _blksize);
+CHECK_SIZE_AND_OFFSET(FILE, _offset);
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
+CHECK_SIZE_AND_OFFSET(tm, tm_zone);
+
+CHECK_TYPE_SIZE(ether_addr);
+
+CHECK_TYPE_SIZE(ipc_perm);
+CHECK_SIZE_AND_OFFSET(ipc_perm, _key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, _seq);
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+
+CHECK_TYPE_SIZE(clock_t);
+
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+// Compare against the union, because we can't reach into the union in a
+// compliant way.
+#ifdef ifa_dstaddr
+#undef ifa_dstaddr
+#endif
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+CHECK_TYPE_SIZE(modctl_load_t);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_filename);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_flags);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_props);
+CHECK_SIZE_AND_OFFSET(modctl_load_t, ml_propslen);
+
+#endif // SANITIZER_NETBSD
//===-- sanitizer_platform_limits_netbsd.h --------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#if defined(__x86_64__)
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
- _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 312)
+ _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 264)
#elif defined(__i386__)
#define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
- _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 164)
+ _GET_LINK_MAP_BY_DLOPEN_HANDLE(handle, 136)
#endif
namespace __sanitizer {
extern unsigned struct_sembuf_sz;
extern unsigned struct_kevent_sz;
+extern unsigned struct_FTS_sz;
+extern unsigned struct_FTSENT_sz;
+
+extern unsigned struct_regex_sz;
+extern unsigned struct_regmatch_sz;
+
+extern unsigned struct_fstab_sz;
+
+struct __sanitizer_regmatch {
+ OFF_T rm_so;
+ OFF_T rm_eo;
+};
+
+typedef struct __sanitizer_modctl_load {
+ const char *ml_filename;
+ int ml_flags;
+ const char *ml_props;
+ uptr ml_propslen;
+} __sanitizer_modctl_load_t;
+extern const int modctl_load;
+extern const int modctl_unload;
+extern const int modctl_stat;
+extern const int modctl_exists;
union __sanitizer_sigval {
int sival_int;
uptr we_nbytes;
};
-typedef char __sanitizer_FILE;
-#define SANITIZER_HAS_STRUCT_FILE 0
+struct __sanitizer_FILE {
+ unsigned char *_p;
+ int _r;
+ int _w;
+ unsigned short _flags;
+ short _file;
+ struct {
+ unsigned char *_base;
+ int _size;
+ } _bf;
+ int _lbfsize;
+ void *_cookie;
+ int (*_close)(void *ptr);
+ u64 (*_read)(void *, void *, uptr);
+ u64 (*_seek)(void *, u64, int);
+ uptr (*_write)(void *, const void *, uptr);
+ struct {
+ unsigned char *_base;
+ int _size;
+ } _ext;
+ unsigned char *_up;
+ int _ur;
+ unsigned char _ubuf[3];
+ unsigned char _nbuf[1];
+ int (*_flush)(void *ptr);
+ char _lb_unused[sizeof(uptr)];
+ int _blksize;
+ u64 _offset;
+};
+#define SANITIZER_HAS_STRUCT_FILE 1
extern int shmctl_ipc_stat;
char *ty_class;
};
+extern const unsigned long __sanitizer_bufsiz;
+
#define IOC_NRBITS 8
#define IOC_TYPEBITS 8
#define IOC_SIZEBITS 14
extern unsigned struct_atabusioscan_args_sz;
extern unsigned struct_ath_diag_sz;
extern unsigned struct_atm_flowmap_sz;
-extern unsigned struct_atm_pseudoioctl_sz;
extern unsigned struct_audio_buf_info_sz;
extern unsigned struct_audio_device_sz;
extern unsigned struct_audio_encoding_sz;
extern unsigned struct_rio_conf_sz;
extern unsigned struct_rio_interface_sz;
extern unsigned struct_rio_stats_sz;
-extern unsigned struct_satlink_id_sz;
extern unsigned struct_scan_io_sz;
extern unsigned struct_scbusaccel_args_sz;
extern unsigned struct_scbusiodetach_args_sz;
extern unsigned struct_usb_ctl_report_desc_sz;
extern unsigned struct_usb_ctl_report_sz;
extern unsigned struct_usb_ctl_request_sz;
+#if defined(__x86_64__)
+extern unsigned struct_nvmm_ioc_capability_sz;
+extern unsigned struct_nvmm_ioc_machine_create_sz;
+extern unsigned struct_nvmm_ioc_machine_destroy_sz;
+extern unsigned struct_nvmm_ioc_machine_configure_sz;
+extern unsigned struct_nvmm_ioc_vcpu_create_sz;
+extern unsigned struct_nvmm_ioc_vcpu_destroy_sz;
+extern unsigned struct_nvmm_ioc_vcpu_setstate_sz;
+extern unsigned struct_nvmm_ioc_vcpu_getstate_sz;
+extern unsigned struct_nvmm_ioc_vcpu_inject_sz;
+extern unsigned struct_nvmm_ioc_vcpu_run_sz;
+extern unsigned struct_nvmm_ioc_gpa_map_sz;
+extern unsigned struct_nvmm_ioc_gpa_unmap_sz;
+extern unsigned struct_nvmm_ioc_hva_map_sz;
+extern unsigned struct_nvmm_ioc_hva_unmap_sz;
+extern unsigned struct_nvmm_ioc_ctl_sz;
+#endif
+extern unsigned struct_spi_ioctl_configure_sz;
+extern unsigned struct_spi_ioctl_transfer_sz;
+extern unsigned struct_autofs_daemon_request_sz;
+extern unsigned struct_autofs_daemon_done_sz;
+extern unsigned struct_sctp_connectx_addrs_sz;
extern unsigned struct_usb_device_info_old_sz;
extern unsigned struct_usb_device_info_sz;
extern unsigned struct_usb_device_stats_sz;
extern unsigned struct_vt_stat_sz;
extern unsigned struct_wdog_conf_sz;
extern unsigned struct_wdog_mode_sz;
+extern unsigned struct_ipmi_recv_sz;
+extern unsigned struct_ipmi_req_sz;
+extern unsigned struct_ipmi_cmdspec_sz;
extern unsigned struct_wfq_conf_sz;
extern unsigned struct_wfq_getqid_sz;
extern unsigned struct_wfq_getstats_sz;
extern unsigned struct_isp_stats_sz;
extern unsigned struct_lsenable_sz;
extern unsigned struct_lsdisable_sz;
+extern unsigned struct_audio_format_query_sz;
extern unsigned struct_mixer_ctrl_sz;
extern unsigned struct_mixer_devinfo_sz;
extern unsigned struct_mpu_command_rec_sz;
extern unsigned struct_RF_ComponentLabel_sz;
extern unsigned struct_RF_SingleComponent_sz;
extern unsigned struct_RF_ProgressInfo_sz;
+extern unsigned struct_nvlist_ref_sz;
+extern unsigned struct_StringList_sz;
// A special value to mark ioctls that are not present on the target platform,
extern unsigned IOCTL_MLX_GET_SYSDRIVE;
extern unsigned IOCTL_MLX_GET_CINFO;
extern unsigned IOCTL_NVME_PASSTHROUGH_CMD;
+extern unsigned IOCTL_FWCFGIO_SET_INDEX;
extern unsigned IOCTL_IRDA_RESET_PARAMS;
extern unsigned IOCTL_IRDA_SET_PARAMS;
extern unsigned IOCTL_IRDA_GET_SPEEDMASK;
extern unsigned IOCTL_IRFRAMETTY_GET_DEVICE;
extern unsigned IOCTL_IRFRAMETTY_GET_DONGLE;
extern unsigned IOCTL_IRFRAMETTY_SET_DONGLE;
-extern unsigned IOCTL_SATIORESET;
-extern unsigned IOCTL_SATIOGID;
-extern unsigned IOCTL_SATIOSBUFSIZE;
extern unsigned IOCTL_ISV_CMD;
extern unsigned IOCTL_WTQICMD;
extern unsigned IOCTL_ISCSI_GET_VERSION;
extern unsigned IOCTL_SPKRTUNE;
extern unsigned IOCTL_SPKRGETVOL;
extern unsigned IOCTL_SPKRSETVOL;
+#if defined(__x86_64__)
+extern unsigned IOCTL_NVMM_IOC_CAPABILITY;
+extern unsigned IOCTL_NVMM_IOC_MACHINE_CREATE;
+extern unsigned IOCTL_NVMM_IOC_MACHINE_DESTROY;
+extern unsigned IOCTL_NVMM_IOC_MACHINE_CONFIGURE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_CREATE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_DESTROY;
+extern unsigned IOCTL_NVMM_IOC_VCPU_SETSTATE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_GETSTATE;
+extern unsigned IOCTL_NVMM_IOC_VCPU_INJECT;
+extern unsigned IOCTL_NVMM_IOC_VCPU_RUN;
+extern unsigned IOCTL_NVMM_IOC_GPA_MAP;
+extern unsigned IOCTL_NVMM_IOC_GPA_UNMAP;
+extern unsigned IOCTL_NVMM_IOC_HVA_MAP;
+extern unsigned IOCTL_NVMM_IOC_HVA_UNMAP;
+extern unsigned IOCTL_NVMM_IOC_CTL;
+#endif
+extern unsigned IOCTL_AUTOFSREQUEST;
+extern unsigned IOCTL_AUTOFSDONE;
extern unsigned IOCTL_BIOCGBLEN;
extern unsigned IOCTL_BIOCSBLEN;
extern unsigned IOCTL_BIOCSETF;
extern unsigned IOCTL_BIOCSHDRCMPLT;
extern unsigned IOCTL_BIOCSDLT;
extern unsigned IOCTL_BIOCGDLTLIST;
-extern unsigned IOCTL_BIOCGSEESENT;
-extern unsigned IOCTL_BIOCSSEESENT;
+extern unsigned IOCTL_BIOCGDIRECTION;
+extern unsigned IOCTL_BIOCSDIRECTION;
extern unsigned IOCTL_BIOCSRTIMEOUT;
extern unsigned IOCTL_BIOCGRTIMEOUT;
extern unsigned IOCTL_BIOCGFEEDBACK;
extern unsigned IOCTL_BIOCSFEEDBACK;
-extern unsigned IOCTL_SIOCRAWATM;
-extern unsigned IOCTL_SIOCATMENA;
-extern unsigned IOCTL_SIOCATMDIS;
-extern unsigned IOCTL_SIOCSPVCTX;
-extern unsigned IOCTL_SIOCGPVCTX;
-extern unsigned IOCTL_SIOCSPVCSIF;
-extern unsigned IOCTL_SIOCGPVCSIF;
extern unsigned IOCTL_GRESADDRS;
extern unsigned IOCTL_GRESADDRD;
extern unsigned IOCTL_GREGADDRS;
extern unsigned IOCTL_SIOCGNATS;
extern unsigned IOCTL_SIOCGNATL;
extern unsigned IOCTL_SIOCPURGENAT;
+extern unsigned IOCTL_SIOCCONNECTX;
+extern unsigned IOCTL_SIOCCONNECTXDEL;
extern unsigned IOCTL_SIOCSIFINFO_FLAGS;
extern unsigned IOCTL_SIOCAADDRCTL_POLICY;
extern unsigned IOCTL_SIOCDADDRCTL_POLICY;
extern unsigned IOCTL_AUDIO_GETBUFINFO;
extern unsigned IOCTL_AUDIO_SETCHAN;
extern unsigned IOCTL_AUDIO_GETCHAN;
+extern unsigned IOCTL_AUDIO_QUERYFORMAT;
+extern unsigned IOCTL_AUDIO_GETFORMAT;
+extern unsigned IOCTL_AUDIO_SETFORMAT;
extern unsigned IOCTL_AUDIO_MIXER_READ;
extern unsigned IOCTL_AUDIO_MIXER_WRITE;
extern unsigned IOCTL_AUDIO_MIXER_DEVINFO;
extern unsigned IOCTL_DIOCMWEDGES;
extern unsigned IOCTL_DIOCGSECTORSIZE;
extern unsigned IOCTL_DIOCGMEDIASIZE;
+extern unsigned IOCTL_DIOCRMWEDGES;
extern unsigned IOCTL_DRVDETACHDEV;
extern unsigned IOCTL_DRVRESCANBUS;
extern unsigned IOCTL_DRVCTLCOMMAND;
extern unsigned IOCTL_FDIOCFORMAT_TRACK;
extern unsigned IOCTL_FIOCLEX;
extern unsigned IOCTL_FIONCLEX;
+extern unsigned IOCTL_FIOSEEKDATA;
+extern unsigned IOCTL_FIOSEEKHOLE;
extern unsigned IOCTL_FIONREAD;
extern unsigned IOCTL_FIONBIO;
extern unsigned IOCTL_FIOASYNC;
extern unsigned IOCTL_SEQUENCER_TMR_SOURCE;
extern unsigned IOCTL_SEQUENCER_TMR_METRONOME;
extern unsigned IOCTL_SEQUENCER_TMR_SELECT;
+extern unsigned IOCTL_SPI_IOCTL_CONFIGURE;
+extern unsigned IOCTL_SPI_IOCTL_TRANSFER;
extern unsigned IOCTL_MTIOCTOP;
extern unsigned IOCTL_MTIOCGET;
extern unsigned IOCTL_MTIOCIEOT;
extern unsigned IOCTL_MTIOCHLOCATE;
extern unsigned IOCTL_POWER_EVENT_RECVDICT;
extern unsigned IOCTL_POWER_IOC_GET_TYPE;
-extern unsigned IOCTL_POWER_IOC_GET_TYPE_WITH_LOSSAGE;
extern unsigned IOCTL_RIOCGINFO;
extern unsigned IOCTL_RIOCSINFO;
extern unsigned IOCTL_RIOCSSRCH;
extern unsigned IOCTL_SIOCATMARK;
extern unsigned IOCTL_SIOCSPGRP;
extern unsigned IOCTL_SIOCGPGRP;
+extern unsigned IOCTL_SIOCPEELOFF;
extern unsigned IOCTL_SIOCADDRT;
extern unsigned IOCTL_SIOCDELRT;
extern unsigned IOCTL_SIOCSIFADDR;
extern unsigned IOCTL_SIOCSLINKSTR;
extern unsigned IOCTL_SIOCGETHERCAP;
extern unsigned IOCTL_SIOCGIFINDEX;
+extern unsigned IOCTL_SIOCSETHERCAP;
+extern unsigned IOCTL_SIOCSIFDESCR;
+extern unsigned IOCTL_SIOCGIFDESCR;
+extern unsigned IOCTL_SIOCGUMBINFO;
+extern unsigned IOCTL_SIOCSUMBPARAM;
+extern unsigned IOCTL_SIOCGUMBPARAM;
extern unsigned IOCTL_SIOCSETPFSYNC;
extern unsigned IOCTL_SIOCGETPFSYNC;
extern unsigned IOCTL_PPS_IOC_CREATE;
extern unsigned IOCTL_WDOGIOC_TICKLE;
extern unsigned IOCTL_WDOGIOC_GTICKLER;
extern unsigned IOCTL_WDOGIOC_GWDOGS;
+extern unsigned IOCTL_KCOV_IOC_SETBUFSIZE;
+extern unsigned IOCTL_KCOV_IOC_ENABLE;
+extern unsigned IOCTL_KCOV_IOC_DISABLE;
+extern unsigned IOCTL_IPMICTL_RECEIVE_MSG_TRUNC;
+extern unsigned IOCTL_IPMICTL_RECEIVE_MSG;
+extern unsigned IOCTL_IPMICTL_SEND_COMMAND;
+extern unsigned IOCTL_IPMICTL_REGISTER_FOR_CMD;
+extern unsigned IOCTL_IPMICTL_UNREGISTER_FOR_CMD;
+extern unsigned IOCTL_IPMICTL_SET_GETS_EVENTS_CMD;
+extern unsigned IOCTL_IPMICTL_SET_MY_ADDRESS_CMD;
+extern unsigned IOCTL_IPMICTL_GET_MY_ADDRESS_CMD;
+extern unsigned IOCTL_IPMICTL_SET_MY_LUN_CMD;
+extern unsigned IOCTL_IPMICTL_GET_MY_LUN_CMD;
extern unsigned IOCTL_SNDCTL_DSP_RESET;
extern unsigned IOCTL_SNDCTL_DSP_SYNC;
extern unsigned IOCTL_SNDCTL_DSP_SPEED;
extern const int si_SEGV_MAPERR;
extern const int si_SEGV_ACCERR;
+
+extern const unsigned SHA1_CTX_sz;
+extern const unsigned SHA1_return_length;
+
+extern const unsigned MD4_CTX_sz;
+extern const unsigned MD4_return_length;
+
+extern const unsigned RMD160_CTX_sz;
+extern const unsigned RMD160_return_length;
+
+extern const unsigned MD5_CTX_sz;
+extern const unsigned MD5_return_length;
+
+extern const unsigned fpos_t_sz;
+
+extern const unsigned MD2_CTX_sz;
+extern const unsigned MD2_return_length;
+
+#define SHA2_EXTERN(LEN) \
+ extern const unsigned SHA##LEN##_CTX_sz; \
+ extern const unsigned SHA##LEN##_return_length; \
+ extern const unsigned SHA##LEN##_block_length; \
+ extern const unsigned SHA##LEN##_digest_length
+
+SHA2_EXTERN(224);
+SHA2_EXTERN(256);
+SHA2_EXTERN(384);
+SHA2_EXTERN(512);
+
+#undef SHA2_EXTERN
+
+extern const int unvis_valid;
+extern const int unvis_validpush;
+
+struct __sanitizer_cdbr {
+ void (*unmap)(void *, void *, uptr);
+ void *cookie;
+ u8 *mmap_base;
+ uptr mmap_size;
+
+ u8 *hash_base;
+ u8 *offset_base;
+ u8 *data_base;
+
+ u32 data_size;
+ u32 entries;
+ u32 entries_index;
+ u32 seed;
+
+ u8 offset_size;
+ u8 index_size;
+
+ u32 entries_m;
+ u32 entries_index_m;
+ u8 entries_s1, entries_s2;
+ u8 entries_index_s1, entries_index_s2;
+};
+
+struct __sanitizer_cdbw {
+ uptr data_counter;
+ uptr data_allocated;
+ uptr data_size;
+ uptr *data_len;
+ void **data_ptr;
+ uptr hash_size;
+ void *hash;
+ uptr key_counter;
+};
} // namespace __sanitizer
#define CHECK_TYPE_SIZE(TYPE) \
+++ /dev/null
-//===-- sanitizer_platform_limits_openbsd.cc ------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of Sanitizer common code.
-//
-// Sizes and layouts of platform-specific NetBSD data structures.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_OPENBSD
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <glob.h>
-#include <grp.h>
-#include <ifaddrs.h>
-#include <limits.h>
-#include <link_elf.h>
-#include <sys/socket.h>
-#include <net/if.h>
-#include <net/ppp_defs.h>
-#include <net/route.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/ip_mroute.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <soundcard.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <sys/filio.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/msg.h>
-#include <sys/mtio.h>
-#include <sys/ptrace.h>
-#include <sys/resource.h>
-#include <sys/shm.h>
-#include <sys/signal.h>
-#include <sys/sockio.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/time.h>
-#include <sys/times.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <term.h>
-#include <time.h>
-#include <utime.h>
-#include <utmp.h>
-#include <wchar.h>
-
-// Include these after system headers to avoid name clashes and ambiguities.
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_openbsd.h"
-
-namespace __sanitizer {
-unsigned struct_utsname_sz = sizeof(struct utsname);
-unsigned struct_stat_sz = sizeof(struct stat);
-unsigned struct_rusage_sz = sizeof(struct rusage);
-unsigned struct_tm_sz = sizeof(struct tm);
-unsigned struct_passwd_sz = sizeof(struct passwd);
-unsigned struct_group_sz = sizeof(struct group);
-unsigned siginfo_t_sz = sizeof(siginfo_t);
-unsigned struct_sigaction_sz = sizeof(struct sigaction);
-unsigned struct_itimerval_sz = sizeof(struct itimerval);
-unsigned pthread_t_sz = sizeof(pthread_t);
-unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
-unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
-unsigned pid_t_sz = sizeof(pid_t);
-unsigned timeval_sz = sizeof(timeval);
-unsigned uid_t_sz = sizeof(uid_t);
-unsigned gid_t_sz = sizeof(gid_t);
-unsigned mbstate_t_sz = sizeof(mbstate_t);
-unsigned sigset_t_sz = sizeof(sigset_t);
-unsigned struct_timezone_sz = sizeof(struct timezone);
-unsigned struct_tms_sz = sizeof(struct tms);
-unsigned struct_sched_param_sz = sizeof(struct sched_param);
-unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
-unsigned struct_rlimit_sz = sizeof(struct rlimit);
-unsigned struct_timespec_sz = sizeof(struct timespec);
-unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
-unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
-unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
-unsigned struct_statvfs_sz = sizeof(struct statvfs);
-
-const uptr sig_ign = (uptr)SIG_IGN;
-const uptr sig_dfl = (uptr)SIG_DFL;
-const uptr sig_err = (uptr)SIG_ERR;
-const uptr sa_siginfo = (uptr)SA_SIGINFO;
-
-int shmctl_ipc_stat = (int)IPC_STAT;
-
-unsigned struct_utmp_sz = sizeof(struct utmp);
-
-int map_fixed = MAP_FIXED;
-
-int af_inet = (int)AF_INET;
-int af_inet6 = (int)AF_INET6;
-
-uptr __sanitizer_in_addr_sz(int af) {
- if (af == AF_INET)
- return sizeof(struct in_addr);
- else if (af == AF_INET6)
- return sizeof(struct in6_addr);
- else
- return 0;
-}
-
-unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
-
-int glob_nomatch = GLOB_NOMATCH;
-int glob_altdirfunc = GLOB_ALTDIRFUNC;
-
-unsigned path_max = PATH_MAX;
-
-const int si_SEGV_MAPERR = SEGV_MAPERR;
-const int si_SEGV_ACCERR = SEGV_ACCERR;
-} // namespace __sanitizer
-
-using namespace __sanitizer;
-
-COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
-
-COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
-CHECK_TYPE_SIZE(pthread_key_t);
-
-CHECK_TYPE_SIZE(dl_phdr_info);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
-
-CHECK_TYPE_SIZE(glob_t);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
-
-CHECK_TYPE_SIZE(addrinfo);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_next);
-
-CHECK_TYPE_SIZE(hostent);
-CHECK_SIZE_AND_OFFSET(hostent, h_name);
-CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
-CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
-CHECK_SIZE_AND_OFFSET(hostent, h_length);
-CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
-
-CHECK_TYPE_SIZE(iovec);
-CHECK_SIZE_AND_OFFSET(iovec, iov_base);
-CHECK_SIZE_AND_OFFSET(iovec, iov_len);
-
-CHECK_TYPE_SIZE(msghdr);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
-
-CHECK_TYPE_SIZE(cmsghdr);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
-
-COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
-CHECK_SIZE_AND_OFFSET(dirent, d_fileno);
-CHECK_SIZE_AND_OFFSET(dirent, d_off);
-CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
-
-CHECK_TYPE_SIZE(ifconf);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
-
-CHECK_TYPE_SIZE(pollfd);
-CHECK_SIZE_AND_OFFSET(pollfd, fd);
-CHECK_SIZE_AND_OFFSET(pollfd, events);
-CHECK_SIZE_AND_OFFSET(pollfd, revents);
-
-CHECK_TYPE_SIZE(nfds_t);
-
-CHECK_TYPE_SIZE(sigset_t);
-
-COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
-// Can't write checks for sa_handler and sa_sigaction due to them being
-// preprocessor macros.
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
-
-CHECK_TYPE_SIZE(tm);
-CHECK_SIZE_AND_OFFSET(tm, tm_sec);
-CHECK_SIZE_AND_OFFSET(tm, tm_min);
-CHECK_SIZE_AND_OFFSET(tm, tm_hour);
-CHECK_SIZE_AND_OFFSET(tm, tm_mday);
-CHECK_SIZE_AND_OFFSET(tm, tm_mon);
-CHECK_SIZE_AND_OFFSET(tm, tm_year);
-CHECK_SIZE_AND_OFFSET(tm, tm_wday);
-CHECK_SIZE_AND_OFFSET(tm, tm_yday);
-CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
-CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
-CHECK_SIZE_AND_OFFSET(tm, tm_zone);
-
-CHECK_TYPE_SIZE(ipc_perm);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
-CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
-CHECK_SIZE_AND_OFFSET(ipc_perm, key);
-
-CHECK_TYPE_SIZE(shmid_ds);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_atimensec);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_dtimensec);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_ctimensec);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
-
-CHECK_TYPE_SIZE(clock_t);
-
-CHECK_TYPE_SIZE(ifaddrs);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
-// Compare against the union, because we can't reach into the union in a
-// compliant way.
-#ifdef ifa_dstaddr
-#undef ifa_dstaddr
-#endif
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
-
-CHECK_TYPE_SIZE(passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_name);
-CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
-CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
-
-CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
-
-CHECK_TYPE_SIZE(group);
-CHECK_SIZE_AND_OFFSET(group, gr_name);
-CHECK_SIZE_AND_OFFSET(group, gr_passwd);
-CHECK_SIZE_AND_OFFSET(group, gr_gid);
-CHECK_SIZE_AND_OFFSET(group, gr_mem);
-
-#endif // SANITIZER_OPENBSD
--- /dev/null
+//===-- sanitizer_platform_limits_openbsd.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific NetBSD data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_OPENBSD
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <glob.h>
+#include <grp.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <link_elf.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/ppp_defs.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <netinet/ip_mroute.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <soundcard.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/filio.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/msg.h>
+#include <sys/mtio.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/shm.h>
+#include <sys/signal.h>
+#include <sys/sockio.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <term.h>
+#include <time.h>
+#include <utime.h>
+#include <utmp.h>
+#include <wchar.h>
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_openbsd.h"
+
+namespace __sanitizer {
+unsigned struct_utsname_sz = sizeof(struct utsname);
+unsigned struct_stat_sz = sizeof(struct stat);
+unsigned struct_rusage_sz = sizeof(struct rusage);
+unsigned struct_tm_sz = sizeof(struct tm);
+unsigned struct_passwd_sz = sizeof(struct passwd);
+unsigned struct_group_sz = sizeof(struct group);
+unsigned siginfo_t_sz = sizeof(siginfo_t);
+unsigned struct_sigaction_sz = sizeof(struct sigaction);
+unsigned struct_itimerval_sz = sizeof(struct itimerval);
+unsigned pthread_t_sz = sizeof(pthread_t);
+unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
+unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+unsigned pid_t_sz = sizeof(pid_t);
+unsigned timeval_sz = sizeof(timeval);
+unsigned uid_t_sz = sizeof(uid_t);
+unsigned gid_t_sz = sizeof(gid_t);
+unsigned mbstate_t_sz = sizeof(mbstate_t);
+unsigned sigset_t_sz = sizeof(sigset_t);
+unsigned struct_timezone_sz = sizeof(struct timezone);
+unsigned struct_tms_sz = sizeof(struct tms);
+unsigned struct_sched_param_sz = sizeof(struct sched_param);
+unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+unsigned struct_rlimit_sz = sizeof(struct rlimit);
+unsigned struct_timespec_sz = sizeof(struct timespec);
+unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
+unsigned struct_statvfs_sz = sizeof(struct statvfs);
+
+const uptr sig_ign = (uptr)SIG_IGN;
+const uptr sig_dfl = (uptr)SIG_DFL;
+const uptr sig_err = (uptr)SIG_ERR;
+const uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+int shmctl_ipc_stat = (int)IPC_STAT;
+
+unsigned struct_utmp_sz = sizeof(struct utmp);
+
+int map_fixed = MAP_FIXED;
+
+int af_inet = (int)AF_INET;
+int af_inet6 = (int)AF_INET6;
+
+uptr __sanitizer_in_addr_sz(int af) {
+ if (af == AF_INET)
+ return sizeof(struct in_addr);
+ else if (af == AF_INET6)
+ return sizeof(struct in6_addr);
+ else
+ return 0;
+}
+
+unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+
+int glob_nomatch = GLOB_NOMATCH;
+int glob_altdirfunc = GLOB_ALTDIRFUNC;
+
+unsigned path_max = PATH_MAX;
+
+const int si_SEGV_MAPERR = SEGV_MAPERR;
+const int si_SEGV_ACCERR = SEGV_ACCERR;
+} // namespace __sanitizer
+
+using namespace __sanitizer;
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+CHECK_TYPE_SIZE(dl_phdr_info);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_next);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_fileno);
+CHECK_SIZE_AND_OFFSET(dirent, d_off);
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+// Can't write checks for sa_handler and sa_sigaction due to them being
+// preprocessor macros.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
+CHECK_SIZE_AND_OFFSET(tm, tm_zone);
+
+CHECK_TYPE_SIZE(ipc_perm);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_atimensec);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_dtimensec);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, __shm_ctimensec);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+
+CHECK_TYPE_SIZE(clock_t);
+
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+// Compare against the union, because we can't reach into the union in a
+// compliant way.
+#ifdef ifa_dstaddr
+#undef ifa_dstaddr
+#endif
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+#endif // SANITIZER_OPENBSD
//===-- sanitizer_platform_limits_openbsd.h -------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_platform_limits_posix.cc --------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of Sanitizer common code.
-//
-// Sizes and layouts of platform-specific POSIX data structures.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
-// Tests in this file assume that off_t-dependent data structures match the
-// libc ABI. For example, struct dirent here is what readdir() function (as
-// exported from libc) returns, and not the user-facing "dirent", which
-// depends on _FILE_OFFSET_BITS setting.
-// To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below.
-#ifdef _FILE_OFFSET_BITS
-#undef _FILE_OFFSET_BITS
-#endif
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <grp.h>
-#include <limits.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <signal.h>
-#include <stddef.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/times.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <termios.h>
-#include <time.h>
-#include <wchar.h>
-#if !SANITIZER_MAC && !SANITIZER_FREEBSD
-#include <utmp.h>
-#endif
-
-#if !SANITIZER_IOS
-#include <net/route.h>
-#endif
-
-#if !SANITIZER_ANDROID
-#include <sys/mount.h>
-#include <sys/timeb.h>
-#include <utmpx.h>
-#endif
-
-#if SANITIZER_LINUX
-#include <malloc.h>
-#include <mntent.h>
-#include <netinet/ether.h>
-#include <sys/sysinfo.h>
-#include <sys/vt.h>
-#include <linux/cdrom.h>
-#include <linux/fd.h>
-#include <linux/fs.h>
-#include <linux/hdreg.h>
-#include <linux/input.h>
-#include <linux/ioctl.h>
-#include <linux/soundcard.h>
-#include <linux/sysctl.h>
-#include <linux/utsname.h>
-#include <linux/posix_types.h>
-#include <net/if_arp.h>
-#endif
-
-#if SANITIZER_FREEBSD
-# include <sys/mount.h>
-# include <sys/sockio.h>
-# include <sys/socket.h>
-# include <sys/filio.h>
-# include <sys/signal.h>
-# include <sys/timespec.h>
-# include <sys/timex.h>
-# include <sys/mqueue.h>
-# include <sys/msg.h>
-# include <sys/ipc.h>
-# include <sys/msg.h>
-# include <sys/statvfs.h>
-# include <sys/soundcard.h>
-# include <sys/mtio.h>
-# include <sys/consio.h>
-# include <sys/kbio.h>
-# include <sys/link_elf.h>
-# include <netinet/ip_mroute.h>
-# include <netinet/in.h>
-# include <net/ethernet.h>
-# include <net/ppp_defs.h>
-# include <glob.h>
-# include <term.h>
-
-#define _KERNEL // to declare 'shminfo' structure
-# include <sys/shm.h>
-#undef _KERNEL
-
-#undef INLINE // to avoid clashes with sanitizers' definitions
-#endif
-
-#if SANITIZER_FREEBSD || SANITIZER_IOS
-#undef IOC_DIRMASK
-#endif
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
-# include <utime.h>
-# include <sys/ptrace.h>
-# if defined(__mips64) || defined(__aarch64__) || defined(__arm__)
-# include <asm/ptrace.h>
-# ifdef __arm__
-typedef struct user_fpregs elf_fpregset_t;
-# define ARM_VFPREGS_SIZE_ASAN (32 * 8 /*fpregs*/ + 4 /*fpscr*/)
-# if !defined(ARM_VFPREGS_SIZE)
-# define ARM_VFPREGS_SIZE ARM_VFPREGS_SIZE_ASAN
-# endif
-# endif
-# endif
-# include <semaphore.h>
-#endif
-
-#if !SANITIZER_ANDROID
-#include <ifaddrs.h>
-#include <sys/ucontext.h>
-#include <wordexp.h>
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-#include <glob.h>
-#include <obstack.h>
-#include <mqueue.h>
-#include <net/if_ppp.h>
-#include <netax25/ax25.h>
-#include <netipx/ipx.h>
-#include <netrom/netrom.h>
-#if HAVE_RPC_XDR_H
-# include <rpc/xdr.h>
-#elif HAVE_TIRPC_RPC_XDR_H
-# include <tirpc/rpc/xdr.h>
-#endif
-#include <scsi/scsi.h>
-#include <sys/mtio.h>
-#include <sys/kd.h>
-#include <sys/shm.h>
-#include <sys/statvfs.h>
-#include <sys/timex.h>
-#if defined(__mips64)
-# include <sys/procfs.h>
-#endif
-#include <sys/user.h>
-#include <linux/cyclades.h>
-#include <linux/if_eql.h>
-#include <linux/if_plip.h>
-#include <linux/lp.h>
-#include <linux/mroute.h>
-#include <linux/mroute6.h>
-#include <linux/scc.h>
-#include <linux/serial.h>
-#include <sys/msg.h>
-#include <sys/ipc.h>
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-
-#if SANITIZER_ANDROID
-#include <linux/kd.h>
-#include <linux/mtio.h>
-#include <linux/ppp_defs.h>
-#include <linux/if_ppp.h>
-#endif
-
-#if SANITIZER_LINUX
-#include <link.h>
-#include <sys/vfs.h>
-#include <sys/epoll.h>
-#include <linux/capability.h>
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_MAC
-#include <net/ethernet.h>
-#include <sys/filio.h>
-#include <sys/sockio.h>
-#endif
-
-// Include these after system headers to avoid name clashes and ambiguities.
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_posix.h"
-
-namespace __sanitizer {
- unsigned struct_utsname_sz = sizeof(struct utsname);
- unsigned struct_stat_sz = sizeof(struct stat);
-#if !SANITIZER_IOS && !SANITIZER_FREEBSD
- unsigned struct_stat64_sz = sizeof(struct stat64);
-#endif // !SANITIZER_IOS && !SANITIZER_FREEBSD
- unsigned struct_rusage_sz = sizeof(struct rusage);
- unsigned struct_tm_sz = sizeof(struct tm);
- unsigned struct_passwd_sz = sizeof(struct passwd);
- unsigned struct_group_sz = sizeof(struct group);
- unsigned siginfo_t_sz = sizeof(siginfo_t);
- unsigned struct_sigaction_sz = sizeof(struct sigaction);
- unsigned struct_itimerval_sz = sizeof(struct itimerval);
- unsigned pthread_t_sz = sizeof(pthread_t);
- unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
- unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
- unsigned pid_t_sz = sizeof(pid_t);
- unsigned timeval_sz = sizeof(timeval);
- unsigned uid_t_sz = sizeof(uid_t);
- unsigned gid_t_sz = sizeof(gid_t);
- unsigned mbstate_t_sz = sizeof(mbstate_t);
- unsigned sigset_t_sz = sizeof(sigset_t);
- unsigned struct_timezone_sz = sizeof(struct timezone);
- unsigned struct_tms_sz = sizeof(struct tms);
- unsigned struct_sigevent_sz = sizeof(struct sigevent);
- unsigned struct_sched_param_sz = sizeof(struct sched_param);
-
-
-#if SANITIZER_MAC && !SANITIZER_IOS
- unsigned struct_statfs64_sz = sizeof(struct statfs64);
-#endif // SANITIZER_MAC && !SANITIZER_IOS
-
-#if !SANITIZER_ANDROID
- unsigned struct_statfs_sz = sizeof(struct statfs);
- unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
- unsigned ucontext_t_sz = sizeof(ucontext_t);
-#endif // !SANITIZER_ANDROID
-
-#if SANITIZER_LINUX
- unsigned struct_epoll_event_sz = sizeof(struct epoll_event);
- unsigned struct_sysinfo_sz = sizeof(struct sysinfo);
- unsigned __user_cap_header_struct_sz =
- sizeof(struct __user_cap_header_struct);
- unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct);
- unsigned struct_new_utsname_sz = sizeof(struct new_utsname);
- unsigned struct_old_utsname_sz = sizeof(struct old_utsname);
- unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
- unsigned struct_rlimit_sz = sizeof(struct rlimit);
- unsigned struct_timespec_sz = sizeof(struct timespec);
- unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
- unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- // Use pre-computed size of struct ustat to avoid <sys/ustat.h> which
- // has been removed from glibc 2.28.
-#if defined(__aarch64__) || defined(__s390x__) || defined (__mips64) \
- || defined(__powerpc64__) || defined(__arch64__) || defined(__sparcv9) \
- || defined(__x86_64__)
-#define SIZEOF_STRUCT_USTAT 32
-#elif defined(__arm__) || defined(__i386__) || defined(__mips__) \
- || defined(__powerpc__) || defined(__s390__) || defined(__sparc__)
-#define SIZEOF_STRUCT_USTAT 20
-#else
-#error Unknown size of struct ustat
-#endif
- unsigned struct_ustat_sz = SIZEOF_STRUCT_USTAT;
- unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
- unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
- unsigned struct_timex_sz = sizeof(struct timex);
- unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
- unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
- unsigned struct_statvfs_sz = sizeof(struct statvfs);
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
-
- const uptr sig_ign = (uptr)SIG_IGN;
- const uptr sig_dfl = (uptr)SIG_DFL;
- const uptr sig_err = (uptr)SIG_ERR;
- const uptr sa_siginfo = (uptr)SA_SIGINFO;
-
-#if SANITIZER_LINUX
- int e_tabsz = (int)E_TABSZ;
-#endif
-
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
- unsigned struct_shminfo_sz = sizeof(struct shminfo);
- unsigned struct_shm_info_sz = sizeof(struct shm_info);
- int shmctl_ipc_stat = (int)IPC_STAT;
- int shmctl_ipc_info = (int)IPC_INFO;
- int shmctl_shm_info = (int)SHM_INFO;
- int shmctl_shm_stat = (int)SHM_STAT;
-#endif
-
-#if !SANITIZER_MAC && !SANITIZER_FREEBSD
- unsigned struct_utmp_sz = sizeof(struct utmp);
-#endif
-#if !SANITIZER_ANDROID
- unsigned struct_utmpx_sz = sizeof(struct utmpx);
-#endif
-
- int map_fixed = MAP_FIXED;
-
- int af_inet = (int)AF_INET;
- int af_inet6 = (int)AF_INET6;
-
- uptr __sanitizer_in_addr_sz(int af) {
- if (af == AF_INET)
- return sizeof(struct in_addr);
- else if (af == AF_INET6)
- return sizeof(struct in6_addr);
- else
- return 0;
- }
-
-#if SANITIZER_LINUX
-unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
-#elif SANITIZER_FREEBSD
-unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
-#endif
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
- int glob_nomatch = GLOB_NOMATCH;
- int glob_altdirfunc = GLOB_ALTDIRFUNC;
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
- (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
- defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
- defined(__s390__))
-#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
- unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
- unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
-#elif defined(__aarch64__)
- unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs);
- unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state);
-#elif defined(__s390__)
- unsigned struct_user_regs_struct_sz = sizeof(struct _user_regs_struct);
- unsigned struct_user_fpregs_struct_sz = sizeof(struct _user_fpregs_struct);
-#else
- unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
- unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
-#endif // __mips64 || __powerpc64__ || __aarch64__
-#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
- defined(__aarch64__) || defined(__arm__) || defined(__s390__)
- unsigned struct_user_fpxregs_struct_sz = 0;
-#else
- unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
-#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
-// || __s390__
-#ifdef __arm__
- unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
-#else
- unsigned struct_user_vfpregs_struct_sz = 0;
-#endif
-
- int ptrace_peektext = PTRACE_PEEKTEXT;
- int ptrace_peekdata = PTRACE_PEEKDATA;
- int ptrace_peekuser = PTRACE_PEEKUSER;
-#if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \
- (defined(PT_GETREGS) && defined(PT_SETREGS))
- int ptrace_getregs = PTRACE_GETREGS;
- int ptrace_setregs = PTRACE_SETREGS;
-#else
- int ptrace_getregs = -1;
- int ptrace_setregs = -1;
-#endif
-#if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \
- (defined(PT_GETFPREGS) && defined(PT_SETFPREGS))
- int ptrace_getfpregs = PTRACE_GETFPREGS;
- int ptrace_setfpregs = PTRACE_SETFPREGS;
-#else
- int ptrace_getfpregs = -1;
- int ptrace_setfpregs = -1;
-#endif
-#if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \
- (defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS))
- int ptrace_getfpxregs = PTRACE_GETFPXREGS;
- int ptrace_setfpxregs = PTRACE_SETFPXREGS;
-#else
- int ptrace_getfpxregs = -1;
- int ptrace_setfpxregs = -1;
-#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
-#if defined(PTRACE_GETVFPREGS) && defined(PTRACE_SETVFPREGS)
- int ptrace_getvfpregs = PTRACE_GETVFPREGS;
- int ptrace_setvfpregs = PTRACE_SETVFPREGS;
-#else
- int ptrace_getvfpregs = -1;
- int ptrace_setvfpregs = -1;
-#endif
- int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
-#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
- (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
- int ptrace_getsiginfo = PTRACE_GETSIGINFO;
- int ptrace_setsiginfo = PTRACE_SETSIGINFO;
-#else
- int ptrace_getsiginfo = -1;
- int ptrace_setsiginfo = -1;
-#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO
-#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET)
- int ptrace_getregset = PTRACE_GETREGSET;
- int ptrace_setregset = PTRACE_SETREGSET;
-#else
- int ptrace_getregset = -1;
- int ptrace_setregset = -1;
-#endif // PTRACE_GETREGSET/PTRACE_SETREGSET
-#endif
-
- unsigned path_max = PATH_MAX;
-
- // ioctl arguments
- unsigned struct_ifreq_sz = sizeof(struct ifreq);
- unsigned struct_termios_sz = sizeof(struct termios);
- unsigned struct_winsize_sz = sizeof(struct winsize);
-
-#if SANITIZER_LINUX
- unsigned struct_arpreq_sz = sizeof(struct arpreq);
- unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf);
- unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession);
- unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio);
- unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl);
- unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti);
- unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry);
- unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr);
- unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl);
- unsigned struct_ff_effect_sz = sizeof(struct ff_effect);
- unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params);
- unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct);
- unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state);
- unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors);
- unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd);
- unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct);
- unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors);
- unsigned struct_format_descr_sz = sizeof(struct format_descr);
- unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid);
- unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry);
- unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo);
- unsigned struct_input_id_sz = sizeof(struct input_id);
- unsigned struct_mtpos_sz = sizeof(struct mtpos);
- unsigned struct_rtentry_sz = sizeof(struct rtentry);
- unsigned struct_termio_sz = sizeof(struct termio);
- unsigned struct_vt_consize_sz = sizeof(struct vt_consize);
- unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes);
- unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
-#if SOUND_VERSION >= 0x040000
- unsigned struct_copr_buffer_sz = 0;
- unsigned struct_copr_debug_buf_sz = 0;
- unsigned struct_copr_msg_sz = 0;
-#else
- unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
- unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
- unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
-#endif
- unsigned struct_midi_info_sz = sizeof(struct midi_info);
- unsigned struct_mtget_sz = sizeof(struct mtget);
- unsigned struct_mtop_sz = sizeof(struct mtop);
- unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
- unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
- unsigned struct_synth_info_sz = sizeof(struct synth_info);
- unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
- unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor);
-#if EV_VERSION > (0x010000)
- unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry);
-#else
- unsigned struct_input_keymap_entry_sz = 0;
-#endif
- unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data);
- unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs);
- unsigned struct_kbentry_sz = sizeof(struct kbentry);
- unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode);
- unsigned struct_kbsentry_sz = sizeof(struct kbsentry);
- unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo);
- unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct);
- unsigned struct_scc_modem_sz = sizeof(struct scc_modem);
- unsigned struct_scc_stat_sz = sizeof(struct scc_stat);
- unsigned struct_serial_multiport_struct_sz
- = sizeof(struct serial_multiport_struct);
- unsigned struct_serial_struct_sz = sizeof(struct serial_struct);
- unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25);
- unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc);
- unsigned struct_unimapinit_sz = sizeof(struct unimapinit);
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
- unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
- unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
-
-#if !SANITIZER_ANDROID && !SANITIZER_MAC
- unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
- unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
-#endif
-
- const unsigned IOCTL_NOT_PRESENT = 0;
-
- unsigned IOCTL_FIOASYNC = FIOASYNC;
- unsigned IOCTL_FIOCLEX = FIOCLEX;
- unsigned IOCTL_FIOGETOWN = FIOGETOWN;
- unsigned IOCTL_FIONBIO = FIONBIO;
- unsigned IOCTL_FIONCLEX = FIONCLEX;
- unsigned IOCTL_FIOSETOWN = FIOSETOWN;
- unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
- unsigned IOCTL_SIOCATMARK = SIOCATMARK;
- unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
- unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
- unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
- unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
- unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
- unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
- unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
- unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
- unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
- unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
- unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
- unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
- unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
- unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
- unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
- unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
- unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
- unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
- unsigned IOCTL_TIOCCONS = TIOCCONS;
- unsigned IOCTL_TIOCEXCL = TIOCEXCL;
- unsigned IOCTL_TIOCGETD = TIOCGETD;
- unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
- unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
- unsigned IOCTL_TIOCMBIC = TIOCMBIC;
- unsigned IOCTL_TIOCMBIS = TIOCMBIS;
- unsigned IOCTL_TIOCMGET = TIOCMGET;
- unsigned IOCTL_TIOCMSET = TIOCMSET;
- unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
- unsigned IOCTL_TIOCNXCL = TIOCNXCL;
- unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
- unsigned IOCTL_TIOCPKT = TIOCPKT;
- unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
- unsigned IOCTL_TIOCSETD = TIOCSETD;
- unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
- unsigned IOCTL_TIOCSTI = TIOCSTI;
- unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
-#if ((SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID)
- unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
- unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
-#endif
-
-#if SANITIZER_LINUX
- unsigned IOCTL_EVIOCGABS = EVIOCGABS(0);
- unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0);
- unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS;
- unsigned IOCTL_EVIOCGID = EVIOCGID;
- unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0);
- unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE;
- unsigned IOCTL_EVIOCGLED = EVIOCGLED(0);
- unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0);
- unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0);
- unsigned IOCTL_EVIOCGRAB = EVIOCGRAB;
- unsigned IOCTL_EVIOCGREP = EVIOCGREP;
- unsigned IOCTL_EVIOCGSND = EVIOCGSND(0);
- unsigned IOCTL_EVIOCGSW = EVIOCGSW(0);
- unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0);
- unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION;
- unsigned IOCTL_EVIOCRMFF = EVIOCRMFF;
- unsigned IOCTL_EVIOCSABS = EVIOCSABS(0);
- unsigned IOCTL_EVIOCSFF = EVIOCSFF;
- unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE;
- unsigned IOCTL_EVIOCSREP = EVIOCSREP;
- unsigned IOCTL_BLKFLSBUF = BLKFLSBUF;
- unsigned IOCTL_BLKGETSIZE = BLKGETSIZE;
- unsigned IOCTL_BLKRAGET = BLKRAGET;
- unsigned IOCTL_BLKRASET = BLKRASET;
- unsigned IOCTL_BLKROGET = BLKROGET;
- unsigned IOCTL_BLKROSET = BLKROSET;
- unsigned IOCTL_BLKRRPART = BLKRRPART;
- unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ;
- unsigned IOCTL_CDROMEJECT = CDROMEJECT;
- unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW;
- unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION;
- unsigned IOCTL_CDROMPAUSE = CDROMPAUSE;
- unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF;
- unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND;
- unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO;
- unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED;
- unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1;
- unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2;
- unsigned IOCTL_CDROMREADRAW = CDROMREADRAW;
- unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY;
- unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR;
- unsigned IOCTL_CDROMRESET = CDROMRESET;
- unsigned IOCTL_CDROMRESUME = CDROMRESUME;
- unsigned IOCTL_CDROMSEEK = CDROMSEEK;
- unsigned IOCTL_CDROMSTART = CDROMSTART;
- unsigned IOCTL_CDROMSTOP = CDROMSTOP;
- unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL;
- unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL;
- unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD;
- unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC;
- unsigned IOCTL_FDCLRPRM = FDCLRPRM;
- unsigned IOCTL_FDDEFPRM = FDDEFPRM;
- unsigned IOCTL_FDFLUSH = FDFLUSH;
- unsigned IOCTL_FDFMTBEG = FDFMTBEG;
- unsigned IOCTL_FDFMTEND = FDFMTEND;
- unsigned IOCTL_FDFMTTRK = FDFMTTRK;
- unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM;
- unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT;
- unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP;
- unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT;
- unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS;
- unsigned IOCTL_FDGETPRM = FDGETPRM;
- unsigned IOCTL_FDMSGOFF = FDMSGOFF;
- unsigned IOCTL_FDMSGON = FDMSGON;
- unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT;
- unsigned IOCTL_FDRAWCMD = FDRAWCMD;
- unsigned IOCTL_FDRESET = FDRESET;
- unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM;
- unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH;
- unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS;
- unsigned IOCTL_FDSETPRM = FDSETPRM;
- unsigned IOCTL_FDTWADDLE = FDTWADDLE;
- unsigned IOCTL_FDWERRORCLR = FDWERRORCLR;
- unsigned IOCTL_FDWERRORGET = FDWERRORGET;
- unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD;
- unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO;
- unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT;
- unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA;
- unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY;
- unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS;
- unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT;
- unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR;
- unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR;
- unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT;
- unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA;
- unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS;
- unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT;
- unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR;
- unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR;
- unsigned IOCTL_MTIOCPOS = MTIOCPOS;
- unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP;
- unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG;
- unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS;
- unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT;
- unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP;
- unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP;
- unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG;
- unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS;
- unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID;
- unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU;
- unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP;
- unsigned IOCTL_SIOCADDRT = SIOCADDRT;
- unsigned IOCTL_SIOCDARP = SIOCDARP;
- unsigned IOCTL_SIOCDELRT = SIOCDELRT;
- unsigned IOCTL_SIOCDRARP = SIOCDRARP;
- unsigned IOCTL_SIOCGARP = SIOCGARP;
- unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP;
- unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR;
- unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP;
- unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM;
- unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME;
- unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE;
- unsigned IOCTL_SIOCGRARP = SIOCGRARP;
- unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP;
- unsigned IOCTL_SIOCSARP = SIOCSARP;
- unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP;
- unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR;
- unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK;
- unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP;
- unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM;
- unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE;
- unsigned IOCTL_SIOCSRARP = SIOCSRARP;
-# if SOUND_VERSION >= 0x040000
- unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT;
- unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT;
-# else // SOUND_VERSION
- unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT;
- unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD;
- unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE;
- unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG;
- unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA;
- unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET;
- unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN;
- unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG;
- unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE;
- unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA;
- unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS;
- unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS;
- unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER;
- unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
- unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS;
- unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
-#endif // SOUND_VERSION
- unsigned IOCTL_TCFLSH = TCFLSH;
- unsigned IOCTL_TCGETA = TCGETA;
- unsigned IOCTL_TCGETS = TCGETS;
- unsigned IOCTL_TCSBRK = TCSBRK;
- unsigned IOCTL_TCSBRKP = TCSBRKP;
- unsigned IOCTL_TCSETA = TCSETA;
- unsigned IOCTL_TCSETAF = TCSETAF;
- unsigned IOCTL_TCSETAW = TCSETAW;
- unsigned IOCTL_TCSETS = TCSETS;
- unsigned IOCTL_TCSETSF = TCSETSF;
- unsigned IOCTL_TCSETSW = TCSETSW;
- unsigned IOCTL_TCXONC = TCXONC;
- unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS;
- unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR;
- unsigned IOCTL_TIOCINQ = TIOCINQ;
- unsigned IOCTL_TIOCLINUX = TIOCLINUX;
- unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG;
- unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR;
- unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD;
- unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD;
- unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS;
- unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR;
- unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE;
- unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
- unsigned IOCTL_VT_RESIZE = VT_RESIZE;
- unsigned IOCTL_VT_RESIZEX = VT_RESIZEX;
- unsigned IOCTL_VT_SENDSIG = VT_SENDSIG;
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
- unsigned IOCTL_MTIOCGET = MTIOCGET;
- unsigned IOCTL_MTIOCTOP = MTIOCTOP;
- unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
- unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
- unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
- unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
- unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
- unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
- unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
- unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
- unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
- unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
- unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
- unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE;
- unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR;
- unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO;
- unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME;
- unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE;
- unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT;
- unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT;
- unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS;
- unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS;
- unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND;
- unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC;
- unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE;
- unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET;
- unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES;
- unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC;
- unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI;
- unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD;
- unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO;
- unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL;
- unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE;
- unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME;
- unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT;
- unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE;
- unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START;
- unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP;
- unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO;
- unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE;
- unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM;
- unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS;
- unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS;
- unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD;
- unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK;
- unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE;
- unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN;
- unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX;
- unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE;
- unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1;
- unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2;
- unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3;
- unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD;
- unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC;
- unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE;
- unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN;
- unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM;
- unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV;
- unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK;
- unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC;
- unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER;
- unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS;
- unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH;
- unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE;
- unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME;
- unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM;
- unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS;
- unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD;
- unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE;
- unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN;
- unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX;
- unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE;
- unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1;
- unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2;
- unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3;
- unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD;
- unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC;
- unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE;
- unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN;
- unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM;
- unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV;
- unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC;
- unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER;
- unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
- unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
- unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
- unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
- unsigned IOCTL_VT_GETMODE = VT_GETMODE;
- unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
- unsigned IOCTL_VT_RELDISP = VT_RELDISP;
- unsigned IOCTL_VT_SETMODE = VT_SETMODE;
- unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH;
- unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT;
- unsigned IOCTL_CYGETMON = CYGETMON;
- unsigned IOCTL_CYGETTHRESH = CYGETTHRESH;
- unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT;
- unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH;
- unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT;
- unsigned IOCTL_CYSETTHRESH = CYSETTHRESH;
- unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT;
- unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE;
- unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE;
- unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG;
- unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG;
- unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG;
- unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG;
-#if EV_VERSION > (0x010000)
- unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2;
- unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0);
- unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2;
-#else
- unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT;
- unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT;
- unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT;
-#endif
- unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS;
- unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION;
- unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS;
- unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION;
- unsigned IOCTL_GIO_CMAP = GIO_CMAP;
- unsigned IOCTL_GIO_FONT = GIO_FONT;
- unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP;
- unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP;
- unsigned IOCTL_KDADDIO = KDADDIO;
- unsigned IOCTL_KDDELIO = KDDELIO;
- unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE;
- unsigned IOCTL_KDGKBDIACR = KDGKBDIACR;
- unsigned IOCTL_KDGKBENT = KDGKBENT;
- unsigned IOCTL_KDGKBLED = KDGKBLED;
- unsigned IOCTL_KDGKBMETA = KDGKBMETA;
- unsigned IOCTL_KDGKBSENT = KDGKBSENT;
- unsigned IOCTL_KDMAPDISP = KDMAPDISP;
- unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE;
- unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT;
- unsigned IOCTL_KDSKBDIACR = KDSKBDIACR;
- unsigned IOCTL_KDSKBENT = KDSKBENT;
- unsigned IOCTL_KDSKBLED = KDSKBLED;
- unsigned IOCTL_KDSKBMETA = KDSKBMETA;
- unsigned IOCTL_KDSKBSENT = KDSKBSENT;
- unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP;
- unsigned IOCTL_LPABORT = LPABORT;
- unsigned IOCTL_LPABORTOPEN = LPABORTOPEN;
- unsigned IOCTL_LPCAREFUL = LPCAREFUL;
- unsigned IOCTL_LPCHAR = LPCHAR;
- unsigned IOCTL_LPGETIRQ = LPGETIRQ;
- unsigned IOCTL_LPGETSTATUS = LPGETSTATUS;
- unsigned IOCTL_LPRESET = LPRESET;
- unsigned IOCTL_LPSETIRQ = LPSETIRQ;
- unsigned IOCTL_LPTIME = LPTIME;
- unsigned IOCTL_LPWAIT = LPWAIT;
- unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG;
- unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG;
- unsigned IOCTL_PIO_CMAP = PIO_CMAP;
- unsigned IOCTL_PIO_FONT = PIO_FONT;
- unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP;
- unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR;
- unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP;
- unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN;
- unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST;
- unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE;
- unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE;
- unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT;
- unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT;
- unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID;
- unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID;
- unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS;
- unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID;
- unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID;
- unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS;
- unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP;
- unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA;
- unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS;
- unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS;
- unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL;
- unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS;
- unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL;
- unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI;
- unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI;
- unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
- unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
- unsigned IOCTL_KDDISABIO = KDDISABIO;
- unsigned IOCTL_KDENABIO = KDENABIO;
- unsigned IOCTL_KDGETLED = KDGETLED;
- unsigned IOCTL_KDGETMODE = KDGETMODE;
- unsigned IOCTL_KDGKBMODE = KDGKBMODE;
- unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
- unsigned IOCTL_KDMKTONE = KDMKTONE;
- unsigned IOCTL_KDSETLED = KDSETLED;
- unsigned IOCTL_KDSETMODE = KDSETMODE;
- unsigned IOCTL_KDSKBMODE = KDSKBMODE;
- unsigned IOCTL_KIOCSOUND = KIOCSOUND;
- unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
- unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
- unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
-
- const int si_SEGV_MAPERR = SEGV_MAPERR;
- const int si_SEGV_ACCERR = SEGV_ACCERR;
-} // namespace __sanitizer
-
-using namespace __sanitizer;
-
-COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
-
-COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
-CHECK_TYPE_SIZE(pthread_key_t);
-
-#if SANITIZER_LINUX
-// FIXME: We define those on Linux and Mac, but only check on Linux.
-COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS);
-COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS);
-COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS);
-COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS);
-COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK);
-COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK);
-COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK);
-COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK);
-COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT);
-COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT);
-COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT);
-COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT);
-COMPILER_CHECK(IOC_NONE == _IOC_NONE);
-COMPILER_CHECK(IOC_WRITE == _IOC_WRITE);
-COMPILER_CHECK(IOC_READ == _IOC_READ);
-COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX);
-COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX);
-COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
-COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678));
-COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678));
-COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678));
-#endif // SANITIZER_LINUX
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
-// There are more undocumented fields in dl_phdr_info that we are not interested
-// in.
-COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
-CHECK_TYPE_SIZE(glob_t);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
-#endif
-
-CHECK_TYPE_SIZE(addrinfo);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
-
-CHECK_TYPE_SIZE(hostent);
-CHECK_SIZE_AND_OFFSET(hostent, h_name);
-CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
-CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
-CHECK_SIZE_AND_OFFSET(hostent, h_length);
-CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
-
-CHECK_TYPE_SIZE(iovec);
-CHECK_SIZE_AND_OFFSET(iovec, iov_base);
-CHECK_SIZE_AND_OFFSET(iovec, iov_len);
-
-CHECK_TYPE_SIZE(msghdr);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
-
-CHECK_TYPE_SIZE(cmsghdr);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
-
-#ifndef __GLIBC_PREREQ
-#define __GLIBC_PREREQ(x, y) 0
-#endif
-
-#if SANITIZER_LINUX && (__ANDROID_API__ >= 21 || __GLIBC_PREREQ (2, 14))
-CHECK_TYPE_SIZE(mmsghdr);
-CHECK_SIZE_AND_OFFSET(mmsghdr, msg_hdr);
-CHECK_SIZE_AND_OFFSET(mmsghdr, msg_len);
-#endif
-
-COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
-CHECK_SIZE_AND_OFFSET(dirent, d_ino);
-#if SANITIZER_MAC
-CHECK_SIZE_AND_OFFSET(dirent, d_seekoff);
-#elif SANITIZER_FREEBSD
-// There is no 'd_off' field on FreeBSD.
-#else
-CHECK_SIZE_AND_OFFSET(dirent, d_off);
-#endif
-CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64));
-CHECK_SIZE_AND_OFFSET(dirent64, d_ino);
-CHECK_SIZE_AND_OFFSET(dirent64, d_off);
-CHECK_SIZE_AND_OFFSET(dirent64, d_reclen);
-#endif
-
-CHECK_TYPE_SIZE(ifconf);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
-
-CHECK_TYPE_SIZE(pollfd);
-CHECK_SIZE_AND_OFFSET(pollfd, fd);
-CHECK_SIZE_AND_OFFSET(pollfd, events);
-CHECK_SIZE_AND_OFFSET(pollfd, revents);
-
-CHECK_TYPE_SIZE(nfds_t);
-
-CHECK_TYPE_SIZE(sigset_t);
-
-COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
-// Can't write checks for sa_handler and sa_sigaction due to them being
-// preprocessor macros.
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
-#if !defined(__s390x__) || __GLIBC_PREREQ (2, 20)
-// On s390x glibc 2.19 and earlier sa_flags was unsigned long, and sa_resv
-// didn't exist.
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
-#endif
-#if SANITIZER_LINUX && (!SANITIZER_ANDROID || !SANITIZER_MIPS32)
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer);
-#endif
-
-#if SANITIZER_LINUX
-CHECK_TYPE_SIZE(__sysctl_args);
-CHECK_SIZE_AND_OFFSET(__sysctl_args, name);
-CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen);
-CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval);
-CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp);
-CHECK_SIZE_AND_OFFSET(__sysctl_args, newval);
-CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen);
-
-CHECK_TYPE_SIZE(__kernel_uid_t);
-CHECK_TYPE_SIZE(__kernel_gid_t);
-
-#if SANITIZER_USES_UID16_SYSCALLS
-CHECK_TYPE_SIZE(__kernel_old_uid_t);
-CHECK_TYPE_SIZE(__kernel_old_gid_t);
-#endif
-
-CHECK_TYPE_SIZE(__kernel_off_t);
-CHECK_TYPE_SIZE(__kernel_loff_t);
-CHECK_TYPE_SIZE(__kernel_fd_set);
-#endif
-
-#if !SANITIZER_ANDROID
-CHECK_TYPE_SIZE(wordexp_t);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
-#endif
-
-CHECK_TYPE_SIZE(tm);
-CHECK_SIZE_AND_OFFSET(tm, tm_sec);
-CHECK_SIZE_AND_OFFSET(tm, tm_min);
-CHECK_SIZE_AND_OFFSET(tm, tm_hour);
-CHECK_SIZE_AND_OFFSET(tm, tm_mday);
-CHECK_SIZE_AND_OFFSET(tm, tm_mon);
-CHECK_SIZE_AND_OFFSET(tm, tm_year);
-CHECK_SIZE_AND_OFFSET(tm, tm_wday);
-CHECK_SIZE_AND_OFFSET(tm, tm_yday);
-CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
-CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
-CHECK_SIZE_AND_OFFSET(tm, tm_zone);
-
-#if SANITIZER_LINUX
-CHECK_TYPE_SIZE(mntent);
-CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname);
-CHECK_SIZE_AND_OFFSET(mntent, mnt_dir);
-CHECK_SIZE_AND_OFFSET(mntent, mnt_type);
-CHECK_SIZE_AND_OFFSET(mntent, mnt_opts);
-CHECK_SIZE_AND_OFFSET(mntent, mnt_freq);
-CHECK_SIZE_AND_OFFSET(mntent, mnt_passno);
-#endif
-
-CHECK_TYPE_SIZE(ether_addr);
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
-CHECK_TYPE_SIZE(ipc_perm);
-# if SANITIZER_FREEBSD
-CHECK_SIZE_AND_OFFSET(ipc_perm, key);
-CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
-# else
-CHECK_SIZE_AND_OFFSET(ipc_perm, __key);
-CHECK_SIZE_AND_OFFSET(ipc_perm, __seq);
-# endif
-CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
-#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)
-/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */
-CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
-#endif
-
-CHECK_TYPE_SIZE(shmid_ds);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
-#endif
-
-CHECK_TYPE_SIZE(clock_t);
-
-#if SANITIZER_LINUX
-CHECK_TYPE_SIZE(clockid_t);
-#endif
-
-#if !SANITIZER_ANDROID
-CHECK_TYPE_SIZE(ifaddrs);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
-// Compare against the union, because we can't reach into the union in a
-// compliant way.
-#ifdef ifa_dstaddr
-#undef ifa_dstaddr
-#endif
-# if SANITIZER_FREEBSD
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
-# else
-COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
- sizeof(((ifaddrs *)nullptr)->ifa_ifu));
-COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
- offsetof(ifaddrs, ifa_ifu));
-# endif // SANITIZER_FREEBSD
-#else
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
-#endif // SANITIZER_LINUX
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
-#endif
-
-#if SANITIZER_LINUX
-COMPILER_CHECK(sizeof(__sanitizer_struct_mallinfo) == sizeof(struct mallinfo));
-#endif
-
-#if !SANITIZER_ANDROID
-CHECK_TYPE_SIZE(timeb);
-CHECK_SIZE_AND_OFFSET(timeb, time);
-CHECK_SIZE_AND_OFFSET(timeb, millitm);
-CHECK_SIZE_AND_OFFSET(timeb, timezone);
-CHECK_SIZE_AND_OFFSET(timeb, dstflag);
-#endif
-
-CHECK_TYPE_SIZE(passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_name);
-CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
-CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
-
-#if !SANITIZER_ANDROID
-CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
-#endif
-
-#if SANITIZER_MAC
-CHECK_SIZE_AND_OFFSET(passwd, pw_change);
-CHECK_SIZE_AND_OFFSET(passwd, pw_expire);
-CHECK_SIZE_AND_OFFSET(passwd, pw_class);
-#endif
-
-
-CHECK_TYPE_SIZE(group);
-CHECK_SIZE_AND_OFFSET(group, gr_name);
-CHECK_SIZE_AND_OFFSET(group, gr_passwd);
-CHECK_SIZE_AND_OFFSET(group, gr_gid);
-CHECK_SIZE_AND_OFFSET(group, gr_mem);
-
-#if HAVE_RPC_XDR_H || HAVE_TIRPC_RPC_XDR_H
-CHECK_TYPE_SIZE(XDR);
-CHECK_SIZE_AND_OFFSET(XDR, x_op);
-CHECK_SIZE_AND_OFFSET(XDR, x_ops);
-CHECK_SIZE_AND_OFFSET(XDR, x_public);
-CHECK_SIZE_AND_OFFSET(XDR, x_private);
-CHECK_SIZE_AND_OFFSET(XDR, x_base);
-CHECK_SIZE_AND_OFFSET(XDR, x_handy);
-COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
-COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
-COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE));
-CHECK_SIZE_AND_OFFSET(FILE, _flags);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_read_end);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_read_base);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_write_ptr);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_write_end);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_write_base);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_base);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_end);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_save_base);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_backup_base);
-CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end);
-CHECK_SIZE_AND_OFFSET(FILE, _markers);
-CHECK_SIZE_AND_OFFSET(FILE, _chain);
-CHECK_SIZE_AND_OFFSET(FILE, _fileno);
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk));
-CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit);
-CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev);
-CHECK_TYPE_SIZE(obstack);
-CHECK_SIZE_AND_OFFSET(obstack, chunk_size);
-CHECK_SIZE_AND_OFFSET(obstack, chunk);
-CHECK_SIZE_AND_OFFSET(obstack, object_base);
-CHECK_SIZE_AND_OFFSET(obstack, next_free);
-
-CHECK_TYPE_SIZE(cookie_io_functions_t);
-CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read);
-CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write);
-CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek);
-CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
-#endif
-
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
-CHECK_TYPE_SIZE(sem_t);
-#endif
-
-#if SANITIZER_LINUX && defined(__arm__)
-COMPILER_CHECK(ARM_VFPREGS_SIZE == ARM_VFPREGS_SIZE_ASAN);
-#endif
-
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
--- /dev/null
+//===-- sanitizer_platform_limits_posix.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific POSIX data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_LINUX || SANITIZER_MAC
+// Tests in this file assume that off_t-dependent data structures match the
+// libc ABI. For example, struct dirent here is what readdir() function (as
+// exported from libc) returns, and not the user-facing "dirent", which
+// depends on _FILE_OFFSET_BITS setting.
+// To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below.
+#ifdef _FILE_OFFSET_BITS
+#undef _FILE_OFFSET_BITS
+#endif
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <grp.h>
+#include <limits.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <termios.h>
+#include <time.h>
+#include <wchar.h>
+#include <regex.h>
+#if !SANITIZER_MAC
+#include <utmp.h>
+#endif
+
+#if !SANITIZER_IOS
+#include <net/route.h>
+#endif
+
+#if !SANITIZER_ANDROID
+#include <fstab.h>
+#include <sys/mount.h>
+#include <sys/timeb.h>
+#include <utmpx.h>
+#endif
+
+#if SANITIZER_LINUX
+#include <malloc.h>
+#include <mntent.h>
+#include <netinet/ether.h>
+#include <sys/sysinfo.h>
+#include <sys/vt.h>
+#include <linux/cdrom.h>
+#include <linux/fd.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <linux/input.h>
+#include <linux/ioctl.h>
+#include <linux/soundcard.h>
+#include <linux/sysctl.h>
+#include <linux/utsname.h>
+#include <linux/posix_types.h>
+#include <net/if_arp.h>
+#endif
+
+#if SANITIZER_IOS
+#undef IOC_DIRMASK
+#endif
+
+#if SANITIZER_LINUX
+# include <utime.h>
+# include <sys/ptrace.h>
+# if defined(__mips64) || defined(__aarch64__) || defined(__arm__)
+# include <asm/ptrace.h>
+# ifdef __arm__
+typedef struct user_fpregs elf_fpregset_t;
+# define ARM_VFPREGS_SIZE_ASAN (32 * 8 /*fpregs*/ + 4 /*fpscr*/)
+# if !defined(ARM_VFPREGS_SIZE)
+# define ARM_VFPREGS_SIZE ARM_VFPREGS_SIZE_ASAN
+# endif
+# endif
+# endif
+# include <semaphore.h>
+#endif
+
+#if !SANITIZER_ANDROID
+#include <ifaddrs.h>
+#include <sys/ucontext.h>
+#include <wordexp.h>
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+#include <glob.h>
+#include <obstack.h>
+#include <mqueue.h>
+#include <net/if_ppp.h>
+#include <netax25/ax25.h>
+#include <netipx/ipx.h>
+#include <netrom/netrom.h>
+#if HAVE_RPC_XDR_H
+# include <rpc/xdr.h>
+#endif
+#include <scsi/scsi.h>
+#include <sys/mtio.h>
+#include <sys/kd.h>
+#include <sys/shm.h>
+#include <sys/statvfs.h>
+#include <sys/timex.h>
+#if defined(__mips64)
+# include <sys/procfs.h>
+#endif
+#include <sys/user.h>
+#include <linux/cyclades.h>
+#include <linux/if_eql.h>
+#include <linux/if_plip.h>
+#include <linux/lp.h>
+#include <linux/mroute.h>
+#include <linux/mroute6.h>
+#include <linux/scc.h>
+#include <linux/serial.h>
+#include <sys/msg.h>
+#include <sys/ipc.h>
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if SANITIZER_ANDROID
+#include <linux/kd.h>
+#include <linux/mtio.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif
+
+#if SANITIZER_LINUX
+#include <link.h>
+#include <sys/vfs.h>
+#include <sys/epoll.h>
+#include <linux/capability.h>
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_MAC
+#include <net/ethernet.h>
+#include <sys/filio.h>
+#include <sys/sockio.h>
+#endif
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_posix.h"
+
+namespace __sanitizer {
+ unsigned struct_utsname_sz = sizeof(struct utsname);
+ unsigned struct_stat_sz = sizeof(struct stat);
+#if !SANITIZER_IOS
+ unsigned struct_stat64_sz = sizeof(struct stat64);
+#endif // !SANITIZER_IOS
+ unsigned struct_rusage_sz = sizeof(struct rusage);
+ unsigned struct_tm_sz = sizeof(struct tm);
+ unsigned struct_passwd_sz = sizeof(struct passwd);
+ unsigned struct_group_sz = sizeof(struct group);
+ unsigned siginfo_t_sz = sizeof(siginfo_t);
+ unsigned struct_sigaction_sz = sizeof(struct sigaction);
+ unsigned struct_itimerval_sz = sizeof(struct itimerval);
+ unsigned pthread_t_sz = sizeof(pthread_t);
+ unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
+ unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+ unsigned pid_t_sz = sizeof(pid_t);
+ unsigned timeval_sz = sizeof(timeval);
+ unsigned uid_t_sz = sizeof(uid_t);
+ unsigned gid_t_sz = sizeof(gid_t);
+ unsigned mbstate_t_sz = sizeof(mbstate_t);
+ unsigned sigset_t_sz = sizeof(sigset_t);
+ unsigned struct_timezone_sz = sizeof(struct timezone);
+ unsigned struct_tms_sz = sizeof(struct tms);
+ unsigned struct_sigevent_sz = sizeof(struct sigevent);
+ unsigned struct_sched_param_sz = sizeof(struct sched_param);
+ unsigned struct_regex_sz = sizeof(regex_t);
+ unsigned struct_regmatch_sz = sizeof(regmatch_t);
+
+#if SANITIZER_MAC && !SANITIZER_IOS
+ unsigned struct_statfs64_sz = sizeof(struct statfs64);
+#endif // SANITIZER_MAC && !SANITIZER_IOS
+
+#if !SANITIZER_ANDROID
+ unsigned struct_fstab_sz = sizeof(struct fstab);
+ unsigned struct_statfs_sz = sizeof(struct statfs);
+ unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+ unsigned ucontext_t_sz = sizeof(ucontext_t);
+#endif // !SANITIZER_ANDROID
+
+#if SANITIZER_LINUX
+ unsigned struct_epoll_event_sz = sizeof(struct epoll_event);
+ unsigned struct_sysinfo_sz = sizeof(struct sysinfo);
+ unsigned __user_cap_header_struct_sz =
+ sizeof(struct __user_cap_header_struct);
+ unsigned __user_cap_data_struct_sz = sizeof(struct __user_cap_data_struct);
+ unsigned struct_new_utsname_sz = sizeof(struct new_utsname);
+ unsigned struct_old_utsname_sz = sizeof(struct old_utsname);
+ unsigned struct_oldold_utsname_sz = sizeof(struct oldold_utsname);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX
+ unsigned struct_rlimit_sz = sizeof(struct rlimit);
+ unsigned struct_timespec_sz = sizeof(struct timespec);
+ unsigned struct_utimbuf_sz = sizeof(struct utimbuf);
+ unsigned struct_itimerspec_sz = sizeof(struct itimerspec);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ // Use pre-computed size of struct ustat to avoid <sys/ustat.h> which
+ // has been removed from glibc 2.28.
+#if defined(__aarch64__) || defined(__s390x__) || defined (__mips64) \
+ || defined(__powerpc64__) || defined(__arch64__) || defined(__sparcv9) \
+ || defined(__x86_64__)
+#define SIZEOF_STRUCT_USTAT 32
+#elif defined(__arm__) || defined(__i386__) || defined(__mips__) \
+ || defined(__powerpc__) || defined(__s390__) || defined(__sparc__)
+#define SIZEOF_STRUCT_USTAT 20
+#else
+#error Unknown size of struct ustat
+#endif
+ unsigned struct_ustat_sz = SIZEOF_STRUCT_USTAT;
+ unsigned struct_rlimit64_sz = sizeof(struct rlimit64);
+ unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned struct_timex_sz = sizeof(struct timex);
+ unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds);
+ unsigned struct_mq_attr_sz = sizeof(struct mq_attr);
+ unsigned struct_statvfs_sz = sizeof(struct statvfs);
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+ const uptr sig_ign = (uptr)SIG_IGN;
+ const uptr sig_dfl = (uptr)SIG_DFL;
+ const uptr sig_err = (uptr)SIG_ERR;
+ const uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+#if SANITIZER_LINUX
+ int e_tabsz = (int)E_TABSZ;
+#endif
+
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned struct_shminfo_sz = sizeof(struct shminfo);
+ unsigned struct_shm_info_sz = sizeof(struct shm_info);
+ int shmctl_ipc_stat = (int)IPC_STAT;
+ int shmctl_ipc_info = (int)IPC_INFO;
+ int shmctl_shm_info = (int)SHM_INFO;
+ int shmctl_shm_stat = (int)SHM_STAT;
+#endif
+
+#if !SANITIZER_MAC && !SANITIZER_FREEBSD
+ unsigned struct_utmp_sz = sizeof(struct utmp);
+#endif
+#if !SANITIZER_ANDROID
+ unsigned struct_utmpx_sz = sizeof(struct utmpx);
+#endif
+
+ int map_fixed = MAP_FIXED;
+
+ int af_inet = (int)AF_INET;
+ int af_inet6 = (int)AF_INET6;
+
+ uptr __sanitizer_in_addr_sz(int af) {
+ if (af == AF_INET)
+ return sizeof(struct in_addr);
+ else if (af == AF_INET6)
+ return sizeof(struct in6_addr);
+ else
+ return 0;
+ }
+
+#if SANITIZER_LINUX
+unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
+#elif SANITIZER_FREEBSD
+unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ int glob_nomatch = GLOB_NOMATCH;
+ int glob_altdirfunc = GLOB_ALTDIRFUNC;
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && \
+ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \
+ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \
+ defined(__s390__))
+#if defined(__mips64) || defined(__powerpc64__) || defined(__arm__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t);
+#elif defined(__aarch64__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct user_pt_regs);
+ unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpsimd_state);
+#elif defined(__s390__)
+ unsigned struct_user_regs_struct_sz = sizeof(struct _user_regs_struct);
+ unsigned struct_user_fpregs_struct_sz = sizeof(struct _user_fpregs_struct);
+#else
+ unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct);
+ unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct);
+#endif // __mips64 || __powerpc64__ || __aarch64__
+#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) || \
+ defined(__aarch64__) || defined(__arm__) || defined(__s390__)
+ unsigned struct_user_fpxregs_struct_sz = 0;
+#else
+ unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct);
+#endif // __x86_64 || __mips64 || __powerpc64__ || __aarch64__ || __arm__
+// || __s390__
+#ifdef __arm__
+ unsigned struct_user_vfpregs_struct_sz = ARM_VFPREGS_SIZE;
+#else
+ unsigned struct_user_vfpregs_struct_sz = 0;
+#endif
+
+ int ptrace_peektext = PTRACE_PEEKTEXT;
+ int ptrace_peekdata = PTRACE_PEEKDATA;
+ int ptrace_peekuser = PTRACE_PEEKUSER;
+#if (defined(PTRACE_GETREGS) && defined(PTRACE_SETREGS)) || \
+ (defined(PT_GETREGS) && defined(PT_SETREGS))
+ int ptrace_getregs = PTRACE_GETREGS;
+ int ptrace_setregs = PTRACE_SETREGS;
+#else
+ int ptrace_getregs = -1;
+ int ptrace_setregs = -1;
+#endif
+#if (defined(PTRACE_GETFPREGS) && defined(PTRACE_SETFPREGS)) || \
+ (defined(PT_GETFPREGS) && defined(PT_SETFPREGS))
+ int ptrace_getfpregs = PTRACE_GETFPREGS;
+ int ptrace_setfpregs = PTRACE_SETFPREGS;
+#else
+ int ptrace_getfpregs = -1;
+ int ptrace_setfpregs = -1;
+#endif
+#if (defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS)) || \
+ (defined(PT_GETFPXREGS) && defined(PT_SETFPXREGS))
+ int ptrace_getfpxregs = PTRACE_GETFPXREGS;
+ int ptrace_setfpxregs = PTRACE_SETFPXREGS;
+#else
+ int ptrace_getfpxregs = -1;
+ int ptrace_setfpxregs = -1;
+#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS
+#if defined(PTRACE_GETVFPREGS) && defined(PTRACE_SETVFPREGS)
+ int ptrace_getvfpregs = PTRACE_GETVFPREGS;
+ int ptrace_setvfpregs = PTRACE_SETVFPREGS;
+#else
+ int ptrace_getvfpregs = -1;
+ int ptrace_setvfpregs = -1;
+#endif
+ int ptrace_geteventmsg = PTRACE_GETEVENTMSG;
+#if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \
+ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO))
+ int ptrace_getsiginfo = PTRACE_GETSIGINFO;
+ int ptrace_setsiginfo = PTRACE_SETSIGINFO;
+#else
+ int ptrace_getsiginfo = -1;
+ int ptrace_setsiginfo = -1;
+#endif // PTRACE_GETSIGINFO/PTRACE_SETSIGINFO
+#if defined(PTRACE_GETREGSET) && defined(PTRACE_SETREGSET)
+ int ptrace_getregset = PTRACE_GETREGSET;
+ int ptrace_setregset = PTRACE_SETREGSET;
+#else
+ int ptrace_getregset = -1;
+ int ptrace_setregset = -1;
+#endif // PTRACE_GETREGSET/PTRACE_SETREGSET
+#endif
+
+ unsigned path_max = PATH_MAX;
+
+ // ioctl arguments
+ unsigned struct_ifreq_sz = sizeof(struct ifreq);
+ unsigned struct_termios_sz = sizeof(struct termios);
+ unsigned struct_winsize_sz = sizeof(struct winsize);
+
+#if SANITIZER_LINUX
+ unsigned struct_arpreq_sz = sizeof(struct arpreq);
+ unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf);
+ unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession);
+ unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio);
+ unsigned struct_cdrom_subchnl_sz = sizeof(struct cdrom_subchnl);
+ unsigned struct_cdrom_ti_sz = sizeof(struct cdrom_ti);
+ unsigned struct_cdrom_tocentry_sz = sizeof(struct cdrom_tocentry);
+ unsigned struct_cdrom_tochdr_sz = sizeof(struct cdrom_tochdr);
+ unsigned struct_cdrom_volctrl_sz = sizeof(struct cdrom_volctrl);
+ unsigned struct_ff_effect_sz = sizeof(struct ff_effect);
+ unsigned struct_floppy_drive_params_sz = sizeof(struct floppy_drive_params);
+ unsigned struct_floppy_drive_struct_sz = sizeof(struct floppy_drive_struct);
+ unsigned struct_floppy_fdc_state_sz = sizeof(struct floppy_fdc_state);
+ unsigned struct_floppy_max_errors_sz = sizeof(struct floppy_max_errors);
+ unsigned struct_floppy_raw_cmd_sz = sizeof(struct floppy_raw_cmd);
+ unsigned struct_floppy_struct_sz = sizeof(struct floppy_struct);
+ unsigned struct_floppy_write_errors_sz = sizeof(struct floppy_write_errors);
+ unsigned struct_format_descr_sz = sizeof(struct format_descr);
+ unsigned struct_hd_driveid_sz = sizeof(struct hd_driveid);
+ unsigned struct_hd_geometry_sz = sizeof(struct hd_geometry);
+ unsigned struct_input_absinfo_sz = sizeof(struct input_absinfo);
+ unsigned struct_input_id_sz = sizeof(struct input_id);
+ unsigned struct_mtpos_sz = sizeof(struct mtpos);
+ unsigned struct_rtentry_sz = sizeof(struct rtentry);
+ unsigned struct_termio_sz = sizeof(struct termio);
+ unsigned struct_vt_consize_sz = sizeof(struct vt_consize);
+ unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes);
+ unsigned struct_vt_stat_sz = sizeof(struct vt_stat);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX
+#if SOUND_VERSION >= 0x040000
+ unsigned struct_copr_buffer_sz = 0;
+ unsigned struct_copr_debug_buf_sz = 0;
+ unsigned struct_copr_msg_sz = 0;
+#else
+ unsigned struct_copr_buffer_sz = sizeof(struct copr_buffer);
+ unsigned struct_copr_debug_buf_sz = sizeof(struct copr_debug_buf);
+ unsigned struct_copr_msg_sz = sizeof(struct copr_msg);
+#endif
+ unsigned struct_midi_info_sz = sizeof(struct midi_info);
+ unsigned struct_mtget_sz = sizeof(struct mtget);
+ unsigned struct_mtop_sz = sizeof(struct mtop);
+ unsigned struct_sbi_instrument_sz = sizeof(struct sbi_instrument);
+ unsigned struct_seq_event_rec_sz = sizeof(struct seq_event_rec);
+ unsigned struct_synth_info_sz = sizeof(struct synth_info);
+ unsigned struct_vt_mode_sz = sizeof(struct vt_mode);
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct);
+ unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor);
+#if EV_VERSION > (0x010000)
+ unsigned struct_input_keymap_entry_sz = sizeof(struct input_keymap_entry);
+#else
+ unsigned struct_input_keymap_entry_sz = 0;
+#endif
+ unsigned struct_ipx_config_data_sz = sizeof(struct ipx_config_data);
+ unsigned struct_kbdiacrs_sz = sizeof(struct kbdiacrs);
+ unsigned struct_kbentry_sz = sizeof(struct kbentry);
+ unsigned struct_kbkeycode_sz = sizeof(struct kbkeycode);
+ unsigned struct_kbsentry_sz = sizeof(struct kbsentry);
+ unsigned struct_mtconfiginfo_sz = sizeof(struct mtconfiginfo);
+ unsigned struct_nr_parms_struct_sz = sizeof(struct nr_parms_struct);
+ unsigned struct_scc_modem_sz = sizeof(struct scc_modem);
+ unsigned struct_scc_stat_sz = sizeof(struct scc_stat);
+ unsigned struct_serial_multiport_struct_sz
+ = sizeof(struct serial_multiport_struct);
+ unsigned struct_serial_struct_sz = sizeof(struct serial_struct);
+ unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25);
+ unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc);
+ unsigned struct_unimapinit_sz = sizeof(struct unimapinit);
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info);
+ unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats);
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+#if !SANITIZER_ANDROID && !SANITIZER_MAC
+ unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
+ unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
+#endif
+
+ const unsigned long __sanitizer_bufsiz = BUFSIZ;
+
+ const unsigned IOCTL_NOT_PRESENT = 0;
+
+ unsigned IOCTL_FIOASYNC = FIOASYNC;
+ unsigned IOCTL_FIOCLEX = FIOCLEX;
+ unsigned IOCTL_FIOGETOWN = FIOGETOWN;
+ unsigned IOCTL_FIONBIO = FIONBIO;
+ unsigned IOCTL_FIONCLEX = FIONCLEX;
+ unsigned IOCTL_FIOSETOWN = FIOSETOWN;
+ unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
+ unsigned IOCTL_SIOCATMARK = SIOCATMARK;
+ unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
+ unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
+ unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
+ unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
+ unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
+ unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
+ unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
+ unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
+ unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
+ unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+ unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
+ unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
+ unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
+ unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
+ unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
+ unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
+ unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
+ unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
+ unsigned IOCTL_TIOCCONS = TIOCCONS;
+ unsigned IOCTL_TIOCEXCL = TIOCEXCL;
+ unsigned IOCTL_TIOCGETD = TIOCGETD;
+ unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
+ unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
+ unsigned IOCTL_TIOCMBIC = TIOCMBIC;
+ unsigned IOCTL_TIOCMBIS = TIOCMBIS;
+ unsigned IOCTL_TIOCMGET = TIOCMGET;
+ unsigned IOCTL_TIOCMSET = TIOCMSET;
+ unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
+ unsigned IOCTL_TIOCNXCL = TIOCNXCL;
+ unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
+ unsigned IOCTL_TIOCPKT = TIOCPKT;
+ unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
+ unsigned IOCTL_TIOCSETD = TIOCSETD;
+ unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
+ unsigned IOCTL_TIOCSTI = TIOCSTI;
+ unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT;
+ unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT;
+#endif
+
+#if SANITIZER_LINUX
+ unsigned IOCTL_EVIOCGABS = EVIOCGABS(0);
+ unsigned IOCTL_EVIOCGBIT = EVIOCGBIT(0, 0);
+ unsigned IOCTL_EVIOCGEFFECTS = EVIOCGEFFECTS;
+ unsigned IOCTL_EVIOCGID = EVIOCGID;
+ unsigned IOCTL_EVIOCGKEY = EVIOCGKEY(0);
+ unsigned IOCTL_EVIOCGKEYCODE = EVIOCGKEYCODE;
+ unsigned IOCTL_EVIOCGLED = EVIOCGLED(0);
+ unsigned IOCTL_EVIOCGNAME = EVIOCGNAME(0);
+ unsigned IOCTL_EVIOCGPHYS = EVIOCGPHYS(0);
+ unsigned IOCTL_EVIOCGRAB = EVIOCGRAB;
+ unsigned IOCTL_EVIOCGREP = EVIOCGREP;
+ unsigned IOCTL_EVIOCGSND = EVIOCGSND(0);
+ unsigned IOCTL_EVIOCGSW = EVIOCGSW(0);
+ unsigned IOCTL_EVIOCGUNIQ = EVIOCGUNIQ(0);
+ unsigned IOCTL_EVIOCGVERSION = EVIOCGVERSION;
+ unsigned IOCTL_EVIOCRMFF = EVIOCRMFF;
+ unsigned IOCTL_EVIOCSABS = EVIOCSABS(0);
+ unsigned IOCTL_EVIOCSFF = EVIOCSFF;
+ unsigned IOCTL_EVIOCSKEYCODE = EVIOCSKEYCODE;
+ unsigned IOCTL_EVIOCSREP = EVIOCSREP;
+ unsigned IOCTL_BLKFLSBUF = BLKFLSBUF;
+ unsigned IOCTL_BLKGETSIZE = BLKGETSIZE;
+ unsigned IOCTL_BLKRAGET = BLKRAGET;
+ unsigned IOCTL_BLKRASET = BLKRASET;
+ unsigned IOCTL_BLKROGET = BLKROGET;
+ unsigned IOCTL_BLKROSET = BLKROSET;
+ unsigned IOCTL_BLKRRPART = BLKRRPART;
+ unsigned IOCTL_CDROMAUDIOBUFSIZ = CDROMAUDIOBUFSIZ;
+ unsigned IOCTL_CDROMEJECT = CDROMEJECT;
+ unsigned IOCTL_CDROMEJECT_SW = CDROMEJECT_SW;
+ unsigned IOCTL_CDROMMULTISESSION = CDROMMULTISESSION;
+ unsigned IOCTL_CDROMPAUSE = CDROMPAUSE;
+ unsigned IOCTL_CDROMPLAYMSF = CDROMPLAYMSF;
+ unsigned IOCTL_CDROMPLAYTRKIND = CDROMPLAYTRKIND;
+ unsigned IOCTL_CDROMREADAUDIO = CDROMREADAUDIO;
+ unsigned IOCTL_CDROMREADCOOKED = CDROMREADCOOKED;
+ unsigned IOCTL_CDROMREADMODE1 = CDROMREADMODE1;
+ unsigned IOCTL_CDROMREADMODE2 = CDROMREADMODE2;
+ unsigned IOCTL_CDROMREADRAW = CDROMREADRAW;
+ unsigned IOCTL_CDROMREADTOCENTRY = CDROMREADTOCENTRY;
+ unsigned IOCTL_CDROMREADTOCHDR = CDROMREADTOCHDR;
+ unsigned IOCTL_CDROMRESET = CDROMRESET;
+ unsigned IOCTL_CDROMRESUME = CDROMRESUME;
+ unsigned IOCTL_CDROMSEEK = CDROMSEEK;
+ unsigned IOCTL_CDROMSTART = CDROMSTART;
+ unsigned IOCTL_CDROMSTOP = CDROMSTOP;
+ unsigned IOCTL_CDROMSUBCHNL = CDROMSUBCHNL;
+ unsigned IOCTL_CDROMVOLCTRL = CDROMVOLCTRL;
+ unsigned IOCTL_CDROMVOLREAD = CDROMVOLREAD;
+ unsigned IOCTL_CDROM_GET_UPC = CDROM_GET_UPC;
+ unsigned IOCTL_FDCLRPRM = FDCLRPRM;
+ unsigned IOCTL_FDDEFPRM = FDDEFPRM;
+ unsigned IOCTL_FDFLUSH = FDFLUSH;
+ unsigned IOCTL_FDFMTBEG = FDFMTBEG;
+ unsigned IOCTL_FDFMTEND = FDFMTEND;
+ unsigned IOCTL_FDFMTTRK = FDFMTTRK;
+ unsigned IOCTL_FDGETDRVPRM = FDGETDRVPRM;
+ unsigned IOCTL_FDGETDRVSTAT = FDGETDRVSTAT;
+ unsigned IOCTL_FDGETDRVTYP = FDGETDRVTYP;
+ unsigned IOCTL_FDGETFDCSTAT = FDGETFDCSTAT;
+ unsigned IOCTL_FDGETMAXERRS = FDGETMAXERRS;
+ unsigned IOCTL_FDGETPRM = FDGETPRM;
+ unsigned IOCTL_FDMSGOFF = FDMSGOFF;
+ unsigned IOCTL_FDMSGON = FDMSGON;
+ unsigned IOCTL_FDPOLLDRVSTAT = FDPOLLDRVSTAT;
+ unsigned IOCTL_FDRAWCMD = FDRAWCMD;
+ unsigned IOCTL_FDRESET = FDRESET;
+ unsigned IOCTL_FDSETDRVPRM = FDSETDRVPRM;
+ unsigned IOCTL_FDSETEMSGTRESH = FDSETEMSGTRESH;
+ unsigned IOCTL_FDSETMAXERRS = FDSETMAXERRS;
+ unsigned IOCTL_FDSETPRM = FDSETPRM;
+ unsigned IOCTL_FDTWADDLE = FDTWADDLE;
+ unsigned IOCTL_FDWERRORCLR = FDWERRORCLR;
+ unsigned IOCTL_FDWERRORGET = FDWERRORGET;
+ unsigned IOCTL_HDIO_DRIVE_CMD = HDIO_DRIVE_CMD;
+ unsigned IOCTL_HDIO_GETGEO = HDIO_GETGEO;
+ unsigned IOCTL_HDIO_GET_32BIT = HDIO_GET_32BIT;
+ unsigned IOCTL_HDIO_GET_DMA = HDIO_GET_DMA;
+ unsigned IOCTL_HDIO_GET_IDENTITY = HDIO_GET_IDENTITY;
+ unsigned IOCTL_HDIO_GET_KEEPSETTINGS = HDIO_GET_KEEPSETTINGS;
+ unsigned IOCTL_HDIO_GET_MULTCOUNT = HDIO_GET_MULTCOUNT;
+ unsigned IOCTL_HDIO_GET_NOWERR = HDIO_GET_NOWERR;
+ unsigned IOCTL_HDIO_GET_UNMASKINTR = HDIO_GET_UNMASKINTR;
+ unsigned IOCTL_HDIO_SET_32BIT = HDIO_SET_32BIT;
+ unsigned IOCTL_HDIO_SET_DMA = HDIO_SET_DMA;
+ unsigned IOCTL_HDIO_SET_KEEPSETTINGS = HDIO_SET_KEEPSETTINGS;
+ unsigned IOCTL_HDIO_SET_MULTCOUNT = HDIO_SET_MULTCOUNT;
+ unsigned IOCTL_HDIO_SET_NOWERR = HDIO_SET_NOWERR;
+ unsigned IOCTL_HDIO_SET_UNMASKINTR = HDIO_SET_UNMASKINTR;
+ unsigned IOCTL_MTIOCPOS = MTIOCPOS;
+ unsigned IOCTL_PPPIOCGASYNCMAP = PPPIOCGASYNCMAP;
+ unsigned IOCTL_PPPIOCGDEBUG = PPPIOCGDEBUG;
+ unsigned IOCTL_PPPIOCGFLAGS = PPPIOCGFLAGS;
+ unsigned IOCTL_PPPIOCGUNIT = PPPIOCGUNIT;
+ unsigned IOCTL_PPPIOCGXASYNCMAP = PPPIOCGXASYNCMAP;
+ unsigned IOCTL_PPPIOCSASYNCMAP = PPPIOCSASYNCMAP;
+ unsigned IOCTL_PPPIOCSDEBUG = PPPIOCSDEBUG;
+ unsigned IOCTL_PPPIOCSFLAGS = PPPIOCSFLAGS;
+ unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID;
+ unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU;
+ unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP;
+ unsigned IOCTL_SIOCADDRT = SIOCADDRT;
+ unsigned IOCTL_SIOCDARP = SIOCDARP;
+ unsigned IOCTL_SIOCDELRT = SIOCDELRT;
+ unsigned IOCTL_SIOCDRARP = SIOCDRARP;
+ unsigned IOCTL_SIOCGARP = SIOCGARP;
+ unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP;
+ unsigned IOCTL_SIOCGIFHWADDR = SIOCGIFHWADDR;
+ unsigned IOCTL_SIOCGIFMAP = SIOCGIFMAP;
+ unsigned IOCTL_SIOCGIFMEM = SIOCGIFMEM;
+ unsigned IOCTL_SIOCGIFNAME = SIOCGIFNAME;
+ unsigned IOCTL_SIOCGIFSLAVE = SIOCGIFSLAVE;
+ unsigned IOCTL_SIOCGRARP = SIOCGRARP;
+ unsigned IOCTL_SIOCGSTAMP = SIOCGSTAMP;
+ unsigned IOCTL_SIOCSARP = SIOCSARP;
+ unsigned IOCTL_SIOCSIFENCAP = SIOCSIFENCAP;
+ unsigned IOCTL_SIOCSIFHWADDR = SIOCSIFHWADDR;
+ unsigned IOCTL_SIOCSIFLINK = SIOCSIFLINK;
+ unsigned IOCTL_SIOCSIFMAP = SIOCSIFMAP;
+ unsigned IOCTL_SIOCSIFMEM = SIOCSIFMEM;
+ unsigned IOCTL_SIOCSIFSLAVE = SIOCSIFSLAVE;
+ unsigned IOCTL_SIOCSRARP = SIOCSRARP;
+# if SOUND_VERSION >= 0x040000
+ unsigned IOCTL_SNDCTL_COPR_HALT = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_LOAD = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_RCODE = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_RCVMSG = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_RDATA = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_RESET = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_RUN = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_SENDMSG = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_WCODE = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SNDCTL_COPR_WDATA = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SOUND_PCM_READ_BITS = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SOUND_PCM_READ_CHANNELS = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SOUND_PCM_READ_FILTER = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SOUND_PCM_READ_RATE = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_SOUND_PCM_WRITE_FILTER = IOCTL_NOT_PRESENT;
+# else // SOUND_VERSION
+ unsigned IOCTL_SNDCTL_COPR_HALT = SNDCTL_COPR_HALT;
+ unsigned IOCTL_SNDCTL_COPR_LOAD = SNDCTL_COPR_LOAD;
+ unsigned IOCTL_SNDCTL_COPR_RCODE = SNDCTL_COPR_RCODE;
+ unsigned IOCTL_SNDCTL_COPR_RCVMSG = SNDCTL_COPR_RCVMSG;
+ unsigned IOCTL_SNDCTL_COPR_RDATA = SNDCTL_COPR_RDATA;
+ unsigned IOCTL_SNDCTL_COPR_RESET = SNDCTL_COPR_RESET;
+ unsigned IOCTL_SNDCTL_COPR_RUN = SNDCTL_COPR_RUN;
+ unsigned IOCTL_SNDCTL_COPR_SENDMSG = SNDCTL_COPR_SENDMSG;
+ unsigned IOCTL_SNDCTL_COPR_WCODE = SNDCTL_COPR_WCODE;
+ unsigned IOCTL_SNDCTL_COPR_WDATA = SNDCTL_COPR_WDATA;
+ unsigned IOCTL_SOUND_PCM_READ_BITS = SOUND_PCM_READ_BITS;
+ unsigned IOCTL_SOUND_PCM_READ_CHANNELS = SOUND_PCM_READ_CHANNELS;
+ unsigned IOCTL_SOUND_PCM_READ_FILTER = SOUND_PCM_READ_FILTER;
+ unsigned IOCTL_SOUND_PCM_READ_RATE = SOUND_PCM_READ_RATE;
+ unsigned IOCTL_SOUND_PCM_WRITE_CHANNELS = SOUND_PCM_WRITE_CHANNELS;
+ unsigned IOCTL_SOUND_PCM_WRITE_FILTER = SOUND_PCM_WRITE_FILTER;
+#endif // SOUND_VERSION
+ unsigned IOCTL_TCFLSH = TCFLSH;
+ unsigned IOCTL_TCGETA = TCGETA;
+ unsigned IOCTL_TCGETS = TCGETS;
+ unsigned IOCTL_TCSBRK = TCSBRK;
+ unsigned IOCTL_TCSBRKP = TCSBRKP;
+ unsigned IOCTL_TCSETA = TCSETA;
+ unsigned IOCTL_TCSETAF = TCSETAF;
+ unsigned IOCTL_TCSETAW = TCSETAW;
+ unsigned IOCTL_TCSETS = TCSETS;
+ unsigned IOCTL_TCSETSF = TCSETSF;
+ unsigned IOCTL_TCSETSW = TCSETSW;
+ unsigned IOCTL_TCXONC = TCXONC;
+ unsigned IOCTL_TIOCGLCKTRMIOS = TIOCGLCKTRMIOS;
+ unsigned IOCTL_TIOCGSOFTCAR = TIOCGSOFTCAR;
+ unsigned IOCTL_TIOCINQ = TIOCINQ;
+ unsigned IOCTL_TIOCLINUX = TIOCLINUX;
+ unsigned IOCTL_TIOCSERCONFIG = TIOCSERCONFIG;
+ unsigned IOCTL_TIOCSERGETLSR = TIOCSERGETLSR;
+ unsigned IOCTL_TIOCSERGWILD = TIOCSERGWILD;
+ unsigned IOCTL_TIOCSERSWILD = TIOCSERSWILD;
+ unsigned IOCTL_TIOCSLCKTRMIOS = TIOCSLCKTRMIOS;
+ unsigned IOCTL_TIOCSSOFTCAR = TIOCSSOFTCAR;
+ unsigned IOCTL_VT_DISALLOCATE = VT_DISALLOCATE;
+ unsigned IOCTL_VT_GETSTATE = VT_GETSTATE;
+ unsigned IOCTL_VT_RESIZE = VT_RESIZE;
+ unsigned IOCTL_VT_RESIZEX = VT_RESIZEX;
+ unsigned IOCTL_VT_SENDSIG = VT_SENDSIG;
+ unsigned IOCTL_MTIOCGET = MTIOCGET;
+ unsigned IOCTL_MTIOCTOP = MTIOCTOP;
+ unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
+ unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
+ unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;
+ unsigned IOCTL_SNDCTL_DSP_POST = SNDCTL_DSP_POST;
+ unsigned IOCTL_SNDCTL_DSP_RESET = SNDCTL_DSP_RESET;
+ unsigned IOCTL_SNDCTL_DSP_SETFMT = SNDCTL_DSP_SETFMT;
+ unsigned IOCTL_SNDCTL_DSP_SETFRAGMENT = SNDCTL_DSP_SETFRAGMENT;
+ unsigned IOCTL_SNDCTL_DSP_SPEED = SNDCTL_DSP_SPEED;
+ unsigned IOCTL_SNDCTL_DSP_STEREO = SNDCTL_DSP_STEREO;
+ unsigned IOCTL_SNDCTL_DSP_SUBDIVIDE = SNDCTL_DSP_SUBDIVIDE;
+ unsigned IOCTL_SNDCTL_DSP_SYNC = SNDCTL_DSP_SYNC;
+ unsigned IOCTL_SNDCTL_FM_4OP_ENABLE = SNDCTL_FM_4OP_ENABLE;
+ unsigned IOCTL_SNDCTL_FM_LOAD_INSTR = SNDCTL_FM_LOAD_INSTR;
+ unsigned IOCTL_SNDCTL_MIDI_INFO = SNDCTL_MIDI_INFO;
+ unsigned IOCTL_SNDCTL_MIDI_PRETIME = SNDCTL_MIDI_PRETIME;
+ unsigned IOCTL_SNDCTL_SEQ_CTRLRATE = SNDCTL_SEQ_CTRLRATE;
+ unsigned IOCTL_SNDCTL_SEQ_GETINCOUNT = SNDCTL_SEQ_GETINCOUNT;
+ unsigned IOCTL_SNDCTL_SEQ_GETOUTCOUNT = SNDCTL_SEQ_GETOUTCOUNT;
+ unsigned IOCTL_SNDCTL_SEQ_NRMIDIS = SNDCTL_SEQ_NRMIDIS;
+ unsigned IOCTL_SNDCTL_SEQ_NRSYNTHS = SNDCTL_SEQ_NRSYNTHS;
+ unsigned IOCTL_SNDCTL_SEQ_OUTOFBAND = SNDCTL_SEQ_OUTOFBAND;
+ unsigned IOCTL_SNDCTL_SEQ_PANIC = SNDCTL_SEQ_PANIC;
+ unsigned IOCTL_SNDCTL_SEQ_PERCMODE = SNDCTL_SEQ_PERCMODE;
+ unsigned IOCTL_SNDCTL_SEQ_RESET = SNDCTL_SEQ_RESET;
+ unsigned IOCTL_SNDCTL_SEQ_RESETSAMPLES = SNDCTL_SEQ_RESETSAMPLES;
+ unsigned IOCTL_SNDCTL_SEQ_SYNC = SNDCTL_SEQ_SYNC;
+ unsigned IOCTL_SNDCTL_SEQ_TESTMIDI = SNDCTL_SEQ_TESTMIDI;
+ unsigned IOCTL_SNDCTL_SEQ_THRESHOLD = SNDCTL_SEQ_THRESHOLD;
+ unsigned IOCTL_SNDCTL_SYNTH_INFO = SNDCTL_SYNTH_INFO;
+ unsigned IOCTL_SNDCTL_SYNTH_MEMAVL = SNDCTL_SYNTH_MEMAVL;
+ unsigned IOCTL_SNDCTL_TMR_CONTINUE = SNDCTL_TMR_CONTINUE;
+ unsigned IOCTL_SNDCTL_TMR_METRONOME = SNDCTL_TMR_METRONOME;
+ unsigned IOCTL_SNDCTL_TMR_SELECT = SNDCTL_TMR_SELECT;
+ unsigned IOCTL_SNDCTL_TMR_SOURCE = SNDCTL_TMR_SOURCE;
+ unsigned IOCTL_SNDCTL_TMR_START = SNDCTL_TMR_START;
+ unsigned IOCTL_SNDCTL_TMR_STOP = SNDCTL_TMR_STOP;
+ unsigned IOCTL_SNDCTL_TMR_TEMPO = SNDCTL_TMR_TEMPO;
+ unsigned IOCTL_SNDCTL_TMR_TIMEBASE = SNDCTL_TMR_TIMEBASE;
+ unsigned IOCTL_SOUND_MIXER_READ_ALTPCM = SOUND_MIXER_READ_ALTPCM;
+ unsigned IOCTL_SOUND_MIXER_READ_BASS = SOUND_MIXER_READ_BASS;
+ unsigned IOCTL_SOUND_MIXER_READ_CAPS = SOUND_MIXER_READ_CAPS;
+ unsigned IOCTL_SOUND_MIXER_READ_CD = SOUND_MIXER_READ_CD;
+ unsigned IOCTL_SOUND_MIXER_READ_DEVMASK = SOUND_MIXER_READ_DEVMASK;
+ unsigned IOCTL_SOUND_MIXER_READ_ENHANCE = SOUND_MIXER_READ_ENHANCE;
+ unsigned IOCTL_SOUND_MIXER_READ_IGAIN = SOUND_MIXER_READ_IGAIN;
+ unsigned IOCTL_SOUND_MIXER_READ_IMIX = SOUND_MIXER_READ_IMIX;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE = SOUND_MIXER_READ_LINE;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE1 = SOUND_MIXER_READ_LINE1;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE2 = SOUND_MIXER_READ_LINE2;
+ unsigned IOCTL_SOUND_MIXER_READ_LINE3 = SOUND_MIXER_READ_LINE3;
+ unsigned IOCTL_SOUND_MIXER_READ_LOUD = SOUND_MIXER_READ_LOUD;
+ unsigned IOCTL_SOUND_MIXER_READ_MIC = SOUND_MIXER_READ_MIC;
+ unsigned IOCTL_SOUND_MIXER_READ_MUTE = SOUND_MIXER_READ_MUTE;
+ unsigned IOCTL_SOUND_MIXER_READ_OGAIN = SOUND_MIXER_READ_OGAIN;
+ unsigned IOCTL_SOUND_MIXER_READ_PCM = SOUND_MIXER_READ_PCM;
+ unsigned IOCTL_SOUND_MIXER_READ_RECLEV = SOUND_MIXER_READ_RECLEV;
+ unsigned IOCTL_SOUND_MIXER_READ_RECMASK = SOUND_MIXER_READ_RECMASK;
+ unsigned IOCTL_SOUND_MIXER_READ_RECSRC = SOUND_MIXER_READ_RECSRC;
+ unsigned IOCTL_SOUND_MIXER_READ_SPEAKER = SOUND_MIXER_READ_SPEAKER;
+ unsigned IOCTL_SOUND_MIXER_READ_STEREODEVS = SOUND_MIXER_READ_STEREODEVS;
+ unsigned IOCTL_SOUND_MIXER_READ_SYNTH = SOUND_MIXER_READ_SYNTH;
+ unsigned IOCTL_SOUND_MIXER_READ_TREBLE = SOUND_MIXER_READ_TREBLE;
+ unsigned IOCTL_SOUND_MIXER_READ_VOLUME = SOUND_MIXER_READ_VOLUME;
+ unsigned IOCTL_SOUND_MIXER_WRITE_ALTPCM = SOUND_MIXER_WRITE_ALTPCM;
+ unsigned IOCTL_SOUND_MIXER_WRITE_BASS = SOUND_MIXER_WRITE_BASS;
+ unsigned IOCTL_SOUND_MIXER_WRITE_CD = SOUND_MIXER_WRITE_CD;
+ unsigned IOCTL_SOUND_MIXER_WRITE_ENHANCE = SOUND_MIXER_WRITE_ENHANCE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_IGAIN = SOUND_MIXER_WRITE_IGAIN;
+ unsigned IOCTL_SOUND_MIXER_WRITE_IMIX = SOUND_MIXER_WRITE_IMIX;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE = SOUND_MIXER_WRITE_LINE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE1 = SOUND_MIXER_WRITE_LINE1;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE2 = SOUND_MIXER_WRITE_LINE2;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LINE3 = SOUND_MIXER_WRITE_LINE3;
+ unsigned IOCTL_SOUND_MIXER_WRITE_LOUD = SOUND_MIXER_WRITE_LOUD;
+ unsigned IOCTL_SOUND_MIXER_WRITE_MIC = SOUND_MIXER_WRITE_MIC;
+ unsigned IOCTL_SOUND_MIXER_WRITE_MUTE = SOUND_MIXER_WRITE_MUTE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_OGAIN = SOUND_MIXER_WRITE_OGAIN;
+ unsigned IOCTL_SOUND_MIXER_WRITE_PCM = SOUND_MIXER_WRITE_PCM;
+ unsigned IOCTL_SOUND_MIXER_WRITE_RECLEV = SOUND_MIXER_WRITE_RECLEV;
+ unsigned IOCTL_SOUND_MIXER_WRITE_RECSRC = SOUND_MIXER_WRITE_RECSRC;
+ unsigned IOCTL_SOUND_MIXER_WRITE_SPEAKER = SOUND_MIXER_WRITE_SPEAKER;
+ unsigned IOCTL_SOUND_MIXER_WRITE_SYNTH = SOUND_MIXER_WRITE_SYNTH;
+ unsigned IOCTL_SOUND_MIXER_WRITE_TREBLE = SOUND_MIXER_WRITE_TREBLE;
+ unsigned IOCTL_SOUND_MIXER_WRITE_VOLUME = SOUND_MIXER_WRITE_VOLUME;
+ unsigned IOCTL_VT_ACTIVATE = VT_ACTIVATE;
+ unsigned IOCTL_VT_GETMODE = VT_GETMODE;
+ unsigned IOCTL_VT_OPENQRY = VT_OPENQRY;
+ unsigned IOCTL_VT_RELDISP = VT_RELDISP;
+ unsigned IOCTL_VT_SETMODE = VT_SETMODE;
+ unsigned IOCTL_VT_WAITACTIVE = VT_WAITACTIVE;
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned IOCTL_CYGETDEFTHRESH = CYGETDEFTHRESH;
+ unsigned IOCTL_CYGETDEFTIMEOUT = CYGETDEFTIMEOUT;
+ unsigned IOCTL_CYGETMON = CYGETMON;
+ unsigned IOCTL_CYGETTHRESH = CYGETTHRESH;
+ unsigned IOCTL_CYGETTIMEOUT = CYGETTIMEOUT;
+ unsigned IOCTL_CYSETDEFTHRESH = CYSETDEFTHRESH;
+ unsigned IOCTL_CYSETDEFTIMEOUT = CYSETDEFTIMEOUT;
+ unsigned IOCTL_CYSETTHRESH = CYSETTHRESH;
+ unsigned IOCTL_CYSETTIMEOUT = CYSETTIMEOUT;
+ unsigned IOCTL_EQL_EMANCIPATE = EQL_EMANCIPATE;
+ unsigned IOCTL_EQL_ENSLAVE = EQL_ENSLAVE;
+ unsigned IOCTL_EQL_GETMASTRCFG = EQL_GETMASTRCFG;
+ unsigned IOCTL_EQL_GETSLAVECFG = EQL_GETSLAVECFG;
+ unsigned IOCTL_EQL_SETMASTRCFG = EQL_SETMASTRCFG;
+ unsigned IOCTL_EQL_SETSLAVECFG = EQL_SETSLAVECFG;
+#if EV_VERSION > (0x010000)
+ unsigned IOCTL_EVIOCGKEYCODE_V2 = EVIOCGKEYCODE_V2;
+ unsigned IOCTL_EVIOCGPROP = EVIOCGPROP(0);
+ unsigned IOCTL_EVIOCSKEYCODE_V2 = EVIOCSKEYCODE_V2;
+#else
+ unsigned IOCTL_EVIOCGKEYCODE_V2 = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_EVIOCGPROP = IOCTL_NOT_PRESENT;
+ unsigned IOCTL_EVIOCSKEYCODE_V2 = IOCTL_NOT_PRESENT;
+#endif
+ unsigned IOCTL_FS_IOC_GETFLAGS = FS_IOC_GETFLAGS;
+ unsigned IOCTL_FS_IOC_GETVERSION = FS_IOC_GETVERSION;
+ unsigned IOCTL_FS_IOC_SETFLAGS = FS_IOC_SETFLAGS;
+ unsigned IOCTL_FS_IOC_SETVERSION = FS_IOC_SETVERSION;
+ unsigned IOCTL_GIO_CMAP = GIO_CMAP;
+ unsigned IOCTL_GIO_FONT = GIO_FONT;
+ unsigned IOCTL_GIO_UNIMAP = GIO_UNIMAP;
+ unsigned IOCTL_GIO_UNISCRNMAP = GIO_UNISCRNMAP;
+ unsigned IOCTL_KDADDIO = KDADDIO;
+ unsigned IOCTL_KDDELIO = KDDELIO;
+ unsigned IOCTL_KDGETKEYCODE = KDGETKEYCODE;
+ unsigned IOCTL_KDGKBDIACR = KDGKBDIACR;
+ unsigned IOCTL_KDGKBENT = KDGKBENT;
+ unsigned IOCTL_KDGKBLED = KDGKBLED;
+ unsigned IOCTL_KDGKBMETA = KDGKBMETA;
+ unsigned IOCTL_KDGKBSENT = KDGKBSENT;
+ unsigned IOCTL_KDMAPDISP = KDMAPDISP;
+ unsigned IOCTL_KDSETKEYCODE = KDSETKEYCODE;
+ unsigned IOCTL_KDSIGACCEPT = KDSIGACCEPT;
+ unsigned IOCTL_KDSKBDIACR = KDSKBDIACR;
+ unsigned IOCTL_KDSKBENT = KDSKBENT;
+ unsigned IOCTL_KDSKBLED = KDSKBLED;
+ unsigned IOCTL_KDSKBMETA = KDSKBMETA;
+ unsigned IOCTL_KDSKBSENT = KDSKBSENT;
+ unsigned IOCTL_KDUNMAPDISP = KDUNMAPDISP;
+ unsigned IOCTL_LPABORT = LPABORT;
+ unsigned IOCTL_LPABORTOPEN = LPABORTOPEN;
+ unsigned IOCTL_LPCAREFUL = LPCAREFUL;
+ unsigned IOCTL_LPCHAR = LPCHAR;
+ unsigned IOCTL_LPGETIRQ = LPGETIRQ;
+ unsigned IOCTL_LPGETSTATUS = LPGETSTATUS;
+ unsigned IOCTL_LPRESET = LPRESET;
+ unsigned IOCTL_LPSETIRQ = LPSETIRQ;
+ unsigned IOCTL_LPTIME = LPTIME;
+ unsigned IOCTL_LPWAIT = LPWAIT;
+ unsigned IOCTL_MTIOCGETCONFIG = MTIOCGETCONFIG;
+ unsigned IOCTL_MTIOCSETCONFIG = MTIOCSETCONFIG;
+ unsigned IOCTL_PIO_CMAP = PIO_CMAP;
+ unsigned IOCTL_PIO_FONT = PIO_FONT;
+ unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP;
+ unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR;
+ unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP;
+ unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN;
+ unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST;
+ unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE;
+ unsigned IOCTL_SCSI_IOCTL_TAGGED_ENABLE = SCSI_IOCTL_TAGGED_ENABLE;
+ unsigned IOCTL_SIOCAIPXITFCRT = SIOCAIPXITFCRT;
+ unsigned IOCTL_SIOCAIPXPRISLT = SIOCAIPXPRISLT;
+ unsigned IOCTL_SIOCAX25ADDUID = SIOCAX25ADDUID;
+ unsigned IOCTL_SIOCAX25DELUID = SIOCAX25DELUID;
+ unsigned IOCTL_SIOCAX25GETPARMS = SIOCAX25GETPARMS;
+ unsigned IOCTL_SIOCAX25GETUID = SIOCAX25GETUID;
+ unsigned IOCTL_SIOCAX25NOUID = SIOCAX25NOUID;
+ unsigned IOCTL_SIOCAX25SETPARMS = SIOCAX25SETPARMS;
+ unsigned IOCTL_SIOCDEVPLIP = SIOCDEVPLIP;
+ unsigned IOCTL_SIOCIPXCFGDATA = SIOCIPXCFGDATA;
+ unsigned IOCTL_SIOCNRDECOBS = SIOCNRDECOBS;
+ unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS;
+ unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL;
+ unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS;
+ unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL;
+ unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI;
+ unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI;
+ unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL;
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ unsigned IOCTL_GIO_SCRNMAP = GIO_SCRNMAP;
+ unsigned IOCTL_KDDISABIO = KDDISABIO;
+ unsigned IOCTL_KDENABIO = KDENABIO;
+ unsigned IOCTL_KDGETLED = KDGETLED;
+ unsigned IOCTL_KDGETMODE = KDGETMODE;
+ unsigned IOCTL_KDGKBMODE = KDGKBMODE;
+ unsigned IOCTL_KDGKBTYPE = KDGKBTYPE;
+ unsigned IOCTL_KDMKTONE = KDMKTONE;
+ unsigned IOCTL_KDSETLED = KDSETLED;
+ unsigned IOCTL_KDSETMODE = KDSETMODE;
+ unsigned IOCTL_KDSKBMODE = KDSKBMODE;
+ unsigned IOCTL_KIOCSOUND = KIOCSOUND;
+ unsigned IOCTL_PIO_SCRNMAP = PIO_SCRNMAP;
+ unsigned IOCTL_SNDCTL_DSP_GETISPACE = SNDCTL_DSP_GETISPACE;
+ unsigned IOCTL_SNDCTL_DSP_GETOSPACE = SNDCTL_DSP_GETOSPACE;
+#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+
+ const int si_SEGV_MAPERR = SEGV_MAPERR;
+ const int si_SEGV_ACCERR = SEGV_ACCERR;
+} // namespace __sanitizer
+
+using namespace __sanitizer;
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+#if SANITIZER_LINUX
+// FIXME: We define those on Linux and Mac, but only check on Linux.
+COMPILER_CHECK(IOC_NRBITS == _IOC_NRBITS);
+COMPILER_CHECK(IOC_TYPEBITS == _IOC_TYPEBITS);
+COMPILER_CHECK(IOC_SIZEBITS == _IOC_SIZEBITS);
+COMPILER_CHECK(IOC_DIRBITS == _IOC_DIRBITS);
+COMPILER_CHECK(IOC_NRMASK == _IOC_NRMASK);
+COMPILER_CHECK(IOC_TYPEMASK == _IOC_TYPEMASK);
+COMPILER_CHECK(IOC_SIZEMASK == _IOC_SIZEMASK);
+COMPILER_CHECK(IOC_DIRMASK == _IOC_DIRMASK);
+COMPILER_CHECK(IOC_NRSHIFT == _IOC_NRSHIFT);
+COMPILER_CHECK(IOC_TYPESHIFT == _IOC_TYPESHIFT);
+COMPILER_CHECK(IOC_SIZESHIFT == _IOC_SIZESHIFT);
+COMPILER_CHECK(IOC_DIRSHIFT == _IOC_DIRSHIFT);
+COMPILER_CHECK(IOC_NONE == _IOC_NONE);
+COMPILER_CHECK(IOC_WRITE == _IOC_WRITE);
+COMPILER_CHECK(IOC_READ == _IOC_READ);
+COMPILER_CHECK(EVIOC_ABS_MAX == ABS_MAX);
+COMPILER_CHECK(EVIOC_EV_MAX == EV_MAX);
+COMPILER_CHECK(IOC_SIZE(0x12345678) == _IOC_SIZE(0x12345678));
+COMPILER_CHECK(IOC_DIR(0x12345678) == _IOC_DIR(0x12345678));
+COMPILER_CHECK(IOC_NR(0x12345678) == _IOC_NR(0x12345678));
+COMPILER_CHECK(IOC_TYPE(0x12345678) == _IOC_TYPE(0x12345678));
+#endif // SANITIZER_LINUX
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+// There are more undocumented fields in dl_phdr_info that we are not interested
+// in.
+COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_flags);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_closedir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_stat);
+#endif
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+#ifndef __GLIBC_PREREQ
+#define __GLIBC_PREREQ(x, y) 0
+#endif
+
+#if SANITIZER_LINUX && (__ANDROID_API__ >= 21 || __GLIBC_PREREQ (2, 14))
+CHECK_TYPE_SIZE(mmsghdr);
+CHECK_SIZE_AND_OFFSET(mmsghdr, msg_hdr);
+CHECK_SIZE_AND_OFFSET(mmsghdr, msg_len);
+#endif
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_ino);
+#if SANITIZER_MAC
+CHECK_SIZE_AND_OFFSET(dirent, d_seekoff);
+#elif SANITIZER_FREEBSD
+// There is no 'd_off' field on FreeBSD.
+#else
+CHECK_SIZE_AND_OFFSET(dirent, d_off);
+#endif
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64));
+CHECK_SIZE_AND_OFFSET(dirent64, d_ino);
+CHECK_SIZE_AND_OFFSET(dirent64, d_off);
+CHECK_SIZE_AND_OFFSET(dirent64, d_reclen);
+#endif
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+// Can't write checks for sa_handler and sa_sigaction due to them being
+// preprocessor macros.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
+#if !defined(__s390x__) || __GLIBC_PREREQ (2, 20)
+// On s390x glibc 2.19 and earlier sa_flags was unsigned long, and sa_resv
+// didn't exist.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
+#endif
+#if SANITIZER_LINUX && (!SANITIZER_ANDROID || !SANITIZER_MIPS32)
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer);
+#endif
+
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(__sysctl_args);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, name);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, nlen);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, oldval);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, oldlenp);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, newval);
+CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen);
+
+CHECK_TYPE_SIZE(__kernel_uid_t);
+CHECK_TYPE_SIZE(__kernel_gid_t);
+
+#if SANITIZER_USES_UID16_SYSCALLS
+CHECK_TYPE_SIZE(__kernel_old_uid_t);
+CHECK_TYPE_SIZE(__kernel_old_gid_t);
+#endif
+
+CHECK_TYPE_SIZE(__kernel_off_t);
+CHECK_TYPE_SIZE(__kernel_loff_t);
+CHECK_TYPE_SIZE(__kernel_fd_set);
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(wordexp_t);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+#endif
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff);
+CHECK_SIZE_AND_OFFSET(tm, tm_zone);
+
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(mntent);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_dir);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_type);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_opts);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_freq);
+CHECK_SIZE_AND_OFFSET(mntent, mnt_passno);
+#endif
+
+CHECK_TYPE_SIZE(ether_addr);
+
+#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(ipc_perm);
+# if SANITIZER_FREEBSD
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+# else
+CHECK_SIZE_AND_OFFSET(ipc_perm, __key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, __seq);
+# endif
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+#if !defined(__aarch64__) || !SANITIZER_LINUX || __GLIBC_PREREQ (2, 21)
+/* On aarch64 glibc 2.20 and earlier provided incorrect mode field. */
+CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
+#endif
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+#endif
+
+CHECK_TYPE_SIZE(clock_t);
+
+#if SANITIZER_LINUX
+CHECK_TYPE_SIZE(clockid_t);
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+// Compare against the union, because we can't reach into the union in a
+// compliant way.
+#ifdef ifa_dstaddr
+#undef ifa_dstaddr
+#endif
+# if SANITIZER_FREEBSD
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+# else
+COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
+ sizeof(((ifaddrs *)nullptr)->ifa_ifu));
+COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
+ offsetof(ifaddrs, ifa_ifu));
+# endif // SANITIZER_FREEBSD
+#else
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr);
+#endif // SANITIZER_LINUX
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+#endif
+
+#if SANITIZER_LINUX
+COMPILER_CHECK(sizeof(__sanitizer_struct_mallinfo) == sizeof(struct mallinfo));
+#endif
+
+#if !SANITIZER_ANDROID
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+#endif
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+#if !SANITIZER_ANDROID
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+#endif
+
+#if SANITIZER_MAC
+CHECK_SIZE_AND_OFFSET(passwd, pw_change);
+CHECK_SIZE_AND_OFFSET(passwd, pw_expire);
+CHECK_SIZE_AND_OFFSET(passwd, pw_class);
+#endif
+
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+#if HAVE_RPC_XDR_H
+CHECK_TYPE_SIZE(XDR);
+CHECK_SIZE_AND_OFFSET(XDR, x_op);
+CHECK_SIZE_AND_OFFSET(XDR, x_ops);
+CHECK_SIZE_AND_OFFSET(XDR, x_public);
+CHECK_SIZE_AND_OFFSET(XDR, x_private);
+CHECK_SIZE_AND_OFFSET(XDR, x_base);
+CHECK_SIZE_AND_OFFSET(XDR, x_handy);
+COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
+COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
+COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE));
+CHECK_SIZE_AND_OFFSET(FILE, _flags);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_read_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_ptr);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_write_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_buf_end);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_save_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_backup_base);
+CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end);
+CHECK_SIZE_AND_OFFSET(FILE, _markers);
+CHECK_SIZE_AND_OFFSET(FILE, _chain);
+CHECK_SIZE_AND_OFFSET(FILE, _fileno);
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk));
+CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit);
+CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev);
+CHECK_TYPE_SIZE(obstack);
+CHECK_SIZE_AND_OFFSET(obstack, chunk_size);
+CHECK_SIZE_AND_OFFSET(obstack, chunk);
+CHECK_SIZE_AND_OFFSET(obstack, object_base);
+CHECK_SIZE_AND_OFFSET(obstack, next_free);
+
+CHECK_TYPE_SIZE(cookie_io_functions_t);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek);
+CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close);
+#endif
+
+#if SANITIZER_LINUX || SANITIZER_FREEBSD
+CHECK_TYPE_SIZE(sem_t);
+#endif
+
+#if SANITIZER_LINUX && defined(__arm__)
+COMPILER_CHECK(ARM_VFPREGS_SIZE == ARM_VFPREGS_SIZE_ASAN);
+#endif
+
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
//===-- sanitizer_platform_limits_posix.h ---------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H
#define SANITIZER_PLATFORM_LIMITS_POSIX_H
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
+#if SANITIZER_LINUX || SANITIZER_MAC
#include "sanitizer_internal_defs.h"
#include "sanitizer_platform.h"
-#if SANITIZER_FREEBSD
-// FreeBSD's dlopen() returns a pointer to an Obj_Entry structure that
-// incorporates the map structure.
-# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) \
- ((link_map*)((handle) == nullptr ? nullptr : ((char*)(handle) + 560)))
-// Get sys/_types.h, because that tells us whether 64-bit inodes are
-// used in struct dirent below.
-#include <sys/_types.h>
-#else
# define GET_LINK_MAP_BY_DLOPEN_HANDLE(handle) ((link_map*)(handle))
-#endif // !SANITIZER_FREEBSD
#ifndef __GLIBC_PREREQ
#define __GLIBC_PREREQ(x, y) 0
namespace __sanitizer {
extern unsigned struct_utsname_sz;
extern unsigned struct_stat_sz;
-#if !SANITIZER_FREEBSD && !SANITIZER_IOS
+#if !SANITIZER_IOS
extern unsigned struct_stat64_sz;
#endif
extern unsigned struct_rusage_sz;
extern unsigned struct_sigevent_sz;
extern unsigned struct_sched_param_sz;
extern unsigned struct_statfs64_sz;
+ extern unsigned struct_regex_sz;
+ extern unsigned struct_regmatch_sz;
#if !SANITIZER_ANDROID
+ extern unsigned struct_fstab_sz;
extern unsigned struct_statfs_sz;
extern unsigned struct_sockaddr_sz;
extern unsigned ucontext_t_sz;
#elif defined(__mips__)
const unsigned struct_kernel_stat_sz =
SANITIZER_ANDROID ? FIRST_32_SECOND_64(104, 128) :
- FIRST_32_SECOND_64(144, 216);
+ FIRST_32_SECOND_64(160, 216);
const unsigned struct_kernel_stat64_sz = 104;
#elif defined(__s390__) && !defined(__s390x__)
const unsigned struct_kernel_stat_sz = 64;
const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long);
#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
#if defined(__powerpc64__) || defined(__s390__)
const unsigned struct___old_kernel_stat_sz = 0;
int data;
#elif SANITIZER_LINUX
uptr data[4];
-#elif SANITIZER_FREEBSD
- u32 data[4];
#endif
};
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_ANDROID
struct __sanitizer_struct_mallinfo {
#endif
#endif
};
-#elif SANITIZER_FREEBSD
- struct __sanitizer_ipc_perm {
- unsigned int cuid;
- unsigned int cgid;
- unsigned int uid;
- unsigned int gid;
- unsigned short mode;
- unsigned short seq;
- long key;
- };
-
- struct __sanitizer_shmid_ds {
- __sanitizer_ipc_perm shm_perm;
- unsigned long shm_segsz;
- unsigned int shm_lpid;
- unsigned int shm_cpid;
- int shm_nattch;
- unsigned long shm_atime;
- unsigned long shm_dtime;
- unsigned long shm_ctime;
- };
#endif
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_msqid_ds_sz;
extern unsigned struct_mq_attr_sz;
extern unsigned struct_timex_sz;
extern unsigned struct_statvfs_sz;
-#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
struct __sanitizer_iovec {
void *iov_base;
char *pw_passwd;
int pw_uid;
int pw_gid;
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
long pw_change;
char *pw_class;
#endif
#endif
char *pw_dir;
char *pw_shell;
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
long pw_expire;
-#endif
-#if SANITIZER_FREEBSD
- int pw_fields;
#endif
};
};
#endif
-#if SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_MAC
struct __sanitizer_msghdr {
void *msg_name;
unsigned msg_namelen;
unsigned short d_reclen;
// more fields that we don't care about
};
-#elif SANITIZER_FREEBSD
- struct __sanitizer_dirent {
-#if defined(__INO64)
- unsigned long long d_fileno;
- unsigned long long d_off;
-#else
- unsigned int d_fileno;
-#endif
- unsigned short d_reclen;
- // more fields that we don't care about
- };
#elif SANITIZER_ANDROID || defined(__x86_64__)
struct __sanitizer_dirent {
unsigned long long d_ino;
};
#endif
-// 'clock_t' is 32 bits wide on x64 FreeBSD
-#if SANITIZER_FREEBSD
- typedef int __sanitizer_clock_t;
-#elif defined(__x86_64__) && !defined(_LP64)
+#if defined(__x86_64__) && !defined(_LP64)
typedef long long __sanitizer_clock_t;
#else
typedef long __sanitizer_clock_t;
#endif
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
typedef int __sanitizer_clockid_t;
#endif
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
#if defined(_LP64) || defined(__x86_64__) || defined(__powerpc__)\
|| defined(__mips__)
typedef unsigned __sanitizer___kernel_uid_t;
#endif
// This thing depends on the platform. We are only interested in the upper
- // limit. Verified with a compiler assert in .cc.
+ // limit. Verified with a compiler assert in .cpp.
const int pthread_attr_t_max_sz = 128;
union __sanitizer_pthread_attr_t {
char size[pthread_attr_t_max_sz]; // NOLINT
// The size is determined by looking at sizeof of real sigset_t on linux.
uptr val[128 / sizeof(uptr)];
};
-#elif SANITIZER_FREEBSD
- struct __sanitizer_sigset_t {
- // uint32_t * 4
- unsigned int __bits[4];
- };
#endif
struct __sanitizer_siginfo {
};
#endif // !SANITIZER_ANDROID
-#if SANITIZER_FREEBSD
- typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t;
-#elif defined(__mips__)
+#if defined(__mips__)
struct __sanitizer_kernel_sigset_t {
uptr sig[2];
};
extern int af_inet6;
uptr __sanitizer_in_addr_sz(int af);
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
struct __sanitizer_dl_phdr_info {
uptr dlpi_addr;
const char *dlpi_name;
int ai_family;
int ai_socktype;
int ai_protocol;
-#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_ANDROID || SANITIZER_MAC
unsigned ai_addrlen;
char *ai_canonname;
void *ai_addr;
short revents;
};
-#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD
+#if SANITIZER_ANDROID || SANITIZER_MAC
typedef unsigned __sanitizer_nfds_t;
#else
typedef unsigned long __sanitizer_nfds_t;
int (*gl_lstat)(const char *, void *);
int (*gl_stat)(const char *, void *);
};
-# elif SANITIZER_FREEBSD
- struct __sanitizer_glob_t {
- uptr gl_pathc;
- uptr gl_matchc;
- uptr gl_offs;
- int gl_flags;
- char **gl_pathv;
- int (*gl_errfunc)(const char*, int);
- void (*gl_closedir)(void *dirp);
- struct dirent *(*gl_readdir)(void *dirp);
- void *(*gl_opendir)(const char*);
- int (*gl_lstat)(const char*, void* /* struct stat* */);
- int (*gl_stat)(const char*, void* /* struct stat* */);
- };
-# endif // SANITIZER_FREEBSD
+# endif // SANITIZER_LINUX
-# if SANITIZER_LINUX || SANITIZER_FREEBSD
+# if SANITIZER_LINUX
extern int glob_nomatch;
extern int glob_altdirfunc;
# endif
uptr we_wordc;
char **we_wordv;
uptr we_offs;
-#if SANITIZER_FREEBSD
- char *we_strings;
- uptr we_nbytes;
-#endif
};
#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern int ptrace_geteventmsg;
#endif
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_shminfo_sz;
extern unsigned struct_shm_info_sz;
extern int shmctl_ipc_stat;
extern unsigned struct_vt_stat_sz;
#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
+#if SANITIZER_LINUX
extern unsigned struct_copr_buffer_sz;
extern unsigned struct_copr_debug_buf_sz;
extern unsigned struct_copr_msg_sz;
extern unsigned struct_seq_event_rec_sz;
extern unsigned struct_synth_info_sz;
extern unsigned struct_vt_mode_sz;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_ax25_parms_struct_sz;
extern unsigned struct_unimapinit_sz;
#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+ extern const unsigned long __sanitizer_bufsiz;
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned struct_audio_buf_info_sz;
extern unsigned struct_ppp_stats_sz;
#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
extern unsigned IOCTL_TIOCSPGRP;
extern unsigned IOCTL_TIOCSTI;
extern unsigned IOCTL_TIOCSWINSZ;
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned IOCTL_SIOCGETSGCNT;
extern unsigned IOCTL_SIOCGETVIFCNT;
#endif
extern unsigned IOCTL_VT_RESIZE;
extern unsigned IOCTL_VT_RESIZEX;
extern unsigned IOCTL_VT_SENDSIG;
-#endif // SANITIZER_LINUX
-#if SANITIZER_LINUX || SANITIZER_FREEBSD
extern unsigned IOCTL_MTIOCGET;
extern unsigned IOCTL_MTIOCTOP;
extern unsigned IOCTL_SIOCADDRT;
extern unsigned IOCTL_VT_RELDISP;
extern unsigned IOCTL_VT_SETMODE;
extern unsigned IOCTL_VT_WAITACTIVE;
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD
+#endif // SANITIZER_LINUX
#if SANITIZER_LINUX && !SANITIZER_ANDROID
extern unsigned IOCTL_CYGETDEFTHRESH;
extern unsigned IOCTL_TIOCSERGETMULTI;
extern unsigned IOCTL_TIOCSERSETMULTI;
extern unsigned IOCTL_TIOCSSERIAL;
-#endif // SANITIZER_LINUX && !SANITIZER_ANDROID
-
-#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID
extern unsigned IOCTL_GIO_SCRNMAP;
extern unsigned IOCTL_KDDISABIO;
extern unsigned IOCTL_KDENABIO;
#define SIGACTION_SYMNAME sigaction
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC
+#endif // SANITIZER_LINUX || SANITIZER_MAC
#endif
+++ /dev/null
-//===-- sanitizer_platform_limits_solaris.cc ------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of Sanitizer common code.
-//
-// Sizes and layouts of platform-specific Solaris data structures.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_SOLARIS
-#include <arpa/inet.h>
-#include <dirent.h>
-#include <glob.h>
-#include <grp.h>
-#include <ifaddrs.h>
-#include <limits.h>
-#include <link.h>
-#include <net/if.h>
-#include <net/route.h>
-#include <netdb.h>
-#include <netinet/ip_mroute.h>
-#include <poll.h>
-#include <pthread.h>
-#include <pwd.h>
-#include <rpc/xdr.h>
-#include <semaphore.h>
-#include <signal.h>
-#include <stddef.h>
-#include <sys/ethernet.h>
-#include <sys/filio.h>
-#include <sys/ipc.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/mtio.h>
-#include <sys/ptyvar.h>
-#include <sys/resource.h>
-#include <sys/shm.h>
-#include <sys/socket.h>
-#include <sys/sockio.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/statvfs.h>
-#include <sys/time.h>
-#include <sys/timeb.h>
-#include <sys/times.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <termios.h>
-#include <time.h>
-#include <utmp.h>
-#include <utmpx.h>
-#include <wchar.h>
-#include <wordexp.h>
-
-// Include these after system headers to avoid name clashes and ambiguities.
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_platform_limits_solaris.h"
-
-namespace __sanitizer {
- unsigned struct_utsname_sz = sizeof(struct utsname);
- unsigned struct_stat_sz = sizeof(struct stat);
- unsigned struct_stat64_sz = sizeof(struct stat64);
- unsigned struct_rusage_sz = sizeof(struct rusage);
- unsigned struct_tm_sz = sizeof(struct tm);
- unsigned struct_passwd_sz = sizeof(struct passwd);
- unsigned struct_group_sz = sizeof(struct group);
- unsigned siginfo_t_sz = sizeof(siginfo_t);
- unsigned struct_sigaction_sz = sizeof(struct sigaction);
- unsigned struct_itimerval_sz = sizeof(struct itimerval);
- unsigned pthread_t_sz = sizeof(pthread_t);
- unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
- unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
- unsigned pid_t_sz = sizeof(pid_t);
- unsigned timeval_sz = sizeof(timeval);
- unsigned uid_t_sz = sizeof(uid_t);
- unsigned gid_t_sz = sizeof(gid_t);
- unsigned mbstate_t_sz = sizeof(mbstate_t);
- unsigned sigset_t_sz = sizeof(sigset_t);
- unsigned struct_timezone_sz = sizeof(struct timezone);
- unsigned struct_tms_sz = sizeof(struct tms);
- unsigned struct_sigevent_sz = sizeof(struct sigevent);
- unsigned struct_sched_param_sz = sizeof(struct sched_param);
- unsigned struct_statfs_sz = sizeof(struct statfs);
- unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
- unsigned ucontext_t_sz = sizeof(ucontext_t);
- unsigned struct_timespec_sz = sizeof(struct timespec);
-#if SANITIZER_SOLARIS32
- unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
-#endif
- unsigned struct_statvfs_sz = sizeof(struct statvfs);
-
- const uptr sig_ign = (uptr)SIG_IGN;
- const uptr sig_dfl = (uptr)SIG_DFL;
- const uptr sig_err = (uptr)SIG_ERR;
- const uptr sa_siginfo = (uptr)SA_SIGINFO;
-
- int shmctl_ipc_stat = (int)IPC_STAT;
-
- unsigned struct_utmp_sz = sizeof(struct utmp);
- unsigned struct_utmpx_sz = sizeof(struct utmpx);
-
- int map_fixed = MAP_FIXED;
-
- int af_inet = (int)AF_INET;
- int af_inet6 = (int)AF_INET6;
-
- uptr __sanitizer_in_addr_sz(int af) {
- if (af == AF_INET)
- return sizeof(struct in_addr);
- else if (af == AF_INET6)
- return sizeof(struct in6_addr);
- else
- return 0;
- }
-
- unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
-
- int glob_nomatch = GLOB_NOMATCH;
-
- unsigned path_max = PATH_MAX;
-
- // ioctl arguments
- unsigned struct_ifreq_sz = sizeof(struct ifreq);
- unsigned struct_termios_sz = sizeof(struct termios);
- unsigned struct_winsize_sz = sizeof(struct winsize);
-
- unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
- unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
-
- const unsigned IOCTL_NOT_PRESENT = 0;
-
- unsigned IOCTL_FIOASYNC = FIOASYNC;
- unsigned IOCTL_FIOCLEX = FIOCLEX;
- unsigned IOCTL_FIOGETOWN = FIOGETOWN;
- unsigned IOCTL_FIONBIO = FIONBIO;
- unsigned IOCTL_FIONCLEX = FIONCLEX;
- unsigned IOCTL_FIOSETOWN = FIOSETOWN;
- unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
- unsigned IOCTL_SIOCATMARK = SIOCATMARK;
- unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
- unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
- unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
- unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
- unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
- unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
- unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
- unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
- unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
- unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
- unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
- unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
- unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
- unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
- unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
- unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
- unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
- unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
- unsigned IOCTL_TIOCEXCL = TIOCEXCL;
- unsigned IOCTL_TIOCGETD = TIOCGETD;
- unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
- unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
- unsigned IOCTL_TIOCMBIC = TIOCMBIC;
- unsigned IOCTL_TIOCMBIS = TIOCMBIS;
- unsigned IOCTL_TIOCMGET = TIOCMGET;
- unsigned IOCTL_TIOCMSET = TIOCMSET;
- unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
- unsigned IOCTL_TIOCNXCL = TIOCNXCL;
- unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
- unsigned IOCTL_TIOCPKT = TIOCPKT;
- unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
- unsigned IOCTL_TIOCSETD = TIOCSETD;
- unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
- unsigned IOCTL_TIOCSTI = TIOCSTI;
- unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
-
- unsigned IOCTL_MTIOCGET = MTIOCGET;
- unsigned IOCTL_MTIOCTOP = MTIOCTOP;
-
- const int si_SEGV_MAPERR = SEGV_MAPERR;
- const int si_SEGV_ACCERR = SEGV_ACCERR;
-} // namespace __sanitizer
-
-using namespace __sanitizer;
-
-COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
-
-COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
-CHECK_TYPE_SIZE(pthread_key_t);
-
-// There are more undocumented fields in dl_phdr_info that we are not interested
-// in.
-COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
-CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
-
-CHECK_TYPE_SIZE(glob_t);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
-CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
-
-CHECK_TYPE_SIZE(addrinfo);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
-CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
-
-CHECK_TYPE_SIZE(hostent);
-CHECK_SIZE_AND_OFFSET(hostent, h_name);
-CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
-CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
-CHECK_SIZE_AND_OFFSET(hostent, h_length);
-CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
-
-CHECK_TYPE_SIZE(iovec);
-CHECK_SIZE_AND_OFFSET(iovec, iov_base);
-CHECK_SIZE_AND_OFFSET(iovec, iov_len);
-
-CHECK_TYPE_SIZE(msghdr);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
-CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
-
-CHECK_TYPE_SIZE(cmsghdr);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
-CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
-
-COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
-CHECK_SIZE_AND_OFFSET(dirent, d_ino);
-CHECK_SIZE_AND_OFFSET(dirent, d_off);
-CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
-
-#if SANITIZER_SOLARIS32
-COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64));
-CHECK_SIZE_AND_OFFSET(dirent64, d_ino);
-CHECK_SIZE_AND_OFFSET(dirent64, d_off);
-CHECK_SIZE_AND_OFFSET(dirent64, d_reclen);
-#endif
-
-CHECK_TYPE_SIZE(ifconf);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
-CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
-
-CHECK_TYPE_SIZE(pollfd);
-CHECK_SIZE_AND_OFFSET(pollfd, fd);
-CHECK_SIZE_AND_OFFSET(pollfd, events);
-CHECK_SIZE_AND_OFFSET(pollfd, revents);
-
-CHECK_TYPE_SIZE(nfds_t);
-
-CHECK_TYPE_SIZE(sigset_t);
-
-COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
-// Can't write checks for sa_handler and sa_sigaction due to them being
-// preprocessor macros.
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
-CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
-
-CHECK_TYPE_SIZE(wordexp_t);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
-CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
-
-CHECK_TYPE_SIZE(tm);
-CHECK_SIZE_AND_OFFSET(tm, tm_sec);
-CHECK_SIZE_AND_OFFSET(tm, tm_min);
-CHECK_SIZE_AND_OFFSET(tm, tm_hour);
-CHECK_SIZE_AND_OFFSET(tm, tm_mday);
-CHECK_SIZE_AND_OFFSET(tm, tm_mon);
-CHECK_SIZE_AND_OFFSET(tm, tm_year);
-CHECK_SIZE_AND_OFFSET(tm, tm_wday);
-CHECK_SIZE_AND_OFFSET(tm, tm_yday);
-CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
-
-CHECK_TYPE_SIZE(ether_addr);
-
-CHECK_TYPE_SIZE(ipc_perm);
-CHECK_SIZE_AND_OFFSET(ipc_perm, key);
-CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
-CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
-CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
-
-CHECK_TYPE_SIZE(shmid_ds);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
-CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
-
-CHECK_TYPE_SIZE(clock_t);
-
-CHECK_TYPE_SIZE(ifaddrs);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
-// Compare against the union, because we can't reach into the union in a
-// compliant way.
-#ifdef ifa_dstaddr
-#undef ifa_dstaddr
-#endif
-COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
- sizeof(((ifaddrs *)nullptr)->ifa_ifu));
-COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
- offsetof(ifaddrs, ifa_ifu));
-CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
-
-CHECK_TYPE_SIZE(timeb);
-CHECK_SIZE_AND_OFFSET(timeb, time);
-CHECK_SIZE_AND_OFFSET(timeb, millitm);
-CHECK_SIZE_AND_OFFSET(timeb, timezone);
-CHECK_SIZE_AND_OFFSET(timeb, dstflag);
-
-CHECK_TYPE_SIZE(passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_name);
-CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
-CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
-CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
-CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
-
-CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
-
-CHECK_TYPE_SIZE(group);
-CHECK_SIZE_AND_OFFSET(group, gr_name);
-CHECK_SIZE_AND_OFFSET(group, gr_passwd);
-CHECK_SIZE_AND_OFFSET(group, gr_gid);
-CHECK_SIZE_AND_OFFSET(group, gr_mem);
-
-CHECK_TYPE_SIZE(XDR);
-CHECK_SIZE_AND_OFFSET(XDR, x_op);
-CHECK_SIZE_AND_OFFSET(XDR, x_ops);
-CHECK_SIZE_AND_OFFSET(XDR, x_public);
-CHECK_SIZE_AND_OFFSET(XDR, x_private);
-CHECK_SIZE_AND_OFFSET(XDR, x_base);
-CHECK_SIZE_AND_OFFSET(XDR, x_handy);
-COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
-COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
-COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
-
-CHECK_TYPE_SIZE(sem_t);
-
-#endif // SANITIZER_SOLARIS
--- /dev/null
+//===-- sanitizer_platform_limits_solaris.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of Sanitizer common code.
+//
+// Sizes and layouts of platform-specific Solaris data structures.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_SOLARIS
+#include <arpa/inet.h>
+#include <dirent.h>
+#include <glob.h>
+#include <grp.h>
+#include <ifaddrs.h>
+#include <limits.h>
+#include <link.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <netinet/ip_mroute.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <rpc/xdr.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stddef.h>
+#include <sys/ethernet.h>
+#include <sys/filio.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/mtio.h>
+#include <sys/ptyvar.h>
+#include <sys/resource.h>
+#include <sys/shm.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/timeb.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <termios.h>
+#include <time.h>
+#include <utmp.h>
+#include <utmpx.h>
+#include <wchar.h>
+#include <wordexp.h>
+
+// Include these after system headers to avoid name clashes and ambiguities.
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_solaris.h"
+
+namespace __sanitizer {
+ unsigned struct_utsname_sz = sizeof(struct utsname);
+ unsigned struct_stat_sz = sizeof(struct stat);
+ unsigned struct_stat64_sz = sizeof(struct stat64);
+ unsigned struct_rusage_sz = sizeof(struct rusage);
+ unsigned struct_tm_sz = sizeof(struct tm);
+ unsigned struct_passwd_sz = sizeof(struct passwd);
+ unsigned struct_group_sz = sizeof(struct group);
+ unsigned siginfo_t_sz = sizeof(siginfo_t);
+ unsigned struct_sigaction_sz = sizeof(struct sigaction);
+ unsigned struct_itimerval_sz = sizeof(struct itimerval);
+ unsigned pthread_t_sz = sizeof(pthread_t);
+ unsigned pthread_mutex_t_sz = sizeof(pthread_mutex_t);
+ unsigned pthread_cond_t_sz = sizeof(pthread_cond_t);
+ unsigned pid_t_sz = sizeof(pid_t);
+ unsigned timeval_sz = sizeof(timeval);
+ unsigned uid_t_sz = sizeof(uid_t);
+ unsigned gid_t_sz = sizeof(gid_t);
+ unsigned mbstate_t_sz = sizeof(mbstate_t);
+ unsigned sigset_t_sz = sizeof(sigset_t);
+ unsigned struct_timezone_sz = sizeof(struct timezone);
+ unsigned struct_tms_sz = sizeof(struct tms);
+ unsigned struct_sigevent_sz = sizeof(struct sigevent);
+ unsigned struct_sched_param_sz = sizeof(struct sched_param);
+ unsigned struct_statfs_sz = sizeof(struct statfs);
+ unsigned struct_sockaddr_sz = sizeof(struct sockaddr);
+ unsigned ucontext_t_sz = sizeof(ucontext_t);
+ unsigned struct_timespec_sz = sizeof(struct timespec);
+#if SANITIZER_SOLARIS32
+ unsigned struct_statvfs64_sz = sizeof(struct statvfs64);
+#endif
+ unsigned struct_statvfs_sz = sizeof(struct statvfs);
+
+ const uptr sig_ign = (uptr)SIG_IGN;
+ const uptr sig_dfl = (uptr)SIG_DFL;
+ const uptr sig_err = (uptr)SIG_ERR;
+ const uptr sa_siginfo = (uptr)SA_SIGINFO;
+
+ int shmctl_ipc_stat = (int)IPC_STAT;
+
+ unsigned struct_utmp_sz = sizeof(struct utmp);
+ unsigned struct_utmpx_sz = sizeof(struct utmpx);
+
+ int map_fixed = MAP_FIXED;
+
+ int af_inet = (int)AF_INET;
+ int af_inet6 = (int)AF_INET6;
+
+ uptr __sanitizer_in_addr_sz(int af) {
+ if (af == AF_INET)
+ return sizeof(struct in_addr);
+ else if (af == AF_INET6)
+ return sizeof(struct in6_addr);
+ else
+ return 0;
+ }
+
+ unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr));
+
+ int glob_nomatch = GLOB_NOMATCH;
+
+ unsigned path_max = PATH_MAX;
+
+ // ioctl arguments
+ unsigned struct_ifreq_sz = sizeof(struct ifreq);
+ unsigned struct_termios_sz = sizeof(struct termios);
+ unsigned struct_winsize_sz = sizeof(struct winsize);
+
+ unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req);
+ unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req);
+
+ const unsigned IOCTL_NOT_PRESENT = 0;
+
+ unsigned IOCTL_FIOASYNC = FIOASYNC;
+ unsigned IOCTL_FIOCLEX = FIOCLEX;
+ unsigned IOCTL_FIOGETOWN = FIOGETOWN;
+ unsigned IOCTL_FIONBIO = FIONBIO;
+ unsigned IOCTL_FIONCLEX = FIONCLEX;
+ unsigned IOCTL_FIOSETOWN = FIOSETOWN;
+ unsigned IOCTL_SIOCADDMULTI = SIOCADDMULTI;
+ unsigned IOCTL_SIOCATMARK = SIOCATMARK;
+ unsigned IOCTL_SIOCDELMULTI = SIOCDELMULTI;
+ unsigned IOCTL_SIOCGIFADDR = SIOCGIFADDR;
+ unsigned IOCTL_SIOCGIFBRDADDR = SIOCGIFBRDADDR;
+ unsigned IOCTL_SIOCGIFCONF = SIOCGIFCONF;
+ unsigned IOCTL_SIOCGIFDSTADDR = SIOCGIFDSTADDR;
+ unsigned IOCTL_SIOCGIFFLAGS = SIOCGIFFLAGS;
+ unsigned IOCTL_SIOCGIFMETRIC = SIOCGIFMETRIC;
+ unsigned IOCTL_SIOCGIFMTU = SIOCGIFMTU;
+ unsigned IOCTL_SIOCGIFNETMASK = SIOCGIFNETMASK;
+ unsigned IOCTL_SIOCGPGRP = SIOCGPGRP;
+ unsigned IOCTL_SIOCSIFADDR = SIOCSIFADDR;
+ unsigned IOCTL_SIOCSIFBRDADDR = SIOCSIFBRDADDR;
+ unsigned IOCTL_SIOCSIFDSTADDR = SIOCSIFDSTADDR;
+ unsigned IOCTL_SIOCSIFFLAGS = SIOCSIFFLAGS;
+ unsigned IOCTL_SIOCSIFMETRIC = SIOCSIFMETRIC;
+ unsigned IOCTL_SIOCSIFMTU = SIOCSIFMTU;
+ unsigned IOCTL_SIOCSIFNETMASK = SIOCSIFNETMASK;
+ unsigned IOCTL_SIOCSPGRP = SIOCSPGRP;
+ unsigned IOCTL_TIOCEXCL = TIOCEXCL;
+ unsigned IOCTL_TIOCGETD = TIOCGETD;
+ unsigned IOCTL_TIOCGPGRP = TIOCGPGRP;
+ unsigned IOCTL_TIOCGWINSZ = TIOCGWINSZ;
+ unsigned IOCTL_TIOCMBIC = TIOCMBIC;
+ unsigned IOCTL_TIOCMBIS = TIOCMBIS;
+ unsigned IOCTL_TIOCMGET = TIOCMGET;
+ unsigned IOCTL_TIOCMSET = TIOCMSET;
+ unsigned IOCTL_TIOCNOTTY = TIOCNOTTY;
+ unsigned IOCTL_TIOCNXCL = TIOCNXCL;
+ unsigned IOCTL_TIOCOUTQ = TIOCOUTQ;
+ unsigned IOCTL_TIOCPKT = TIOCPKT;
+ unsigned IOCTL_TIOCSCTTY = TIOCSCTTY;
+ unsigned IOCTL_TIOCSETD = TIOCSETD;
+ unsigned IOCTL_TIOCSPGRP = TIOCSPGRP;
+ unsigned IOCTL_TIOCSTI = TIOCSTI;
+ unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ;
+
+ unsigned IOCTL_MTIOCGET = MTIOCGET;
+ unsigned IOCTL_MTIOCTOP = MTIOCTOP;
+
+ const int si_SEGV_MAPERR = SEGV_MAPERR;
+ const int si_SEGV_ACCERR = SEGV_ACCERR;
+} // namespace __sanitizer
+
+using namespace __sanitizer;
+
+COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t));
+
+COMPILER_CHECK(sizeof(socklen_t) == sizeof(unsigned));
+CHECK_TYPE_SIZE(pthread_key_t);
+
+// There are more undocumented fields in dl_phdr_info that we are not interested
+// in.
+COMPILER_CHECK(sizeof(__sanitizer_dl_phdr_info) <= sizeof(dl_phdr_info));
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_addr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_name);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr);
+CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum);
+
+CHECK_TYPE_SIZE(glob_t);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv);
+CHECK_SIZE_AND_OFFSET(glob_t, gl_offs);
+
+CHECK_TYPE_SIZE(addrinfo);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_family);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_socktype);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_protocol);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addrlen);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_canonname);
+CHECK_SIZE_AND_OFFSET(addrinfo, ai_addr);
+
+CHECK_TYPE_SIZE(hostent);
+CHECK_SIZE_AND_OFFSET(hostent, h_name);
+CHECK_SIZE_AND_OFFSET(hostent, h_aliases);
+CHECK_SIZE_AND_OFFSET(hostent, h_addrtype);
+CHECK_SIZE_AND_OFFSET(hostent, h_length);
+CHECK_SIZE_AND_OFFSET(hostent, h_addr_list);
+
+CHECK_TYPE_SIZE(iovec);
+CHECK_SIZE_AND_OFFSET(iovec, iov_base);
+CHECK_SIZE_AND_OFFSET(iovec, iov_len);
+
+CHECK_TYPE_SIZE(msghdr);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_name);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iov);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_control);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen);
+CHECK_SIZE_AND_OFFSET(msghdr, msg_flags);
+
+CHECK_TYPE_SIZE(cmsghdr);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level);
+CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type);
+
+COMPILER_CHECK(sizeof(__sanitizer_dirent) <= sizeof(dirent));
+CHECK_SIZE_AND_OFFSET(dirent, d_ino);
+CHECK_SIZE_AND_OFFSET(dirent, d_off);
+CHECK_SIZE_AND_OFFSET(dirent, d_reclen);
+
+#if SANITIZER_SOLARIS32
+COMPILER_CHECK(sizeof(__sanitizer_dirent64) <= sizeof(dirent64));
+CHECK_SIZE_AND_OFFSET(dirent64, d_ino);
+CHECK_SIZE_AND_OFFSET(dirent64, d_off);
+CHECK_SIZE_AND_OFFSET(dirent64, d_reclen);
+#endif
+
+CHECK_TYPE_SIZE(ifconf);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_len);
+CHECK_SIZE_AND_OFFSET(ifconf, ifc_ifcu);
+
+CHECK_TYPE_SIZE(pollfd);
+CHECK_SIZE_AND_OFFSET(pollfd, fd);
+CHECK_SIZE_AND_OFFSET(pollfd, events);
+CHECK_SIZE_AND_OFFSET(pollfd, revents);
+
+CHECK_TYPE_SIZE(nfds_t);
+
+CHECK_TYPE_SIZE(sigset_t);
+
+COMPILER_CHECK(sizeof(__sanitizer_sigaction) == sizeof(struct sigaction));
+// Can't write checks for sa_handler and sa_sigaction due to them being
+// preprocessor macros.
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_mask);
+CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags);
+
+CHECK_TYPE_SIZE(wordexp_t);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordc);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv);
+CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs);
+
+CHECK_TYPE_SIZE(tm);
+CHECK_SIZE_AND_OFFSET(tm, tm_sec);
+CHECK_SIZE_AND_OFFSET(tm, tm_min);
+CHECK_SIZE_AND_OFFSET(tm, tm_hour);
+CHECK_SIZE_AND_OFFSET(tm, tm_mday);
+CHECK_SIZE_AND_OFFSET(tm, tm_mon);
+CHECK_SIZE_AND_OFFSET(tm, tm_year);
+CHECK_SIZE_AND_OFFSET(tm, tm_wday);
+CHECK_SIZE_AND_OFFSET(tm, tm_yday);
+CHECK_SIZE_AND_OFFSET(tm, tm_isdst);
+
+CHECK_TYPE_SIZE(ether_addr);
+
+CHECK_TYPE_SIZE(ipc_perm);
+CHECK_SIZE_AND_OFFSET(ipc_perm, key);
+CHECK_SIZE_AND_OFFSET(ipc_perm, seq);
+CHECK_SIZE_AND_OFFSET(ipc_perm, uid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, gid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cuid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, cgid);
+CHECK_SIZE_AND_OFFSET(ipc_perm, mode);
+
+CHECK_TYPE_SIZE(shmid_ds);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid);
+CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch);
+
+CHECK_TYPE_SIZE(clock_t);
+
+CHECK_TYPE_SIZE(ifaddrs);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_next);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_name);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_addr);
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_netmask);
+// Compare against the union, because we can't reach into the union in a
+// compliant way.
+#ifdef ifa_dstaddr
+#undef ifa_dstaddr
+#endif
+COMPILER_CHECK(sizeof(((__sanitizer_ifaddrs *)nullptr)->ifa_dstaddr) ==
+ sizeof(((ifaddrs *)nullptr)->ifa_ifu));
+COMPILER_CHECK(offsetof(__sanitizer_ifaddrs, ifa_dstaddr) ==
+ offsetof(ifaddrs, ifa_ifu));
+CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data);
+
+CHECK_TYPE_SIZE(timeb);
+CHECK_SIZE_AND_OFFSET(timeb, time);
+CHECK_SIZE_AND_OFFSET(timeb, millitm);
+CHECK_SIZE_AND_OFFSET(timeb, timezone);
+CHECK_SIZE_AND_OFFSET(timeb, dstflag);
+
+CHECK_TYPE_SIZE(passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_name);
+CHECK_SIZE_AND_OFFSET(passwd, pw_passwd);
+CHECK_SIZE_AND_OFFSET(passwd, pw_uid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_gid);
+CHECK_SIZE_AND_OFFSET(passwd, pw_dir);
+CHECK_SIZE_AND_OFFSET(passwd, pw_shell);
+
+CHECK_SIZE_AND_OFFSET(passwd, pw_gecos);
+
+CHECK_TYPE_SIZE(group);
+CHECK_SIZE_AND_OFFSET(group, gr_name);
+CHECK_SIZE_AND_OFFSET(group, gr_passwd);
+CHECK_SIZE_AND_OFFSET(group, gr_gid);
+CHECK_SIZE_AND_OFFSET(group, gr_mem);
+
+CHECK_TYPE_SIZE(XDR);
+CHECK_SIZE_AND_OFFSET(XDR, x_op);
+CHECK_SIZE_AND_OFFSET(XDR, x_ops);
+CHECK_SIZE_AND_OFFSET(XDR, x_public);
+CHECK_SIZE_AND_OFFSET(XDR, x_private);
+CHECK_SIZE_AND_OFFSET(XDR, x_base);
+CHECK_SIZE_AND_OFFSET(XDR, x_handy);
+COMPILER_CHECK(__sanitizer_XDR_ENCODE == XDR_ENCODE);
+COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE);
+COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE);
+
+CHECK_TYPE_SIZE(sem_t);
+
+#endif // SANITIZER_SOLARIS
//===-- sanitizer_platform_limits_solaris.h -------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
typedef int __sanitizer_clockid_t;
// This thing depends on the platform. We are only interested in the upper
-// limit. Verified with a compiler assert in .cc.
+// limit. Verified with a compiler assert in .cpp.
const int pthread_attr_t_max_sz = 128;
union __sanitizer_pthread_attr_t {
char size[pthread_attr_t_max_sz]; // NOLINT
+++ /dev/null
-//===-- sanitizer_posix.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements POSIX-specific functions from
-// sanitizer_posix.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_POSIX
-
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_posix.h"
-#include "sanitizer_procmaps.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <sys/mman.h>
-
-#if SANITIZER_FREEBSD
-// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
-// that, it was never implemented. So just define it to zero.
-#undef MAP_NORESERVE
-#define MAP_NORESERVE 0
-#endif
-
-namespace __sanitizer {
-
-// ------------- sanitizer_common.h
-uptr GetMmapGranularity() {
- return GetPageSize();
-}
-
-void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
- size = RoundUpTo(size, GetPageSizeCached());
- uptr res = internal_mmap(nullptr, size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
- int reserrno;
- if (UNLIKELY(internal_iserror(res, &reserrno)))
- ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report);
- IncreaseTotalMmap(size);
- return (void *)res;
-}
-
-void UnmapOrDie(void *addr, uptr size) {
- if (!addr || !size) return;
- uptr res = internal_munmap(addr, size);
- if (UNLIKELY(internal_iserror(res))) {
- Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
- SanitizerToolName, size, size, addr);
- CHECK("unable to unmap" && 0);
- }
- DecreaseTotalMmap(size);
-}
-
-void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
- size = RoundUpTo(size, GetPageSizeCached());
- uptr res = internal_mmap(nullptr, size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
- int reserrno;
- if (UNLIKELY(internal_iserror(res, &reserrno))) {
- if (reserrno == ENOMEM)
- return nullptr;
- ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
- }
- IncreaseTotalMmap(size);
- return (void *)res;
-}
-
-// We want to map a chunk of address space aligned to 'alignment'.
-// We do it by mapping a bit more and then unmapping redundant pieces.
-// We probably can do it with fewer syscalls in some OS-dependent way.
-void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
- const char *mem_type) {
- CHECK(IsPowerOfTwo(size));
- CHECK(IsPowerOfTwo(alignment));
- uptr map_size = size + alignment;
- uptr map_res = (uptr)MmapOrDieOnFatalError(map_size, mem_type);
- if (UNLIKELY(!map_res))
- return nullptr;
- uptr map_end = map_res + map_size;
- uptr res = map_res;
- if (!IsAligned(res, alignment)) {
- res = (map_res + alignment - 1) & ~(alignment - 1);
- UnmapOrDie((void*)map_res, res - map_res);
- }
- uptr end = res + size;
- if (end != map_end)
- UnmapOrDie((void*)end, map_end - end);
- return (void*)res;
-}
-
-void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
- uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap(nullptr,
- RoundUpTo(size, PageSize),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
- -1, 0);
- int reserrno;
- if (UNLIKELY(internal_iserror(p, &reserrno)))
- ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
- IncreaseTotalMmap(size);
- return (void *)p;
-}
-
-void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem) {
- uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)),
- RoundUpTo(size, PageSize),
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED,
- -1, 0);
- int reserrno;
- if (UNLIKELY(internal_iserror(p, &reserrno))) {
- if (tolerate_enomem && reserrno == ENOMEM)
- return nullptr;
- char mem_type[40];
- internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
- fixed_addr);
- ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
- }
- IncreaseTotalMmap(size);
- return (void *)p;
-}
-
-void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
- return MmapFixedImpl(fixed_addr, size, false /*tolerate_enomem*/);
-}
-
-void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size) {
- return MmapFixedImpl(fixed_addr, size, true /*tolerate_enomem*/);
-}
-
-bool MprotectNoAccess(uptr addr, uptr size) {
- return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
-}
-
-bool MprotectReadOnly(uptr addr, uptr size) {
- return 0 == internal_mprotect((void *)addr, size, PROT_READ);
-}
-
-#if !SANITIZER_MAC
-void MprotectMallocZones(void *addr, int prot) {}
-#endif
-
-fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
- int flags;
- switch (mode) {
- case RdOnly: flags = O_RDONLY; break;
- case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
- case RdWr: flags = O_RDWR | O_CREAT; break;
- }
- fd_t res = internal_open(filename, flags, 0660);
- if (internal_iserror(res, errno_p))
- return kInvalidFd;
- return res;
-}
-
-void CloseFile(fd_t fd) {
- internal_close(fd);
-}
-
-bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
- error_t *error_p) {
- uptr res = internal_read(fd, buff, buff_size);
- if (internal_iserror(res, error_p))
- return false;
- if (bytes_read)
- *bytes_read = res;
- return true;
-}
-
-bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
- error_t *error_p) {
- uptr res = internal_write(fd, buff, buff_size);
- if (internal_iserror(res, error_p))
- return false;
- if (bytes_written)
- *bytes_written = res;
- return true;
-}
-
-bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
- uptr res = internal_rename(oldpath, newpath);
- return !internal_iserror(res, error_p);
-}
-
-void *MapFileToMemory(const char *file_name, uptr *buff_size) {
- fd_t fd = OpenFile(file_name, RdOnly);
- CHECK(fd != kInvalidFd);
- uptr fsize = internal_filesize(fd);
- CHECK_NE(fsize, (uptr)-1);
- CHECK_GT(fsize, 0);
- *buff_size = RoundUpTo(fsize, GetPageSizeCached());
- uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
- return internal_iserror(map) ? nullptr : (void *)map;
-}
-
-void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
- uptr flags = MAP_SHARED;
- if (addr) flags |= MAP_FIXED;
- uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
- int mmap_errno = 0;
- if (internal_iserror(p, &mmap_errno)) {
- Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
- fd, (long long)offset, size, p, mmap_errno);
- return nullptr;
- }
- return (void *)p;
-}
-
-static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
- uptr start2, uptr end2) {
- CHECK(start1 <= end1);
- CHECK(start2 <= end2);
- return (end1 < start2) || (end2 < start1);
-}
-
-// FIXME: this is thread-unsafe, but should not cause problems most of the time.
-// When the shadow is mapped only a single thread usually exists (plus maybe
-// several worker threads on Mac, which aren't expected to map big chunks of
-// memory).
-bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- MemoryMappedSegment segment;
- while (proc_maps.Next(&segment)) {
- if (segment.start == segment.end) continue; // Empty range.
- CHECK_NE(0, segment.end);
- if (!IntervalsAreSeparate(segment.start, segment.end - 1, range_start,
- range_end))
- return false;
- }
- return true;
-}
-
-void DumpProcessMap() {
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- const sptr kBufSize = 4095;
- char *filename = (char*)MmapOrDie(kBufSize, __func__);
- MemoryMappedSegment segment(filename, kBufSize);
- Report("Process memory map follows:\n");
- while (proc_maps.Next(&segment)) {
- Printf("\t%p-%p\t%s\n", (void *)segment.start, (void *)segment.end,
- segment.filename);
- }
- Report("End of process memory map.\n");
- UnmapOrDie(filename, kBufSize);
-}
-
-const char *GetPwd() {
- return GetEnv("PWD");
-}
-
-bool IsPathSeparator(const char c) {
- return c == '/';
-}
-
-bool IsAbsolutePath(const char *path) {
- return path != nullptr && IsPathSeparator(path[0]);
-}
-
-void ReportFile::Write(const char *buffer, uptr length) {
- SpinMutexLock l(mu);
- static const char *kWriteError =
- "ReportFile::Write() can't output requested buffer!\n";
- ReopenIfNecessary();
- if (length != internal_write(fd, buffer, length)) {
- internal_write(fd, kWriteError, internal_strlen(kWriteError));
- Die();
- }
-}
-
-bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
- MemoryMappingLayout proc_maps(/*cache_enabled*/false);
- InternalScopedString buff(kMaxPathLength);
- MemoryMappedSegment segment(buff.data(), kMaxPathLength);
- while (proc_maps.Next(&segment)) {
- if (segment.IsExecutable() &&
- internal_strcmp(module, segment.filename) == 0) {
- *start = segment.start;
- *end = segment.end;
- return true;
- }
- }
- return false;
-}
-
-uptr SignalContext::GetAddress() const {
- auto si = static_cast<const siginfo_t *>(siginfo);
- return (uptr)si->si_addr;
-}
-
-bool SignalContext::IsMemoryAccess() const {
- auto si = static_cast<const siginfo_t *>(siginfo);
- return si->si_signo == SIGSEGV;
-}
-
-int SignalContext::GetType() const {
- return static_cast<const siginfo_t *>(siginfo)->si_signo;
-}
-
-const char *SignalContext::Describe() const {
- switch (GetType()) {
- case SIGFPE:
- return "FPE";
- case SIGILL:
- return "ILL";
- case SIGABRT:
- return "ABRT";
- case SIGSEGV:
- return "SEGV";
- case SIGBUS:
- return "BUS";
- }
- return "UNKNOWN SIGNAL";
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_POSIX
--- /dev/null
+//===-- sanitizer_posix.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements POSIX-specific functions from
+// sanitizer_posix.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_POSIX
+
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/mman.h>
+
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
+namespace __sanitizer {
+
+// ------------- sanitizer_common.h
+uptr GetMmapGranularity() {
+ return GetPageSize();
+}
+
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
+ size = RoundUpTo(size, GetPageSizeCached());
+ uptr res = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, mem_type);
+ int reserrno;
+ if (UNLIKELY(internal_iserror(res, &reserrno)))
+ ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report);
+ IncreaseTotalMmap(size);
+ return (void *)res;
+}
+
+void UnmapOrDie(void *addr, uptr size) {
+ if (!addr || !size) return;
+ uptr res = internal_munmap(addr, size);
+ if (UNLIKELY(internal_iserror(res))) {
+ Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
+ SanitizerToolName, size, size, addr);
+ CHECK("unable to unmap" && 0);
+ }
+ DecreaseTotalMmap(size);
+}
+
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
+ size = RoundUpTo(size, GetPageSizeCached());
+ uptr res = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, mem_type);
+ int reserrno;
+ if (UNLIKELY(internal_iserror(res, &reserrno))) {
+ if (reserrno == ENOMEM)
+ return nullptr;
+ ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+ }
+ IncreaseTotalMmap(size);
+ return (void *)res;
+}
+
+// We want to map a chunk of address space aligned to 'alignment'.
+// We do it by mapping a bit more and then unmapping redundant pieces.
+// We probably can do it with fewer syscalls in some OS-dependent way.
+void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
+ const char *mem_type) {
+ CHECK(IsPowerOfTwo(size));
+ CHECK(IsPowerOfTwo(alignment));
+ uptr map_size = size + alignment;
+ uptr map_res = (uptr)MmapOrDieOnFatalError(map_size, mem_type);
+ if (UNLIKELY(!map_res))
+ return nullptr;
+ uptr map_end = map_res + map_size;
+ uptr res = map_res;
+ if (!IsAligned(res, alignment)) {
+ res = (map_res + alignment - 1) & ~(alignment - 1);
+ UnmapOrDie((void*)map_res, res - map_res);
+ }
+ uptr end = res + size;
+ if (end != map_end)
+ UnmapOrDie((void*)end, map_end - end);
+ return (void*)res;
+}
+
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+ size = RoundUpTo(size, GetPageSizeCached());
+ uptr p = MmapNamed(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, mem_type);
+ int reserrno;
+ if (UNLIKELY(internal_iserror(p, &reserrno)))
+ ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno);
+ IncreaseTotalMmap(size);
+ return (void *)p;
+}
+
+static void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem,
+ const char *name) {
+ size = RoundUpTo(size, GetPageSizeCached());
+ fixed_addr = RoundDownTo(fixed_addr, GetPageSizeCached());
+ uptr p = MmapNamed((void *)fixed_addr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON | MAP_FIXED, name);
+ int reserrno;
+ if (UNLIKELY(internal_iserror(p, &reserrno))) {
+ if (tolerate_enomem && reserrno == ENOMEM)
+ return nullptr;
+ char mem_type[40];
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
+ fixed_addr);
+ ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno);
+ }
+ IncreaseTotalMmap(size);
+ return (void *)p;
+}
+
+void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) {
+ return MmapFixedImpl(fixed_addr, size, false /*tolerate_enomem*/, name);
+}
+
+void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) {
+ return MmapFixedImpl(fixed_addr, size, true /*tolerate_enomem*/, name);
+}
+
+bool MprotectNoAccess(uptr addr, uptr size) {
+ return 0 == internal_mprotect((void*)addr, size, PROT_NONE);
+}
+
+bool MprotectReadOnly(uptr addr, uptr size) {
+ return 0 == internal_mprotect((void *)addr, size, PROT_READ);
+}
+
+#if !SANITIZER_MAC
+void MprotectMallocZones(void *addr, int prot) {}
+#endif
+
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+ if (ShouldMockFailureToOpen(filename))
+ return kInvalidFd;
+ int flags;
+ switch (mode) {
+ case RdOnly: flags = O_RDONLY; break;
+ case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
+ case RdWr: flags = O_RDWR | O_CREAT; break;
+ }
+ fd_t res = internal_open(filename, flags, 0660);
+ if (internal_iserror(res, errno_p))
+ return kInvalidFd;
+ return ReserveStandardFds(res);
+}
+
+void CloseFile(fd_t fd) {
+ internal_close(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ uptr res = internal_read(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_read)
+ *bytes_read = res;
+ return true;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ uptr res = internal_write(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_written)
+ *bytes_written = res;
+ return true;
+}
+
+void *MapFileToMemory(const char *file_name, uptr *buff_size) {
+ fd_t fd = OpenFile(file_name, RdOnly);
+ CHECK(fd != kInvalidFd);
+ uptr fsize = internal_filesize(fd);
+ CHECK_NE(fsize, (uptr)-1);
+ CHECK_GT(fsize, 0);
+ *buff_size = RoundUpTo(fsize, GetPageSizeCached());
+ uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ return internal_iserror(map) ? nullptr : (void *)map;
+}
+
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
+ uptr flags = MAP_SHARED;
+ if (addr) flags |= MAP_FIXED;
+ uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset);
+ int mmap_errno = 0;
+ if (internal_iserror(p, &mmap_errno)) {
+ Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n",
+ fd, (long long)offset, size, p, mmap_errno);
+ return nullptr;
+ }
+ return (void *)p;
+}
+
+static inline bool IntervalsAreSeparate(uptr start1, uptr end1,
+ uptr start2, uptr end2) {
+ CHECK(start1 <= end1);
+ CHECK(start2 <= end2);
+ return (end1 < start2) || (end2 < start1);
+}
+
+// FIXME: this is thread-unsafe, but should not cause problems most of the time.
+// When the shadow is mapped only a single thread usually exists (plus maybe
+// several worker threads on Mac, which aren't expected to map big chunks of
+// memory).
+bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ if (proc_maps.Error())
+ return true; // and hope for the best
+ MemoryMappedSegment segment;
+ while (proc_maps.Next(&segment)) {
+ if (segment.start == segment.end) continue; // Empty range.
+ CHECK_NE(0, segment.end);
+ if (!IntervalsAreSeparate(segment.start, segment.end - 1, range_start,
+ range_end))
+ return false;
+ }
+ return true;
+}
+
+void DumpProcessMap() {
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ const sptr kBufSize = 4095;
+ char *filename = (char*)MmapOrDie(kBufSize, __func__);
+ MemoryMappedSegment segment(filename, kBufSize);
+ Report("Process memory map follows:\n");
+ while (proc_maps.Next(&segment)) {
+ Printf("\t%p-%p\t%s\n", (void *)segment.start, (void *)segment.end,
+ segment.filename);
+ }
+ Report("End of process memory map.\n");
+ UnmapOrDie(filename, kBufSize);
+}
+
+const char *GetPwd() {
+ return GetEnv("PWD");
+}
+
+bool IsPathSeparator(const char c) {
+ return c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+ return path != nullptr && IsPathSeparator(path[0]);
+}
+
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ internal_write(fd, buffer, length);
+}
+
+bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) {
+ MemoryMappingLayout proc_maps(/*cache_enabled*/false);
+ InternalScopedString buff(kMaxPathLength);
+ MemoryMappedSegment segment(buff.data(), kMaxPathLength);
+ while (proc_maps.Next(&segment)) {
+ if (segment.IsExecutable() &&
+ internal_strcmp(module, segment.filename) == 0) {
+ *start = segment.start;
+ *end = segment.end;
+ return true;
+ }
+ }
+ return false;
+}
+
+uptr SignalContext::GetAddress() const {
+ auto si = static_cast<const siginfo_t *>(siginfo);
+ return (uptr)si->si_addr;
+}
+
+bool SignalContext::IsMemoryAccess() const {
+ auto si = static_cast<const siginfo_t *>(siginfo);
+ return si->si_signo == SIGSEGV;
+}
+
+int SignalContext::GetType() const {
+ return static_cast<const siginfo_t *>(siginfo)->si_signo;
+}
+
+const char *SignalContext::Describe() const {
+ switch (GetType()) {
+ case SIGFPE:
+ return "FPE";
+ case SIGILL:
+ return "ILL";
+ case SIGABRT:
+ return "ABRT";
+ case SIGSEGV:
+ return "SEGV";
+ case SIGBUS:
+ return "BUS";
+ }
+ return "UNKNOWN SIGNAL";
+}
+
+fd_t ReserveStandardFds(fd_t fd) {
+ CHECK_GE(fd, 0);
+ if (fd > 2)
+ return fd;
+ bool used[3];
+ internal_memset(used, 0, sizeof(used));
+ while (fd <= 2) {
+ used[fd] = true;
+ fd = internal_dup(fd);
+ }
+ for (int i = 0; i <= 2; ++i)
+ if (used[i])
+ internal_close(i);
+ return fd;
+}
+
+bool ShouldMockFailureToOpen(const char *path) {
+ return common_flags()->test_only_emulate_no_memorymap &&
+ internal_strncmp(path, "/proc/", 6) == 0;
+}
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID && !SANITIZER_GO
+int GetNamedMappingFd(const char *name, uptr size, int *flags) {
+ if (!common_flags()->decorate_proc_maps || !name)
+ return -1;
+ char shmname[200];
+ CHECK(internal_strlen(name) < sizeof(shmname) - 10);
+ internal_snprintf(shmname, sizeof(shmname), "/dev/shm/%zu [%s]",
+ internal_getpid(), name);
+ int fd = ReserveStandardFds(
+ internal_open(shmname, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRWXU));
+ CHECK_GE(fd, 0);
+ int res = internal_ftruncate(fd, size);
+ CHECK_EQ(0, res);
+ res = internal_unlink(shmname);
+ CHECK_EQ(0, res);
+ *flags &= ~(MAP_ANON | MAP_ANONYMOUS);
+ return fd;
+}
+#else
+int GetNamedMappingFd(const char *name, uptr size, int *flags) {
+ return -1;
+}
+#endif
+
+#if SANITIZER_ANDROID
+#define PR_SET_VMA 0x53564d41
+#define PR_SET_VMA_ANON_NAME 0
+void DecorateMapping(uptr addr, uptr size, const char *name) {
+ if (!common_flags()->decorate_proc_maps || !name)
+ return;
+ internal_prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, addr, size, (uptr)name);
+}
+#else
+void DecorateMapping(uptr addr, uptr size, const char *name) {
+}
+#endif
+
+uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name) {
+ int fd = GetNamedMappingFd(name, length, &flags);
+ uptr res = internal_mmap(addr, length, prot, flags, fd, 0);
+ if (!internal_iserror(res))
+ DecorateMapping(res, length, name);
+ return res;
+}
+
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
//===-- sanitizer_posix.h -------------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// ----------- ATTENTION -------------
// This header should NOT include any other headers from sanitizer runtime.
#include "sanitizer_internal_defs.h"
+#include "sanitizer_platform_limits_freebsd.h"
#include "sanitizer_platform_limits_netbsd.h"
#include "sanitizer_platform_limits_openbsd.h"
#include "sanitizer_platform_limits_posix.h"
uptr internal_stat(const char *path, void *buf);
uptr internal_lstat(const char *path, void *buf);
uptr internal_fstat(fd_t fd, void *buf);
+uptr internal_dup(int oldfd);
uptr internal_dup2(int oldfd, int newfd);
uptr internal_readlink(const char *path, char *buf, uptr bufsize);
uptr internal_unlink(const char *path);
uptr internal_rename(const char *oldpath, const char *newpath);
uptr internal_lseek(fd_t fd, OFF_T offset, int whence);
+#if SANITIZER_NETBSD
+uptr internal_ptrace(int request, int pid, void *addr, int data);
+#else
uptr internal_ptrace(int request, int pid, void *addr, void *data);
+#endif
uptr internal_waitpid(int pid, int *status, int options);
int internal_fork();
bool IsStateDetached(int state);
+// Move the fd out of {0, 1, 2} range.
+fd_t ReserveStandardFds(fd_t fd);
+
+bool ShouldMockFailureToOpen(const char *path);
+
+// Create a non-file mapping with a given /proc/self/maps name.
+uptr MmapNamed(void *addr, uptr length, int prot, int flags, const char *name);
+
+// Platforms should implement at most one of these.
+// 1. Provide a pre-decorated file descriptor to use instead of an anonymous
+// mapping.
+int GetNamedMappingFd(const char *name, uptr size, int *flags);
+// 2. Add name to an existing anonymous mapping. The caller must keep *name
+// alive at least as long as the mapping exists.
+void DecorateMapping(uptr addr, uptr size, const char *name);
+
+
} // namespace __sanitizer
#endif // SANITIZER_POSIX_H
+++ /dev/null
-//===-- sanitizer_posix_libcdep.cc ----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements libc-dependent POSIX-specific functions
-// from sanitizer_libc.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_POSIX
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_platform_limits_openbsd.h"
-#include "sanitizer_platform_limits_posix.h"
-#include "sanitizer_platform_limits_solaris.h"
-#include "sanitizer_posix.h"
-#include "sanitizer_procmaps.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#if SANITIZER_FREEBSD
-// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
-// that, it was never implemented. So just define it to zero.
-#undef MAP_NORESERVE
-#define MAP_NORESERVE 0
-#endif
-
-typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
-
-namespace __sanitizer {
-
-u32 GetUid() {
- return getuid();
-}
-
-uptr GetThreadSelf() {
- return (uptr)pthread_self();
-}
-
-void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
- uptr page_size = GetPageSizeCached();
- uptr beg_aligned = RoundUpTo(beg, page_size);
- uptr end_aligned = RoundDownTo(end, page_size);
- if (beg_aligned < end_aligned)
- // In the default Solaris compilation environment, madvise() is declared
- // to take a caddr_t arg; casting it to void * results in an invalid
- // conversion error, so use char * instead.
- madvise((char *)beg_aligned, end_aligned - beg_aligned,
- SANITIZER_MADVISE_DONTNEED);
-}
-
-bool NoHugePagesInRegion(uptr addr, uptr size) {
-#ifdef MADV_NOHUGEPAGE // May not be defined on old systems.
- return madvise((char *)addr, size, MADV_NOHUGEPAGE) == 0;
-#else
- return true;
-#endif // MADV_NOHUGEPAGE
-}
-
-bool DontDumpShadowMemory(uptr addr, uptr length) {
-#if defined(MADV_DONTDUMP)
- return madvise((char *)addr, length, MADV_DONTDUMP) == 0;
-#elif defined(MADV_NOCORE)
- return madvise((char *)addr, length, MADV_NOCORE) == 0;
-#else
- return true;
-#endif // MADV_DONTDUMP
-}
-
-static rlim_t getlim(int res) {
- rlimit rlim;
- CHECK_EQ(0, getrlimit(res, &rlim));
- return rlim.rlim_cur;
-}
-
-static void setlim(int res, rlim_t lim) {
- // The following magic is to prevent clang from replacing it with memset.
- volatile struct rlimit rlim;
- rlim.rlim_cur = lim;
- rlim.rlim_max = lim;
- if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
- Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
- Die();
- }
-}
-
-void DisableCoreDumperIfNecessary() {
- if (common_flags()->disable_coredump) {
- setlim(RLIMIT_CORE, 0);
- }
-}
-
-bool StackSizeIsUnlimited() {
- rlim_t stack_size = getlim(RLIMIT_STACK);
- return (stack_size == RLIM_INFINITY);
-}
-
-uptr GetStackSizeLimitInBytes() {
- return (uptr)getlim(RLIMIT_STACK);
-}
-
-void SetStackSizeLimitInBytes(uptr limit) {
- setlim(RLIMIT_STACK, (rlim_t)limit);
- CHECK(!StackSizeIsUnlimited());
-}
-
-bool AddressSpaceIsUnlimited() {
- rlim_t as_size = getlim(RLIMIT_AS);
- return (as_size == RLIM_INFINITY);
-}
-
-void SetAddressSpaceUnlimited() {
- setlim(RLIMIT_AS, RLIM_INFINITY);
- CHECK(AddressSpaceIsUnlimited());
-}
-
-void SleepForSeconds(int seconds) {
- sleep(seconds);
-}
-
-void SleepForMillis(int millis) {
- usleep(millis * 1000);
-}
-
-void Abort() {
-#if !SANITIZER_GO
- // If we are handling SIGABRT, unhandle it first.
- // TODO(vitalybuka): Check if handler belongs to sanitizer.
- if (GetHandleSignalMode(SIGABRT) != kHandleSignalNo) {
- struct sigaction sigact;
- internal_memset(&sigact, 0, sizeof(sigact));
- sigact.sa_sigaction = (sa_sigaction_t)SIG_DFL;
- internal_sigaction(SIGABRT, &sigact, nullptr);
- }
-#endif
-
- abort();
-}
-
-int Atexit(void (*function)(void)) {
-#if !SANITIZER_GO
- return atexit(function);
-#else
- return 0;
-#endif
-}
-
-bool SupportsColoredOutput(fd_t fd) {
- return isatty(fd) != 0;
-}
-
-#if !SANITIZER_GO
-// TODO(glider): different tools may require different altstack size.
-static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.
-
-void SetAlternateSignalStack() {
- stack_t altstack, oldstack;
- CHECK_EQ(0, sigaltstack(nullptr, &oldstack));
- // If the alternate stack is already in place, do nothing.
- // Android always sets an alternate stack, but it's too small for us.
- if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;
- // TODO(glider): the mapped stack should have the MAP_STACK flag in the
- // future. It is not required by man 2 sigaltstack now (they're using
- // malloc()).
- void* base = MmapOrDie(kAltStackSize, __func__);
- altstack.ss_sp = (char*) base;
- altstack.ss_flags = 0;
- altstack.ss_size = kAltStackSize;
- CHECK_EQ(0, sigaltstack(&altstack, nullptr));
-}
-
-void UnsetAlternateSignalStack() {
- stack_t altstack, oldstack;
- altstack.ss_sp = nullptr;
- altstack.ss_flags = SS_DISABLE;
- altstack.ss_size = kAltStackSize; // Some sane value required on Darwin.
- CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
- UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
-}
-
-static void MaybeInstallSigaction(int signum,
- SignalHandlerType handler) {
- if (GetHandleSignalMode(signum) == kHandleSignalNo) return;
-
- struct sigaction sigact;
- internal_memset(&sigact, 0, sizeof(sigact));
- sigact.sa_sigaction = (sa_sigaction_t)handler;
- // Do not block the signal from being received in that signal's handler.
- // Clients are responsible for handling this correctly.
- sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
- if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
- CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr));
- VReport(1, "Installed the sigaction for signal %d\n", signum);
-}
-
-void InstallDeadlySignalHandlers(SignalHandlerType handler) {
- // Set the alternate signal stack for the main thread.
- // This will cause SetAlternateSignalStack to be called twice, but the stack
- // will be actually set only once.
- if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
- MaybeInstallSigaction(SIGSEGV, handler);
- MaybeInstallSigaction(SIGBUS, handler);
- MaybeInstallSigaction(SIGABRT, handler);
- MaybeInstallSigaction(SIGFPE, handler);
- MaybeInstallSigaction(SIGILL, handler);
- MaybeInstallSigaction(SIGTRAP, handler);
-}
-
-bool SignalContext::IsStackOverflow() const {
- // Access at a reasonable offset above SP, or slightly below it (to account
- // for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is
- // probably a stack overflow.
-#ifdef __s390__
- // On s390, the fault address in siginfo points to start of the page, not
- // to the precise word that was accessed. Mask off the low bits of sp to
- // take it into account.
- bool IsStackAccess = addr >= (sp & ~0xFFF) && addr < sp + 0xFFFF;
-#else
- // Let's accept up to a page size away from top of stack. Things like stack
- // probing can trigger accesses with such large offsets.
- bool IsStackAccess = addr + GetPageSizeCached() > sp && addr < sp + 0xFFFF;
-#endif
-
-#if __powerpc__
- // Large stack frames can be allocated with e.g.
- // lis r0,-10000
- // stdux r1,r1,r0 # store sp to [sp-10000] and update sp by -10000
- // If the store faults then sp will not have been updated, so test above
- // will not work, because the fault address will be more than just "slightly"
- // below sp.
- if (!IsStackAccess && IsAccessibleMemoryRange(pc, 4)) {
- u32 inst = *(unsigned *)pc;
- u32 ra = (inst >> 16) & 0x1F;
- u32 opcd = inst >> 26;
- u32 xo = (inst >> 1) & 0x3FF;
- // Check for store-with-update to sp. The instructions we accept are:
- // stbu rs,d(ra) stbux rs,ra,rb
- // sthu rs,d(ra) sthux rs,ra,rb
- // stwu rs,d(ra) stwux rs,ra,rb
- // stdu rs,ds(ra) stdux rs,ra,rb
- // where ra is r1 (the stack pointer).
- if (ra == 1 &&
- (opcd == 39 || opcd == 45 || opcd == 37 || opcd == 62 ||
- (opcd == 31 && (xo == 247 || xo == 439 || xo == 183 || xo == 181))))
- IsStackAccess = true;
- }
-#endif // __powerpc__
-
- // We also check si_code to filter out SEGV caused by something else other
- // then hitting the guard page or unmapped memory, like, for example,
- // unaligned memory access.
- auto si = static_cast<const siginfo_t *>(siginfo);
- return IsStackAccess &&
- (si->si_code == si_SEGV_MAPERR || si->si_code == si_SEGV_ACCERR);
-}
-
-#endif // SANITIZER_GO
-
-bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- uptr page_size = GetPageSizeCached();
- // Checking too large memory ranges is slow.
- CHECK_LT(size, page_size * 10);
- int sock_pair[2];
- if (pipe(sock_pair))
- return false;
- uptr bytes_written =
- internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
- int write_errno;
- bool result;
- if (internal_iserror(bytes_written, &write_errno)) {
- CHECK_EQ(EFAULT, write_errno);
- result = false;
- } else {
- result = (bytes_written == size);
- }
- internal_close(sock_pair[0]);
- internal_close(sock_pair[1]);
- return result;
-}
-
-void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
- // Some kinds of sandboxes may forbid filesystem access, so we won't be able
- // to read the file mappings from /proc/self/maps. Luckily, neither the
- // process will be able to load additional libraries, so it's fine to use the
- // cached mappings.
- MemoryMappingLayout::CacheMemoryMappings();
-}
-
-#if SANITIZER_ANDROID || SANITIZER_GO
-int GetNamedMappingFd(const char *name, uptr size) {
- return -1;
-}
-#else
-int GetNamedMappingFd(const char *name, uptr size) {
- if (!common_flags()->decorate_proc_maps)
- return -1;
- char shmname[200];
- CHECK(internal_strlen(name) < sizeof(shmname) - 10);
- internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(),
- name);
- int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU);
- CHECK_GE(fd, 0);
- int res = internal_ftruncate(fd, size);
- CHECK_EQ(0, res);
- res = shm_unlink(shmname);
- CHECK_EQ(0, res);
- return fd;
-}
-#endif
-
-bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
- int fd = name ? GetNamedMappingFd(name, size) : -1;
- unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
- if (fd == -1) flags |= MAP_ANON;
-
- uptr PageSize = GetPageSizeCached();
- uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)),
- RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE,
- flags, fd, 0);
- int reserrno;
- if (internal_iserror(p, &reserrno)) {
- Report("ERROR: %s failed to "
- "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
- SanitizerToolName, size, size, fixed_addr, reserrno);
- return false;
- }
- IncreaseTotalMmap(size);
- return true;
-}
-
-uptr ReservedAddressRange::Init(uptr size, const char *name, uptr fixed_addr) {
- // We don't pass `name` along because, when you enable `decorate_proc_maps`
- // AND actually use a named mapping AND are using a sanitizer intercepting
- // `open` (e.g. TSAN, ESAN), then you'll get a failure during initialization.
- // TODO(flowerhack): Fix the implementation of GetNamedMappingFd to solve
- // this problem.
- base_ = fixed_addr ? MmapFixedNoAccess(fixed_addr, size) : MmapNoAccess(size);
- size_ = size;
- name_ = name;
- (void)os_handle_; // unsupported
- return reinterpret_cast<uptr>(base_);
-}
-
-// Uses fixed_addr for now.
-// Will use offset instead once we've implemented this function for real.
-uptr ReservedAddressRange::Map(uptr fixed_addr, uptr size) {
- return reinterpret_cast<uptr>(MmapFixedOrDieOnFatalError(fixed_addr, size));
-}
-
-uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr size) {
- return reinterpret_cast<uptr>(MmapFixedOrDie(fixed_addr, size));
-}
-
-void ReservedAddressRange::Unmap(uptr addr, uptr size) {
- CHECK_LE(size, size_);
- if (addr == reinterpret_cast<uptr>(base_))
- // If we unmap the whole range, just null out the base.
- base_ = (size == size_) ? nullptr : reinterpret_cast<void*>(addr + size);
- else
- CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
- size_ -= size;
- UnmapOrDie(reinterpret_cast<void*>(addr), size);
-}
-
-void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
- int fd = name ? GetNamedMappingFd(name, size) : -1;
- unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE;
- if (fd == -1) flags |= MAP_ANON;
-
- return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd,
- 0);
-}
-
-void *MmapNoAccess(uptr size) {
- unsigned flags = MAP_PRIVATE | MAP_ANON | MAP_NORESERVE;
- return (void *)internal_mmap(nullptr, size, PROT_NONE, flags, -1, 0);
-}
-
-// This function is defined elsewhere if we intercepted pthread_attr_getstack.
-extern "C" {
-SANITIZER_WEAK_ATTRIBUTE int
-real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
-} // extern "C"
-
-int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) {
-#if !SANITIZER_GO && !SANITIZER_MAC
- if (&real_pthread_attr_getstack)
- return real_pthread_attr_getstack((pthread_attr_t *)attr, addr,
- (size_t *)size);
-#endif
- return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size);
-}
-
-#if !SANITIZER_GO
-void AdjustStackSize(void *attr_) {
- pthread_attr_t *attr = (pthread_attr_t *)attr_;
- uptr stackaddr = 0;
- uptr stacksize = 0;
- my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
- // GLibC will return (0 - stacksize) as the stack address in the case when
- // stacksize is set, but stackaddr is not.
- bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
- // We place a lot of tool data into TLS, account for that.
- const uptr minstacksize = GetTlsSize() + 128*1024;
- if (stacksize < minstacksize) {
- if (!stack_set) {
- if (stacksize != 0) {
- VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
- minstacksize);
- pthread_attr_setstacksize(attr, minstacksize);
- }
- } else {
- Printf("Sanitizer: pre-allocated stack size is insufficient: "
- "%zu < %zu\n", stacksize, minstacksize);
- Printf("Sanitizer: pthread_create is likely to fail.\n");
- }
- }
-}
-#endif // !SANITIZER_GO
-
-pid_t StartSubprocess(const char *program, const char *const argv[],
- fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
- auto file_closer = at_scope_exit([&] {
- if (stdin_fd != kInvalidFd) {
- internal_close(stdin_fd);
- }
- if (stdout_fd != kInvalidFd) {
- internal_close(stdout_fd);
- }
- if (stderr_fd != kInvalidFd) {
- internal_close(stderr_fd);
- }
- });
-
- int pid = internal_fork();
-
- if (pid < 0) {
- int rverrno;
- if (internal_iserror(pid, &rverrno)) {
- Report("WARNING: failed to fork (errno %d)\n", rverrno);
- }
- return pid;
- }
-
- if (pid == 0) {
- // Child subprocess
- if (stdin_fd != kInvalidFd) {
- internal_close(STDIN_FILENO);
- internal_dup2(stdin_fd, STDIN_FILENO);
- internal_close(stdin_fd);
- }
- if (stdout_fd != kInvalidFd) {
- internal_close(STDOUT_FILENO);
- internal_dup2(stdout_fd, STDOUT_FILENO);
- internal_close(stdout_fd);
- }
- if (stderr_fd != kInvalidFd) {
- internal_close(STDERR_FILENO);
- internal_dup2(stderr_fd, STDERR_FILENO);
- internal_close(stderr_fd);
- }
-
- for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd);
-
- execv(program, const_cast<char **>(&argv[0]));
- internal__exit(1);
- }
-
- return pid;
-}
-
-bool IsProcessRunning(pid_t pid) {
- int process_status;
- uptr waitpid_status = internal_waitpid(pid, &process_status, WNOHANG);
- int local_errno;
- if (internal_iserror(waitpid_status, &local_errno)) {
- VReport(1, "Waiting on the process failed (errno %d).\n", local_errno);
- return false;
- }
- return waitpid_status == 0;
-}
-
-int WaitForProcess(pid_t pid) {
- int process_status;
- uptr waitpid_status = internal_waitpid(pid, &process_status, 0);
- int local_errno;
- if (internal_iserror(waitpid_status, &local_errno)) {
- VReport(1, "Waiting on the process failed (errno %d).\n", local_errno);
- return -1;
- }
- return process_status;
-}
-
-bool IsStateDetached(int state) {
- return state == PTHREAD_CREATE_DETACHED;
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_POSIX
--- /dev/null
+//===-- sanitizer_posix_libcdep.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements libc-dependent POSIX-specific functions
+// from sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_POSIX
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_limits_netbsd.h"
+#include "sanitizer_platform_limits_openbsd.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_platform_limits_solaris.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if SANITIZER_FREEBSD
+// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before
+// that, it was never implemented. So just define it to zero.
+#undef MAP_NORESERVE
+#define MAP_NORESERVE 0
+#endif
+
+typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
+
+namespace __sanitizer {
+
+u32 GetUid() {
+ return getuid();
+}
+
+uptr GetThreadSelf() {
+ return (uptr)pthread_self();
+}
+
+void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
+ uptr page_size = GetPageSizeCached();
+ uptr beg_aligned = RoundUpTo(beg, page_size);
+ uptr end_aligned = RoundDownTo(end, page_size);
+ if (beg_aligned < end_aligned)
+ // In the default Solaris compilation environment, madvise() is declared
+ // to take a caddr_t arg; casting it to void * results in an invalid
+ // conversion error, so use char * instead.
+ madvise((char *)beg_aligned, end_aligned - beg_aligned,
+ SANITIZER_MADVISE_DONTNEED);
+}
+
+void SetShadowRegionHugePageMode(uptr addr, uptr size) {
+#ifdef MADV_NOHUGEPAGE // May not be defined on old systems.
+ if (common_flags()->no_huge_pages_for_shadow)
+ madvise((char *)addr, size, MADV_NOHUGEPAGE);
+ else
+ madvise((char *)addr, size, MADV_HUGEPAGE);
+#endif // MADV_NOHUGEPAGE
+}
+
+bool DontDumpShadowMemory(uptr addr, uptr length) {
+#if defined(MADV_DONTDUMP)
+ return madvise((char *)addr, length, MADV_DONTDUMP) == 0;
+#elif defined(MADV_NOCORE)
+ return madvise((char *)addr, length, MADV_NOCORE) == 0;
+#else
+ return true;
+#endif // MADV_DONTDUMP
+}
+
+static rlim_t getlim(int res) {
+ rlimit rlim;
+ CHECK_EQ(0, getrlimit(res, &rlim));
+ return rlim.rlim_cur;
+}
+
+static void setlim(int res, rlim_t lim) {
+ struct rlimit rlim;
+ if (getrlimit(res, const_cast<struct rlimit *>(&rlim))) {
+ Report("ERROR: %s getrlimit() failed %d\n", SanitizerToolName, errno);
+ Die();
+ }
+ rlim.rlim_cur = lim;
+ if (setrlimit(res, const_cast<struct rlimit *>(&rlim))) {
+ Report("ERROR: %s setrlimit() failed %d\n", SanitizerToolName, errno);
+ Die();
+ }
+}
+
+void DisableCoreDumperIfNecessary() {
+ if (common_flags()->disable_coredump) {
+ setlim(RLIMIT_CORE, 0);
+ }
+}
+
+bool StackSizeIsUnlimited() {
+ rlim_t stack_size = getlim(RLIMIT_STACK);
+ return (stack_size == RLIM_INFINITY);
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+ setlim(RLIMIT_STACK, (rlim_t)limit);
+ CHECK(!StackSizeIsUnlimited());
+}
+
+bool AddressSpaceIsUnlimited() {
+ rlim_t as_size = getlim(RLIMIT_AS);
+ return (as_size == RLIM_INFINITY);
+}
+
+void SetAddressSpaceUnlimited() {
+ setlim(RLIMIT_AS, RLIM_INFINITY);
+ CHECK(AddressSpaceIsUnlimited());
+}
+
+void SleepForSeconds(int seconds) {
+ sleep(seconds);
+}
+
+void SleepForMillis(int millis) {
+ usleep(millis * 1000);
+}
+
+void Abort() {
+#if !SANITIZER_GO
+ // If we are handling SIGABRT, unhandle it first.
+ // TODO(vitalybuka): Check if handler belongs to sanitizer.
+ if (GetHandleSignalMode(SIGABRT) != kHandleSignalNo) {
+ struct sigaction sigact;
+ internal_memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = (sa_sigaction_t)SIG_DFL;
+ internal_sigaction(SIGABRT, &sigact, nullptr);
+ }
+#endif
+
+ abort();
+}
+
+int Atexit(void (*function)(void)) {
+#if !SANITIZER_GO
+ return atexit(function);
+#else
+ return 0;
+#endif
+}
+
+bool SupportsColoredOutput(fd_t fd) {
+ return isatty(fd) != 0;
+}
+
+#if !SANITIZER_GO
+// TODO(glider): different tools may require different altstack size.
+static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough.
+
+void SetAlternateSignalStack() {
+ stack_t altstack, oldstack;
+ CHECK_EQ(0, sigaltstack(nullptr, &oldstack));
+ // If the alternate stack is already in place, do nothing.
+ // Android always sets an alternate stack, but it's too small for us.
+ if (!SANITIZER_ANDROID && !(oldstack.ss_flags & SS_DISABLE)) return;
+ // TODO(glider): the mapped stack should have the MAP_STACK flag in the
+ // future. It is not required by man 2 sigaltstack now (they're using
+ // malloc()).
+ void* base = MmapOrDie(kAltStackSize, __func__);
+ altstack.ss_sp = (char*) base;
+ altstack.ss_flags = 0;
+ altstack.ss_size = kAltStackSize;
+ CHECK_EQ(0, sigaltstack(&altstack, nullptr));
+}
+
+void UnsetAlternateSignalStack() {
+ stack_t altstack, oldstack;
+ altstack.ss_sp = nullptr;
+ altstack.ss_flags = SS_DISABLE;
+ altstack.ss_size = kAltStackSize; // Some sane value required on Darwin.
+ CHECK_EQ(0, sigaltstack(&altstack, &oldstack));
+ UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
+}
+
+static void MaybeInstallSigaction(int signum,
+ SignalHandlerType handler) {
+ if (GetHandleSignalMode(signum) == kHandleSignalNo) return;
+
+ struct sigaction sigact;
+ internal_memset(&sigact, 0, sizeof(sigact));
+ sigact.sa_sigaction = (sa_sigaction_t)handler;
+ // Do not block the signal from being received in that signal's handler.
+ // Clients are responsible for handling this correctly.
+ sigact.sa_flags = SA_SIGINFO | SA_NODEFER;
+ if (common_flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK;
+ CHECK_EQ(0, internal_sigaction(signum, &sigact, nullptr));
+ VReport(1, "Installed the sigaction for signal %d\n", signum);
+}
+
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {
+ // Set the alternate signal stack for the main thread.
+ // This will cause SetAlternateSignalStack to be called twice, but the stack
+ // will be actually set only once.
+ if (common_flags()->use_sigaltstack) SetAlternateSignalStack();
+ MaybeInstallSigaction(SIGSEGV, handler);
+ MaybeInstallSigaction(SIGBUS, handler);
+ MaybeInstallSigaction(SIGABRT, handler);
+ MaybeInstallSigaction(SIGFPE, handler);
+ MaybeInstallSigaction(SIGILL, handler);
+ MaybeInstallSigaction(SIGTRAP, handler);
+}
+
+bool SignalContext::IsStackOverflow() const {
+ // Access at a reasonable offset above SP, or slightly below it (to account
+ // for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is
+ // probably a stack overflow.
+#ifdef __s390__
+ // On s390, the fault address in siginfo points to start of the page, not
+ // to the precise word that was accessed. Mask off the low bits of sp to
+ // take it into account.
+ bool IsStackAccess = addr >= (sp & ~0xFFF) && addr < sp + 0xFFFF;
+#else
+ // Let's accept up to a page size away from top of stack. Things like stack
+ // probing can trigger accesses with such large offsets.
+ bool IsStackAccess = addr + GetPageSizeCached() > sp && addr < sp + 0xFFFF;
+#endif
+
+#if __powerpc__
+ // Large stack frames can be allocated with e.g.
+ // lis r0,-10000
+ // stdux r1,r1,r0 # store sp to [sp-10000] and update sp by -10000
+ // If the store faults then sp will not have been updated, so test above
+ // will not work, because the fault address will be more than just "slightly"
+ // below sp.
+ if (!IsStackAccess && IsAccessibleMemoryRange(pc, 4)) {
+ u32 inst = *(unsigned *)pc;
+ u32 ra = (inst >> 16) & 0x1F;
+ u32 opcd = inst >> 26;
+ u32 xo = (inst >> 1) & 0x3FF;
+ // Check for store-with-update to sp. The instructions we accept are:
+ // stbu rs,d(ra) stbux rs,ra,rb
+ // sthu rs,d(ra) sthux rs,ra,rb
+ // stwu rs,d(ra) stwux rs,ra,rb
+ // stdu rs,ds(ra) stdux rs,ra,rb
+ // where ra is r1 (the stack pointer).
+ if (ra == 1 &&
+ (opcd == 39 || opcd == 45 || opcd == 37 || opcd == 62 ||
+ (opcd == 31 && (xo == 247 || xo == 439 || xo == 183 || xo == 181))))
+ IsStackAccess = true;
+ }
+#endif // __powerpc__
+
+ // We also check si_code to filter out SEGV caused by something else other
+ // then hitting the guard page or unmapped memory, like, for example,
+ // unaligned memory access.
+ auto si = static_cast<const siginfo_t *>(siginfo);
+ return IsStackAccess &&
+ (si->si_code == si_SEGV_MAPERR || si->si_code == si_SEGV_ACCERR);
+}
+
+#endif // SANITIZER_GO
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ uptr page_size = GetPageSizeCached();
+ // Checking too large memory ranges is slow.
+ CHECK_LT(size, page_size * 10);
+ int sock_pair[2];
+ if (pipe(sock_pair))
+ return false;
+ uptr bytes_written =
+ internal_write(sock_pair[1], reinterpret_cast<void *>(beg), size);
+ int write_errno;
+ bool result;
+ if (internal_iserror(bytes_written, &write_errno)) {
+ CHECK_EQ(EFAULT, write_errno);
+ result = false;
+ } else {
+ result = (bytes_written == size);
+ }
+ internal_close(sock_pair[0]);
+ internal_close(sock_pair[1]);
+ return result;
+}
+
+void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {
+ // Some kinds of sandboxes may forbid filesystem access, so we won't be able
+ // to read the file mappings from /proc/self/maps. Luckily, neither the
+ // process will be able to load additional libraries, so it's fine to use the
+ // cached mappings.
+ MemoryMappingLayout::CacheMemoryMappings();
+}
+
+bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+ size = RoundUpTo(size, GetPageSizeCached());
+ fixed_addr = RoundDownTo(fixed_addr, GetPageSizeCached());
+ uptr p = MmapNamed((void *)fixed_addr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON, name);
+ int reserrno;
+ if (internal_iserror(p, &reserrno)) {
+ Report("ERROR: %s failed to "
+ "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, reserrno);
+ return false;
+ }
+ IncreaseTotalMmap(size);
+ return true;
+}
+
+uptr ReservedAddressRange::Init(uptr size, const char *name, uptr fixed_addr) {
+ base_ = fixed_addr ? MmapFixedNoAccess(fixed_addr, size, name)
+ : MmapNoAccess(size);
+ size_ = size;
+ name_ = name;
+ (void)os_handle_; // unsupported
+ return reinterpret_cast<uptr>(base_);
+}
+
+// Uses fixed_addr for now.
+// Will use offset instead once we've implemented this function for real.
+uptr ReservedAddressRange::Map(uptr fixed_addr, uptr size, const char *name) {
+ return reinterpret_cast<uptr>(
+ MmapFixedOrDieOnFatalError(fixed_addr, size, name));
+}
+
+uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr size,
+ const char *name) {
+ return reinterpret_cast<uptr>(MmapFixedOrDie(fixed_addr, size, name));
+}
+
+void ReservedAddressRange::Unmap(uptr addr, uptr size) {
+ CHECK_LE(size, size_);
+ if (addr == reinterpret_cast<uptr>(base_))
+ // If we unmap the whole range, just null out the base.
+ base_ = (size == size_) ? nullptr : reinterpret_cast<void*>(addr + size);
+ else
+ CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
+ size_ -= size;
+ UnmapOrDie(reinterpret_cast<void*>(addr), size);
+}
+
+void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ return (void *)MmapNamed((void *)fixed_addr, size, PROT_NONE,
+ MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE | MAP_ANON,
+ name);
+}
+
+void *MmapNoAccess(uptr size) {
+ unsigned flags = MAP_PRIVATE | MAP_ANON | MAP_NORESERVE;
+ return (void *)internal_mmap(nullptr, size, PROT_NONE, flags, -1, 0);
+}
+
+// This function is defined elsewhere if we intercepted pthread_attr_getstack.
+extern "C" {
+SANITIZER_WEAK_ATTRIBUTE int
+real_pthread_attr_getstack(void *attr, void **addr, size_t *size);
+} // extern "C"
+
+int my_pthread_attr_getstack(void *attr, void **addr, uptr *size) {
+#if !SANITIZER_GO && !SANITIZER_MAC
+ if (&real_pthread_attr_getstack)
+ return real_pthread_attr_getstack((pthread_attr_t *)attr, addr,
+ (size_t *)size);
+#endif
+ return pthread_attr_getstack((pthread_attr_t *)attr, addr, (size_t *)size);
+}
+
+#if !SANITIZER_GO
+void AdjustStackSize(void *attr_) {
+ pthread_attr_t *attr = (pthread_attr_t *)attr_;
+ uptr stackaddr = 0;
+ uptr stacksize = 0;
+ my_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize);
+ // GLibC will return (0 - stacksize) as the stack address in the case when
+ // stacksize is set, but stackaddr is not.
+ bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0);
+ // We place a lot of tool data into TLS, account for that.
+ const uptr minstacksize = GetTlsSize() + 128*1024;
+ if (stacksize < minstacksize) {
+ if (!stack_set) {
+ if (stacksize != 0) {
+ VPrintf(1, "Sanitizer: increasing stacksize %zu->%zu\n", stacksize,
+ minstacksize);
+ pthread_attr_setstacksize(attr, minstacksize);
+ }
+ } else {
+ Printf("Sanitizer: pre-allocated stack size is insufficient: "
+ "%zu < %zu\n", stacksize, minstacksize);
+ Printf("Sanitizer: pthread_create is likely to fail.\n");
+ }
+ }
+}
+#endif // !SANITIZER_GO
+
+pid_t StartSubprocess(const char *program, const char *const argv[],
+ fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
+ auto file_closer = at_scope_exit([&] {
+ if (stdin_fd != kInvalidFd) {
+ internal_close(stdin_fd);
+ }
+ if (stdout_fd != kInvalidFd) {
+ internal_close(stdout_fd);
+ }
+ if (stderr_fd != kInvalidFd) {
+ internal_close(stderr_fd);
+ }
+ });
+
+ int pid = internal_fork();
+
+ if (pid < 0) {
+ int rverrno;
+ if (internal_iserror(pid, &rverrno)) {
+ Report("WARNING: failed to fork (errno %d)\n", rverrno);
+ }
+ return pid;
+ }
+
+ if (pid == 0) {
+ // Child subprocess
+ if (stdin_fd != kInvalidFd) {
+ internal_close(STDIN_FILENO);
+ internal_dup2(stdin_fd, STDIN_FILENO);
+ internal_close(stdin_fd);
+ }
+ if (stdout_fd != kInvalidFd) {
+ internal_close(STDOUT_FILENO);
+ internal_dup2(stdout_fd, STDOUT_FILENO);
+ internal_close(stdout_fd);
+ }
+ if (stderr_fd != kInvalidFd) {
+ internal_close(STDERR_FILENO);
+ internal_dup2(stderr_fd, STDERR_FILENO);
+ internal_close(stderr_fd);
+ }
+
+ for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd);
+
+ execv(program, const_cast<char **>(&argv[0]));
+ internal__exit(1);
+ }
+
+ return pid;
+}
+
+bool IsProcessRunning(pid_t pid) {
+ int process_status;
+ uptr waitpid_status = internal_waitpid(pid, &process_status, WNOHANG);
+ int local_errno;
+ if (internal_iserror(waitpid_status, &local_errno)) {
+ VReport(1, "Waiting on the process failed (errno %d).\n", local_errno);
+ return false;
+ }
+ return waitpid_status == 0;
+}
+
+int WaitForProcess(pid_t pid) {
+ int process_status;
+ uptr waitpid_status = internal_waitpid(pid, &process_status, 0);
+ int local_errno;
+ if (internal_iserror(waitpid_status, &local_errno)) {
+ VReport(1, "Waiting on the process failed (errno %d).\n", local_errno);
+ return -1;
+ }
+ return process_status;
+}
+
+bool IsStateDetached(int state) {
+ return state == PTHREAD_CREATE_DETACHED;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
+++ /dev/null
-//===-- sanitizer_printf.cc -----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer.
-//
-// Internal printf function, used inside run-time libraries.
-// We can't use libc printf because we intercept some of the functions used
-// inside it.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_libc.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
- !defined(va_copy)
-# define va_copy(dst, src) ((dst) = (src))
-#endif
-
-namespace __sanitizer {
-
-static int AppendChar(char **buff, const char *buff_end, char c) {
- if (*buff < buff_end) {
- **buff = c;
- (*buff)++;
- }
- return 1;
-}
-
-// Appends number in a given base to buffer. If its length is less than
-// |minimal_num_length|, it is padded with leading zeroes or spaces, depending
-// on the value of |pad_with_zero|.
-static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
- u8 base, u8 minimal_num_length, bool pad_with_zero,
- bool negative, bool uppercase) {
- uptr const kMaxLen = 30;
- RAW_CHECK(base == 10 || base == 16);
- RAW_CHECK(base == 10 || !negative);
- RAW_CHECK(absolute_value || !negative);
- RAW_CHECK(minimal_num_length < kMaxLen);
- int result = 0;
- if (negative && minimal_num_length)
- --minimal_num_length;
- if (negative && pad_with_zero)
- result += AppendChar(buff, buff_end, '-');
- uptr num_buffer[kMaxLen];
- int pos = 0;
- do {
- RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
- num_buffer[pos++] = absolute_value % base;
- absolute_value /= base;
- } while (absolute_value > 0);
- if (pos < minimal_num_length) {
- // Make sure compiler doesn't insert call to memset here.
- internal_memset(&num_buffer[pos], 0,
- sizeof(num_buffer[0]) * (minimal_num_length - pos));
- pos = minimal_num_length;
- }
- RAW_CHECK(pos > 0);
- pos--;
- for (; pos >= 0 && num_buffer[pos] == 0; pos--) {
- char c = (pad_with_zero || pos == 0) ? '0' : ' ';
- result += AppendChar(buff, buff_end, c);
- }
- if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
- for (; pos >= 0; pos--) {
- char digit = static_cast<char>(num_buffer[pos]);
- digit = (digit < 10) ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10;
- result += AppendChar(buff, buff_end, digit);
- }
- return result;
-}
-
-static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
- u8 minimal_num_length, bool pad_with_zero,
- bool uppercase) {
- return AppendNumber(buff, buff_end, num, base, minimal_num_length,
- pad_with_zero, false /* negative */, uppercase);
-}
-
-static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
- u8 minimal_num_length, bool pad_with_zero) {
- bool negative = (num < 0);
- return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
- minimal_num_length, pad_with_zero, negative,
- false /* uppercase */);
-}
-
-
-// Use the fact that explicitly requesting 0 width (%0s) results in UB and
-// interpret width == 0 as "no width requested":
-// width == 0 - no width requested
-// width < 0 - left-justify s within and pad it to -width chars, if necessary
-// width > 0 - right-justify s, not implemented yet
-static int AppendString(char **buff, const char *buff_end, int width,
- int max_chars, const char *s) {
- if (!s)
- s = "<null>";
- int result = 0;
- for (; *s; s++) {
- if (max_chars >= 0 && result >= max_chars)
- break;
- result += AppendChar(buff, buff_end, *s);
- }
- // Only the left justified strings are supported.
- while (width < -result)
- result += AppendChar(buff, buff_end, ' ');
- return result;
-}
-
-static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
- int result = 0;
- result += AppendString(buff, buff_end, 0, -1, "0x");
- result += AppendUnsigned(buff, buff_end, ptr_value, 16,
- SANITIZER_POINTER_FORMAT_LENGTH,
- true /* pad_with_zero */, false /* uppercase */);
- return result;
-}
-
-int VSNPrintf(char *buff, int buff_length,
- const char *format, va_list args) {
- static const char *kPrintfFormatsHelp =
- "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
- "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
- RAW_CHECK(format);
- RAW_CHECK(buff_length > 0);
- const char *buff_end = &buff[buff_length - 1];
- const char *cur = format;
- int result = 0;
- for (; *cur; cur++) {
- if (*cur != '%') {
- result += AppendChar(&buff, buff_end, *cur);
- continue;
- }
- cur++;
- bool left_justified = *cur == '-';
- if (left_justified)
- cur++;
- bool have_width = (*cur >= '0' && *cur <= '9');
- bool pad_with_zero = (*cur == '0');
- int width = 0;
- if (have_width) {
- while (*cur >= '0' && *cur <= '9') {
- width = width * 10 + *cur++ - '0';
- }
- }
- bool have_precision = (cur[0] == '.' && cur[1] == '*');
- int precision = -1;
- if (have_precision) {
- cur += 2;
- precision = va_arg(args, int);
- }
- bool have_z = (*cur == 'z');
- cur += have_z;
- bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
- cur += have_ll * 2;
- s64 dval;
- u64 uval;
- const bool have_length = have_z || have_ll;
- const bool have_flags = have_width || have_length;
- // At the moment only %s supports precision and left-justification.
- CHECK(!((precision >= 0 || left_justified) && *cur != 's'));
- switch (*cur) {
- case 'd': {
- dval = have_ll ? va_arg(args, s64)
- : have_z ? va_arg(args, sptr)
- : va_arg(args, int);
- result += AppendSignedDecimal(&buff, buff_end, dval, width,
- pad_with_zero);
- break;
- }
- case 'u':
- case 'x':
- case 'X': {
- uval = have_ll ? va_arg(args, u64)
- : have_z ? va_arg(args, uptr)
- : va_arg(args, unsigned);
- bool uppercase = (*cur == 'X');
- result += AppendUnsigned(&buff, buff_end, uval, (*cur == 'u') ? 10 : 16,
- width, pad_with_zero, uppercase);
- break;
- }
- case 'p': {
- RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
- result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
- break;
- }
- case 's': {
- RAW_CHECK_MSG(!have_length, kPrintfFormatsHelp);
- // Only left-justified width is supported.
- CHECK(!have_width || left_justified);
- result += AppendString(&buff, buff_end, left_justified ? -width : width,
- precision, va_arg(args, char*));
- break;
- }
- case 'c': {
- RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
- result += AppendChar(&buff, buff_end, va_arg(args, int));
- break;
- }
- case '%' : {
- RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
- result += AppendChar(&buff, buff_end, '%');
- break;
- }
- default: {
- RAW_CHECK_MSG(false, kPrintfFormatsHelp);
- }
- }
- }
- RAW_CHECK(buff <= buff_end);
- AppendChar(&buff, buff_end + 1, '\0');
- return result;
-}
-
-static void (*PrintfAndReportCallback)(const char *);
-void SetPrintfAndReportCallback(void (*callback)(const char *)) {
- PrintfAndReportCallback = callback;
-}
-
-// Can be overriden in frontend.
-#if SANITIZER_GO && defined(TSAN_EXTERNAL_HOOKS)
-// Implementation must be defined in frontend.
-extern "C" void OnPrint(const char *str);
-#else
-SANITIZER_INTERFACE_WEAK_DEF(void, OnPrint, const char *str) {
- (void)str;
-}
-#endif
-
-static void CallPrintfAndReportCallback(const char *str) {
- OnPrint(str);
- if (PrintfAndReportCallback)
- PrintfAndReportCallback(str);
-}
-
-static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid,
- char *local_buffer,
- int buffer_size,
- const char *format,
- va_list args) {
- va_list args2;
- va_copy(args2, args);
- const int kLen = 16 * 1024;
- int needed_length;
- char *buffer = local_buffer;
- // First try to print a message using a local buffer, and then fall back to
- // mmaped buffer.
- for (int use_mmap = 0; use_mmap < 2; use_mmap++) {
- if (use_mmap) {
- va_end(args);
- va_copy(args, args2);
- buffer = (char*)MmapOrDie(kLen, "Report");
- buffer_size = kLen;
- }
- needed_length = 0;
- // Check that data fits into the current buffer.
-# define CHECK_NEEDED_LENGTH \
- if (needed_length >= buffer_size) { \
- if (!use_mmap) continue; \
- RAW_CHECK_MSG(needed_length < kLen, \
- "Buffer in Report is too short!\n"); \
- }
- // Fuchsia's logging infrastructure always keeps track of the logging
- // process, thread, and timestamp, so never prepend such information.
- if (!SANITIZER_FUCHSIA && append_pid) {
- int pid = internal_getpid();
- const char *exe_name = GetProcessName();
- if (common_flags()->log_exe_name && exe_name) {
- needed_length += internal_snprintf(buffer, buffer_size,
- "==%s", exe_name);
- CHECK_NEEDED_LENGTH
- }
- needed_length += internal_snprintf(
- buffer + needed_length, buffer_size - needed_length, "==%d==", pid);
- CHECK_NEEDED_LENGTH
- }
- needed_length += VSNPrintf(buffer + needed_length,
- buffer_size - needed_length, format, args);
- CHECK_NEEDED_LENGTH
- // If the message fit into the buffer, print it and exit.
- break;
-# undef CHECK_NEEDED_LENGTH
- }
- RawWrite(buffer);
-
- // Remove color sequences from the message.
- RemoveANSIEscapeSequencesFromString(buffer);
- CallPrintfAndReportCallback(buffer);
- LogMessageOnPrintf(buffer);
-
- // If we had mapped any memory, clean up.
- if (buffer != local_buffer)
- UnmapOrDie((void *)buffer, buffer_size);
- va_end(args2);
-}
-
-static void NOINLINE SharedPrintfCode(bool append_pid, const char *format,
- va_list args) {
- // |local_buffer| is small enough not to overflow the stack and/or violate
- // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other
- // hand, the bigger the buffer is, the more the chance the error report will
- // fit into it.
- char local_buffer[400];
- SharedPrintfCodeNoBuffer(append_pid, local_buffer, ARRAY_SIZE(local_buffer),
- format, args);
-}
-
-FORMAT(1, 2)
-void Printf(const char *format, ...) {
- va_list args;
- va_start(args, format);
- SharedPrintfCode(false, format, args);
- va_end(args);
-}
-
-// Like Printf, but prints the current PID before the output string.
-FORMAT(1, 2)
-void Report(const char *format, ...) {
- va_list args;
- va_start(args, format);
- SharedPrintfCode(true, format, args);
- va_end(args);
-}
-
-// Writes at most "length" symbols to "buffer" (including trailing '\0').
-// Returns the number of symbols that should have been written to buffer
-// (not including trailing '\0'). Thus, the string is truncated
-// iff return value is not less than "length".
-FORMAT(3, 4)
-int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
- va_list args;
- va_start(args, format);
- int needed_length = VSNPrintf(buffer, length, format, args);
- va_end(args);
- return needed_length;
-}
-
-FORMAT(2, 3)
-void InternalScopedString::append(const char *format, ...) {
- CHECK_LT(length_, size());
- va_list args;
- va_start(args, format);
- VSNPrintf(data() + length_, size() - length_, format, args);
- va_end(args);
- length_ += internal_strlen(data() + length_);
- CHECK_LT(length_, size());
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_printf.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer.
+//
+// Internal printf function, used inside run-time libraries.
+// We can't use libc printf because we intercept some of the functions used
+// inside it.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#if SANITIZER_WINDOWS && defined(_MSC_VER) && _MSC_VER < 1800 && \
+ !defined(va_copy)
+# define va_copy(dst, src) ((dst) = (src))
+#endif
+
+namespace __sanitizer {
+
+static int AppendChar(char **buff, const char *buff_end, char c) {
+ if (*buff < buff_end) {
+ **buff = c;
+ (*buff)++;
+ }
+ return 1;
+}
+
+// Appends number in a given base to buffer. If its length is less than
+// |minimal_num_length|, it is padded with leading zeroes or spaces, depending
+// on the value of |pad_with_zero|.
+static int AppendNumber(char **buff, const char *buff_end, u64 absolute_value,
+ u8 base, u8 minimal_num_length, bool pad_with_zero,
+ bool negative, bool uppercase) {
+ uptr const kMaxLen = 30;
+ RAW_CHECK(base == 10 || base == 16);
+ RAW_CHECK(base == 10 || !negative);
+ RAW_CHECK(absolute_value || !negative);
+ RAW_CHECK(minimal_num_length < kMaxLen);
+ int result = 0;
+ if (negative && minimal_num_length)
+ --minimal_num_length;
+ if (negative && pad_with_zero)
+ result += AppendChar(buff, buff_end, '-');
+ uptr num_buffer[kMaxLen];
+ int pos = 0;
+ do {
+ RAW_CHECK_MSG((uptr)pos < kMaxLen, "AppendNumber buffer overflow");
+ num_buffer[pos++] = absolute_value % base;
+ absolute_value /= base;
+ } while (absolute_value > 0);
+ if (pos < minimal_num_length) {
+ // Make sure compiler doesn't insert call to memset here.
+ internal_memset(&num_buffer[pos], 0,
+ sizeof(num_buffer[0]) * (minimal_num_length - pos));
+ pos = minimal_num_length;
+ }
+ RAW_CHECK(pos > 0);
+ pos--;
+ for (; pos >= 0 && num_buffer[pos] == 0; pos--) {
+ char c = (pad_with_zero || pos == 0) ? '0' : ' ';
+ result += AppendChar(buff, buff_end, c);
+ }
+ if (negative && !pad_with_zero) result += AppendChar(buff, buff_end, '-');
+ for (; pos >= 0; pos--) {
+ char digit = static_cast<char>(num_buffer[pos]);
+ digit = (digit < 10) ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10;
+ result += AppendChar(buff, buff_end, digit);
+ }
+ return result;
+}
+
+static int AppendUnsigned(char **buff, const char *buff_end, u64 num, u8 base,
+ u8 minimal_num_length, bool pad_with_zero,
+ bool uppercase) {
+ return AppendNumber(buff, buff_end, num, base, minimal_num_length,
+ pad_with_zero, false /* negative */, uppercase);
+}
+
+static int AppendSignedDecimal(char **buff, const char *buff_end, s64 num,
+ u8 minimal_num_length, bool pad_with_zero) {
+ bool negative = (num < 0);
+ return AppendNumber(buff, buff_end, (u64)(negative ? -num : num), 10,
+ minimal_num_length, pad_with_zero, negative,
+ false /* uppercase */);
+}
+
+
+// Use the fact that explicitly requesting 0 width (%0s) results in UB and
+// interpret width == 0 as "no width requested":
+// width == 0 - no width requested
+// width < 0 - left-justify s within and pad it to -width chars, if necessary
+// width > 0 - right-justify s, not implemented yet
+static int AppendString(char **buff, const char *buff_end, int width,
+ int max_chars, const char *s) {
+ if (!s)
+ s = "<null>";
+ int result = 0;
+ for (; *s; s++) {
+ if (max_chars >= 0 && result >= max_chars)
+ break;
+ result += AppendChar(buff, buff_end, *s);
+ }
+ // Only the left justified strings are supported.
+ while (width < -result)
+ result += AppendChar(buff, buff_end, ' ');
+ return result;
+}
+
+static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
+ int result = 0;
+ result += AppendString(buff, buff_end, 0, -1, "0x");
+ result += AppendUnsigned(buff, buff_end, ptr_value, 16,
+ SANITIZER_POINTER_FORMAT_LENGTH,
+ true /* pad_with_zero */, false /* uppercase */);
+ return result;
+}
+
+int VSNPrintf(char *buff, int buff_length,
+ const char *format, va_list args) {
+ static const char *kPrintfFormatsHelp =
+ "Supported Printf formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
+ "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
+ RAW_CHECK(format);
+ RAW_CHECK(buff_length > 0);
+ const char *buff_end = &buff[buff_length - 1];
+ const char *cur = format;
+ int result = 0;
+ for (; *cur; cur++) {
+ if (*cur != '%') {
+ result += AppendChar(&buff, buff_end, *cur);
+ continue;
+ }
+ cur++;
+ bool left_justified = *cur == '-';
+ if (left_justified)
+ cur++;
+ bool have_width = (*cur >= '0' && *cur <= '9');
+ bool pad_with_zero = (*cur == '0');
+ int width = 0;
+ if (have_width) {
+ while (*cur >= '0' && *cur <= '9') {
+ width = width * 10 + *cur++ - '0';
+ }
+ }
+ bool have_precision = (cur[0] == '.' && cur[1] == '*');
+ int precision = -1;
+ if (have_precision) {
+ cur += 2;
+ precision = va_arg(args, int);
+ }
+ bool have_z = (*cur == 'z');
+ cur += have_z;
+ bool have_ll = !have_z && (cur[0] == 'l' && cur[1] == 'l');
+ cur += have_ll * 2;
+ s64 dval;
+ u64 uval;
+ const bool have_length = have_z || have_ll;
+ const bool have_flags = have_width || have_length;
+ // At the moment only %s supports precision and left-justification.
+ CHECK(!((precision >= 0 || left_justified) && *cur != 's'));
+ switch (*cur) {
+ case 'd': {
+ dval = have_ll ? va_arg(args, s64)
+ : have_z ? va_arg(args, sptr)
+ : va_arg(args, int);
+ result += AppendSignedDecimal(&buff, buff_end, dval, width,
+ pad_with_zero);
+ break;
+ }
+ case 'u':
+ case 'x':
+ case 'X': {
+ uval = have_ll ? va_arg(args, u64)
+ : have_z ? va_arg(args, uptr)
+ : va_arg(args, unsigned);
+ bool uppercase = (*cur == 'X');
+ result += AppendUnsigned(&buff, buff_end, uval, (*cur == 'u') ? 10 : 16,
+ width, pad_with_zero, uppercase);
+ break;
+ }
+ case 'p': {
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+ result += AppendPointer(&buff, buff_end, va_arg(args, uptr));
+ break;
+ }
+ case 's': {
+ RAW_CHECK_MSG(!have_length, kPrintfFormatsHelp);
+ // Only left-justified width is supported.
+ CHECK(!have_width || left_justified);
+ result += AppendString(&buff, buff_end, left_justified ? -width : width,
+ precision, va_arg(args, char*));
+ break;
+ }
+ case 'c': {
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+ result += AppendChar(&buff, buff_end, va_arg(args, int));
+ break;
+ }
+ case '%' : {
+ RAW_CHECK_MSG(!have_flags, kPrintfFormatsHelp);
+ result += AppendChar(&buff, buff_end, '%');
+ break;
+ }
+ default: {
+ RAW_CHECK_MSG(false, kPrintfFormatsHelp);
+ }
+ }
+ }
+ RAW_CHECK(buff <= buff_end);
+ AppendChar(&buff, buff_end + 1, '\0');
+ return result;
+}
+
+static void (*PrintfAndReportCallback)(const char *);
+void SetPrintfAndReportCallback(void (*callback)(const char *)) {
+ PrintfAndReportCallback = callback;
+}
+
+// Can be overriden in frontend.
+#if SANITIZER_GO && defined(TSAN_EXTERNAL_HOOKS)
+// Implementation must be defined in frontend.
+extern "C" void OnPrint(const char *str);
+#else
+SANITIZER_INTERFACE_WEAK_DEF(void, OnPrint, const char *str) {
+ (void)str;
+}
+#endif
+
+static void CallPrintfAndReportCallback(const char *str) {
+ OnPrint(str);
+ if (PrintfAndReportCallback)
+ PrintfAndReportCallback(str);
+}
+
+static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid,
+ char *local_buffer,
+ int buffer_size,
+ const char *format,
+ va_list args) {
+ va_list args2;
+ va_copy(args2, args);
+ const int kLen = 16 * 1024;
+ int needed_length;
+ char *buffer = local_buffer;
+ // First try to print a message using a local buffer, and then fall back to
+ // mmaped buffer.
+ for (int use_mmap = 0; use_mmap < 2; use_mmap++) {
+ if (use_mmap) {
+ va_end(args);
+ va_copy(args, args2);
+ buffer = (char*)MmapOrDie(kLen, "Report");
+ buffer_size = kLen;
+ }
+ needed_length = 0;
+ // Check that data fits into the current buffer.
+# define CHECK_NEEDED_LENGTH \
+ if (needed_length >= buffer_size) { \
+ if (!use_mmap) continue; \
+ RAW_CHECK_MSG(needed_length < kLen, \
+ "Buffer in Report is too short!\n"); \
+ }
+ // Fuchsia's logging infrastructure always keeps track of the logging
+ // process, thread, and timestamp, so never prepend such information.
+ if (!SANITIZER_FUCHSIA && append_pid) {
+ int pid = internal_getpid();
+ const char *exe_name = GetProcessName();
+ if (common_flags()->log_exe_name && exe_name) {
+ needed_length += internal_snprintf(buffer, buffer_size,
+ "==%s", exe_name);
+ CHECK_NEEDED_LENGTH
+ }
+ needed_length += internal_snprintf(
+ buffer + needed_length, buffer_size - needed_length, "==%d==", pid);
+ CHECK_NEEDED_LENGTH
+ }
+ needed_length += VSNPrintf(buffer + needed_length,
+ buffer_size - needed_length, format, args);
+ CHECK_NEEDED_LENGTH
+ // If the message fit into the buffer, print it and exit.
+ break;
+# undef CHECK_NEEDED_LENGTH
+ }
+ RawWrite(buffer);
+
+ // Remove color sequences from the message.
+ RemoveANSIEscapeSequencesFromString(buffer);
+ CallPrintfAndReportCallback(buffer);
+ LogMessageOnPrintf(buffer);
+
+ // If we had mapped any memory, clean up.
+ if (buffer != local_buffer)
+ UnmapOrDie((void *)buffer, buffer_size);
+ va_end(args2);
+}
+
+static void NOINLINE SharedPrintfCode(bool append_pid, const char *format,
+ va_list args) {
+ // |local_buffer| is small enough not to overflow the stack and/or violate
+ // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other
+ // hand, the bigger the buffer is, the more the chance the error report will
+ // fit into it.
+ char local_buffer[400];
+ SharedPrintfCodeNoBuffer(append_pid, local_buffer, ARRAY_SIZE(local_buffer),
+ format, args);
+}
+
+FORMAT(1, 2)
+void Printf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ SharedPrintfCode(false, format, args);
+ va_end(args);
+}
+
+// Like Printf, but prints the current PID before the output string.
+FORMAT(1, 2)
+void Report(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ SharedPrintfCode(true, format, args);
+ va_end(args);
+}
+
+// Writes at most "length" symbols to "buffer" (including trailing '\0').
+// Returns the number of symbols that should have been written to buffer
+// (not including trailing '\0'). Thus, the string is truncated
+// iff return value is not less than "length".
+FORMAT(3, 4)
+int internal_snprintf(char *buffer, uptr length, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ int needed_length = VSNPrintf(buffer, length, format, args);
+ va_end(args);
+ return needed_length;
+}
+
+FORMAT(2, 3)
+void InternalScopedString::append(const char *format, ...) {
+ CHECK_LT(length_, size());
+ va_list args;
+ va_start(args, format);
+ VSNPrintf(data() + length_, size() - length_, format, args);
+ va_end(args);
+ length_ += internal_strlen(data() + length_);
+ CHECK_LT(length_, size());
+}
+
+} // namespace __sanitizer
//===-- sanitizer_procmaps.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
explicit MemoryMappingLayout(bool cache_enabled);
~MemoryMappingLayout();
bool Next(MemoryMappedSegment *segment);
+ bool Error() const;
void Reset();
// In some cases, e.g. when running under a sandbox on Linux, ASan is unable
// to obtain the memory mappings. It should fall back to pre-cached data
+++ /dev/null
-//===-- sanitizer_procmaps_bsd.cc -----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Information about the process mappings
-// (FreeBSD, OpenBSD and NetBSD-specific parts).
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
-#include "sanitizer_common.h"
-#if SANITIZER_FREEBSD
-#include "sanitizer_freebsd.h"
-#endif
-#include "sanitizer_procmaps.h"
-
-// clang-format off
-#include <sys/types.h>
-#include <sys/sysctl.h>
-// clang-format on
-#include <unistd.h>
-#if SANITIZER_FREEBSD
-#include <sys/user.h>
-#endif
-
-#include <limits.h>
-#if SANITIZER_OPENBSD
-#define KVME_PROT_READ KVE_PROT_READ
-#define KVME_PROT_WRITE KVE_PROT_WRITE
-#define KVME_PROT_EXEC KVE_PROT_EXEC
-#endif
-
-// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
-#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
-#include <osreldate.h>
-#if __FreeBSD_version <= 902001 // v9.2
-#define kinfo_vmentry xkinfo_vmentry
-#endif
-#endif
-
-namespace __sanitizer {
-
-void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
- const int Mib[] = {
-#if SANITIZER_FREEBSD
- CTL_KERN,
- KERN_PROC,
- KERN_PROC_VMMAP,
- getpid()
-#elif SANITIZER_OPENBSD
- CTL_KERN,
- KERN_PROC_VMMAP,
- getpid()
-#elif SANITIZER_NETBSD
- CTL_VM,
- VM_PROC,
- VM_PROC_MAP,
- getpid(),
- sizeof(struct kinfo_vmentry)
-#else
-#error "not supported"
-#endif
- };
-
- uptr Size = 0;
- int Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), NULL, &Size, NULL, 0);
- CHECK_EQ(Err, 0);
- CHECK_GT(Size, 0);
-
-#if !SANITIZER_OPENBSD
- size_t MmapedSize = Size * 4 / 3;
- void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
- Size = MmapedSize;
- Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), VmMap, &Size, NULL, 0);
- CHECK_EQ(Err, 0);
- proc_maps->data = (char *)VmMap;
-#else
- size_t PageSize = GetPageSize();
- size_t MmapedSize = Size;
- MmapedSize = ((MmapedSize - 1) / PageSize + 1) * PageSize;
- char *Mem = (char *)MmapOrDie(MmapedSize, "ReadProcMaps()");
- Size = 2 * Size + 10 * sizeof(struct kinfo_vmentry);
- if (Size > 0x10000)
- Size = 0x10000;
- Size = (Size / sizeof(struct kinfo_vmentry)) * sizeof(struct kinfo_vmentry);
- Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), Mem, &Size, NULL, 0);
- CHECK_EQ(Err, 0);
- MmapedSize = Size;
- proc_maps->data = Mem;
-#endif
-
- proc_maps->mmaped_size = MmapedSize;
- proc_maps->len = Size;
-}
-
-bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
- char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
- if (data_.current >= last)
- return false;
- const struct kinfo_vmentry *VmEntry =
- (const struct kinfo_vmentry *)data_.current;
-
- segment->start = (uptr)VmEntry->kve_start;
- segment->end = (uptr)VmEntry->kve_end;
- segment->offset = (uptr)VmEntry->kve_offset;
-
- segment->protection = 0;
- if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
- segment->protection |= kProtectionRead;
- if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
- segment->protection |= kProtectionWrite;
- if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
- segment->protection |= kProtectionExecute;
-
-#if !SANITIZER_OPENBSD
- if (segment->filename != NULL && segment->filename_size > 0) {
- internal_snprintf(segment->filename,
- Min(segment->filename_size, (uptr)PATH_MAX), "%s",
- VmEntry->kve_path);
- }
-#endif
-
-#if SANITIZER_FREEBSD
- data_.current += VmEntry->kve_structsize;
-#else
- data_.current += sizeof(*VmEntry);
-#endif
-
- return true;
-}
-
-} // namespace __sanitizer
-
-#endif
--- /dev/null
+//===-- sanitizer_procmaps_bsd.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings
+// (FreeBSD, OpenBSD and NetBSD-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_OPENBSD
+#include "sanitizer_common.h"
+#if SANITIZER_FREEBSD
+#include "sanitizer_freebsd.h"
+#endif
+#include "sanitizer_procmaps.h"
+
+// clang-format off
+#include <sys/types.h>
+#include <sys/sysctl.h>
+// clang-format on
+#include <unistd.h>
+#if SANITIZER_FREEBSD
+#include <sys/user.h>
+#endif
+
+#include <limits.h>
+#if SANITIZER_OPENBSD
+#define KVME_PROT_READ KVE_PROT_READ
+#define KVME_PROT_WRITE KVE_PROT_WRITE
+#define KVME_PROT_EXEC KVE_PROT_EXEC
+#endif
+
+// Fix 'kinfo_vmentry' definition on FreeBSD prior v9.2 in 32-bit mode.
+#if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32)
+#include <osreldate.h>
+#if __FreeBSD_version <= 902001 // v9.2
+#define kinfo_vmentry xkinfo_vmentry
+#endif
+#endif
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+ const int Mib[] = {
+#if SANITIZER_FREEBSD
+ CTL_KERN,
+ KERN_PROC,
+ KERN_PROC_VMMAP,
+ getpid()
+#elif SANITIZER_OPENBSD
+ CTL_KERN,
+ KERN_PROC_VMMAP,
+ getpid()
+#elif SANITIZER_NETBSD
+ CTL_VM,
+ VM_PROC,
+ VM_PROC_MAP,
+ getpid(),
+ sizeof(struct kinfo_vmentry)
+#else
+#error "not supported"
+#endif
+ };
+
+ uptr Size = 0;
+ int Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), NULL, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+ CHECK_GT(Size, 0);
+
+#if !SANITIZER_OPENBSD
+ size_t MmapedSize = Size * 4 / 3;
+ void *VmMap = MmapOrDie(MmapedSize, "ReadProcMaps()");
+ Size = MmapedSize;
+ Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), VmMap, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+ proc_maps->data = (char *)VmMap;
+#else
+ size_t PageSize = GetPageSize();
+ size_t MmapedSize = Size;
+ MmapedSize = ((MmapedSize - 1) / PageSize + 1) * PageSize;
+ char *Mem = (char *)MmapOrDie(MmapedSize, "ReadProcMaps()");
+ Size = 2 * Size + 10 * sizeof(struct kinfo_vmentry);
+ if (Size > 0x10000)
+ Size = 0x10000;
+ Size = (Size / sizeof(struct kinfo_vmentry)) * sizeof(struct kinfo_vmentry);
+ Err = internal_sysctl(Mib, ARRAY_SIZE(Mib), Mem, &Size, NULL, 0);
+ CHECK_EQ(Err, 0);
+ MmapedSize = Size;
+ proc_maps->data = Mem;
+#endif
+
+ proc_maps->mmaped_size = MmapedSize;
+ proc_maps->len = Size;
+}
+
+bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ CHECK(!Error()); // can not fail
+ char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
+ if (data_.current >= last)
+ return false;
+ const struct kinfo_vmentry *VmEntry =
+ (const struct kinfo_vmentry *)data_.current;
+
+ segment->start = (uptr)VmEntry->kve_start;
+ segment->end = (uptr)VmEntry->kve_end;
+ segment->offset = (uptr)VmEntry->kve_offset;
+
+ segment->protection = 0;
+ if ((VmEntry->kve_protection & KVME_PROT_READ) != 0)
+ segment->protection |= kProtectionRead;
+ if ((VmEntry->kve_protection & KVME_PROT_WRITE) != 0)
+ segment->protection |= kProtectionWrite;
+ if ((VmEntry->kve_protection & KVME_PROT_EXEC) != 0)
+ segment->protection |= kProtectionExecute;
+
+#if !SANITIZER_OPENBSD
+ if (segment->filename != NULL && segment->filename_size > 0) {
+ internal_snprintf(segment->filename,
+ Min(segment->filename_size, (uptr)PATH_MAX), "%s",
+ VmEntry->kve_path);
+ }
+#endif
+
+#if SANITIZER_FREEBSD
+ data_.current += VmEntry->kve_structsize;
+#else
+ data_.current += sizeof(*VmEntry);
+#endif
+
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-//===-- sanitizer_procmaps_common.cc --------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Information about the process mappings (common parts).
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
- SANITIZER_OPENBSD || SANITIZER_SOLARIS
-
-#include "sanitizer_common.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-
-namespace __sanitizer {
-
-static ProcSelfMapsBuff cached_proc_self_maps;
-static StaticSpinMutex cache_lock;
-
-static int TranslateDigit(char c) {
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return c - 'a' + 10;
- if (c >= 'A' && c <= 'F')
- return c - 'A' + 10;
- return -1;
-}
-
-// Parse a number and promote 'p' up to the first non-digit character.
-static uptr ParseNumber(const char **p, int base) {
- uptr n = 0;
- int d;
- CHECK(base >= 2 && base <= 16);
- while ((d = TranslateDigit(**p)) >= 0 && d < base) {
- n = n * base + d;
- (*p)++;
- }
- return n;
-}
-
-bool IsDecimal(char c) {
- int d = TranslateDigit(c);
- return d >= 0 && d < 10;
-}
-
-uptr ParseDecimal(const char **p) {
- return ParseNumber(p, 10);
-}
-
-bool IsHex(char c) {
- int d = TranslateDigit(c);
- return d >= 0 && d < 16;
-}
-
-uptr ParseHex(const char **p) {
- return ParseNumber(p, 16);
-}
-
-void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
- // data_ should be unused on this platform
- CHECK(!data_);
- module->addAddressRange(start, end, IsExecutable(), IsWritable());
-}
-
-MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
- // FIXME: in the future we may want to cache the mappings on demand only.
- if (cache_enabled)
- CacheMemoryMappings();
-
- // Read maps after the cache update to capture the maps/unmaps happening in
- // the process of updating.
- ReadProcMaps(&data_.proc_self_maps);
- if (cache_enabled && data_.proc_self_maps.mmaped_size == 0)
- LoadFromCache();
- CHECK_GT(data_.proc_self_maps.mmaped_size, 0);
- CHECK_GT(data_.proc_self_maps.len, 0);
-
- Reset();
-}
-
-MemoryMappingLayout::~MemoryMappingLayout() {
- // Only unmap the buffer if it is different from the cached one. Otherwise
- // it will be unmapped when the cache is refreshed.
- if (data_.proc_self_maps.data != cached_proc_self_maps.data)
- UnmapOrDie(data_.proc_self_maps.data, data_.proc_self_maps.mmaped_size);
-}
-
-void MemoryMappingLayout::Reset() {
- data_.current = data_.proc_self_maps.data;
-}
-
-// static
-void MemoryMappingLayout::CacheMemoryMappings() {
- ProcSelfMapsBuff new_proc_self_maps;
- ReadProcMaps(&new_proc_self_maps);
- // Don't invalidate the cache if the mappings are unavailable.
- if (new_proc_self_maps.mmaped_size == 0)
- return;
- SpinMutexLock l(&cache_lock);
- if (cached_proc_self_maps.mmaped_size)
- UnmapOrDie(cached_proc_self_maps.data, cached_proc_self_maps.mmaped_size);
- cached_proc_self_maps = new_proc_self_maps;
-}
-
-void MemoryMappingLayout::LoadFromCache() {
- SpinMutexLock l(&cache_lock);
- if (cached_proc_self_maps.data)
- data_.proc_self_maps = cached_proc_self_maps;
-}
-
-void MemoryMappingLayout::DumpListOfModules(
- InternalMmapVectorNoCtor<LoadedModule> *modules) {
- Reset();
- InternalScopedString module_name(kMaxPathLength);
- MemoryMappedSegment segment(module_name.data(), module_name.size());
- for (uptr i = 0; Next(&segment); i++) {
- const char *cur_name = segment.filename;
- if (cur_name[0] == '\0')
- continue;
- // Don't subtract 'cur_beg' from the first entry:
- // * If a binary is compiled w/o -pie, then the first entry in
- // process maps is likely the binary itself (all dynamic libs
- // are mapped higher in address space). For such a binary,
- // instruction offset in binary coincides with the actual
- // instruction address in virtual memory (as code section
- // is mapped to a fixed memory range).
- // * If a binary is compiled with -pie, all the modules are
- // mapped high at address space (in particular, higher than
- // shadow memory of the tool), so the module can't be the
- // first entry.
- uptr base_address = (i ? segment.start : 0) - segment.offset;
- LoadedModule cur_module;
- cur_module.set(cur_name, base_address);
- segment.AddAddressRanges(&cur_module);
- modules->push_back(cur_module);
- }
-}
-
-void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
- char *smaps = nullptr;
- uptr smaps_cap = 0;
- uptr smaps_len = 0;
- if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))
- return;
- uptr start = 0;
- bool file = false;
- const char *pos = smaps;
- while (pos < smaps + smaps_len) {
- if (IsHex(pos[0])) {
- start = ParseHex(&pos);
- for (; *pos != '/' && *pos > '\n'; pos++) {}
- file = *pos == '/';
- } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
- while (!IsDecimal(*pos)) pos++;
- uptr rss = ParseDecimal(&pos) * 1024;
- cb(start, rss, file, stats, stats_size);
- }
- while (*pos++ != '\n') {}
- }
- UnmapOrDie(smaps, smaps_cap);
-}
-
-} // namespace __sanitizer
-
-#endif
--- /dev/null
+//===-- sanitizer_procmaps_common.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (common parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+ SANITIZER_OPENBSD || SANITIZER_SOLARIS
+
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+static ProcSelfMapsBuff cached_proc_self_maps;
+static StaticSpinMutex cache_lock;
+
+static int TranslateDigit(char c) {
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ return -1;
+}
+
+// Parse a number and promote 'p' up to the first non-digit character.
+static uptr ParseNumber(const char **p, int base) {
+ uptr n = 0;
+ int d;
+ CHECK(base >= 2 && base <= 16);
+ while ((d = TranslateDigit(**p)) >= 0 && d < base) {
+ n = n * base + d;
+ (*p)++;
+ }
+ return n;
+}
+
+bool IsDecimal(char c) {
+ int d = TranslateDigit(c);
+ return d >= 0 && d < 10;
+}
+
+uptr ParseDecimal(const char **p) {
+ return ParseNumber(p, 10);
+}
+
+bool IsHex(char c) {
+ int d = TranslateDigit(c);
+ return d >= 0 && d < 16;
+}
+
+uptr ParseHex(const char **p) {
+ return ParseNumber(p, 16);
+}
+
+void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
+ // data_ should be unused on this platform
+ CHECK(!data_);
+ module->addAddressRange(start, end, IsExecutable(), IsWritable());
+}
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+ // FIXME: in the future we may want to cache the mappings on demand only.
+ if (cache_enabled)
+ CacheMemoryMappings();
+
+ // Read maps after the cache update to capture the maps/unmaps happening in
+ // the process of updating.
+ ReadProcMaps(&data_.proc_self_maps);
+ if (cache_enabled && data_.proc_self_maps.mmaped_size == 0)
+ LoadFromCache();
+
+ Reset();
+}
+
+bool MemoryMappingLayout::Error() const {
+ return data_.current == nullptr;
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+ // Only unmap the buffer if it is different from the cached one. Otherwise
+ // it will be unmapped when the cache is refreshed.
+ if (data_.proc_self_maps.data != cached_proc_self_maps.data)
+ UnmapOrDie(data_.proc_self_maps.data, data_.proc_self_maps.mmaped_size);
+}
+
+void MemoryMappingLayout::Reset() {
+ data_.current = data_.proc_self_maps.data;
+}
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ ProcSelfMapsBuff new_proc_self_maps;
+ ReadProcMaps(&new_proc_self_maps);
+ // Don't invalidate the cache if the mappings are unavailable.
+ if (new_proc_self_maps.mmaped_size == 0)
+ return;
+ SpinMutexLock l(&cache_lock);
+ if (cached_proc_self_maps.mmaped_size)
+ UnmapOrDie(cached_proc_self_maps.data, cached_proc_self_maps.mmaped_size);
+ cached_proc_self_maps = new_proc_self_maps;
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+ SpinMutexLock l(&cache_lock);
+ if (cached_proc_self_maps.data)
+ data_.proc_self_maps = cached_proc_self_maps;
+}
+
+void MemoryMappingLayout::DumpListOfModules(
+ InternalMmapVectorNoCtor<LoadedModule> *modules) {
+ Reset();
+ InternalScopedString module_name(kMaxPathLength);
+ MemoryMappedSegment segment(module_name.data(), module_name.size());
+ for (uptr i = 0; Next(&segment); i++) {
+ const char *cur_name = segment.filename;
+ if (cur_name[0] == '\0')
+ continue;
+ // Don't subtract 'cur_beg' from the first entry:
+ // * If a binary is compiled w/o -pie, then the first entry in
+ // process maps is likely the binary itself (all dynamic libs
+ // are mapped higher in address space). For such a binary,
+ // instruction offset in binary coincides with the actual
+ // instruction address in virtual memory (as code section
+ // is mapped to a fixed memory range).
+ // * If a binary is compiled with -pie, all the modules are
+ // mapped high at address space (in particular, higher than
+ // shadow memory of the tool), so the module can't be the
+ // first entry.
+ uptr base_address = (i ? segment.start : 0) - segment.offset;
+ LoadedModule cur_module;
+ cur_module.set(cur_name, base_address);
+ segment.AddAddressRanges(&cur_module);
+ modules->push_back(cur_module);
+ }
+}
+
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) {
+ char *smaps = nullptr;
+ uptr smaps_cap = 0;
+ uptr smaps_len = 0;
+ if (!ReadFileToBuffer("/proc/self/smaps", &smaps, &smaps_cap, &smaps_len))
+ return;
+ uptr start = 0;
+ bool file = false;
+ const char *pos = smaps;
+ while (pos < smaps + smaps_len) {
+ if (IsHex(pos[0])) {
+ start = ParseHex(&pos);
+ for (; *pos != '/' && *pos > '\n'; pos++) {}
+ file = *pos == '/';
+ } else if (internal_strncmp(pos, "Rss:", 4) == 0) {
+ while (!IsDecimal(*pos)) pos++;
+ uptr rss = ParseDecimal(&pos) * 1024;
+ cb(start, rss, file, stats, stats_size);
+ }
+ while (*pos++ != '\n') {}
+ }
+ UnmapOrDie(smaps, smaps_cap);
+}
+
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-//===-- sanitizer_procmaps_linux.cc ---------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Information about the process mappings (Linux-specific parts).
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_LINUX
-#include "sanitizer_common.h"
-#include "sanitizer_procmaps.h"
-
-namespace __sanitizer {
-
-void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
- if (!ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
- &proc_maps->mmaped_size, &proc_maps->len)) {
- proc_maps->data = nullptr;
- proc_maps->mmaped_size = 0;
- proc_maps->len = 0;
- }
-}
-
-static bool IsOneOf(char c, char c1, char c2) {
- return c == c1 || c == c2;
-}
-
-bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
- char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
- if (data_.current >= last) return false;
- char *next_line =
- (char *)internal_memchr(data_.current, '\n', last - data_.current);
- if (next_line == 0)
- next_line = last;
- // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
- segment->start = ParseHex(&data_.current);
- CHECK_EQ(*data_.current++, '-');
- segment->end = ParseHex(&data_.current);
- CHECK_EQ(*data_.current++, ' ');
- CHECK(IsOneOf(*data_.current, '-', 'r'));
- segment->protection = 0;
- if (*data_.current++ == 'r') segment->protection |= kProtectionRead;
- CHECK(IsOneOf(*data_.current, '-', 'w'));
- if (*data_.current++ == 'w') segment->protection |= kProtectionWrite;
- CHECK(IsOneOf(*data_.current, '-', 'x'));
- if (*data_.current++ == 'x') segment->protection |= kProtectionExecute;
- CHECK(IsOneOf(*data_.current, 's', 'p'));
- if (*data_.current++ == 's') segment->protection |= kProtectionShared;
- CHECK_EQ(*data_.current++, ' ');
- segment->offset = ParseHex(&data_.current);
- CHECK_EQ(*data_.current++, ' ');
- ParseHex(&data_.current);
- CHECK_EQ(*data_.current++, ':');
- ParseHex(&data_.current);
- CHECK_EQ(*data_.current++, ' ');
- while (IsDecimal(*data_.current)) data_.current++;
- // Qemu may lack the trailing space.
- // https://github.com/google/sanitizers/issues/160
- // CHECK_EQ(*data_.current++, ' ');
- // Skip spaces.
- while (data_.current < next_line && *data_.current == ' ') data_.current++;
- // Fill in the filename.
- if (segment->filename) {
- uptr len =
- Min((uptr)(next_line - data_.current), segment->filename_size - 1);
- internal_strncpy(segment->filename, data_.current, len);
- segment->filename[len] = 0;
- }
-
- data_.current = next_line + 1;
- return true;
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_LINUX
--- /dev/null
+//===-- sanitizer_procmaps_linux.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Linux-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_LINUX
+#include "sanitizer_common.h"
+#include "sanitizer_procmaps.h"
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+ if (!ReadFileToBuffer("/proc/self/maps", &proc_maps->data,
+ &proc_maps->mmaped_size, &proc_maps->len)) {
+ proc_maps->data = nullptr;
+ proc_maps->mmaped_size = 0;
+ proc_maps->len = 0;
+ }
+}
+
+static bool IsOneOf(char c, char c1, char c2) {
+ return c == c1 || c == c2;
+}
+
+bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ if (Error()) return false; // simulate empty maps
+ char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
+ if (data_.current >= last) return false;
+ char *next_line =
+ (char *)internal_memchr(data_.current, '\n', last - data_.current);
+ if (next_line == 0)
+ next_line = last;
+ // Example: 08048000-08056000 r-xp 00000000 03:0c 64593 /foo/bar
+ segment->start = ParseHex(&data_.current);
+ CHECK_EQ(*data_.current++, '-');
+ segment->end = ParseHex(&data_.current);
+ CHECK_EQ(*data_.current++, ' ');
+ CHECK(IsOneOf(*data_.current, '-', 'r'));
+ segment->protection = 0;
+ if (*data_.current++ == 'r') segment->protection |= kProtectionRead;
+ CHECK(IsOneOf(*data_.current, '-', 'w'));
+ if (*data_.current++ == 'w') segment->protection |= kProtectionWrite;
+ CHECK(IsOneOf(*data_.current, '-', 'x'));
+ if (*data_.current++ == 'x') segment->protection |= kProtectionExecute;
+ CHECK(IsOneOf(*data_.current, 's', 'p'));
+ if (*data_.current++ == 's') segment->protection |= kProtectionShared;
+ CHECK_EQ(*data_.current++, ' ');
+ segment->offset = ParseHex(&data_.current);
+ CHECK_EQ(*data_.current++, ' ');
+ ParseHex(&data_.current);
+ CHECK_EQ(*data_.current++, ':');
+ ParseHex(&data_.current);
+ CHECK_EQ(*data_.current++, ' ');
+ while (IsDecimal(*data_.current)) data_.current++;
+ // Qemu may lack the trailing space.
+ // https://github.com/google/sanitizers/issues/160
+ // CHECK_EQ(*data_.current++, ' ');
+ // Skip spaces.
+ while (data_.current < next_line && *data_.current == ' ') data_.current++;
+ // Fill in the filename.
+ if (segment->filename) {
+ uptr len =
+ Min((uptr)(next_line - data_.current), segment->filename_size - 1);
+ internal_strncpy(segment->filename, data_.current, len);
+ segment->filename[len] = 0;
+ }
+
+ data_.current = next_line + 1;
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX
+++ /dev/null
-//===-- sanitizer_procmaps_mac.cc -----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Information about the process mappings (Mac-specific parts).
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_MAC
-#include "sanitizer_common.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_procmaps.h"
-
-#include <mach-o/dyld.h>
-#include <mach-o/loader.h>
-#include <mach/mach.h>
-
-// These are not available in older macOS SDKs.
-#ifndef CPU_SUBTYPE_X86_64_H
-#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) /* Haswell */
-#endif
-#ifndef CPU_SUBTYPE_ARM_V7S
-#define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t)11) /* Swift */
-#endif
-#ifndef CPU_SUBTYPE_ARM_V7K
-#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t)12)
-#endif
-#ifndef CPU_TYPE_ARM64
-#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
-#endif
-
-namespace __sanitizer {
-
-// Contains information used to iterate through sections.
-struct MemoryMappedSegmentData {
- char name[kMaxSegName];
- uptr nsects;
- const char *current_load_cmd_addr;
- u32 lc_type;
- uptr base_virt_addr;
- uptr addr_mask;
-};
-
-template <typename Section>
-static void NextSectionLoad(LoadedModule *module, MemoryMappedSegmentData *data,
- bool isWritable) {
- const Section *sc = (const Section *)data->current_load_cmd_addr;
- data->current_load_cmd_addr += sizeof(Section);
-
- uptr sec_start = (sc->addr & data->addr_mask) + data->base_virt_addr;
- uptr sec_end = sec_start + sc->size;
- module->addAddressRange(sec_start, sec_end, /*executable=*/false, isWritable,
- sc->sectname);
-}
-
-void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
- // Don't iterate over sections when the caller hasn't set up the
- // data pointer, when there are no sections, or when the segment
- // is executable. Avoid iterating over executable sections because
- // it will confuse libignore, and because the extra granularity
- // of information is not needed by any sanitizers.
- if (!data_ || !data_->nsects || IsExecutable()) {
- module->addAddressRange(start, end, IsExecutable(), IsWritable(),
- data_ ? data_->name : nullptr);
- return;
- }
-
- do {
- if (data_->lc_type == LC_SEGMENT) {
- NextSectionLoad<struct section>(module, data_, IsWritable());
-#ifdef MH_MAGIC_64
- } else if (data_->lc_type == LC_SEGMENT_64) {
- NextSectionLoad<struct section_64>(module, data_, IsWritable());
-#endif
- }
- } while (--data_->nsects);
-}
-
-MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
- Reset();
-}
-
-MemoryMappingLayout::~MemoryMappingLayout() {
-}
-
-// More information about Mach-O headers can be found in mach-o/loader.h
-// Each Mach-O image has a header (mach_header or mach_header_64) starting with
-// a magic number, and a list of linker load commands directly following the
-// header.
-// A load command is at least two 32-bit words: the command type and the
-// command size in bytes. We're interested only in segment load commands
-// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
-// into the task's address space.
-// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
-// segment_command_64 correspond to the memory address, memory size and the
-// file offset of the current memory segment.
-// Because these fields are taken from the images as is, one needs to add
-// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
-
-void MemoryMappingLayout::Reset() {
- // Count down from the top.
- // TODO(glider): as per man 3 dyld, iterating over the headers with
- // _dyld_image_count is thread-unsafe. We need to register callbacks for
- // adding and removing images which will invalidate the MemoryMappingLayout
- // state.
- data_.current_image = _dyld_image_count();
- data_.current_load_cmd_count = -1;
- data_.current_load_cmd_addr = 0;
- data_.current_magic = 0;
- data_.current_filetype = 0;
- data_.current_arch = kModuleArchUnknown;
- internal_memset(data_.current_uuid, 0, kModuleUUIDSize);
-}
-
-// The dyld load address should be unchanged throughout process execution,
-// and it is expensive to compute once many libraries have been loaded,
-// so cache it here and do not reset.
-static mach_header *dyld_hdr = 0;
-static const char kDyldPath[] = "/usr/lib/dyld";
-static const int kDyldImageIdx = -1;
-
-// static
-void MemoryMappingLayout::CacheMemoryMappings() {
- // No-op on Mac for now.
-}
-
-void MemoryMappingLayout::LoadFromCache() {
- // No-op on Mac for now.
-}
-
-// _dyld_get_image_header() and related APIs don't report dyld itself.
-// We work around this by manually recursing through the memory map
-// until we hit a Mach header matching dyld instead. These recurse
-// calls are expensive, but the first memory map generation occurs
-// early in the process, when dyld is one of the only images loaded,
-// so it will be hit after only a few iterations.
-static mach_header *get_dyld_image_header() {
- unsigned depth = 1;
- vm_size_t size = 0;
- vm_address_t address = 0;
- kern_return_t err = KERN_SUCCESS;
- mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
-
- while (true) {
- struct vm_region_submap_info_64 info;
- err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth,
- (vm_region_info_t)&info, &count);
- if (err != KERN_SUCCESS) return nullptr;
-
- if (size >= sizeof(mach_header) && info.protection & kProtectionRead) {
- mach_header *hdr = (mach_header *)address;
- if ((hdr->magic == MH_MAGIC || hdr->magic == MH_MAGIC_64) &&
- hdr->filetype == MH_DYLINKER) {
- return hdr;
- }
- }
- address += size;
- }
-}
-
-const mach_header *get_dyld_hdr() {
- if (!dyld_hdr) dyld_hdr = get_dyld_image_header();
-
- return dyld_hdr;
-}
-
-// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
-// Google Perftools, https://github.com/gperftools/gperftools.
-
-// NextSegmentLoad scans the current image for the next segment load command
-// and returns the start and end addresses and file offset of the corresponding
-// segment.
-// Note that the segment addresses are not necessarily sorted.
-template <u32 kLCSegment, typename SegmentCommand>
-static bool NextSegmentLoad(MemoryMappedSegment *segment,
-MemoryMappedSegmentData *seg_data, MemoryMappingLayoutData &layout_data) {
- const char *lc = layout_data.current_load_cmd_addr;
- layout_data.current_load_cmd_addr += ((const load_command *)lc)->cmdsize;
- if (((const load_command *)lc)->cmd == kLCSegment) {
- const SegmentCommand* sc = (const SegmentCommand *)lc;
- uptr base_virt_addr, addr_mask;
- if (layout_data.current_image == kDyldImageIdx) {
- base_virt_addr = (uptr)get_dyld_hdr();
- // vmaddr is masked with 0xfffff because on macOS versions < 10.12,
- // it contains an absolute address rather than an offset for dyld.
- // To make matters even more complicated, this absolute address
- // isn't actually the absolute segment address, but the offset portion
- // of the address is accurate when combined with the dyld base address,
- // and the mask will give just this offset.
- addr_mask = 0xfffff;
- } else {
- base_virt_addr =
- (uptr)_dyld_get_image_vmaddr_slide(layout_data.current_image);
- addr_mask = ~0;
- }
-
- segment->start = (sc->vmaddr & addr_mask) + base_virt_addr;
- segment->end = segment->start + sc->vmsize;
- // Most callers don't need section information, so only fill this struct
- // when required.
- if (seg_data) {
- seg_data->nsects = sc->nsects;
- seg_data->current_load_cmd_addr =
- (const char *)lc + sizeof(SegmentCommand);
- seg_data->lc_type = kLCSegment;
- seg_data->base_virt_addr = base_virt_addr;
- seg_data->addr_mask = addr_mask;
- internal_strncpy(seg_data->name, sc->segname,
- ARRAY_SIZE(seg_data->name));
- }
-
- // Return the initial protection.
- segment->protection = sc->initprot;
- segment->offset = (layout_data.current_filetype ==
- /*MH_EXECUTE*/ 0x2)
- ? sc->vmaddr
- : sc->fileoff;
- if (segment->filename) {
- const char *src = (layout_data.current_image == kDyldImageIdx)
- ? kDyldPath
- : _dyld_get_image_name(layout_data.current_image);
- internal_strncpy(segment->filename, src, segment->filename_size);
- }
- segment->arch = layout_data.current_arch;
- internal_memcpy(segment->uuid, layout_data.current_uuid, kModuleUUIDSize);
- return true;
- }
- return false;
-}
-
-ModuleArch ModuleArchFromCpuType(cpu_type_t cputype, cpu_subtype_t cpusubtype) {
- cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK;
- switch (cputype) {
- case CPU_TYPE_I386:
- return kModuleArchI386;
- case CPU_TYPE_X86_64:
- if (cpusubtype == CPU_SUBTYPE_X86_64_ALL) return kModuleArchX86_64;
- if (cpusubtype == CPU_SUBTYPE_X86_64_H) return kModuleArchX86_64H;
- CHECK(0 && "Invalid subtype of x86_64");
- return kModuleArchUnknown;
- case CPU_TYPE_ARM:
- if (cpusubtype == CPU_SUBTYPE_ARM_V6) return kModuleArchARMV6;
- if (cpusubtype == CPU_SUBTYPE_ARM_V7) return kModuleArchARMV7;
- if (cpusubtype == CPU_SUBTYPE_ARM_V7S) return kModuleArchARMV7S;
- if (cpusubtype == CPU_SUBTYPE_ARM_V7K) return kModuleArchARMV7K;
- CHECK(0 && "Invalid subtype of ARM");
- return kModuleArchUnknown;
- case CPU_TYPE_ARM64:
- return kModuleArchARM64;
- default:
- CHECK(0 && "Invalid CPU type");
- return kModuleArchUnknown;
- }
-}
-
-static const load_command *NextCommand(const load_command *lc) {
- return (const load_command *)((const char *)lc + lc->cmdsize);
-}
-
-static void FindUUID(const load_command *first_lc, u8 *uuid_output) {
- for (const load_command *lc = first_lc; lc->cmd != 0; lc = NextCommand(lc)) {
- if (lc->cmd != LC_UUID) continue;
-
- const uuid_command *uuid_lc = (const uuid_command *)lc;
- const uint8_t *uuid = &uuid_lc->uuid[0];
- internal_memcpy(uuid_output, uuid, kModuleUUIDSize);
- return;
- }
-}
-
-static bool IsModuleInstrumented(const load_command *first_lc) {
- for (const load_command *lc = first_lc; lc->cmd != 0; lc = NextCommand(lc)) {
- if (lc->cmd != LC_LOAD_DYLIB) continue;
-
- const dylib_command *dylib_lc = (const dylib_command *)lc;
- uint32_t dylib_name_offset = dylib_lc->dylib.name.offset;
- const char *dylib_name = ((const char *)dylib_lc) + dylib_name_offset;
- dylib_name = StripModuleName(dylib_name);
- if (dylib_name != 0 && (internal_strstr(dylib_name, "libclang_rt."))) {
- return true;
- }
- }
- return false;
-}
-
-bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
- for (; data_.current_image >= kDyldImageIdx; data_.current_image--) {
- const mach_header *hdr = (data_.current_image == kDyldImageIdx)
- ? get_dyld_hdr()
- : _dyld_get_image_header(data_.current_image);
- if (!hdr) continue;
- if (data_.current_load_cmd_count < 0) {
- // Set up for this image;
- data_.current_load_cmd_count = hdr->ncmds;
- data_.current_magic = hdr->magic;
- data_.current_filetype = hdr->filetype;
- data_.current_arch = ModuleArchFromCpuType(hdr->cputype, hdr->cpusubtype);
- switch (data_.current_magic) {
-#ifdef MH_MAGIC_64
- case MH_MAGIC_64: {
- data_.current_load_cmd_addr =
- (const char *)hdr + sizeof(mach_header_64);
- break;
- }
-#endif
- case MH_MAGIC: {
- data_.current_load_cmd_addr = (const char *)hdr + sizeof(mach_header);
- break;
- }
- default: {
- continue;
- }
- }
- FindUUID((const load_command *)data_.current_load_cmd_addr,
- data_.current_uuid);
- data_.current_instrumented = IsModuleInstrumented(
- (const load_command *)data_.current_load_cmd_addr);
- }
-
- for (; data_.current_load_cmd_count >= 0; data_.current_load_cmd_count--) {
- switch (data_.current_magic) {
- // data_.current_magic may be only one of MH_MAGIC, MH_MAGIC_64.
-#ifdef MH_MAGIC_64
- case MH_MAGIC_64: {
- if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
- segment, segment->data_, data_))
- return true;
- break;
- }
-#endif
- case MH_MAGIC: {
- if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
- segment, segment->data_, data_))
- return true;
- break;
- }
- }
- }
- // If we get here, no more load_cmd's in this image talk about
- // segments. Go on to the next image.
- }
- return false;
-}
-
-void MemoryMappingLayout::DumpListOfModules(
- InternalMmapVectorNoCtor<LoadedModule> *modules) {
- Reset();
- InternalScopedString module_name(kMaxPathLength);
- MemoryMappedSegment segment(module_name.data(), kMaxPathLength);
- MemoryMappedSegmentData data;
- segment.data_ = &data;
- while (Next(&segment)) {
- if (segment.filename[0] == '\0') continue;
- LoadedModule *cur_module = nullptr;
- if (!modules->empty() &&
- 0 == internal_strcmp(segment.filename, modules->back().full_name())) {
- cur_module = &modules->back();
- } else {
- modules->push_back(LoadedModule());
- cur_module = &modules->back();
- cur_module->set(segment.filename, segment.start, segment.arch,
- segment.uuid, data_.current_instrumented);
- }
- segment.AddAddressRanges(cur_module);
- }
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- sanitizer_procmaps_mac.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Mac-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_procmaps.h"
+
+#include <mach-o/dyld.h>
+#include <mach-o/loader.h>
+#include <mach/mach.h>
+
+// These are not available in older macOS SDKs.
+#ifndef CPU_SUBTYPE_X86_64_H
+#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) /* Haswell */
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7S
+#define CPU_SUBTYPE_ARM_V7S ((cpu_subtype_t)11) /* Swift */
+#endif
+#ifndef CPU_SUBTYPE_ARM_V7K
+#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t)12)
+#endif
+#ifndef CPU_TYPE_ARM64
+#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
+#endif
+
+namespace __sanitizer {
+
+// Contains information used to iterate through sections.
+struct MemoryMappedSegmentData {
+ char name[kMaxSegName];
+ uptr nsects;
+ const char *current_load_cmd_addr;
+ u32 lc_type;
+ uptr base_virt_addr;
+ uptr addr_mask;
+};
+
+template <typename Section>
+static void NextSectionLoad(LoadedModule *module, MemoryMappedSegmentData *data,
+ bool isWritable) {
+ const Section *sc = (const Section *)data->current_load_cmd_addr;
+ data->current_load_cmd_addr += sizeof(Section);
+
+ uptr sec_start = (sc->addr & data->addr_mask) + data->base_virt_addr;
+ uptr sec_end = sec_start + sc->size;
+ module->addAddressRange(sec_start, sec_end, /*executable=*/false, isWritable,
+ sc->sectname);
+}
+
+void MemoryMappedSegment::AddAddressRanges(LoadedModule *module) {
+ // Don't iterate over sections when the caller hasn't set up the
+ // data pointer, when there are no sections, or when the segment
+ // is executable. Avoid iterating over executable sections because
+ // it will confuse libignore, and because the extra granularity
+ // of information is not needed by any sanitizers.
+ if (!data_ || !data_->nsects || IsExecutable()) {
+ module->addAddressRange(start, end, IsExecutable(), IsWritable(),
+ data_ ? data_->name : nullptr);
+ return;
+ }
+
+ do {
+ if (data_->lc_type == LC_SEGMENT) {
+ NextSectionLoad<struct section>(module, data_, IsWritable());
+#ifdef MH_MAGIC_64
+ } else if (data_->lc_type == LC_SEGMENT_64) {
+ NextSectionLoad<struct section_64>(module, data_, IsWritable());
+#endif
+ }
+ } while (--data_->nsects);
+}
+
+MemoryMappingLayout::MemoryMappingLayout(bool cache_enabled) {
+ Reset();
+}
+
+MemoryMappingLayout::~MemoryMappingLayout() {
+}
+
+bool MemoryMappingLayout::Error() const {
+ return false;
+}
+
+// More information about Mach-O headers can be found in mach-o/loader.h
+// Each Mach-O image has a header (mach_header or mach_header_64) starting with
+// a magic number, and a list of linker load commands directly following the
+// header.
+// A load command is at least two 32-bit words: the command type and the
+// command size in bytes. We're interested only in segment load commands
+// (LC_SEGMENT and LC_SEGMENT_64), which tell that a part of the file is mapped
+// into the task's address space.
+// The |vmaddr|, |vmsize| and |fileoff| fields of segment_command or
+// segment_command_64 correspond to the memory address, memory size and the
+// file offset of the current memory segment.
+// Because these fields are taken from the images as is, one needs to add
+// _dyld_get_image_vmaddr_slide() to get the actual addresses at runtime.
+
+void MemoryMappingLayout::Reset() {
+ // Count down from the top.
+ // TODO(glider): as per man 3 dyld, iterating over the headers with
+ // _dyld_image_count is thread-unsafe. We need to register callbacks for
+ // adding and removing images which will invalidate the MemoryMappingLayout
+ // state.
+ data_.current_image = _dyld_image_count();
+ data_.current_load_cmd_count = -1;
+ data_.current_load_cmd_addr = 0;
+ data_.current_magic = 0;
+ data_.current_filetype = 0;
+ data_.current_arch = kModuleArchUnknown;
+ internal_memset(data_.current_uuid, 0, kModuleUUIDSize);
+}
+
+// The dyld load address should be unchanged throughout process execution,
+// and it is expensive to compute once many libraries have been loaded,
+// so cache it here and do not reset.
+static mach_header *dyld_hdr = 0;
+static const char kDyldPath[] = "/usr/lib/dyld";
+static const int kDyldImageIdx = -1;
+
+// static
+void MemoryMappingLayout::CacheMemoryMappings() {
+ // No-op on Mac for now.
+}
+
+void MemoryMappingLayout::LoadFromCache() {
+ // No-op on Mac for now.
+}
+
+// _dyld_get_image_header() and related APIs don't report dyld itself.
+// We work around this by manually recursing through the memory map
+// until we hit a Mach header matching dyld instead. These recurse
+// calls are expensive, but the first memory map generation occurs
+// early in the process, when dyld is one of the only images loaded,
+// so it will be hit after only a few iterations.
+static mach_header *get_dyld_image_header() {
+ unsigned depth = 1;
+ vm_size_t size = 0;
+ vm_address_t address = 0;
+ kern_return_t err = KERN_SUCCESS;
+ mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64;
+
+ while (true) {
+ struct vm_region_submap_info_64 info;
+ err = vm_region_recurse_64(mach_task_self(), &address, &size, &depth,
+ (vm_region_info_t)&info, &count);
+ if (err != KERN_SUCCESS) return nullptr;
+
+ if (size >= sizeof(mach_header) && info.protection & kProtectionRead) {
+ mach_header *hdr = (mach_header *)address;
+ if ((hdr->magic == MH_MAGIC || hdr->magic == MH_MAGIC_64) &&
+ hdr->filetype == MH_DYLINKER) {
+ return hdr;
+ }
+ }
+ address += size;
+ }
+}
+
+const mach_header *get_dyld_hdr() {
+ if (!dyld_hdr) dyld_hdr = get_dyld_image_header();
+
+ return dyld_hdr;
+}
+
+// Next and NextSegmentLoad were inspired by base/sysinfo.cc in
+// Google Perftools, https://github.com/gperftools/gperftools.
+
+// NextSegmentLoad scans the current image for the next segment load command
+// and returns the start and end addresses and file offset of the corresponding
+// segment.
+// Note that the segment addresses are not necessarily sorted.
+template <u32 kLCSegment, typename SegmentCommand>
+static bool NextSegmentLoad(MemoryMappedSegment *segment,
+MemoryMappedSegmentData *seg_data, MemoryMappingLayoutData &layout_data) {
+ const char *lc = layout_data.current_load_cmd_addr;
+ layout_data.current_load_cmd_addr += ((const load_command *)lc)->cmdsize;
+ if (((const load_command *)lc)->cmd == kLCSegment) {
+ const SegmentCommand* sc = (const SegmentCommand *)lc;
+ uptr base_virt_addr, addr_mask;
+ if (layout_data.current_image == kDyldImageIdx) {
+ base_virt_addr = (uptr)get_dyld_hdr();
+ // vmaddr is masked with 0xfffff because on macOS versions < 10.12,
+ // it contains an absolute address rather than an offset for dyld.
+ // To make matters even more complicated, this absolute address
+ // isn't actually the absolute segment address, but the offset portion
+ // of the address is accurate when combined with the dyld base address,
+ // and the mask will give just this offset.
+ addr_mask = 0xfffff;
+ } else {
+ base_virt_addr =
+ (uptr)_dyld_get_image_vmaddr_slide(layout_data.current_image);
+ addr_mask = ~0;
+ }
+
+ segment->start = (sc->vmaddr & addr_mask) + base_virt_addr;
+ segment->end = segment->start + sc->vmsize;
+ // Most callers don't need section information, so only fill this struct
+ // when required.
+ if (seg_data) {
+ seg_data->nsects = sc->nsects;
+ seg_data->current_load_cmd_addr =
+ (const char *)lc + sizeof(SegmentCommand);
+ seg_data->lc_type = kLCSegment;
+ seg_data->base_virt_addr = base_virt_addr;
+ seg_data->addr_mask = addr_mask;
+ internal_strncpy(seg_data->name, sc->segname,
+ ARRAY_SIZE(seg_data->name));
+ }
+
+ // Return the initial protection.
+ segment->protection = sc->initprot;
+ segment->offset = (layout_data.current_filetype ==
+ /*MH_EXECUTE*/ 0x2)
+ ? sc->vmaddr
+ : sc->fileoff;
+ if (segment->filename) {
+ const char *src = (layout_data.current_image == kDyldImageIdx)
+ ? kDyldPath
+ : _dyld_get_image_name(layout_data.current_image);
+ internal_strncpy(segment->filename, src, segment->filename_size);
+ }
+ segment->arch = layout_data.current_arch;
+ internal_memcpy(segment->uuid, layout_data.current_uuid, kModuleUUIDSize);
+ return true;
+ }
+ return false;
+}
+
+ModuleArch ModuleArchFromCpuType(cpu_type_t cputype, cpu_subtype_t cpusubtype) {
+ cpusubtype = cpusubtype & ~CPU_SUBTYPE_MASK;
+ switch (cputype) {
+ case CPU_TYPE_I386:
+ return kModuleArchI386;
+ case CPU_TYPE_X86_64:
+ if (cpusubtype == CPU_SUBTYPE_X86_64_ALL) return kModuleArchX86_64;
+ if (cpusubtype == CPU_SUBTYPE_X86_64_H) return kModuleArchX86_64H;
+ CHECK(0 && "Invalid subtype of x86_64");
+ return kModuleArchUnknown;
+ case CPU_TYPE_ARM:
+ if (cpusubtype == CPU_SUBTYPE_ARM_V6) return kModuleArchARMV6;
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7) return kModuleArchARMV7;
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7S) return kModuleArchARMV7S;
+ if (cpusubtype == CPU_SUBTYPE_ARM_V7K) return kModuleArchARMV7K;
+ CHECK(0 && "Invalid subtype of ARM");
+ return kModuleArchUnknown;
+ case CPU_TYPE_ARM64:
+ return kModuleArchARM64;
+ default:
+ CHECK(0 && "Invalid CPU type");
+ return kModuleArchUnknown;
+ }
+}
+
+static const load_command *NextCommand(const load_command *lc) {
+ return (const load_command *)((const char *)lc + lc->cmdsize);
+}
+
+static void FindUUID(const load_command *first_lc, u8 *uuid_output) {
+ for (const load_command *lc = first_lc; lc->cmd != 0; lc = NextCommand(lc)) {
+ if (lc->cmd != LC_UUID) continue;
+
+ const uuid_command *uuid_lc = (const uuid_command *)lc;
+ const uint8_t *uuid = &uuid_lc->uuid[0];
+ internal_memcpy(uuid_output, uuid, kModuleUUIDSize);
+ return;
+ }
+}
+
+static bool IsModuleInstrumented(const load_command *first_lc) {
+ for (const load_command *lc = first_lc; lc->cmd != 0; lc = NextCommand(lc)) {
+ if (lc->cmd != LC_LOAD_DYLIB) continue;
+
+ const dylib_command *dylib_lc = (const dylib_command *)lc;
+ uint32_t dylib_name_offset = dylib_lc->dylib.name.offset;
+ const char *dylib_name = ((const char *)dylib_lc) + dylib_name_offset;
+ dylib_name = StripModuleName(dylib_name);
+ if (dylib_name != 0 && (internal_strstr(dylib_name, "libclang_rt."))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ for (; data_.current_image >= kDyldImageIdx; data_.current_image--) {
+ const mach_header *hdr = (data_.current_image == kDyldImageIdx)
+ ? get_dyld_hdr()
+ : _dyld_get_image_header(data_.current_image);
+ if (!hdr) continue;
+ if (data_.current_load_cmd_count < 0) {
+ // Set up for this image;
+ data_.current_load_cmd_count = hdr->ncmds;
+ data_.current_magic = hdr->magic;
+ data_.current_filetype = hdr->filetype;
+ data_.current_arch = ModuleArchFromCpuType(hdr->cputype, hdr->cpusubtype);
+ switch (data_.current_magic) {
+#ifdef MH_MAGIC_64
+ case MH_MAGIC_64: {
+ data_.current_load_cmd_addr =
+ (const char *)hdr + sizeof(mach_header_64);
+ break;
+ }
+#endif
+ case MH_MAGIC: {
+ data_.current_load_cmd_addr = (const char *)hdr + sizeof(mach_header);
+ break;
+ }
+ default: {
+ continue;
+ }
+ }
+ FindUUID((const load_command *)data_.current_load_cmd_addr,
+ data_.current_uuid);
+ data_.current_instrumented = IsModuleInstrumented(
+ (const load_command *)data_.current_load_cmd_addr);
+ }
+
+ for (; data_.current_load_cmd_count >= 0; data_.current_load_cmd_count--) {
+ switch (data_.current_magic) {
+ // data_.current_magic may be only one of MH_MAGIC, MH_MAGIC_64.
+#ifdef MH_MAGIC_64
+ case MH_MAGIC_64: {
+ if (NextSegmentLoad<LC_SEGMENT_64, struct segment_command_64>(
+ segment, segment->data_, data_))
+ return true;
+ break;
+ }
+#endif
+ case MH_MAGIC: {
+ if (NextSegmentLoad<LC_SEGMENT, struct segment_command>(
+ segment, segment->data_, data_))
+ return true;
+ break;
+ }
+ }
+ }
+ // If we get here, no more load_cmd's in this image talk about
+ // segments. Go on to the next image.
+ }
+ return false;
+}
+
+void MemoryMappingLayout::DumpListOfModules(
+ InternalMmapVectorNoCtor<LoadedModule> *modules) {
+ Reset();
+ InternalScopedString module_name(kMaxPathLength);
+ MemoryMappedSegment segment(module_name.data(), kMaxPathLength);
+ MemoryMappedSegmentData data;
+ segment.data_ = &data;
+ while (Next(&segment)) {
+ if (segment.filename[0] == '\0') continue;
+ LoadedModule *cur_module = nullptr;
+ if (!modules->empty() &&
+ 0 == internal_strcmp(segment.filename, modules->back().full_name())) {
+ cur_module = &modules->back();
+ } else {
+ modules->push_back(LoadedModule());
+ cur_module = &modules->back();
+ cur_module->set(segment.filename, segment.start, segment.arch,
+ segment.uuid, data_.current_instrumented);
+ }
+ segment.AddAddressRanges(cur_module);
+ }
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
+++ /dev/null
-//===-- sanitizer_procmaps_solaris.cc -------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Information about the process mappings (Solaris-specific parts).
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_SOLARIS
-#include "sanitizer_common.h"
-#include "sanitizer_procmaps.h"
-
-// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment.
-#undef _FILE_OFFSET_BITS
-#include <procfs.h>
-#include <limits.h>
-
-namespace __sanitizer {
-
-void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
- ReadFileToBuffer("/proc/self/xmap", &proc_maps->data, &proc_maps->mmaped_size,
- &proc_maps->len);
-}
-
-bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
- char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
- if (data_.current >= last) return false;
-
- prxmap_t *xmapentry = (prxmap_t*)data_.current;
-
- segment->start = (uptr)xmapentry->pr_vaddr;
- segment->end = (uptr)(xmapentry->pr_vaddr + xmapentry->pr_size);
- segment->offset = (uptr)xmapentry->pr_offset;
-
- segment->protection = 0;
- if ((xmapentry->pr_mflags & MA_READ) != 0)
- segment->protection |= kProtectionRead;
- if ((xmapentry->pr_mflags & MA_WRITE) != 0)
- segment->protection |= kProtectionWrite;
- if ((xmapentry->pr_mflags & MA_EXEC) != 0)
- segment->protection |= kProtectionExecute;
-
- if (segment->filename != NULL && segment->filename_size > 0) {
- internal_snprintf(segment->filename,
- Min(segment->filename_size, (uptr)PATH_MAX), "%s",
- xmapentry->pr_mapname);
- }
-
- data_.current += sizeof(prxmap_t);
-
- return true;
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_SOLARIS
--- /dev/null
+//===-- sanitizer_procmaps_solaris.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Information about the process mappings (Solaris-specific parts).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_SOLARIS
+#include "sanitizer_common.h"
+#include "sanitizer_procmaps.h"
+
+// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment.
+#undef _FILE_OFFSET_BITS
+#include <procfs.h>
+#include <limits.h>
+
+namespace __sanitizer {
+
+void ReadProcMaps(ProcSelfMapsBuff *proc_maps) {
+ if (!ReadFileToBuffer("/proc/self/xmap", &proc_maps->data,
+ &proc_maps->mmaped_size, &proc_maps->len)) {
+ proc_maps->data = nullptr;
+ proc_maps->mmaped_size = 0;
+ proc_maps->len = 0;
+ }
+}
+
+bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) {
+ if (Error()) return false; // simulate empty maps
+ char *last = data_.proc_self_maps.data + data_.proc_self_maps.len;
+ if (data_.current >= last) return false;
+
+ prxmap_t *xmapentry = (prxmap_t*)data_.current;
+
+ segment->start = (uptr)xmapentry->pr_vaddr;
+ segment->end = (uptr)(xmapentry->pr_vaddr + xmapentry->pr_size);
+ segment->offset = (uptr)xmapentry->pr_offset;
+
+ segment->protection = 0;
+ if ((xmapentry->pr_mflags & MA_READ) != 0)
+ segment->protection |= kProtectionRead;
+ if ((xmapentry->pr_mflags & MA_WRITE) != 0)
+ segment->protection |= kProtectionWrite;
+ if ((xmapentry->pr_mflags & MA_EXEC) != 0)
+ segment->protection |= kProtectionExecute;
+
+ if (segment->filename != NULL && segment->filename_size > 0) {
+ char proc_path[PATH_MAX + 1];
+
+ internal_snprintf(proc_path, sizeof(proc_path), "/proc/self/path/%s",
+ xmapentry->pr_mapname);
+ internal_readlink(proc_path, segment->filename, segment->filename_size);
+ }
+
+ data_.current += sizeof(prxmap_t);
+
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SOLARIS
//===-- sanitizer_quarantine.h ----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_report_decorator.h ----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_ring_buffer.h ---------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
SetNext(next);
}
- T operator[](uptr Idx) const {
+ const T &operator[](uptr Idx) const {
CHECK_LT(Idx, size());
const T *Begin = (const T *)StartOfStorage();
sptr StorageIdx = Next() - Begin;
+++ /dev/null
-//===-- sanitizer_rtems.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries and
-// implements RTEMS-specific functions.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_rtems.h"
-#if SANITIZER_RTEMS
-
-#define posix_memalign __real_posix_memalign
-#define free __real_free
-#define memset __real_memset
-
-#include "sanitizer_file.h"
-#include "sanitizer_symbolizer.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <sched.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-// There is no mmap on RTEMS. Use memalign, etc.
-#define __mmap_alloc_aligned posix_memalign
-#define __mmap_free free
-#define __mmap_memset memset
-
-namespace __sanitizer {
-
-#include "sanitizer_syscall_generic.inc"
-
-void NORETURN internal__exit(int exitcode) {
- _exit(exitcode);
-}
-
-uptr internal_sched_yield() {
- return sched_yield();
-}
-
-uptr internal_getpid() {
- return getpid();
-}
-
-bool FileExists(const char *filename) {
- struct stat st;
- if (stat(filename, &st))
- return false;
- // Sanity check: filename is a regular file.
- return S_ISREG(st.st_mode);
-}
-
-uptr GetThreadSelf() { return static_cast<uptr>(pthread_self()); }
-
-tid_t GetTid() { return GetThreadSelf(); }
-
-void Abort() { abort(); }
-
-int Atexit(void (*function)(void)) { return atexit(function); }
-
-void SleepForSeconds(int seconds) { sleep(seconds); }
-
-void SleepForMillis(int millis) { usleep(millis * 1000); }
-
-bool SupportsColoredOutput(fd_t fd) { return false; }
-
-void GetThreadStackTopAndBottom(bool at_initialization,
- uptr *stack_top, uptr *stack_bottom) {
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
- void *base = nullptr;
- size_t size = 0;
- CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
- CHECK_EQ(pthread_attr_destroy(&attr), 0);
-
- *stack_bottom = reinterpret_cast<uptr>(base);
- *stack_top = *stack_bottom + size;
-}
-
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
- uptr stack_top, stack_bottom;
- GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
- *tls_addr = *tls_size = 0;
-}
-
-void MaybeReexec() {}
-void CheckASLR() {}
-void DisableCoreDumperIfNecessary() {}
-void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
-void SetAlternateSignalStack() {}
-void UnsetAlternateSignalStack() {}
-void InitTlsSize() {}
-
-void PrintModuleMap() {}
-
-void SignalContext::DumpAllRegisters(void *context) {}
-const char *DescribeSignalOrException(int signo) { UNIMPLEMENTED(); }
-
-enum MutexState { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
-
-BlockingMutex::BlockingMutex() {
- internal_memset(this, 0, sizeof(*this));
-}
-
-void BlockingMutex::Lock() {
- CHECK_EQ(owner_, 0);
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
- return;
- while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
- internal_sched_yield();
- }
-}
-
-void BlockingMutex::Unlock() {
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
- CHECK_NE(v, MtxUnlocked);
-}
-
-void BlockingMutex::CheckLocked() {
- atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
- CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
-}
-
-uptr GetPageSize() { return getpagesize(); }
-
-uptr GetMmapGranularity() { return GetPageSize(); }
-
-uptr GetMaxVirtualAddress() {
- return (1ULL << 32) - 1; // 0xffffffff
-}
-
-void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
- void* ptr = 0;
- int res = __mmap_alloc_aligned(&ptr, GetPageSize(), size);
- if (UNLIKELY(res))
- ReportMmapFailureAndDie(size, mem_type, "allocate", res, raw_report);
- __mmap_memset(ptr, 0, size);
- IncreaseTotalMmap(size);
- return ptr;
-}
-
-void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
- void* ptr = 0;
- int res = __mmap_alloc_aligned(&ptr, GetPageSize(), size);
- if (UNLIKELY(res)) {
- if (res == ENOMEM)
- return nullptr;
- ReportMmapFailureAndDie(size, mem_type, "allocate", false);
- }
- __mmap_memset(ptr, 0, size);
- IncreaseTotalMmap(size);
- return ptr;
-}
-
-void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
- const char *mem_type) {
- CHECK(IsPowerOfTwo(size));
- CHECK(IsPowerOfTwo(alignment));
- void* ptr = 0;
- int res = __mmap_alloc_aligned(&ptr, alignment, size);
- if (res)
- ReportMmapFailureAndDie(size, mem_type, "align allocate", res, false);
- __mmap_memset(ptr, 0, size);
- IncreaseTotalMmap(size);
- return ptr;
-}
-
-void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
- return MmapOrDie(size, mem_type, false);
-}
-
-void UnmapOrDie(void *addr, uptr size) {
- if (!addr || !size) return;
- __mmap_free(addr);
- DecreaseTotalMmap(size);
-}
-
-fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
- int flags;
- switch (mode) {
- case RdOnly: flags = O_RDONLY; break;
- case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
- case RdWr: flags = O_RDWR | O_CREAT; break;
- }
- fd_t res = open(filename, flags, 0660);
- if (internal_iserror(res, errno_p))
- return kInvalidFd;
- return res;
-}
-
-void CloseFile(fd_t fd) {
- close(fd);
-}
-
-bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
- error_t *error_p) {
- uptr res = read(fd, buff, buff_size);
- if (internal_iserror(res, error_p))
- return false;
- if (bytes_read)
- *bytes_read = res;
- return true;
-}
-
-bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
- error_t *error_p) {
- uptr res = write(fd, buff, buff_size);
- if (internal_iserror(res, error_p))
- return false;
- if (bytes_written)
- *bytes_written = res;
- return true;
-}
-
-bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
- uptr res = rename(oldpath, newpath);
- return !internal_iserror(res, error_p);
-}
-
-void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
-void DumpProcessMap() {}
-
-// There is no page protection so everything is "accessible."
-bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- return true;
-}
-
-char **GetArgv() { return nullptr; }
-
-const char *GetEnv(const char *name) {
- return getenv(name);
-}
-
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
- internal_strncpy(buf, "StubBinaryName", buf_len);
- return internal_strlen(buf);
-}
-
-uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
- internal_strncpy(buf, "StubProcessName", buf_len);
- return internal_strlen(buf);
-}
-
-bool IsPathSeparator(const char c) {
- return c == '/';
-}
-
-bool IsAbsolutePath(const char *path) {
- return path != nullptr && IsPathSeparator(path[0]);
-}
-
-void ReportFile::Write(const char *buffer, uptr length) {
- SpinMutexLock l(mu);
- static const char *kWriteError =
- "ReportFile::Write() can't output requested buffer!\n";
- ReopenIfNecessary();
- if (length != write(fd, buffer, length)) {
- write(fd, kWriteError, internal_strlen(kWriteError));
- Die();
- }
-}
-
-uptr MainThreadStackBase, MainThreadStackSize;
-uptr MainThreadTlsBase, MainThreadTlsSize;
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_RTEMS
--- /dev/null
+//===-- sanitizer_rtems.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements RTEMS-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_rtems.h"
+#if SANITIZER_RTEMS
+
+#define posix_memalign __real_posix_memalign
+#define free __real_free
+#define memset __real_memset
+
+#include "sanitizer_file.h"
+#include "sanitizer_symbolizer.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// There is no mmap on RTEMS. Use memalign, etc.
+#define __mmap_alloc_aligned posix_memalign
+#define __mmap_free free
+#define __mmap_memset memset
+
+namespace __sanitizer {
+
+#include "sanitizer_syscall_generic.inc"
+
+void NORETURN internal__exit(int exitcode) {
+ _exit(exitcode);
+}
+
+uptr internal_sched_yield() {
+ return sched_yield();
+}
+
+uptr internal_getpid() {
+ return getpid();
+}
+
+bool FileExists(const char *filename) {
+ struct stat st;
+ if (stat(filename, &st))
+ return false;
+ // Sanity check: filename is a regular file.
+ return S_ISREG(st.st_mode);
+}
+
+uptr GetThreadSelf() { return static_cast<uptr>(pthread_self()); }
+
+tid_t GetTid() { return GetThreadSelf(); }
+
+void Abort() { abort(); }
+
+int Atexit(void (*function)(void)) { return atexit(function); }
+
+void SleepForSeconds(int seconds) { sleep(seconds); }
+
+void SleepForMillis(int millis) { usleep(millis * 1000); }
+
+bool SupportsColoredOutput(fd_t fd) { return false; }
+
+void GetThreadStackTopAndBottom(bool at_initialization,
+ uptr *stack_top, uptr *stack_bottom) {
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
+ void *base = nullptr;
+ size_t size = 0;
+ CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
+ CHECK_EQ(pthread_attr_destroy(&attr), 0);
+
+ *stack_bottom = reinterpret_cast<uptr>(base);
+ *stack_top = *stack_bottom + size;
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+ uptr stack_top, stack_bottom;
+ GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+ *stk_addr = stack_bottom;
+ *stk_size = stack_top - stack_bottom;
+ *tls_addr = *tls_size = 0;
+}
+
+void InitializePlatformEarly() {}
+void MaybeReexec() {}
+void CheckASLR() {}
+void CheckMPROTECT() {}
+void DisableCoreDumperIfNecessary() {}
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
+void SetAlternateSignalStack() {}
+void UnsetAlternateSignalStack() {}
+void InitTlsSize() {}
+
+void PrintModuleMap() {}
+
+void SignalContext::DumpAllRegisters(void *context) {}
+const char *DescribeSignalOrException(int signo) { UNIMPLEMENTED(); }
+
+enum MutexState { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 };
+
+BlockingMutex::BlockingMutex() {
+ internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+ CHECK_EQ(owner_, 0);
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked)
+ return;
+ while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) {
+ internal_sched_yield();
+ }
+}
+
+void BlockingMutex::Unlock() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release);
+ CHECK_NE(v, MtxUnlocked);
+}
+
+void BlockingMutex::CheckLocked() {
+ atomic_uint32_t *m = reinterpret_cast<atomic_uint32_t *>(&opaque_storage_);
+ CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed));
+}
+
+uptr GetPageSize() { return getpagesize(); }
+
+uptr GetMmapGranularity() { return GetPageSize(); }
+
+uptr GetMaxVirtualAddress() {
+ return (1ULL << 32) - 1; // 0xffffffff
+}
+
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
+ void* ptr = 0;
+ int res = __mmap_alloc_aligned(&ptr, GetPageSize(), size);
+ if (UNLIKELY(res))
+ ReportMmapFailureAndDie(size, mem_type, "allocate", res, raw_report);
+ __mmap_memset(ptr, 0, size);
+ IncreaseTotalMmap(size);
+ return ptr;
+}
+
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
+ void* ptr = 0;
+ int res = __mmap_alloc_aligned(&ptr, GetPageSize(), size);
+ if (UNLIKELY(res)) {
+ if (res == ENOMEM)
+ return nullptr;
+ ReportMmapFailureAndDie(size, mem_type, "allocate", false);
+ }
+ __mmap_memset(ptr, 0, size);
+ IncreaseTotalMmap(size);
+ return ptr;
+}
+
+void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
+ const char *mem_type) {
+ CHECK(IsPowerOfTwo(size));
+ CHECK(IsPowerOfTwo(alignment));
+ void* ptr = 0;
+ int res = __mmap_alloc_aligned(&ptr, alignment, size);
+ if (res)
+ ReportMmapFailureAndDie(size, mem_type, "align allocate", res, false);
+ __mmap_memset(ptr, 0, size);
+ IncreaseTotalMmap(size);
+ return ptr;
+}
+
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+ return MmapOrDie(size, mem_type, false);
+}
+
+void UnmapOrDie(void *addr, uptr size) {
+ if (!addr || !size) return;
+ __mmap_free(addr);
+ DecreaseTotalMmap(size);
+}
+
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) {
+ int flags;
+ switch (mode) {
+ case RdOnly: flags = O_RDONLY; break;
+ case WrOnly: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
+ case RdWr: flags = O_RDWR | O_CREAT; break;
+ }
+ fd_t res = open(filename, flags, 0660);
+ if (internal_iserror(res, errno_p))
+ return kInvalidFd;
+ return res;
+}
+
+void CloseFile(fd_t fd) {
+ close(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ uptr res = read(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_read)
+ *bytes_read = res;
+ return true;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ uptr res = write(fd, buff, buff_size);
+ if (internal_iserror(res, error_p))
+ return false;
+ if (bytes_written)
+ *bytes_written = res;
+ return true;
+}
+
+void ReleaseMemoryPagesToOS(uptr beg, uptr end) {}
+void DumpProcessMap() {}
+
+// There is no page protection so everything is "accessible."
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ return true;
+}
+
+char **GetArgv() { return nullptr; }
+char **GetEnviron() { return nullptr; }
+
+const char *GetEnv(const char *name) {
+ return getenv(name);
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ internal_strncpy(buf, "StubBinaryName", buf_len);
+ return internal_strlen(buf);
+}
+
+uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
+ internal_strncpy(buf, "StubProcessName", buf_len);
+ return internal_strlen(buf);
+}
+
+bool IsPathSeparator(const char c) {
+ return c == '/';
+}
+
+bool IsAbsolutePath(const char *path) {
+ return path != nullptr && IsPathSeparator(path[0]);
+}
+
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ static const char *kWriteError =
+ "ReportFile::Write() can't output requested buffer!\n";
+ ReopenIfNecessary();
+ if (length != write(fd, buffer, length)) {
+ write(fd, kWriteError, internal_strlen(kWriteError));
+ Die();
+ }
+}
+
+uptr MainThreadStackBase, MainThreadStackSize;
+uptr MainThreadTlsBase, MainThreadTlsSize;
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_RTEMS
//===-- sanitizer_rtems.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_signal_interceptors.inc -----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_solaris.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries and
-// implements Solaris-specific functions.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_SOLARIS
-
-#include <stdio.h>
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_platform_limits_posix.h"
-#include "sanitizer_procmaps.h"
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <sched.h>
-#include <thread.h>
-#include <synch.h>
-#include <signal.h>
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <errno.h>
-#include <stdlib.h>
-
-namespace __sanitizer {
-
-//#include "sanitizer_syscall_generic.inc"
-
-#define _REAL(func) _ ## func
-#define DECLARE__REAL(ret_type, func, ...) \
- extern "C" ret_type _REAL(func)(__VA_ARGS__)
-#define DECLARE__REAL_AND_INTERNAL(ret_type, func, ...) \
- DECLARE__REAL(ret_type, func, __VA_ARGS__); \
- ret_type internal_ ## func(__VA_ARGS__)
-
-#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
-#define _REAL64(func) _ ## func ## 64
-#else
-#define _REAL64(func) _REAL(func)
-#endif
-#define DECLARE__REAL64(ret_type, func, ...) \
- extern "C" ret_type _REAL64(func)(__VA_ARGS__)
-#define DECLARE__REAL_AND_INTERNAL64(ret_type, func, ...) \
- DECLARE__REAL64(ret_type, func, __VA_ARGS__); \
- ret_type internal_ ## func(__VA_ARGS__)
-
-// ---------------------- sanitizer_libc.h
-DECLARE__REAL_AND_INTERNAL64(uptr, mmap, void *addr, uptr /*size_t*/ length,
- int prot, int flags, int fd, OFF_T offset) {
- return (uptr)_REAL64(mmap)(addr, length, prot, flags, fd, offset);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, munmap, void *addr, uptr length) {
- return _REAL(munmap)(addr, length);
-}
-
-DECLARE__REAL_AND_INTERNAL(int, mprotect, void *addr, uptr length, int prot) {
- return _REAL(mprotect)(addr, length, prot);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, close, fd_t fd) {
- return _REAL(close)(fd);
-}
-
-extern "C" int _REAL64(open)(const char *, int, ...);
-
-uptr internal_open(const char *filename, int flags) {
- return _REAL64(open)(filename, flags);
-}
-
-uptr internal_open(const char *filename, int flags, u32 mode) {
- return _REAL64(open)(filename, flags, mode);
-}
-
-uptr OpenFile(const char *filename, bool write) {
- return internal_open(filename,
- write ? O_WRONLY | O_CREAT : O_RDONLY, 0660);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, read, fd_t fd, void *buf, uptr count) {
- return _REAL(read)(fd, buf, count);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, write, fd_t fd, const void *buf, uptr count) {
- return _REAL(write)(fd, buf, count);
-}
-
-// FIXME: There's only _ftruncate64 beginning with Solaris 11.
-DECLARE__REAL_AND_INTERNAL(uptr, ftruncate, fd_t fd, uptr size) {
- return ftruncate(fd, size);
-}
-
-DECLARE__REAL_AND_INTERNAL64(uptr, stat, const char *path, void *buf) {
- return _REAL64(stat)(path, (struct stat *)buf);
-}
-
-DECLARE__REAL_AND_INTERNAL64(uptr, lstat, const char *path, void *buf) {
- return _REAL64(lstat)(path, (struct stat *)buf);
-}
-
-DECLARE__REAL_AND_INTERNAL64(uptr, fstat, fd_t fd, void *buf) {
- return _REAL64(fstat)(fd, (struct stat *)buf);
-}
-
-uptr internal_filesize(fd_t fd) {
- struct stat st;
- if (internal_fstat(fd, &st))
- return -1;
- return (uptr)st.st_size;
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, dup2, int oldfd, int newfd) {
- return _REAL(dup2)(oldfd, newfd);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, readlink, const char *path, char *buf,
- uptr bufsize) {
- return _REAL(readlink)(path, buf, bufsize);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, unlink, const char *path) {
- return _REAL(unlink)(path);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, rename, const char *oldpath,
- const char *newpath) {
- return _REAL(rename)(oldpath, newpath);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, sched_yield, void) {
- return sched_yield();
-}
-
-DECLARE__REAL_AND_INTERNAL(void, _exit, int exitcode) {
- _exit(exitcode);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, execve, const char *filename,
- char *const argv[], char *const envp[]) {
- return _REAL(execve)(filename, argv, envp);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, waitpid, int pid, int *status, int options) {
- return _REAL(waitpid)(pid, status, options);
-}
-
-DECLARE__REAL_AND_INTERNAL(uptr, getpid, void) {
- return _REAL(getpid)();
-}
-
-// FIXME: This might be wrong: _getdents doesn't take a struct linux_dirent *.
-DECLARE__REAL_AND_INTERNAL64(uptr, getdents, fd_t fd, struct linux_dirent *dirp,
- unsigned int count) {
- return _REAL64(getdents)(fd, dirp, count);
-}
-
-DECLARE__REAL_AND_INTERNAL64(uptr, lseek, fd_t fd, OFF_T offset, int whence) {
- return _REAL64(lseek)(fd, offset, whence);
-}
-
-// FIXME: This might be wrong: _sigfillset doesn't take a
-// __sanitizer_sigset_t *.
-DECLARE__REAL_AND_INTERNAL(void, sigfillset, __sanitizer_sigset_t *set) {
- _REAL(sigfillset)(set);
-}
-
-// FIXME: This might be wrong: _sigprocmask doesn't take __sanitizer_sigset_t *.
-DECLARE__REAL_AND_INTERNAL(uptr, sigprocmask, int how,
- __sanitizer_sigset_t *set,
- __sanitizer_sigset_t *oldset) {
- return _REAL(sigprocmask)(how, set, oldset);
-}
-
-DECLARE__REAL_AND_INTERNAL(int, fork, void) {
- // TODO(glider): this may call user's pthread_atfork() handlers which is bad.
- return _REAL(fork)();
-}
-
-u64 NanoTime() {
- return gethrtime();
-}
-
-uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
- // FIXME: No internal variant.
- return clock_gettime(clk_id, (timespec *)tp);
-}
-
-// ----------------- sanitizer_common.h
-BlockingMutex::BlockingMutex() {
- CHECK(sizeof(mutex_t) <= sizeof(opaque_storage_));
- internal_memset(this, 0, sizeof(*this));
- CHECK_EQ(mutex_init((mutex_t *)&opaque_storage_, USYNC_THREAD, NULL), 0);
-}
-
-void BlockingMutex::Lock() {
- CHECK(sizeof(mutex_t) <= sizeof(opaque_storage_));
- CHECK_NE(owner_, (uptr)thr_self());
- CHECK_EQ(mutex_lock((mutex_t *)&opaque_storage_), 0);
- CHECK(!owner_);
- owner_ = (uptr)thr_self();
-}
-
-void BlockingMutex::Unlock() {
- CHECK(owner_ == (uptr)thr_self());
- owner_ = 0;
- CHECK_EQ(mutex_unlock((mutex_t *)&opaque_storage_), 0);
-}
-
-void BlockingMutex::CheckLocked() {
- CHECK_EQ((uptr)thr_self(), owner_);
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_SOLARIS
--- /dev/null
+//===-- sanitizer_solaris.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries and
+// implements Solaris-specific functions.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_SOLARIS
+
+#include <stdio.h>
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_procmaps.h"
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <sched.h>
+#include <thread.h>
+#include <synch.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+
+namespace __sanitizer {
+
+//#include "sanitizer_syscall_generic.inc"
+
+#define _REAL(func) _ ## func
+#define DECLARE__REAL(ret_type, func, ...) \
+ extern "C" ret_type _REAL(func)(__VA_ARGS__)
+#define DECLARE__REAL_AND_INTERNAL(ret_type, func, ...) \
+ DECLARE__REAL(ret_type, func, __VA_ARGS__); \
+ ret_type internal_ ## func(__VA_ARGS__)
+
+#if !defined(_LP64) && _FILE_OFFSET_BITS == 64
+#define _REAL64(func) _ ## func ## 64
+#else
+#define _REAL64(func) _REAL(func)
+#endif
+#define DECLARE__REAL64(ret_type, func, ...) \
+ extern "C" ret_type _REAL64(func)(__VA_ARGS__)
+#define DECLARE__REAL_AND_INTERNAL64(ret_type, func, ...) \
+ DECLARE__REAL64(ret_type, func, __VA_ARGS__); \
+ ret_type internal_ ## func(__VA_ARGS__)
+
+// ---------------------- sanitizer_libc.h
+DECLARE__REAL_AND_INTERNAL64(uptr, mmap, void *addr, uptr /*size_t*/ length,
+ int prot, int flags, int fd, OFF_T offset) {
+ return (uptr)_REAL64(mmap)(addr, length, prot, flags, fd, offset);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, munmap, void *addr, uptr length) {
+ return _REAL(munmap)(addr, length);
+}
+
+DECLARE__REAL_AND_INTERNAL(int, mprotect, void *addr, uptr length, int prot) {
+ return _REAL(mprotect)(addr, length, prot);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, close, fd_t fd) {
+ return _REAL(close)(fd);
+}
+
+extern "C" int _REAL64(open)(const char *, int, ...);
+
+uptr internal_open(const char *filename, int flags) {
+ return _REAL64(open)(filename, flags);
+}
+
+uptr internal_open(const char *filename, int flags, u32 mode) {
+ return _REAL64(open)(filename, flags, mode);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, read, fd_t fd, void *buf, uptr count) {
+ return _REAL(read)(fd, buf, count);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, write, fd_t fd, const void *buf, uptr count) {
+ return _REAL(write)(fd, buf, count);
+}
+
+// FIXME: There's only _ftruncate64 beginning with Solaris 11.
+DECLARE__REAL_AND_INTERNAL(uptr, ftruncate, fd_t fd, uptr size) {
+ return ftruncate(fd, size);
+}
+
+DECLARE__REAL_AND_INTERNAL64(uptr, stat, const char *path, void *buf) {
+ return _REAL64(stat)(path, (struct stat *)buf);
+}
+
+DECLARE__REAL_AND_INTERNAL64(uptr, lstat, const char *path, void *buf) {
+ return _REAL64(lstat)(path, (struct stat *)buf);
+}
+
+DECLARE__REAL_AND_INTERNAL64(uptr, fstat, fd_t fd, void *buf) {
+ return _REAL64(fstat)(fd, (struct stat *)buf);
+}
+
+uptr internal_filesize(fd_t fd) {
+ struct stat st;
+ if (internal_fstat(fd, &st))
+ return -1;
+ return (uptr)st.st_size;
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, dup, int oldfd) {
+ return _REAL(dup)(oldfd);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, dup2, int oldfd, int newfd) {
+ return _REAL(dup2)(oldfd, newfd);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, readlink, const char *path, char *buf,
+ uptr bufsize) {
+ return _REAL(readlink)(path, buf, bufsize);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, unlink, const char *path) {
+ return _REAL(unlink)(path);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, rename, const char *oldpath,
+ const char *newpath) {
+ return _REAL(rename)(oldpath, newpath);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, sched_yield, void) {
+ return sched_yield();
+}
+
+DECLARE__REAL_AND_INTERNAL(void, _exit, int exitcode) {
+ _exit(exitcode);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, execve, const char *filename,
+ char *const argv[], char *const envp[]) {
+ return _REAL(execve)(filename, argv, envp);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, waitpid, int pid, int *status, int options) {
+ return _REAL(waitpid)(pid, status, options);
+}
+
+DECLARE__REAL_AND_INTERNAL(uptr, getpid, void) {
+ return _REAL(getpid)();
+}
+
+// FIXME: This might be wrong: _getdents doesn't take a struct linux_dirent *.
+DECLARE__REAL_AND_INTERNAL64(uptr, getdents, fd_t fd, struct linux_dirent *dirp,
+ unsigned int count) {
+ return _REAL64(getdents)(fd, dirp, count);
+}
+
+DECLARE__REAL_AND_INTERNAL64(uptr, lseek, fd_t fd, OFF_T offset, int whence) {
+ return _REAL64(lseek)(fd, offset, whence);
+}
+
+// FIXME: This might be wrong: _sigfillset doesn't take a
+// __sanitizer_sigset_t *.
+DECLARE__REAL_AND_INTERNAL(void, sigfillset, __sanitizer_sigset_t *set) {
+ _REAL(sigfillset)(set);
+}
+
+// FIXME: This might be wrong: _sigprocmask doesn't take __sanitizer_sigset_t *.
+DECLARE__REAL_AND_INTERNAL(uptr, sigprocmask, int how,
+ __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ return _REAL(sigprocmask)(how, set, oldset);
+}
+
+DECLARE__REAL_AND_INTERNAL(int, fork, void) {
+ // TODO(glider): this may call user's pthread_atfork() handlers which is bad.
+ return _REAL(fork)();
+}
+
+u64 NanoTime() {
+ return gethrtime();
+}
+
+uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) {
+ // FIXME: No internal variant.
+ return clock_gettime(clk_id, (timespec *)tp);
+}
+
+// ----------------- sanitizer_common.h
+BlockingMutex::BlockingMutex() {
+ CHECK(sizeof(mutex_t) <= sizeof(opaque_storage_));
+ internal_memset(this, 0, sizeof(*this));
+ CHECK_EQ(mutex_init((mutex_t *)&opaque_storage_, USYNC_THREAD, NULL), 0);
+}
+
+void BlockingMutex::Lock() {
+ CHECK(sizeof(mutex_t) <= sizeof(opaque_storage_));
+ CHECK_NE(owner_, (uptr)thr_self());
+ CHECK_EQ(mutex_lock((mutex_t *)&opaque_storage_), 0);
+ CHECK(!owner_);
+ owner_ = (uptr)thr_self();
+}
+
+void BlockingMutex::Unlock() {
+ CHECK(owner_ == (uptr)thr_self());
+ owner_ = 0;
+ CHECK_EQ(mutex_unlock((mutex_t *)&opaque_storage_), 0);
+}
+
+void BlockingMutex::CheckLocked() {
+ CHECK_EQ((uptr)thr_self(), owner_);
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SOLARIS
+++ /dev/null
-//===-- sanitizer_stackdepot.cc -------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_stackdepot.h"
-
-#include "sanitizer_common.h"
-#include "sanitizer_stackdepotbase.h"
-
-namespace __sanitizer {
-
-struct StackDepotNode {
- StackDepotNode *link;
- u32 id;
- atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
- u32 size;
- u32 tag;
- uptr stack[1]; // [size]
-
- static const u32 kTabSizeLog = 20;
- // Lower kTabSizeLog bits are equal for all items in one bucket.
- // We use these bits to store the per-stack use counter.
- static const u32 kUseCountBits = kTabSizeLog;
- static const u32 kMaxUseCount = 1 << kUseCountBits;
- static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
- static const u32 kHashMask = ~kUseCountMask;
-
- typedef StackTrace args_type;
- bool eq(u32 hash, const args_type &args) const {
- u32 hash_bits =
- atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
- if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag)
- return false;
- uptr i = 0;
- for (; i < size; i++) {
- if (stack[i] != args.trace[i]) return false;
- }
- return true;
- }
- static uptr storage_size(const args_type &args) {
- return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
- }
- static u32 hash(const args_type &args) {
- // murmur2
- const u32 m = 0x5bd1e995;
- const u32 seed = 0x9747b28c;
- const u32 r = 24;
- u32 h = seed ^ (args.size * sizeof(uptr));
- for (uptr i = 0; i < args.size; i++) {
- u32 k = args.trace[i];
- k *= m;
- k ^= k >> r;
- k *= m;
- h *= m;
- h ^= k;
- }
- h ^= h >> 13;
- h *= m;
- h ^= h >> 15;
- return h;
- }
- static bool is_valid(const args_type &args) {
- return args.size > 0 && args.trace;
- }
- void store(const args_type &args, u32 hash) {
- atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
- size = args.size;
- tag = args.tag;
- internal_memcpy(stack, args.trace, size * sizeof(uptr));
- }
- args_type load() const {
- return args_type(&stack[0], size, tag);
- }
- StackDepotHandle get_handle() { return StackDepotHandle(this); }
-
- typedef StackDepotHandle handle_type;
-};
-
-COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
-
-u32 StackDepotHandle::id() { return node_->id; }
-int StackDepotHandle::use_count() {
- return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
- StackDepotNode::kUseCountMask;
-}
-void StackDepotHandle::inc_use_count_unsafe() {
- u32 prev =
- atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
- StackDepotNode::kUseCountMask;
- CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
-}
-
-// FIXME(dvyukov): this single reserved bit is used in TSan.
-typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
- StackDepot;
-static StackDepot theDepot;
-
-StackDepotStats *StackDepotGetStats() {
- return theDepot.GetStats();
-}
-
-u32 StackDepotPut(StackTrace stack) {
- StackDepotHandle h = theDepot.Put(stack);
- return h.valid() ? h.id() : 0;
-}
-
-StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
- return theDepot.Put(stack);
-}
-
-StackTrace StackDepotGet(u32 id) {
- return theDepot.Get(id);
-}
-
-void StackDepotLockAll() {
- theDepot.LockAll();
-}
-
-void StackDepotUnlockAll() {
- theDepot.UnlockAll();
-}
-
-bool StackDepotReverseMap::IdDescPair::IdComparator(
- const StackDepotReverseMap::IdDescPair &a,
- const StackDepotReverseMap::IdDescPair &b) {
- return a.id < b.id;
-}
-
-StackDepotReverseMap::StackDepotReverseMap() {
- map_.reserve(StackDepotGetStats()->n_uniq_ids + 100);
- for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
- atomic_uintptr_t *p = &theDepot.tab[idx];
- uptr v = atomic_load(p, memory_order_consume);
- StackDepotNode *s = (StackDepotNode*)(v & ~1);
- for (; s; s = s->link) {
- IdDescPair pair = {s->id, s};
- map_.push_back(pair);
- }
- }
- Sort(map_.data(), map_.size(), &IdDescPair::IdComparator);
-}
-
-StackTrace StackDepotReverseMap::Get(u32 id) {
- if (!map_.size())
- return StackTrace();
- IdDescPair pair = {id, nullptr};
- uptr idx =
- InternalLowerBound(map_, 0, map_.size(), pair, IdDescPair::IdComparator);
- if (idx > map_.size() || map_[idx].id != id)
- return StackTrace();
- return map_[idx].desc->load();
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_stackdepot.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_stackdepot.h"
+
+#include "sanitizer_common.h"
+#include "sanitizer_hash.h"
+#include "sanitizer_stackdepotbase.h"
+
+namespace __sanitizer {
+
+struct StackDepotNode {
+ StackDepotNode *link;
+ u32 id;
+ atomic_uint32_t hash_and_use_count; // hash_bits : 12; use_count : 20;
+ u32 size;
+ u32 tag;
+ uptr stack[1]; // [size]
+
+ static const u32 kTabSizeLog = SANITIZER_ANDROID ? 16 : 20;
+ // Lower kTabSizeLog bits are equal for all items in one bucket.
+ // We use these bits to store the per-stack use counter.
+ static const u32 kUseCountBits = kTabSizeLog;
+ static const u32 kMaxUseCount = 1 << kUseCountBits;
+ static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
+ static const u32 kHashMask = ~kUseCountMask;
+
+ typedef StackTrace args_type;
+ bool eq(u32 hash, const args_type &args) const {
+ u32 hash_bits =
+ atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
+ if ((hash & kHashMask) != hash_bits || args.size != size || args.tag != tag)
+ return false;
+ uptr i = 0;
+ for (; i < size; i++) {
+ if (stack[i] != args.trace[i]) return false;
+ }
+ return true;
+ }
+ static uptr storage_size(const args_type &args) {
+ return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
+ }
+ static u32 hash(const args_type &args) {
+ MurMur2HashBuilder H(args.size * sizeof(uptr));
+ for (uptr i = 0; i < args.size; i++) H.add(args.trace[i]);
+ return H.get();
+ }
+ static bool is_valid(const args_type &args) {
+ return args.size > 0 && args.trace;
+ }
+ void store(const args_type &args, u32 hash) {
+ atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
+ size = args.size;
+ tag = args.tag;
+ internal_memcpy(stack, args.trace, size * sizeof(uptr));
+ }
+ args_type load() const {
+ return args_type(&stack[0], size, tag);
+ }
+ StackDepotHandle get_handle() { return StackDepotHandle(this); }
+
+ typedef StackDepotHandle handle_type;
+};
+
+COMPILER_CHECK(StackDepotNode::kMaxUseCount == (u32)kStackDepotMaxUseCount);
+
+u32 StackDepotHandle::id() { return node_->id; }
+int StackDepotHandle::use_count() {
+ return atomic_load(&node_->hash_and_use_count, memory_order_relaxed) &
+ StackDepotNode::kUseCountMask;
+}
+void StackDepotHandle::inc_use_count_unsafe() {
+ u32 prev =
+ atomic_fetch_add(&node_->hash_and_use_count, 1, memory_order_relaxed) &
+ StackDepotNode::kUseCountMask;
+ CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
+}
+
+// FIXME(dvyukov): this single reserved bit is used in TSan.
+typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
+ StackDepot;
+static StackDepot theDepot;
+
+StackDepotStats *StackDepotGetStats() {
+ return theDepot.GetStats();
+}
+
+u32 StackDepotPut(StackTrace stack) {
+ StackDepotHandle h = theDepot.Put(stack);
+ return h.valid() ? h.id() : 0;
+}
+
+StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
+ return theDepot.Put(stack);
+}
+
+StackTrace StackDepotGet(u32 id) {
+ return theDepot.Get(id);
+}
+
+void StackDepotLockAll() {
+ theDepot.LockAll();
+}
+
+void StackDepotUnlockAll() {
+ theDepot.UnlockAll();
+}
+
+bool StackDepotReverseMap::IdDescPair::IdComparator(
+ const StackDepotReverseMap::IdDescPair &a,
+ const StackDepotReverseMap::IdDescPair &b) {
+ return a.id < b.id;
+}
+
+StackDepotReverseMap::StackDepotReverseMap() {
+ map_.reserve(StackDepotGetStats()->n_uniq_ids + 100);
+ for (int idx = 0; idx < StackDepot::kTabSize; idx++) {
+ atomic_uintptr_t *p = &theDepot.tab[idx];
+ uptr v = atomic_load(p, memory_order_consume);
+ StackDepotNode *s = (StackDepotNode*)(v & ~1);
+ for (; s; s = s->link) {
+ IdDescPair pair = {s->id, s};
+ map_.push_back(pair);
+ }
+ }
+ Sort(map_.data(), map_.size(), &IdDescPair::IdComparator);
+}
+
+StackTrace StackDepotReverseMap::Get(u32 id) {
+ if (!map_.size())
+ return StackTrace();
+ IdDescPair pair = {id, nullptr};
+ uptr idx =
+ InternalLowerBound(map_, 0, map_.size(), pair, IdDescPair::IdComparator);
+ if (idx > map_.size() || map_[idx].id != id)
+ return StackTrace();
+ return map_[idx].desc->load();
+}
+
+} // namespace __sanitizer
//===-- sanitizer_stackdepot.h ----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void inc_use_count_unsafe();
};
-const int kStackDepotMaxUseCount = 1U << 20;
+const int kStackDepotMaxUseCount = 1U << (SANITIZER_ANDROID ? 16 : 20);
StackDepotStats *StackDepotGetStats();
u32 StackDepotPut(StackTrace stack);
//===-- sanitizer_stackdepotbase.h ------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_stacktrace.cc -------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_stacktrace.h"
-
-namespace __sanitizer {
-
-uptr StackTrace::GetNextInstructionPc(uptr pc) {
-#if defined(__sparc__) || defined(__mips__)
- return pc + 8;
-#elif defined(__powerpc__) || defined(__arm__) || defined(__aarch64__)
- return pc + 4;
-#else
- return pc + 1;
-#endif
-}
-
-uptr StackTrace::GetCurrentPc() {
- return GET_CALLER_PC();
-}
-
-void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
- size = cnt + !!extra_top_pc;
- CHECK_LE(size, kStackTraceMax);
- internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
- if (extra_top_pc)
- trace_buffer[cnt] = extra_top_pc;
- top_frame_bp = 0;
-}
-
-// Sparc implemention is in its own file.
-#if !defined(__sparc__)
-
-// In GCC on ARM bp points to saved lr, not fp, so we should check the next
-// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
-// pointer to saved frame pointer in any case.
-static inline uhwptr *GetCanonicFrame(uptr bp,
- uptr stack_top,
- uptr stack_bottom) {
-#ifdef __arm__
- if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
- uhwptr *bp_prev = (uhwptr *)bp;
- if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
- // The next frame pointer does not look right. This could be a GCC frame, step
- // back by 1 word and try again.
- if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
- return bp_prev - 1;
- // Nope, this does not look right either. This means the frame after next does
- // not have a valid frame pointer, but we can still extract the caller PC.
- // Unfortunately, there is no way to decide between GCC and LLVM frame
- // layouts. Assume GCC.
- return bp_prev - 1;
-#else
- return (uhwptr*)bp;
-#endif
-}
-
-void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
- uptr stack_bottom, u32 max_depth) {
- const uptr kPageSize = GetPageSizeCached();
- CHECK_GE(max_depth, 2);
- trace_buffer[0] = pc;
- size = 1;
- if (stack_top < 4096) return; // Sanity check for stack top.
- uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
- // Lowest possible address that makes sense as the next frame pointer.
- // Goes up as we walk the stack.
- uptr bottom = stack_bottom;
- // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
- while (IsValidFrame((uptr)frame, stack_top, bottom) &&
- IsAligned((uptr)frame, sizeof(*frame)) &&
- size < max_depth) {
-#ifdef __powerpc__
- // PowerPC ABIs specify that the return address is saved on the
- // *caller's* stack frame. Thus we must dereference the back chain
- // to find the caller frame before extracting it.
- uhwptr *caller_frame = (uhwptr*)frame[0];
- if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
- !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
- break;
- // For most ABIs the offset where the return address is saved is two
- // register sizes. The exception is the SVR4 ABI, which uses an
- // offset of only one register size.
-#ifdef _CALL_SYSV
- uhwptr pc1 = caller_frame[1];
-#else
- uhwptr pc1 = caller_frame[2];
-#endif
-#elif defined(__s390__)
- uhwptr pc1 = frame[14];
-#else
- uhwptr pc1 = frame[1];
-#endif
- // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
- // x86_64) is invalid and stop unwinding here. If we're adding support for
- // a platform where this isn't true, we need to reconsider this check.
- if (pc1 < kPageSize)
- break;
- if (pc1 != pc) {
- trace_buffer[size++] = (uptr) pc1;
- }
- bottom = (uptr)frame;
- frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom);
- }
-}
-
-#endif // !defined(__sparc__)
-
-void BufferedStackTrace::PopStackFrames(uptr count) {
- CHECK_LT(count, size);
- size -= count;
- for (uptr i = 0; i < size; ++i) {
- trace_buffer[i] = trace_buffer[i + count];
- }
-}
-
-static uptr Distance(uptr a, uptr b) { return a < b ? b - a : a - b; }
-
-uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
- uptr best = 0;
- for (uptr i = 1; i < size; ++i) {
- if (Distance(trace[i], pc) < Distance(trace[best], pc)) best = i;
- }
- return best;
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_stacktrace.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+uptr StackTrace::GetNextInstructionPc(uptr pc) {
+#if defined(__sparc__) || defined(__mips__)
+ return pc + 8;
+#elif defined(__powerpc__) || defined(__arm__) || defined(__aarch64__)
+ return pc + 4;
+#else
+ return pc + 1;
+#endif
+}
+
+uptr StackTrace::GetCurrentPc() {
+ return GET_CALLER_PC();
+}
+
+void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+ size = cnt + !!extra_top_pc;
+ CHECK_LE(size, kStackTraceMax);
+ internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+ if (extra_top_pc)
+ trace_buffer[cnt] = extra_top_pc;
+ top_frame_bp = 0;
+}
+
+// Sparc implemention is in its own file.
+#if !defined(__sparc__)
+
+// In GCC on ARM bp points to saved lr, not fp, so we should check the next
+// cell in stack to be a saved frame pointer. GetCanonicFrame returns the
+// pointer to saved frame pointer in any case.
+static inline uhwptr *GetCanonicFrame(uptr bp,
+ uptr stack_top,
+ uptr stack_bottom) {
+ CHECK_GT(stack_top, stack_bottom);
+#ifdef __arm__
+ if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
+ uhwptr *bp_prev = (uhwptr *)bp;
+ if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
+ // The next frame pointer does not look right. This could be a GCC frame, step
+ // back by 1 word and try again.
+ if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
+ return bp_prev - 1;
+ // Nope, this does not look right either. This means the frame after next does
+ // not have a valid frame pointer, but we can still extract the caller PC.
+ // Unfortunately, there is no way to decide between GCC and LLVM frame
+ // layouts. Assume LLVM.
+ return bp_prev;
+#else
+ return (uhwptr*)bp;
+#endif
+}
+
+void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
+ uptr stack_bottom, u32 max_depth) {
+ // TODO(yln): add arg sanity check for stack_top/stack_bottom
+ CHECK_GE(max_depth, 2);
+ const uptr kPageSize = GetPageSizeCached();
+ trace_buffer[0] = pc;
+ size = 1;
+ if (stack_top < 4096) return; // Sanity check for stack top.
+ uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
+ // Lowest possible address that makes sense as the next frame pointer.
+ // Goes up as we walk the stack.
+ uptr bottom = stack_bottom;
+ // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
+ while (IsValidFrame((uptr)frame, stack_top, bottom) &&
+ IsAligned((uptr)frame, sizeof(*frame)) &&
+ size < max_depth) {
+#ifdef __powerpc__
+ // PowerPC ABIs specify that the return address is saved at offset
+ // 16 of the *caller's* stack frame. Thus we must dereference the
+ // back chain to find the caller frame before extracting it.
+ uhwptr *caller_frame = (uhwptr*)frame[0];
+ if (!IsValidFrame((uptr)caller_frame, stack_top, bottom) ||
+ !IsAligned((uptr)caller_frame, sizeof(uhwptr)))
+ break;
+ uhwptr pc1 = caller_frame[2];
+#elif defined(__s390__)
+ uhwptr pc1 = frame[14];
+#else
+ uhwptr pc1 = frame[1];
+#endif
+ // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
+ // x86_64) is invalid and stop unwinding here. If we're adding support for
+ // a platform where this isn't true, we need to reconsider this check.
+ if (pc1 < kPageSize)
+ break;
+ if (pc1 != pc) {
+ trace_buffer[size++] = (uptr) pc1;
+ }
+ bottom = (uptr)frame;
+ frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom);
+ }
+}
+
+#endif // !defined(__sparc__)
+
+void BufferedStackTrace::PopStackFrames(uptr count) {
+ CHECK_LT(count, size);
+ size -= count;
+ for (uptr i = 0; i < size; ++i) {
+ trace_buffer[i] = trace_buffer[i + count];
+ }
+}
+
+static uptr Distance(uptr a, uptr b) { return a < b ? b - a : a - b; }
+
+uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
+ uptr best = 0;
+ for (uptr i = 1; i < size; ++i) {
+ if (Distance(trace[i], pc) < Distance(trace[best], pc)) best = i;
+ }
+ return best;
+}
+
+} // namespace __sanitizer
//===-- sanitizer_stacktrace.h ----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
namespace __sanitizer {
+struct BufferedStackTrace;
+
static const u32 kStackTraceMax = 256;
#if SANITIZER_LINUX && defined(__mips__)
static bool WillUseFastUnwind(bool request_fast_unwind) {
if (!SANITIZER_CAN_FAST_UNWIND)
return false;
- else if (!SANITIZER_CAN_SLOW_UNWIND)
+ if (!SANITIZER_CAN_SLOW_UNWIND)
return true;
return request_fast_unwind;
}
BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
+
+ // 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.
+ void Unwind(uptr pc, uptr bp, void *context, bool request_fast,
+ u32 max_depth = kStackTraceMax) {
+ top_frame_bp = (max_depth > 0) ? bp : 0;
+ // Small max_depth optimization
+ if (max_depth <= 1) {
+ if (max_depth == 1)
+ trace_buffer[0] = pc;
+ size = max_depth;
+ return;
+ }
+ UnwindImpl(pc, bp, context, request_fast, max_depth);
+ }
+
void Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
uptr stack_bottom, bool request_fast_unwind);
}
private:
- void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
- u32 max_depth);
- void SlowUnwindStack(uptr pc, u32 max_depth);
- void SlowUnwindStackWithContext(uptr pc, void *context,
- u32 max_depth);
+ // Every runtime defines its own implementation of this method
+ void UnwindImpl(uptr pc, uptr bp, void *context, bool request_fast,
+ u32 max_depth);
+
+ // UnwindFast/Slow have platform-specific implementations
+ void UnwindFast(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
+ u32 max_depth);
+ void UnwindSlow(uptr pc, u32 max_depth);
+ void UnwindSlow(uptr pc, void *context, u32 max_depth);
+
void PopStackFrames(uptr count);
uptr LocatePcInTrace(uptr pc);
BufferedStackTrace(const BufferedStackTrace &) = delete;
void operator=(const BufferedStackTrace &) = delete;
+
+ friend class FastUnwindTest;
};
// Check if given pointer points into allocated stack area.
// Use this macro if you want to print stack trace with the caller
// of the current function in the top frame.
-#define GET_CALLER_PC_BP_SP \
- uptr bp = GET_CURRENT_FRAME(); \
- uptr pc = GET_CALLER_PC(); \
- uptr local_stack; \
- uptr sp = (uptr)&local_stack
-
#define GET_CALLER_PC_BP \
uptr bp = GET_CURRENT_FRAME(); \
uptr pc = GET_CALLER_PC();
+#define GET_CALLER_PC_BP_SP \
+ GET_CALLER_PC_BP; \
+ uptr local_stack; \
+ uptr sp = (uptr)&local_stack
+
// Use this macro if you want to print stack trace with the current
// function in the top frame.
-#define GET_CURRENT_PC_BP_SP \
+#define GET_CURRENT_PC_BP \
uptr bp = GET_CURRENT_FRAME(); \
- uptr pc = StackTrace::GetCurrentPc(); \
+ uptr pc = StackTrace::GetCurrentPc()
+
+#define GET_CURRENT_PC_BP_SP \
+ GET_CURRENT_PC_BP; \
uptr local_stack; \
uptr sp = (uptr)&local_stack
+++ /dev/null
-//===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_stacktrace.h"
-#include "sanitizer_stacktrace_printer.h"
-#include "sanitizer_symbolizer.h"
-
-namespace __sanitizer {
-
-void StackTrace::Print() const {
- if (trace == nullptr || size == 0) {
- Printf(" <empty stack>\n\n");
- return;
- }
- InternalScopedString frame_desc(GetPageSizeCached() * 2);
- InternalScopedString dedup_token(GetPageSizeCached());
- int dedup_frames = common_flags()->dedup_token_length;
- uptr frame_num = 0;
- for (uptr i = 0; i < size && trace[i]; i++) {
- // PCs in stack traces are actually the return addresses, that is,
- // addresses of the next instructions after the call.
- uptr pc = GetPreviousInstructionPc(trace[i]);
- SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
- CHECK(frames);
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
- frame_desc.clear();
- RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
- cur->info, common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- Printf("%s\n", frame_desc.data());
- if (dedup_frames-- > 0) {
- if (dedup_token.length())
- dedup_token.append("--");
- if (cur->info.function != nullptr)
- dedup_token.append(cur->info.function);
- }
- }
- frames->ClearAll();
- }
- // Always print a trailing empty line after stack trace.
- Printf("\n");
- if (dedup_token.length())
- Printf("DEDUP_TOKEN: %s\n", dedup_token.data());
-}
-
-void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
- uptr stack_top, uptr stack_bottom,
- bool request_fast_unwind) {
- top_frame_bp = (max_depth > 0) ? bp : 0;
- // Avoid doing any work for small max_depth.
- if (max_depth == 0) {
- size = 0;
- return;
- }
- if (max_depth == 1) {
- size = 1;
- trace_buffer[0] = pc;
- return;
- }
- if (!WillUseFastUnwind(request_fast_unwind)) {
-#if SANITIZER_CAN_SLOW_UNWIND
- if (context)
- SlowUnwindStackWithContext(pc, context, max_depth);
- else
- SlowUnwindStack(pc, max_depth);
-#else
- UNREACHABLE("slow unwind requested but not available");
-#endif
- } else {
- FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
- }
-}
-
-static int GetModuleAndOffsetForPc(uptr pc, char *module_name,
- uptr module_name_len, uptr *pc_offset) {
- const char *found_module_name = nullptr;
- bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
- pc, &found_module_name, pc_offset);
-
- if (!ok) return false;
-
- if (module_name && module_name_len) {
- internal_strncpy(module_name, found_module_name, module_name_len);
- module_name[module_name_len - 1] = '\x00';
- }
- return true;
-}
-
-} // namespace __sanitizer
-using namespace __sanitizer;
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
- uptr out_buf_size) {
- if (!out_buf_size) return;
- pc = StackTrace::GetPreviousInstructionPc(pc);
- SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
- if (!frame) {
- internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
- out_buf[out_buf_size - 1] = 0;
- return;
- }
- InternalScopedString frame_desc(GetPageSizeCached());
- uptr frame_num = 0;
- // Reserve one byte for the final 0.
- char *out_end = out_buf + out_buf_size - 1;
- for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
- cur = cur->next) {
- frame_desc.clear();
- RenderFrame(&frame_desc, fmt, frame_num++, cur->info,
- common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- if (!frame_desc.length())
- continue;
- // Reserve one byte for the terminating 0.
- uptr n = out_end - out_buf - 1;
- internal_strncpy(out_buf, frame_desc.data(), n);
- out_buf += __sanitizer::Min<uptr>(n, frame_desc.length());
- *out_buf++ = 0;
- }
- CHECK(out_buf <= out_end);
- *out_buf = 0;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
- char *out_buf, uptr out_buf_size) {
- if (!out_buf_size) return;
- out_buf[0] = 0;
- DataInfo DI;
- if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
- InternalScopedString data_desc(GetPageSizeCached());
- RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix);
- internal_strncpy(out_buf, data_desc.data(), out_buf_size);
- out_buf[out_buf_size - 1] = 0;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __sanitizer_get_module_and_offset_for_pc( // NOLINT
- uptr pc, char *module_name, uptr module_name_len, uptr *pc_offset) {
- return __sanitizer::GetModuleAndOffsetForPc(pc, module_name, module_name_len,
- pc_offset);
-}
-} // extern "C"
--- /dev/null
+//===-- sanitizer_stacktrace_libcdep.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
+
+namespace __sanitizer {
+
+void StackTrace::Print() const {
+ if (trace == nullptr || size == 0) {
+ Printf(" <empty stack>\n\n");
+ return;
+ }
+ InternalScopedString frame_desc(GetPageSizeCached() * 2);
+ InternalScopedString dedup_token(GetPageSizeCached());
+ int dedup_frames = common_flags()->dedup_token_length;
+ uptr frame_num = 0;
+ for (uptr i = 0; i < size && trace[i]; i++) {
+ // PCs in stack traces are actually the return addresses, that is,
+ // addresses of the next instructions after the call.
+ uptr pc = GetPreviousInstructionPc(trace[i]);
+ SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ CHECK(frames);
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ frame_desc.clear();
+ RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
+ cur->info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ Printf("%s\n", frame_desc.data());
+ if (dedup_frames-- > 0) {
+ if (dedup_token.length())
+ dedup_token.append("--");
+ if (cur->info.function != nullptr)
+ dedup_token.append(cur->info.function);
+ }
+ }
+ frames->ClearAll();
+ }
+ // Always print a trailing empty line after stack trace.
+ Printf("\n");
+ if (dedup_token.length())
+ Printf("DEDUP_TOKEN: %s\n", dedup_token.data());
+}
+
+void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
+ uptr stack_top, uptr stack_bottom,
+ bool request_fast_unwind) {
+ // Ensures all call sites get what they requested.
+ CHECK_EQ(request_fast_unwind, WillUseFastUnwind(request_fast_unwind));
+ top_frame_bp = (max_depth > 0) ? bp : 0;
+ // Avoid doing any work for small max_depth.
+ if (max_depth == 0) {
+ size = 0;
+ return;
+ }
+ if (max_depth == 1) {
+ size = 1;
+ trace_buffer[0] = pc;
+ return;
+ }
+ if (!WillUseFastUnwind(request_fast_unwind)) {
+#if SANITIZER_CAN_SLOW_UNWIND
+ if (context)
+ UnwindSlow(pc, context, max_depth);
+ else
+ UnwindSlow(pc, max_depth);
+#else
+ UNREACHABLE("slow unwind requested but not available");
+#endif
+ } else {
+ UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
+ }
+}
+
+static int GetModuleAndOffsetForPc(uptr pc, char *module_name,
+ uptr module_name_len, uptr *pc_offset) {
+ const char *found_module_name = nullptr;
+ bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
+ pc, &found_module_name, pc_offset);
+
+ if (!ok) return false;
+
+ if (module_name && module_name_len) {
+ internal_strncpy(module_name, found_module_name, module_name_len);
+ module_name[module_name_len - 1] = '\x00';
+ }
+ return true;
+}
+
+} // namespace __sanitizer
+using namespace __sanitizer;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
+ uptr out_buf_size) {
+ if (!out_buf_size) return;
+ pc = StackTrace::GetPreviousInstructionPc(pc);
+ SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ if (!frame) {
+ internal_strncpy(out_buf, "<can't symbolize>", out_buf_size);
+ out_buf[out_buf_size - 1] = 0;
+ return;
+ }
+ InternalScopedString frame_desc(GetPageSizeCached());
+ uptr frame_num = 0;
+ // Reserve one byte for the final 0.
+ char *out_end = out_buf + out_buf_size - 1;
+ for (SymbolizedStack *cur = frame; cur && out_buf < out_end;
+ cur = cur->next) {
+ frame_desc.clear();
+ RenderFrame(&frame_desc, fmt, frame_num++, cur->info,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ if (!frame_desc.length())
+ continue;
+ // Reserve one byte for the terminating 0.
+ uptr n = out_end - out_buf - 1;
+ internal_strncpy(out_buf, frame_desc.data(), n);
+ out_buf += __sanitizer::Min<uptr>(n, frame_desc.length());
+ *out_buf++ = 0;
+ }
+ CHECK(out_buf <= out_end);
+ *out_buf = 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
+ char *out_buf, uptr out_buf_size) {
+ if (!out_buf_size) return;
+ out_buf[0] = 0;
+ DataInfo DI;
+ if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
+ InternalScopedString data_desc(GetPageSizeCached());
+ RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix);
+ internal_strncpy(out_buf, data_desc.data(), out_buf_size);
+ out_buf[out_buf_size - 1] = 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __sanitizer_get_module_and_offset_for_pc( // NOLINT
+ uptr pc, char *module_name, uptr module_name_len, uptr *pc_offset) {
+ return __sanitizer::GetModuleAndOffsetForPc(pc, module_name, module_name_len,
+ pc_offset);
+}
+} // extern "C"
+++ /dev/null
-//===-- sanitizer_common.cc -----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between sanitizers' run-time libraries.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_stacktrace_printer.h"
-#include "sanitizer_file.h"
-#include "sanitizer_fuchsia.h"
-
-namespace __sanitizer {
-
-// sanitizer_symbolizer_markup.cc implements these differently.
-#if !SANITIZER_SYMBOLIZER_MARKUP
-
-static const char *StripFunctionName(const char *function, const char *prefix) {
- if (!function) return nullptr;
- if (!prefix) return function;
- uptr prefix_len = internal_strlen(prefix);
- if (0 == internal_strncmp(function, prefix, prefix_len))
- return function + prefix_len;
- return function;
-}
-
-static const char *DemangleFunctionName(const char *function) {
- if (!function) return nullptr;
-
- // NetBSD uses indirection for old threading functions for historical reasons
- // The mangled names are internal implementation detail and should not be
- // exposed even in backtraces.
-#if SANITIZER_NETBSD
- if (!internal_strcmp(function, "__libc_mutex_init"))
- return "pthread_mutex_init";
- if (!internal_strcmp(function, "__libc_mutex_lock"))
- return "pthread_mutex_lock";
- if (!internal_strcmp(function, "__libc_mutex_trylock"))
- return "pthread_mutex_trylock";
- if (!internal_strcmp(function, "__libc_mutex_unlock"))
- return "pthread_mutex_unlock";
- if (!internal_strcmp(function, "__libc_mutex_destroy"))
- return "pthread_mutex_destroy";
- if (!internal_strcmp(function, "__libc_mutexattr_init"))
- return "pthread_mutexattr_init";
- if (!internal_strcmp(function, "__libc_mutexattr_settype"))
- return "pthread_mutexattr_settype";
- if (!internal_strcmp(function, "__libc_mutexattr_destroy"))
- return "pthread_mutexattr_destroy";
- if (!internal_strcmp(function, "__libc_cond_init"))
- return "pthread_cond_init";
- if (!internal_strcmp(function, "__libc_cond_signal"))
- return "pthread_cond_signal";
- if (!internal_strcmp(function, "__libc_cond_broadcast"))
- return "pthread_cond_broadcast";
- if (!internal_strcmp(function, "__libc_cond_wait"))
- return "pthread_cond_wait";
- if (!internal_strcmp(function, "__libc_cond_timedwait"))
- return "pthread_cond_timedwait";
- if (!internal_strcmp(function, "__libc_cond_destroy"))
- return "pthread_cond_destroy";
- if (!internal_strcmp(function, "__libc_rwlock_init"))
- return "pthread_rwlock_init";
- if (!internal_strcmp(function, "__libc_rwlock_rdlock"))
- return "pthread_rwlock_rdlock";
- if (!internal_strcmp(function, "__libc_rwlock_wrlock"))
- return "pthread_rwlock_wrlock";
- if (!internal_strcmp(function, "__libc_rwlock_tryrdlock"))
- return "pthread_rwlock_tryrdlock";
- if (!internal_strcmp(function, "__libc_rwlock_trywrlock"))
- return "pthread_rwlock_trywrlock";
- if (!internal_strcmp(function, "__libc_rwlock_unlock"))
- return "pthread_rwlock_unlock";
- if (!internal_strcmp(function, "__libc_rwlock_destroy"))
- return "pthread_rwlock_destroy";
- if (!internal_strcmp(function, "__libc_thr_keycreate"))
- return "pthread_key_create";
- if (!internal_strcmp(function, "__libc_thr_setspecific"))
- return "pthread_setspecific";
- if (!internal_strcmp(function, "__libc_thr_getspecific"))
- return "pthread_getspecific";
- if (!internal_strcmp(function, "__libc_thr_keydelete"))
- return "pthread_key_delete";
- if (!internal_strcmp(function, "__libc_thr_once"))
- return "pthread_once";
- if (!internal_strcmp(function, "__libc_thr_self"))
- return "pthread_self";
- if (!internal_strcmp(function, "__libc_thr_exit"))
- return "pthread_exit";
- if (!internal_strcmp(function, "__libc_thr_setcancelstate"))
- return "pthread_setcancelstate";
- if (!internal_strcmp(function, "__libc_thr_equal"))
- return "pthread_equal";
- if (!internal_strcmp(function, "__libc_thr_curcpu"))
- return "pthread_curcpu_np";
-#endif
-
- return function;
-}
-
-static const char kDefaultFormat[] = " #%n %p %F %L";
-
-void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
- const AddressInfo &info, bool vs_style,
- const char *strip_path_prefix, const char *strip_func_prefix) {
- if (0 == internal_strcmp(format, "DEFAULT"))
- format = kDefaultFormat;
- for (const char *p = format; *p != '\0'; p++) {
- if (*p != '%') {
- buffer->append("%c", *p);
- continue;
- }
- p++;
- switch (*p) {
- case '%':
- buffer->append("%%");
- break;
- // Frame number and all fields of AddressInfo structure.
- case 'n':
- buffer->append("%zu", frame_no);
- break;
- case 'p':
- buffer->append("0x%zx", info.address);
- break;
- case 'm':
- buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
- break;
- case 'o':
- buffer->append("0x%zx", info.module_offset);
- break;
- case 'f':
- buffer->append("%s",
- DemangleFunctionName(
- StripFunctionName(info.function, strip_func_prefix)));
- break;
- case 'q':
- buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
- ? info.function_offset
- : 0x0);
- break;
- case 's':
- buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
- break;
- case 'l':
- buffer->append("%d", info.line);
- break;
- case 'c':
- buffer->append("%d", info.column);
- break;
- // Smarter special cases.
- case 'F':
- // Function name and offset, if file is unknown.
- if (info.function) {
- buffer->append("in %s",
- DemangleFunctionName(
- StripFunctionName(info.function, strip_func_prefix)));
- if (!info.file && info.function_offset != AddressInfo::kUnknown)
- buffer->append("+0x%zx", info.function_offset);
- }
- break;
- case 'S':
- // File/line information.
- RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
- strip_path_prefix);
- break;
- case 'L':
- // Source location, or module location.
- if (info.file) {
- RenderSourceLocation(buffer, info.file, info.line, info.column,
- vs_style, strip_path_prefix);
- } else if (info.module) {
- RenderModuleLocation(buffer, info.module, info.module_offset,
- info.module_arch, strip_path_prefix);
- } else {
- buffer->append("(<unknown module>)");
- }
- break;
- case 'M':
- // Module basename and offset, or PC.
- if (info.address & kExternalPCBit)
- {} // There PCs are not meaningful.
- else if (info.module)
- // Always strip the module name for %M.
- RenderModuleLocation(buffer, StripModuleName(info.module),
- info.module_offset, info.module_arch, "");
- else
- buffer->append("(%p)", (void *)info.address);
- break;
- default:
- Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
- *p);
- Die();
- }
- }
-}
-
-void RenderData(InternalScopedString *buffer, const char *format,
- const DataInfo *DI, const char *strip_path_prefix) {
- for (const char *p = format; *p != '\0'; p++) {
- if (*p != '%') {
- buffer->append("%c", *p);
- continue;
- }
- p++;
- switch (*p) {
- case '%':
- buffer->append("%%");
- break;
- case 's':
- buffer->append("%s", StripPathPrefix(DI->file, strip_path_prefix));
- break;
- case 'l':
- buffer->append("%d", DI->line);
- break;
- case 'g':
- buffer->append("%s", DI->name);
- break;
- default:
- Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
- *p);
- Die();
- }
- }
-}
-
-#endif // !SANITIZER_SYMBOLIZER_MARKUP
-
-void RenderSourceLocation(InternalScopedString *buffer, const char *file,
- int line, int column, bool vs_style,
- const char *strip_path_prefix) {
- if (vs_style && line > 0) {
- buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
- if (column > 0)
- buffer->append(",%d", column);
- buffer->append(")");
- return;
- }
-
- buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
- if (line > 0) {
- buffer->append(":%d", line);
- if (column > 0)
- buffer->append(":%d", column);
- }
-}
-
-void RenderModuleLocation(InternalScopedString *buffer, const char *module,
- uptr offset, ModuleArch arch,
- const char *strip_path_prefix) {
- buffer->append("(%s", StripPathPrefix(module, strip_path_prefix));
- if (arch != kModuleArchUnknown) {
- buffer->append(":%s", ModuleArchToString(arch));
- }
- buffer->append("+0x%zx)", offset);
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_common.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizers' run-time libraries.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_file.h"
+#include "sanitizer_fuchsia.h"
+
+namespace __sanitizer {
+
+// sanitizer_symbolizer_markup.cpp implements these differently.
+#if !SANITIZER_SYMBOLIZER_MARKUP
+
+static const char *StripFunctionName(const char *function, const char *prefix) {
+ if (!function) return nullptr;
+ if (!prefix) return function;
+ uptr prefix_len = internal_strlen(prefix);
+ if (0 == internal_strncmp(function, prefix, prefix_len))
+ return function + prefix_len;
+ return function;
+}
+
+static const char *DemangleFunctionName(const char *function) {
+ if (!function) return nullptr;
+
+ // NetBSD uses indirection for old threading functions for historical reasons
+ // The mangled names are internal implementation detail and should not be
+ // exposed even in backtraces.
+#if SANITIZER_NETBSD
+ if (!internal_strcmp(function, "__libc_mutex_init"))
+ return "pthread_mutex_init";
+ if (!internal_strcmp(function, "__libc_mutex_lock"))
+ return "pthread_mutex_lock";
+ if (!internal_strcmp(function, "__libc_mutex_trylock"))
+ return "pthread_mutex_trylock";
+ if (!internal_strcmp(function, "__libc_mutex_unlock"))
+ return "pthread_mutex_unlock";
+ if (!internal_strcmp(function, "__libc_mutex_destroy"))
+ return "pthread_mutex_destroy";
+ if (!internal_strcmp(function, "__libc_mutexattr_init"))
+ return "pthread_mutexattr_init";
+ if (!internal_strcmp(function, "__libc_mutexattr_settype"))
+ return "pthread_mutexattr_settype";
+ if (!internal_strcmp(function, "__libc_mutexattr_destroy"))
+ return "pthread_mutexattr_destroy";
+ if (!internal_strcmp(function, "__libc_cond_init"))
+ return "pthread_cond_init";
+ if (!internal_strcmp(function, "__libc_cond_signal"))
+ return "pthread_cond_signal";
+ if (!internal_strcmp(function, "__libc_cond_broadcast"))
+ return "pthread_cond_broadcast";
+ if (!internal_strcmp(function, "__libc_cond_wait"))
+ return "pthread_cond_wait";
+ if (!internal_strcmp(function, "__libc_cond_timedwait"))
+ return "pthread_cond_timedwait";
+ if (!internal_strcmp(function, "__libc_cond_destroy"))
+ return "pthread_cond_destroy";
+ if (!internal_strcmp(function, "__libc_rwlock_init"))
+ return "pthread_rwlock_init";
+ if (!internal_strcmp(function, "__libc_rwlock_rdlock"))
+ return "pthread_rwlock_rdlock";
+ if (!internal_strcmp(function, "__libc_rwlock_wrlock"))
+ return "pthread_rwlock_wrlock";
+ if (!internal_strcmp(function, "__libc_rwlock_tryrdlock"))
+ return "pthread_rwlock_tryrdlock";
+ if (!internal_strcmp(function, "__libc_rwlock_trywrlock"))
+ return "pthread_rwlock_trywrlock";
+ if (!internal_strcmp(function, "__libc_rwlock_unlock"))
+ return "pthread_rwlock_unlock";
+ if (!internal_strcmp(function, "__libc_rwlock_destroy"))
+ return "pthread_rwlock_destroy";
+ if (!internal_strcmp(function, "__libc_thr_keycreate"))
+ return "pthread_key_create";
+ if (!internal_strcmp(function, "__libc_thr_setspecific"))
+ return "pthread_setspecific";
+ if (!internal_strcmp(function, "__libc_thr_getspecific"))
+ return "pthread_getspecific";
+ if (!internal_strcmp(function, "__libc_thr_keydelete"))
+ return "pthread_key_delete";
+ if (!internal_strcmp(function, "__libc_thr_once"))
+ return "pthread_once";
+ if (!internal_strcmp(function, "__libc_thr_self"))
+ return "pthread_self";
+ if (!internal_strcmp(function, "__libc_thr_exit"))
+ return "pthread_exit";
+ if (!internal_strcmp(function, "__libc_thr_setcancelstate"))
+ return "pthread_setcancelstate";
+ if (!internal_strcmp(function, "__libc_thr_equal"))
+ return "pthread_equal";
+ if (!internal_strcmp(function, "__libc_thr_curcpu"))
+ return "pthread_curcpu_np";
+ if (!internal_strcmp(function, "__libc_thr_sigsetmask"))
+ return "pthread_sigmask";
+#endif
+
+ return function;
+}
+
+static const char kDefaultFormat[] = " #%n %p %F %L";
+
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix, const char *strip_func_prefix) {
+ if (0 == internal_strcmp(format, "DEFAULT"))
+ format = kDefaultFormat;
+ for (const char *p = format; *p != '\0'; p++) {
+ if (*p != '%') {
+ buffer->append("%c", *p);
+ continue;
+ }
+ p++;
+ switch (*p) {
+ case '%':
+ buffer->append("%%");
+ break;
+ // Frame number and all fields of AddressInfo structure.
+ case 'n':
+ buffer->append("%zu", frame_no);
+ break;
+ case 'p':
+ buffer->append("0x%zx", info.address);
+ break;
+ case 'm':
+ buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
+ break;
+ case 'o':
+ buffer->append("0x%zx", info.module_offset);
+ break;
+ case 'f':
+ buffer->append("%s",
+ DemangleFunctionName(
+ StripFunctionName(info.function, strip_func_prefix)));
+ break;
+ case 'q':
+ buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
+ ? info.function_offset
+ : 0x0);
+ break;
+ case 's':
+ buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
+ break;
+ case 'l':
+ buffer->append("%d", info.line);
+ break;
+ case 'c':
+ buffer->append("%d", info.column);
+ break;
+ // Smarter special cases.
+ case 'F':
+ // Function name and offset, if file is unknown.
+ if (info.function) {
+ buffer->append("in %s",
+ DemangleFunctionName(
+ StripFunctionName(info.function, strip_func_prefix)));
+ if (!info.file && info.function_offset != AddressInfo::kUnknown)
+ buffer->append("+0x%zx", info.function_offset);
+ }
+ break;
+ case 'S':
+ // File/line information.
+ RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style,
+ strip_path_prefix);
+ break;
+ case 'L':
+ // Source location, or module location.
+ if (info.file) {
+ RenderSourceLocation(buffer, info.file, info.line, info.column,
+ vs_style, strip_path_prefix);
+ } else if (info.module) {
+ RenderModuleLocation(buffer, info.module, info.module_offset,
+ info.module_arch, strip_path_prefix);
+ } else {
+ buffer->append("(<unknown module>)");
+ }
+ break;
+ case 'M':
+ // Module basename and offset, or PC.
+ if (info.address & kExternalPCBit)
+ {} // There PCs are not meaningful.
+ else if (info.module)
+ // Always strip the module name for %M.
+ RenderModuleLocation(buffer, StripModuleName(info.module),
+ info.module_offset, info.module_arch, "");
+ else
+ buffer->append("(%p)", (void *)info.address);
+ break;
+ default:
+ Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
+ *p);
+ Die();
+ }
+ }
+}
+
+void RenderData(InternalScopedString *buffer, const char *format,
+ const DataInfo *DI, const char *strip_path_prefix) {
+ for (const char *p = format; *p != '\0'; p++) {
+ if (*p != '%') {
+ buffer->append("%c", *p);
+ continue;
+ }
+ p++;
+ switch (*p) {
+ case '%':
+ buffer->append("%%");
+ break;
+ case 's':
+ buffer->append("%s", StripPathPrefix(DI->file, strip_path_prefix));
+ break;
+ case 'l':
+ buffer->append("%d", DI->line);
+ break;
+ case 'g':
+ buffer->append("%s", DI->name);
+ break;
+ default:
+ Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p,
+ *p);
+ Die();
+ }
+ }
+}
+
+#endif // !SANITIZER_SYMBOLIZER_MARKUP
+
+void RenderSourceLocation(InternalScopedString *buffer, const char *file,
+ int line, int column, bool vs_style,
+ const char *strip_path_prefix) {
+ if (vs_style && line > 0) {
+ buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
+ if (column > 0)
+ buffer->append(",%d", column);
+ buffer->append(")");
+ return;
+ }
+
+ buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
+ if (line > 0) {
+ buffer->append(":%d", line);
+ if (column > 0)
+ buffer->append(":%d", column);
+ }
+}
+
+void RenderModuleLocation(InternalScopedString *buffer, const char *module,
+ uptr offset, ModuleArch arch,
+ const char *strip_path_prefix) {
+ buffer->append("(%s", StripPathPrefix(module, strip_path_prefix));
+ if (arch != kModuleArchUnknown) {
+ buffer->append(":%s", ModuleArchToString(arch));
+ }
+ buffer->append("+0x%zx)", offset);
+}
+
+} // namespace __sanitizer
//===-- sanitizer_stacktrace_printer.h --------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_stacktrace_sparc.cc -------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//
-// Implemention of fast stack unwinding for Sparc.
-//===----------------------------------------------------------------------===//
-
-#if defined(__sparc__)
-
-#if defined(__arch64__) || defined(__sparcv9)
-#define STACK_BIAS 2047
-#else
-#define STACK_BIAS 0
-#endif
-
-#include "sanitizer_common.h"
-#include "sanitizer_stacktrace.h"
-
-namespace __sanitizer {
-
-void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
- uptr stack_bottom, u32 max_depth) {
- const uptr kPageSize = GetPageSizeCached();
- CHECK_GE(max_depth, 2);
-#if defined(__GNUC__)
- // __builtin_return_address returns the address of the call instruction
- // on the SPARC and not the return address, so we need to compensate.
- trace_buffer[0] = GetNextInstructionPc(pc);
-#else
- trace_buffer[0] = pc;
-#endif
- size = 1;
- if (stack_top < 4096) return; // Sanity check for stack top.
- // Flush register windows to memory
-#if defined(__sparc_v9__) || defined(__sparcv9__) || defined(__sparcv9)
- asm volatile("flushw" ::: "memory");
-#else
- asm volatile("ta 3" ::: "memory");
-#endif
- // On the SPARC, the return address is not in the frame, it is in a
- // register. There is no way to access it off of the current frame
- // pointer, but it can be accessed off the previous frame pointer by
- // reading the value from the register window save area.
- uptr prev_bp = GET_CURRENT_FRAME();
- uptr next_bp = prev_bp;
- unsigned int i = 0;
- while (next_bp != bp &&
- IsAligned(next_bp, sizeof(uhwptr)) &&
- i++ < 8) {
- prev_bp = next_bp;
- next_bp = (uptr) ((uhwptr *) next_bp)[14] + STACK_BIAS;
- }
- if (next_bp == bp)
- bp = prev_bp;
- // Lowest possible address that makes sense as the next frame pointer.
- // Goes up as we walk the stack.
- uptr bottom = stack_bottom;
- // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
- while (IsValidFrame(bp, stack_top, bottom) &&
- IsAligned(bp, sizeof(uhwptr)) &&
- size < max_depth) {
- uhwptr pc1 = ((uhwptr *)bp)[15];
- // Let's assume that any pointer in the 0th page is invalid and
- // stop unwinding here. If we're adding support for a platform
- // where this isn't true, we need to reconsider this check.
- if (pc1 < kPageSize)
- break;
- if (pc1 != pc) {
- // %o7 contains the address of the call instruction and not the
- // return address, so we need to compensate.
- trace_buffer[size++] = GetNextInstructionPc((uptr) pc1);
- }
- bottom = bp;
- bp = (uptr) ((uhwptr *) bp)[14] + STACK_BIAS;
- }
-}
-
-} // namespace __sanitizer
-
-#endif // !defined(__sparc__)
--- /dev/null
+//===-- sanitizer_stacktrace_sparc.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//
+// Implemention of fast stack unwinding for Sparc.
+//===----------------------------------------------------------------------===//
+
+#if defined(__sparc__)
+
+#if defined(__arch64__) || defined(__sparcv9)
+#define STACK_BIAS 2047
+#else
+#define STACK_BIAS 0
+#endif
+
+#include "sanitizer_common.h"
+#include "sanitizer_stacktrace.h"
+
+namespace __sanitizer {
+
+void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top,
+ uptr stack_bottom, u32 max_depth) {
+ // TODO(yln): add arg sanity check for stack_top/stack_bottom
+ CHECK_GE(max_depth, 2);
+ const uptr kPageSize = GetPageSizeCached();
+#if defined(__GNUC__)
+ // __builtin_return_address returns the address of the call instruction
+ // on the SPARC and not the return address, so we need to compensate.
+ trace_buffer[0] = GetNextInstructionPc(pc);
+#else
+ trace_buffer[0] = pc;
+#endif
+ size = 1;
+ if (stack_top < 4096) return; // Sanity check for stack top.
+ // Flush register windows to memory
+#if defined(__sparc_v9__) || defined(__sparcv9__) || defined(__sparcv9)
+ asm volatile("flushw" ::: "memory");
+#else
+ asm volatile("ta 3" ::: "memory");
+#endif
+ // On the SPARC, the return address is not in the frame, it is in a
+ // register. There is no way to access it off of the current frame
+ // pointer, but it can be accessed off the previous frame pointer by
+ // reading the value from the register window save area.
+ uptr prev_bp = GET_CURRENT_FRAME();
+ uptr next_bp = prev_bp;
+ unsigned int i = 0;
+ while (next_bp != bp && IsAligned(next_bp, sizeof(uhwptr)) && i++ < 8) {
+ prev_bp = next_bp;
+ next_bp = (uptr)((uhwptr *)next_bp)[14] + STACK_BIAS;
+ }
+ if (next_bp == bp)
+ bp = prev_bp;
+ // Lowest possible address that makes sense as the next frame pointer.
+ // Goes up as we walk the stack.
+ uptr bottom = stack_bottom;
+ // Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
+ while (IsValidFrame(bp, stack_top, bottom) && IsAligned(bp, sizeof(uhwptr)) &&
+ size < max_depth) {
+ uhwptr pc1 = ((uhwptr *)bp)[15];
+ // Let's assume that any pointer in the 0th page is invalid and
+ // stop unwinding here. If we're adding support for a platform
+ // where this isn't true, we need to reconsider this check.
+ if (pc1 < kPageSize)
+ break;
+ if (pc1 != pc) {
+ // %o7 contains the address of the call instruction and not the
+ // return address, so we need to compensate.
+ trace_buffer[size++] = GetNextInstructionPc((uptr)pc1);
+ }
+ bottom = bp;
+ bp = (uptr)((uhwptr *)bp)[14] + STACK_BIAS;
+ }
+}
+
+} // namespace __sanitizer
+
+#endif // !defined(__sparc__)
//===-- sanitizer_stoptheworld.h --------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_stoptheworld_linux_libcdep.cc ---------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// See sanitizer_stoptheworld.h for details.
-// This implementation was inspired by Markus Gutschke's linuxthreads.cc.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
- defined(__aarch64__) || defined(__powerpc64__) || \
- defined(__s390__) || defined(__i386__) || \
- defined(__arm__))
-
-#include "sanitizer_stoptheworld.h"
-
-#include "sanitizer_platform_limits_posix.h"
-#include "sanitizer_atomic.h"
-
-#include <errno.h>
-#include <sched.h> // for CLONE_* definitions
-#include <stddef.h>
-#include <sys/prctl.h> // for PR_* definitions
-#include <sys/ptrace.h> // for PTRACE_* definitions
-#include <sys/types.h> // for pid_t
-#include <sys/uio.h> // for iovec
-#include <elf.h> // for NT_PRSTATUS
-#if defined(__aarch64__) && !SANITIZER_ANDROID
-// GLIBC 2.20+ sys/user does not include asm/ptrace.h
-# include <asm/ptrace.h>
-#endif
-#include <sys/user.h> // for user_regs_struct
-#if SANITIZER_ANDROID && SANITIZER_MIPS
-# include <asm/reg.h> // for mips SP register in sys/user.h
-#endif
-#include <sys/wait.h> // for signal-related stuff
-
-#ifdef sa_handler
-# undef sa_handler
-#endif
-
-#ifdef sa_sigaction
-# undef sa_sigaction
-#endif
-
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_placement_new.h"
-
-// Sufficiently old kernel headers don't provide this value, but we can still
-// call prctl with it. If the runtime kernel is new enough, the prctl call will
-// have the desired effect; if the kernel is too old, the call will error and we
-// can ignore said error.
-#ifndef PR_SET_PTRACER
-#define PR_SET_PTRACER 0x59616d61
-#endif
-
-// This module works by spawning a Linux task which then attaches to every
-// thread in the caller process with ptrace. This suspends the threads, and
-// PTRACE_GETREGS can then be used to obtain their register state. The callback
-// supplied to StopTheWorld() is run in the tracer task while the threads are
-// suspended.
-// The tracer task must be placed in a different thread group for ptrace to
-// work, so it cannot be spawned as a pthread. Instead, we use the low-level
-// clone() interface (we want to share the address space with the caller
-// process, so we prefer clone() over fork()).
-//
-// We don't use any libc functions, relying instead on direct syscalls. There
-// are two reasons for this:
-// 1. calling a library function while threads are suspended could cause a
-// deadlock, if one of the treads happens to be holding a libc lock;
-// 2. it's generally not safe to call libc functions from the tracer task,
-// because clone() does not set up a thread-local storage for it. Any
-// thread-local variables used by libc will be shared between the tracer task
-// and the thread which spawned it.
-
-namespace __sanitizer {
-
-class SuspendedThreadsListLinux : public SuspendedThreadsList {
- public:
- SuspendedThreadsListLinux() { thread_ids_.reserve(1024); }
-
- tid_t GetThreadID(uptr index) const;
- uptr ThreadCount() const;
- bool ContainsTid(tid_t thread_id) const;
- void Append(tid_t tid);
-
- PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
- uptr *sp) const;
- uptr RegisterCount() const;
-
- private:
- InternalMmapVector<tid_t> thread_ids_;
-};
-
-// Structure for passing arguments into the tracer thread.
-struct TracerThreadArgument {
- StopTheWorldCallback callback;
- void *callback_argument;
- // The tracer thread waits on this mutex while the parent finishes its
- // preparations.
- BlockingMutex mutex;
- // Tracer thread signals its completion by setting done.
- atomic_uintptr_t done;
- uptr parent_pid;
-};
-
-// This class handles thread suspending/unsuspending in the tracer thread.
-class ThreadSuspender {
- public:
- explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
- : arg(arg)
- , pid_(pid) {
- CHECK_GE(pid, 0);
- }
- bool SuspendAllThreads();
- void ResumeAllThreads();
- void KillAllThreads();
- SuspendedThreadsListLinux &suspended_threads_list() {
- return suspended_threads_list_;
- }
- TracerThreadArgument *arg;
- private:
- SuspendedThreadsListLinux suspended_threads_list_;
- pid_t pid_;
- bool SuspendThread(tid_t thread_id);
-};
-
-bool ThreadSuspender::SuspendThread(tid_t tid) {
- // Are we already attached to this thread?
- // Currently this check takes linear time, however the number of threads is
- // usually small.
- if (suspended_threads_list_.ContainsTid(tid)) return false;
- int pterrno;
- if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
- &pterrno)) {
- // Either the thread is dead, or something prevented us from attaching.
- // Log this event and move on.
- VReport(1, "Could not attach to thread %zu (errno %d).\n", (uptr)tid,
- pterrno);
- return false;
- } else {
- VReport(2, "Attached to thread %zu.\n", (uptr)tid);
- // The thread is not guaranteed to stop before ptrace returns, so we must
- // wait on it. Note: if the thread receives a signal concurrently,
- // we can get notification about the signal before notification about stop.
- // In such case we need to forward the signal to the thread, otherwise
- // the signal will be missed (as we do PTRACE_DETACH with arg=0) and
- // any logic relying on signals will break. After forwarding we need to
- // continue to wait for stopping, because the thread is not stopped yet.
- // We do ignore delivery of SIGSTOP, because we want to make stop-the-world
- // as invisible as possible.
- for (;;) {
- int status;
- uptr waitpid_status;
- HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL));
- int wperrno;
- if (internal_iserror(waitpid_status, &wperrno)) {
- // Got a ECHILD error. I don't think this situation is possible, but it
- // doesn't hurt to report it.
- VReport(1, "Waiting on thread %zu failed, detaching (errno %d).\n",
- (uptr)tid, wperrno);
- internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
- return false;
- }
- if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
- internal_ptrace(PTRACE_CONT, tid, nullptr,
- (void*)(uptr)WSTOPSIG(status));
- continue;
- }
- break;
- }
- suspended_threads_list_.Append(tid);
- return true;
- }
-}
-
-void ThreadSuspender::ResumeAllThreads() {
- for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) {
- pid_t tid = suspended_threads_list_.GetThreadID(i);
- int pterrno;
- if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr),
- &pterrno)) {
- VReport(2, "Detached from thread %d.\n", tid);
- } else {
- // Either the thread is dead, or we are already detached.
- // The latter case is possible, for instance, if this function was called
- // from a signal handler.
- VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno);
- }
- }
-}
-
-void ThreadSuspender::KillAllThreads() {
- for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++)
- internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),
- nullptr, nullptr);
-}
-
-bool ThreadSuspender::SuspendAllThreads() {
- ThreadLister thread_lister(pid_);
- bool retry = true;
- InternalMmapVector<tid_t> threads;
- threads.reserve(128);
- for (int i = 0; i < 30 && retry; ++i) {
- retry = false;
- switch (thread_lister.ListThreads(&threads)) {
- case ThreadLister::Error:
- ResumeAllThreads();
- return false;
- case ThreadLister::Incomplete:
- retry = true;
- break;
- case ThreadLister::Ok:
- break;
- }
- for (tid_t tid : threads)
- if (SuspendThread(tid))
- retry = true;
- };
- return suspended_threads_list_.ThreadCount();
-}
-
-// Pointer to the ThreadSuspender instance for use in signal handler.
-static ThreadSuspender *thread_suspender_instance = nullptr;
-
-// Synchronous signals that should not be blocked.
-static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
- SIGXCPU, SIGXFSZ };
-
-static void TracerThreadDieCallback() {
- // Generally a call to Die() in the tracer thread should be fatal to the
- // parent process as well, because they share the address space.
- // This really only works correctly if all the threads are suspended at this
- // point. So we correctly handle calls to Die() from within the callback, but
- // not those that happen before or after the callback. Hopefully there aren't
- // a lot of opportunities for that to happen...
- ThreadSuspender *inst = thread_suspender_instance;
- if (inst && stoptheworld_tracer_pid == internal_getpid()) {
- inst->KillAllThreads();
- thread_suspender_instance = nullptr;
- }
-}
-
-// Signal handler to wake up suspended threads when the tracer thread dies.
-static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
- void *uctx) {
- SignalContext ctx(siginfo, uctx);
- Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
- ctx.addr, ctx.pc, ctx.sp);
- ThreadSuspender *inst = thread_suspender_instance;
- if (inst) {
- if (signum == SIGABRT)
- inst->KillAllThreads();
- else
- inst->ResumeAllThreads();
- RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
- thread_suspender_instance = nullptr;
- atomic_store(&inst->arg->done, 1, memory_order_relaxed);
- }
- internal__exit((signum == SIGABRT) ? 1 : 2);
-}
-
-// Size of alternative stack for signal handlers in the tracer thread.
-static const int kHandlerStackSize = 8192;
-
-// This function will be run as a cloned task.
-static int TracerThread(void* argument) {
- TracerThreadArgument *tracer_thread_argument =
- (TracerThreadArgument *)argument;
-
- internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
- // Check if parent is already dead.
- if (internal_getppid() != tracer_thread_argument->parent_pid)
- internal__exit(4);
-
- // Wait for the parent thread to finish preparations.
- tracer_thread_argument->mutex.Lock();
- tracer_thread_argument->mutex.Unlock();
-
- RAW_CHECK(AddDieCallback(TracerThreadDieCallback));
-
- ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
- // Global pointer for the signal handler.
- thread_suspender_instance = &thread_suspender;
-
- // Alternate stack for signal handling.
- InternalMmapVector<char> handler_stack_memory(kHandlerStackSize);
- stack_t handler_stack;
- internal_memset(&handler_stack, 0, sizeof(handler_stack));
- handler_stack.ss_sp = handler_stack_memory.data();
- handler_stack.ss_size = kHandlerStackSize;
- internal_sigaltstack(&handler_stack, nullptr);
-
- // Install our handler for synchronous signals. Other signals should be
- // blocked by the mask we inherited from the parent thread.
- for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
- __sanitizer_sigaction act;
- internal_memset(&act, 0, sizeof(act));
- act.sigaction = TracerThreadSignalHandler;
- act.sa_flags = SA_ONSTACK | SA_SIGINFO;
- internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
- }
-
- int exit_code = 0;
- if (!thread_suspender.SuspendAllThreads()) {
- VReport(1, "Failed suspending threads.\n");
- exit_code = 3;
- } else {
- tracer_thread_argument->callback(thread_suspender.suspended_threads_list(),
- tracer_thread_argument->callback_argument);
- thread_suspender.ResumeAllThreads();
- exit_code = 0;
- }
- RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
- thread_suspender_instance = nullptr;
- atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
- return exit_code;
-}
-
-class ScopedStackSpaceWithGuard {
- public:
- explicit ScopedStackSpaceWithGuard(uptr stack_size) {
- stack_size_ = stack_size;
- guard_size_ = GetPageSizeCached();
- // FIXME: Omitting MAP_STACK here works in current kernels but might break
- // in the future.
- guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
- "ScopedStackWithGuard");
- CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
- }
- ~ScopedStackSpaceWithGuard() {
- UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
- }
- void *Bottom() const {
- return (void *)(guard_start_ + stack_size_ + guard_size_);
- }
-
- private:
- uptr stack_size_;
- uptr guard_size_;
- uptr guard_start_;
-};
-
-// We have a limitation on the stack frame size, so some stuff had to be moved
-// into globals.
-static __sanitizer_sigset_t blocked_sigset;
-static __sanitizer_sigset_t old_sigset;
-
-class StopTheWorldScope {
- public:
- StopTheWorldScope() {
- // Make this process dumpable. Processes that are not dumpable cannot be
- // attached to.
- process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
- if (!process_was_dumpable_)
- internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
- }
-
- ~StopTheWorldScope() {
- // Restore the dumpable flag.
- if (!process_was_dumpable_)
- internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
- }
-
- private:
- int process_was_dumpable_;
-};
-
-// When sanitizer output is being redirected to file (i.e. by using log_path),
-// the tracer should write to the parent's log instead of trying to open a new
-// file. Alert the logging code to the fact that we have a tracer.
-struct ScopedSetTracerPID {
- explicit ScopedSetTracerPID(uptr tracer_pid) {
- stoptheworld_tracer_pid = tracer_pid;
- stoptheworld_tracer_ppid = internal_getpid();
- }
- ~ScopedSetTracerPID() {
- stoptheworld_tracer_pid = 0;
- stoptheworld_tracer_ppid = 0;
- }
-};
-
-void StopTheWorld(StopTheWorldCallback callback, void *argument) {
- StopTheWorldScope in_stoptheworld;
- // Prepare the arguments for TracerThread.
- struct TracerThreadArgument tracer_thread_argument;
- tracer_thread_argument.callback = callback;
- tracer_thread_argument.callback_argument = argument;
- tracer_thread_argument.parent_pid = internal_getpid();
- atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
- const uptr kTracerStackSize = 2 * 1024 * 1024;
- ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
- // Block the execution of TracerThread until after we have set ptrace
- // permissions.
- tracer_thread_argument.mutex.Lock();
- // Signal handling story.
- // We don't want async signals to be delivered to the tracer thread,
- // so we block all async signals before creating the thread. An async signal
- // handler can temporary modify errno, which is shared with this thread.
- // We ought to use pthread_sigmask here, because sigprocmask has undefined
- // behavior in multithreaded programs. However, on linux sigprocmask is
- // equivalent to pthread_sigmask with the exception that pthread_sigmask
- // does not allow to block some signals used internally in pthread
- // implementation. We are fine with blocking them here, we are really not
- // going to pthread_cancel the thread.
- // The tracer thread should not raise any synchronous signals. But in case it
- // does, we setup a special handler for sync signals that properly kills the
- // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers
- // in the tracer thread won't interfere with user program. Double note: if a
- // user does something along the lines of 'kill -11 pid', that can kill the
- // process even if user setup own handler for SEGV.
- // Thing to watch out for: this code should not change behavior of user code
- // in any observable way. In particular it should not override user signal
- // handlers.
- internal_sigfillset(&blocked_sigset);
- for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
- internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
- int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
- CHECK_EQ(rv, 0);
- uptr tracer_pid = internal_clone(
- TracerThread, tracer_stack.Bottom(),
- CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
- &tracer_thread_argument, nullptr /* parent_tidptr */,
- nullptr /* newtls */, nullptr /* child_tidptr */);
- internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
- int local_errno = 0;
- if (internal_iserror(tracer_pid, &local_errno)) {
- VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
- tracer_thread_argument.mutex.Unlock();
- } else {
- ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid);
- // On some systems we have to explicitly declare that we want to be traced
- // by the tracer thread.
- internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);
- // Allow the tracer thread to start.
- tracer_thread_argument.mutex.Unlock();
- // NOTE: errno is shared between this thread and the tracer thread.
- // internal_waitpid() may call syscall() which can access/spoil errno,
- // so we can't call it now. Instead we for the tracer thread to finish using
- // the spin loop below. Man page for sched_yield() says "In the Linux
- // implementation, sched_yield() always succeeds", so let's hope it does not
- // spoil errno. Note that this spin loop runs only for brief periods before
- // the tracer thread has suspended us and when it starts unblocking threads.
- while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
- sched_yield();
- // Now the tracer thread is about to exit and does not touch errno,
- // wait for it.
- for (;;) {
- uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL);
- if (!internal_iserror(waitpid_status, &local_errno))
- break;
- if (local_errno == EINTR)
- continue;
- VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
- local_errno);
- break;
- }
- }
-}
-
-// Platform-specific methods from SuspendedThreadsList.
-#if SANITIZER_ANDROID && defined(__arm__)
-typedef pt_regs regs_struct;
-#define REG_SP ARM_sp
-
-#elif SANITIZER_LINUX && defined(__arm__)
-typedef user_regs regs_struct;
-#define REG_SP uregs[13]
-
-#elif defined(__i386__) || defined(__x86_64__)
-typedef user_regs_struct regs_struct;
-#if defined(__i386__)
-#define REG_SP esp
-#else
-#define REG_SP rsp
-#endif
-
-#elif defined(__powerpc__) || defined(__powerpc64__)
-typedef pt_regs regs_struct;
-#define REG_SP gpr[PT_R1]
-
-#elif defined(__mips__)
-typedef struct user regs_struct;
-# if SANITIZER_ANDROID
-# define REG_SP regs[EF_R29]
-# else
-# define REG_SP regs[EF_REG29]
-# endif
-
-#elif defined(__aarch64__)
-typedef struct user_pt_regs regs_struct;
-#define REG_SP sp
-#define ARCH_IOVEC_FOR_GETREGSET
-
-#elif defined(__s390__)
-typedef _user_regs_struct regs_struct;
-#define REG_SP gprs[15]
-#define ARCH_IOVEC_FOR_GETREGSET
-
-#else
-#error "Unsupported architecture"
-#endif // SANITIZER_ANDROID && defined(__arm__)
-
-tid_t SuspendedThreadsListLinux::GetThreadID(uptr index) const {
- CHECK_LT(index, thread_ids_.size());
- return thread_ids_[index];
-}
-
-uptr SuspendedThreadsListLinux::ThreadCount() const {
- return thread_ids_.size();
-}
-
-bool SuspendedThreadsListLinux::ContainsTid(tid_t thread_id) const {
- for (uptr i = 0; i < thread_ids_.size(); i++) {
- if (thread_ids_[i] == thread_id) return true;
- }
- return false;
-}
-
-void SuspendedThreadsListLinux::Append(tid_t tid) {
- thread_ids_.push_back(tid);
-}
-
-PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP(
- uptr index, uptr *buffer, uptr *sp) const {
- pid_t tid = GetThreadID(index);
- regs_struct regs;
- int pterrno;
-#ifdef ARCH_IOVEC_FOR_GETREGSET
- struct iovec regset_io;
- regset_io.iov_base = ®s;
- regset_io.iov_len = sizeof(regs_struct);
- bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid,
- (void*)NT_PRSTATUS, (void*)®set_io),
- &pterrno);
-#else
- bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr,
- ®s), &pterrno);
-#endif
- if (isErr) {
- VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,
- pterrno);
- // ESRCH means that the given thread is not suspended or already dead.
- // Therefore it's unsafe to inspect its data (e.g. walk through stack) and
- // we should notify caller about this.
- return pterrno == ESRCH ? REGISTERS_UNAVAILABLE_FATAL
- : REGISTERS_UNAVAILABLE;
- }
-
- *sp = regs.REG_SP;
- internal_memcpy(buffer, ®s, sizeof(regs));
- return REGISTERS_AVAILABLE;
-}
-
-uptr SuspendedThreadsListLinux::RegisterCount() const {
- return sizeof(regs_struct) / sizeof(uptr);
-}
-} // namespace __sanitizer
-
-#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
- // || defined(__aarch64__) || defined(__powerpc64__)
- // || defined(__s390__) || defined(__i386__) || defined(__arm__)
--- /dev/null
+//===-- sanitizer_stoptheworld_linux_libcdep.cpp --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// See sanitizer_stoptheworld.h for details.
+// This implementation was inspired by Markus Gutschke's linuxthreads.cc.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__) || \
+ defined(__aarch64__) || defined(__powerpc64__) || \
+ defined(__s390__) || defined(__i386__) || \
+ defined(__arm__))
+
+#include "sanitizer_stoptheworld.h"
+
+#include "sanitizer_platform_limits_posix.h"
+#include "sanitizer_atomic.h"
+
+#include <errno.h>
+#include <sched.h> // for CLONE_* definitions
+#include <stddef.h>
+#include <sys/prctl.h> // for PR_* definitions
+#include <sys/ptrace.h> // for PTRACE_* definitions
+#include <sys/types.h> // for pid_t
+#include <sys/uio.h> // for iovec
+#include <elf.h> // for NT_PRSTATUS
+#if defined(__aarch64__) && !SANITIZER_ANDROID
+// GLIBC 2.20+ sys/user does not include asm/ptrace.h
+# include <asm/ptrace.h>
+#endif
+#include <sys/user.h> // for user_regs_struct
+#if SANITIZER_ANDROID && SANITIZER_MIPS
+# include <asm/reg.h> // for mips SP register in sys/user.h
+#endif
+#include <sys/wait.h> // for signal-related stuff
+
+#ifdef sa_handler
+# undef sa_handler
+#endif
+
+#ifdef sa_sigaction
+# undef sa_sigaction
+#endif
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+
+// Sufficiently old kernel headers don't provide this value, but we can still
+// call prctl with it. If the runtime kernel is new enough, the prctl call will
+// have the desired effect; if the kernel is too old, the call will error and we
+// can ignore said error.
+#ifndef PR_SET_PTRACER
+#define PR_SET_PTRACER 0x59616d61
+#endif
+
+// This module works by spawning a Linux task which then attaches to every
+// thread in the caller process with ptrace. This suspends the threads, and
+// PTRACE_GETREGS can then be used to obtain their register state. The callback
+// supplied to StopTheWorld() is run in the tracer task while the threads are
+// suspended.
+// The tracer task must be placed in a different thread group for ptrace to
+// work, so it cannot be spawned as a pthread. Instead, we use the low-level
+// clone() interface (we want to share the address space with the caller
+// process, so we prefer clone() over fork()).
+//
+// We don't use any libc functions, relying instead on direct syscalls. There
+// are two reasons for this:
+// 1. calling a library function while threads are suspended could cause a
+// deadlock, if one of the treads happens to be holding a libc lock;
+// 2. it's generally not safe to call libc functions from the tracer task,
+// because clone() does not set up a thread-local storage for it. Any
+// thread-local variables used by libc will be shared between the tracer task
+// and the thread which spawned it.
+
+namespace __sanitizer {
+
+class SuspendedThreadsListLinux : public SuspendedThreadsList {
+ public:
+ SuspendedThreadsListLinux() { thread_ids_.reserve(1024); }
+
+ tid_t GetThreadID(uptr index) const;
+ uptr ThreadCount() const;
+ bool ContainsTid(tid_t thread_id) const;
+ void Append(tid_t tid);
+
+ PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
+ uptr *sp) const;
+ uptr RegisterCount() const;
+
+ private:
+ InternalMmapVector<tid_t> thread_ids_;
+};
+
+// Structure for passing arguments into the tracer thread.
+struct TracerThreadArgument {
+ StopTheWorldCallback callback;
+ void *callback_argument;
+ // The tracer thread waits on this mutex while the parent finishes its
+ // preparations.
+ BlockingMutex mutex;
+ // Tracer thread signals its completion by setting done.
+ atomic_uintptr_t done;
+ uptr parent_pid;
+};
+
+// This class handles thread suspending/unsuspending in the tracer thread.
+class ThreadSuspender {
+ public:
+ explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+ : arg(arg)
+ , pid_(pid) {
+ CHECK_GE(pid, 0);
+ }
+ bool SuspendAllThreads();
+ void ResumeAllThreads();
+ void KillAllThreads();
+ SuspendedThreadsListLinux &suspended_threads_list() {
+ return suspended_threads_list_;
+ }
+ TracerThreadArgument *arg;
+ private:
+ SuspendedThreadsListLinux suspended_threads_list_;
+ pid_t pid_;
+ bool SuspendThread(tid_t thread_id);
+};
+
+bool ThreadSuspender::SuspendThread(tid_t tid) {
+ // Are we already attached to this thread?
+ // Currently this check takes linear time, however the number of threads is
+ // usually small.
+ if (suspended_threads_list_.ContainsTid(tid)) return false;
+ int pterrno;
+ if (internal_iserror(internal_ptrace(PTRACE_ATTACH, tid, nullptr, nullptr),
+ &pterrno)) {
+ // Either the thread is dead, or something prevented us from attaching.
+ // Log this event and move on.
+ VReport(1, "Could not attach to thread %zu (errno %d).\n", (uptr)tid,
+ pterrno);
+ return false;
+ } else {
+ VReport(2, "Attached to thread %zu.\n", (uptr)tid);
+ // The thread is not guaranteed to stop before ptrace returns, so we must
+ // wait on it. Note: if the thread receives a signal concurrently,
+ // we can get notification about the signal before notification about stop.
+ // In such case we need to forward the signal to the thread, otherwise
+ // the signal will be missed (as we do PTRACE_DETACH with arg=0) and
+ // any logic relying on signals will break. After forwarding we need to
+ // continue to wait for stopping, because the thread is not stopped yet.
+ // We do ignore delivery of SIGSTOP, because we want to make stop-the-world
+ // as invisible as possible.
+ for (;;) {
+ int status;
+ uptr waitpid_status;
+ HANDLE_EINTR(waitpid_status, internal_waitpid(tid, &status, __WALL));
+ int wperrno;
+ if (internal_iserror(waitpid_status, &wperrno)) {
+ // Got a ECHILD error. I don't think this situation is possible, but it
+ // doesn't hurt to report it.
+ VReport(1, "Waiting on thread %zu failed, detaching (errno %d).\n",
+ (uptr)tid, wperrno);
+ internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
+ return false;
+ }
+ if (WIFSTOPPED(status) && WSTOPSIG(status) != SIGSTOP) {
+ internal_ptrace(PTRACE_CONT, tid, nullptr,
+ (void*)(uptr)WSTOPSIG(status));
+ continue;
+ }
+ break;
+ }
+ suspended_threads_list_.Append(tid);
+ return true;
+ }
+}
+
+void ThreadSuspender::ResumeAllThreads() {
+ for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++) {
+ pid_t tid = suspended_threads_list_.GetThreadID(i);
+ int pterrno;
+ if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, nullptr, nullptr),
+ &pterrno)) {
+ VReport(2, "Detached from thread %d.\n", tid);
+ } else {
+ // Either the thread is dead, or we are already detached.
+ // The latter case is possible, for instance, if this function was called
+ // from a signal handler.
+ VReport(1, "Could not detach from thread %d (errno %d).\n", tid, pterrno);
+ }
+ }
+}
+
+void ThreadSuspender::KillAllThreads() {
+ for (uptr i = 0; i < suspended_threads_list_.ThreadCount(); i++)
+ internal_ptrace(PTRACE_KILL, suspended_threads_list_.GetThreadID(i),
+ nullptr, nullptr);
+}
+
+bool ThreadSuspender::SuspendAllThreads() {
+ ThreadLister thread_lister(pid_);
+ bool retry = true;
+ InternalMmapVector<tid_t> threads;
+ threads.reserve(128);
+ for (int i = 0; i < 30 && retry; ++i) {
+ retry = false;
+ switch (thread_lister.ListThreads(&threads)) {
+ case ThreadLister::Error:
+ ResumeAllThreads();
+ return false;
+ case ThreadLister::Incomplete:
+ retry = true;
+ break;
+ case ThreadLister::Ok:
+ break;
+ }
+ for (tid_t tid : threads)
+ if (SuspendThread(tid))
+ retry = true;
+ };
+ return suspended_threads_list_.ThreadCount();
+}
+
+// Pointer to the ThreadSuspender instance for use in signal handler.
+static ThreadSuspender *thread_suspender_instance = nullptr;
+
+// Synchronous signals that should not be blocked.
+static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
+ SIGXCPU, SIGXFSZ };
+
+static void TracerThreadDieCallback() {
+ // Generally a call to Die() in the tracer thread should be fatal to the
+ // parent process as well, because they share the address space.
+ // This really only works correctly if all the threads are suspended at this
+ // point. So we correctly handle calls to Die() from within the callback, but
+ // not those that happen before or after the callback. Hopefully there aren't
+ // a lot of opportunities for that to happen...
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst && stoptheworld_tracer_pid == internal_getpid()) {
+ inst->KillAllThreads();
+ thread_suspender_instance = nullptr;
+ }
+}
+
+// Signal handler to wake up suspended threads when the tracer thread dies.
+static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
+ void *uctx) {
+ SignalContext ctx(siginfo, uctx);
+ Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
+ ctx.addr, ctx.pc, ctx.sp);
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst) {
+ if (signum == SIGABRT)
+ inst->KillAllThreads();
+ else
+ inst->ResumeAllThreads();
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&inst->arg->done, 1, memory_order_relaxed);
+ }
+ internal__exit((signum == SIGABRT) ? 1 : 2);
+}
+
+// Size of alternative stack for signal handlers in the tracer thread.
+static const int kHandlerStackSize = 8192;
+
+// This function will be run as a cloned task.
+static int TracerThread(void* argument) {
+ TracerThreadArgument *tracer_thread_argument =
+ (TracerThreadArgument *)argument;
+
+ internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ // Check if parent is already dead.
+ if (internal_getppid() != tracer_thread_argument->parent_pid)
+ internal__exit(4);
+
+ // Wait for the parent thread to finish preparations.
+ tracer_thread_argument->mutex.Lock();
+ tracer_thread_argument->mutex.Unlock();
+
+ RAW_CHECK(AddDieCallback(TracerThreadDieCallback));
+
+ ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
+ // Global pointer for the signal handler.
+ thread_suspender_instance = &thread_suspender;
+
+ // Alternate stack for signal handling.
+ InternalMmapVector<char> handler_stack_memory(kHandlerStackSize);
+ stack_t handler_stack;
+ internal_memset(&handler_stack, 0, sizeof(handler_stack));
+ handler_stack.ss_sp = handler_stack_memory.data();
+ handler_stack.ss_size = kHandlerStackSize;
+ internal_sigaltstack(&handler_stack, nullptr);
+
+ // Install our handler for synchronous signals. Other signals should be
+ // blocked by the mask we inherited from the parent thread.
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
+ __sanitizer_sigaction act;
+ internal_memset(&act, 0, sizeof(act));
+ act.sigaction = TracerThreadSignalHandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
+ }
+
+ int exit_code = 0;
+ if (!thread_suspender.SuspendAllThreads()) {
+ VReport(1, "Failed suspending threads.\n");
+ exit_code = 3;
+ } else {
+ tracer_thread_argument->callback(thread_suspender.suspended_threads_list(),
+ tracer_thread_argument->callback_argument);
+ thread_suspender.ResumeAllThreads();
+ exit_code = 0;
+ }
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
+ return exit_code;
+}
+
+class ScopedStackSpaceWithGuard {
+ public:
+ explicit ScopedStackSpaceWithGuard(uptr stack_size) {
+ stack_size_ = stack_size;
+ guard_size_ = GetPageSizeCached();
+ // FIXME: Omitting MAP_STACK here works in current kernels but might break
+ // in the future.
+ guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_,
+ "ScopedStackWithGuard");
+ CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
+ }
+ ~ScopedStackSpaceWithGuard() {
+ UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
+ }
+ void *Bottom() const {
+ return (void *)(guard_start_ + stack_size_ + guard_size_);
+ }
+
+ private:
+ uptr stack_size_;
+ uptr guard_size_;
+ uptr guard_start_;
+};
+
+// We have a limitation on the stack frame size, so some stuff had to be moved
+// into globals.
+static __sanitizer_sigset_t blocked_sigset;
+static __sanitizer_sigset_t old_sigset;
+
+class StopTheWorldScope {
+ public:
+ StopTheWorldScope() {
+ // Make this process dumpable. Processes that are not dumpable cannot be
+ // attached to.
+ process_was_dumpable_ = internal_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+ if (!process_was_dumpable_)
+ internal_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+ }
+
+ ~StopTheWorldScope() {
+ // Restore the dumpable flag.
+ if (!process_was_dumpable_)
+ internal_prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
+ }
+
+ private:
+ int process_was_dumpable_;
+};
+
+// When sanitizer output is being redirected to file (i.e. by using log_path),
+// the tracer should write to the parent's log instead of trying to open a new
+// file. Alert the logging code to the fact that we have a tracer.
+struct ScopedSetTracerPID {
+ explicit ScopedSetTracerPID(uptr tracer_pid) {
+ stoptheworld_tracer_pid = tracer_pid;
+ stoptheworld_tracer_ppid = internal_getpid();
+ }
+ ~ScopedSetTracerPID() {
+ stoptheworld_tracer_pid = 0;
+ stoptheworld_tracer_ppid = 0;
+ }
+};
+
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {
+ StopTheWorldScope in_stoptheworld;
+ // Prepare the arguments for TracerThread.
+ struct TracerThreadArgument tracer_thread_argument;
+ tracer_thread_argument.callback = callback;
+ tracer_thread_argument.callback_argument = argument;
+ tracer_thread_argument.parent_pid = internal_getpid();
+ atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
+ const uptr kTracerStackSize = 2 * 1024 * 1024;
+ ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
+ // Block the execution of TracerThread until after we have set ptrace
+ // permissions.
+ tracer_thread_argument.mutex.Lock();
+ // Signal handling story.
+ // We don't want async signals to be delivered to the tracer thread,
+ // so we block all async signals before creating the thread. An async signal
+ // handler can temporary modify errno, which is shared with this thread.
+ // We ought to use pthread_sigmask here, because sigprocmask has undefined
+ // behavior in multithreaded programs. However, on linux sigprocmask is
+ // equivalent to pthread_sigmask with the exception that pthread_sigmask
+ // does not allow to block some signals used internally in pthread
+ // implementation. We are fine with blocking them here, we are really not
+ // going to pthread_cancel the thread.
+ // The tracer thread should not raise any synchronous signals. But in case it
+ // does, we setup a special handler for sync signals that properly kills the
+ // parent as well. Note: we don't pass CLONE_SIGHAND to clone, so handlers
+ // in the tracer thread won't interfere with user program. Double note: if a
+ // user does something along the lines of 'kill -11 pid', that can kill the
+ // process even if user setup own handler for SEGV.
+ // Thing to watch out for: this code should not change behavior of user code
+ // in any observable way. In particular it should not override user signal
+ // handlers.
+ internal_sigfillset(&blocked_sigset);
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
+ internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
+ int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+ CHECK_EQ(rv, 0);
+ uptr tracer_pid = internal_clone(
+ TracerThread, tracer_stack.Bottom(),
+ CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED,
+ &tracer_thread_argument, nullptr /* parent_tidptr */,
+ nullptr /* newtls */, nullptr /* child_tidptr */);
+ internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
+ int local_errno = 0;
+ if (internal_iserror(tracer_pid, &local_errno)) {
+ VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
+ tracer_thread_argument.mutex.Unlock();
+ } else {
+ ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid);
+ // On some systems we have to explicitly declare that we want to be traced
+ // by the tracer thread.
+ internal_prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);
+ // Allow the tracer thread to start.
+ tracer_thread_argument.mutex.Unlock();
+ // NOTE: errno is shared between this thread and the tracer thread.
+ // internal_waitpid() may call syscall() which can access/spoil errno,
+ // so we can't call it now. Instead we for the tracer thread to finish using
+ // the spin loop below. Man page for sched_yield() says "In the Linux
+ // implementation, sched_yield() always succeeds", so let's hope it does not
+ // spoil errno. Note that this spin loop runs only for brief periods before
+ // the tracer thread has suspended us and when it starts unblocking threads.
+ while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+ sched_yield();
+ // Now the tracer thread is about to exit and does not touch errno,
+ // wait for it.
+ for (;;) {
+ uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL);
+ if (!internal_iserror(waitpid_status, &local_errno))
+ break;
+ if (local_errno == EINTR)
+ continue;
+ VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
+ local_errno);
+ break;
+ }
+ }
+}
+
+// Platform-specific methods from SuspendedThreadsList.
+#if SANITIZER_ANDROID && defined(__arm__)
+typedef pt_regs regs_struct;
+#define REG_SP ARM_sp
+
+#elif SANITIZER_LINUX && defined(__arm__)
+typedef user_regs regs_struct;
+#define REG_SP uregs[13]
+
+#elif defined(__i386__) || defined(__x86_64__)
+typedef user_regs_struct regs_struct;
+#if defined(__i386__)
+#define REG_SP esp
+#else
+#define REG_SP rsp
+#endif
+
+#elif defined(__powerpc__) || defined(__powerpc64__)
+typedef pt_regs regs_struct;
+#define REG_SP gpr[PT_R1]
+
+#elif defined(__mips__)
+typedef struct user regs_struct;
+# if SANITIZER_ANDROID
+# define REG_SP regs[EF_R29]
+# else
+# define REG_SP regs[EF_REG29]
+# endif
+
+#elif defined(__aarch64__)
+typedef struct user_pt_regs regs_struct;
+#define REG_SP sp
+#define ARCH_IOVEC_FOR_GETREGSET
+
+#elif defined(__s390__)
+typedef _user_regs_struct regs_struct;
+#define REG_SP gprs[15]
+#define ARCH_IOVEC_FOR_GETREGSET
+
+#else
+#error "Unsupported architecture"
+#endif // SANITIZER_ANDROID && defined(__arm__)
+
+tid_t SuspendedThreadsListLinux::GetThreadID(uptr index) const {
+ CHECK_LT(index, thread_ids_.size());
+ return thread_ids_[index];
+}
+
+uptr SuspendedThreadsListLinux::ThreadCount() const {
+ return thread_ids_.size();
+}
+
+bool SuspendedThreadsListLinux::ContainsTid(tid_t thread_id) const {
+ for (uptr i = 0; i < thread_ids_.size(); i++) {
+ if (thread_ids_[i] == thread_id) return true;
+ }
+ return false;
+}
+
+void SuspendedThreadsListLinux::Append(tid_t tid) {
+ thread_ids_.push_back(tid);
+}
+
+PtraceRegistersStatus SuspendedThreadsListLinux::GetRegistersAndSP(
+ uptr index, uptr *buffer, uptr *sp) const {
+ pid_t tid = GetThreadID(index);
+ regs_struct regs;
+ int pterrno;
+#ifdef ARCH_IOVEC_FOR_GETREGSET
+ struct iovec regset_io;
+ regset_io.iov_base = ®s;
+ regset_io.iov_len = sizeof(regs_struct);
+ bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGSET, tid,
+ (void*)NT_PRSTATUS, (void*)®set_io),
+ &pterrno);
+#else
+ bool isErr = internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, nullptr,
+ ®s), &pterrno);
+#endif
+ if (isErr) {
+ VReport(1, "Could not get registers from thread %d (errno %d).\n", tid,
+ pterrno);
+ // ESRCH means that the given thread is not suspended or already dead.
+ // Therefore it's unsafe to inspect its data (e.g. walk through stack) and
+ // we should notify caller about this.
+ return pterrno == ESRCH ? REGISTERS_UNAVAILABLE_FATAL
+ : REGISTERS_UNAVAILABLE;
+ }
+
+ *sp = regs.REG_SP;
+ internal_memcpy(buffer, ®s, sizeof(regs));
+ return REGISTERS_AVAILABLE;
+}
+
+uptr SuspendedThreadsListLinux::RegisterCount() const {
+ return sizeof(regs_struct) / sizeof(uptr);
+}
+} // namespace __sanitizer
+
+#endif // SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips__)
+ // || defined(__aarch64__) || defined(__powerpc64__)
+ // || defined(__s390__) || defined(__i386__) || defined(__arm__)
+++ /dev/null
-//===-- sanitizer_stoptheworld_mac.cc -------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// See sanitizer_stoptheworld.h for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#if SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__) || \
- defined(__i386))
-
-#include <mach/mach.h>
-#include <mach/thread_info.h>
-#include <pthread.h>
-
-#include "sanitizer_stoptheworld.h"
-
-namespace __sanitizer {
-typedef struct {
- tid_t tid;
- thread_t thread;
-} SuspendedThreadInfo;
-
-class SuspendedThreadsListMac : public SuspendedThreadsList {
- public:
- SuspendedThreadsListMac() : threads_(1024) {}
-
- tid_t GetThreadID(uptr index) const;
- thread_t GetThread(uptr index) const;
- uptr ThreadCount() const;
- bool ContainsThread(thread_t thread) const;
- void Append(thread_t thread);
-
- PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
- uptr *sp) const;
- uptr RegisterCount() const;
-
- private:
- InternalMmapVector<SuspendedThreadInfo> threads_;
-};
-
-struct RunThreadArgs {
- StopTheWorldCallback callback;
- void *argument;
-};
-
-void RunThread(void *arg) {
- struct RunThreadArgs *run_args = (struct RunThreadArgs *)arg;
- SuspendedThreadsListMac suspended_threads_list;
-
- thread_array_t threads;
- mach_msg_type_number_t num_threads;
- kern_return_t err = task_threads(mach_task_self(), &threads, &num_threads);
- if (err != KERN_SUCCESS) {
- VReport(1, "Failed to get threads for task (errno %d).\n", err);
- return;
- }
-
- thread_t thread_self = mach_thread_self();
- for (unsigned int i = 0; i < num_threads; ++i) {
- if (threads[i] == thread_self) continue;
-
- thread_suspend(threads[i]);
- suspended_threads_list.Append(threads[i]);
- }
-
- run_args->callback(suspended_threads_list, run_args->argument);
-
- uptr num_suspended = suspended_threads_list.ThreadCount();
- for (unsigned int i = 0; i < num_suspended; ++i) {
- thread_resume(suspended_threads_list.GetThread(i));
- }
-}
-
-void StopTheWorld(StopTheWorldCallback callback, void *argument) {
- struct RunThreadArgs arg = {callback, argument};
- pthread_t run_thread = (pthread_t)internal_start_thread(RunThread, &arg);
- internal_join_thread(run_thread);
-}
-
-#if defined(__x86_64__)
-typedef x86_thread_state64_t regs_struct;
-
-#define SP_REG __rsp
-
-#elif defined(__aarch64__)
-typedef arm_thread_state64_t regs_struct;
-
-# if __DARWIN_UNIX03
-# define SP_REG __sp
-# else
-# define SP_REG sp
-# endif
-
-#elif defined(__i386)
-typedef x86_thread_state32_t regs_struct;
-
-#define SP_REG __esp
-
-#else
-#error "Unsupported architecture"
-#endif
-
-tid_t SuspendedThreadsListMac::GetThreadID(uptr index) const {
- CHECK_LT(index, threads_.size());
- return threads_[index].tid;
-}
-
-thread_t SuspendedThreadsListMac::GetThread(uptr index) const {
- CHECK_LT(index, threads_.size());
- return threads_[index].thread;
-}
-
-uptr SuspendedThreadsListMac::ThreadCount() const {
- return threads_.size();
-}
-
-bool SuspendedThreadsListMac::ContainsThread(thread_t thread) const {
- for (uptr i = 0; i < threads_.size(); i++) {
- if (threads_[i].thread == thread) return true;
- }
- return false;
-}
-
-void SuspendedThreadsListMac::Append(thread_t thread) {
- thread_identifier_info_data_t info;
- mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT;
- kern_return_t err = thread_info(thread, THREAD_IDENTIFIER_INFO,
- (thread_info_t)&info, &info_count);
- if (err != KERN_SUCCESS) {
- VReport(1, "Error - unable to get thread ident for a thread\n");
- return;
- }
- threads_.push_back({info.thread_id, thread});
-}
-
-PtraceRegistersStatus SuspendedThreadsListMac::GetRegistersAndSP(
- uptr index, uptr *buffer, uptr *sp) const {
- thread_t thread = GetThread(index);
- regs_struct regs;
- int err;
- mach_msg_type_number_t reg_count = MACHINE_THREAD_STATE_COUNT;
- err = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)®s,
- ®_count);
- if (err != KERN_SUCCESS) {
- VReport(1, "Error - unable to get registers for a thread\n");
- // KERN_INVALID_ARGUMENT indicates that either the flavor is invalid,
- // or the thread does not exist. The other possible error case,
- // MIG_ARRAY_TOO_LARGE, means that the state is too large, but it's
- // still safe to proceed.
- return err == KERN_INVALID_ARGUMENT ? REGISTERS_UNAVAILABLE_FATAL
- : REGISTERS_UNAVAILABLE;
- }
-
- internal_memcpy(buffer, ®s, sizeof(regs));
- *sp = regs.SP_REG;
-
- // On x86_64 and aarch64, we must account for the stack redzone, which is 128
- // bytes.
- if (SANITIZER_WORDSIZE == 64) *sp -= 128;
-
- return REGISTERS_AVAILABLE;
-}
-
-uptr SuspendedThreadsListMac::RegisterCount() const {
- return MACHINE_THREAD_STATE_COUNT;
-}
-} // namespace __sanitizer
-
-#endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) ||
- // defined(__i386))
--- /dev/null
+//===-- sanitizer_stoptheworld_mac.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// See sanitizer_stoptheworld.h for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__) || \
+ defined(__i386))
+
+#include <mach/mach.h>
+#include <mach/thread_info.h>
+#include <pthread.h>
+
+#include "sanitizer_stoptheworld.h"
+
+namespace __sanitizer {
+typedef struct {
+ tid_t tid;
+ thread_t thread;
+} SuspendedThreadInfo;
+
+class SuspendedThreadsListMac : public SuspendedThreadsList {
+ public:
+ SuspendedThreadsListMac() : threads_(1024) {}
+
+ tid_t GetThreadID(uptr index) const;
+ thread_t GetThread(uptr index) const;
+ uptr ThreadCount() const;
+ bool ContainsThread(thread_t thread) const;
+ void Append(thread_t thread);
+
+ PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
+ uptr *sp) const;
+ uptr RegisterCount() const;
+
+ private:
+ InternalMmapVector<SuspendedThreadInfo> threads_;
+};
+
+struct RunThreadArgs {
+ StopTheWorldCallback callback;
+ void *argument;
+};
+
+void RunThread(void *arg) {
+ struct RunThreadArgs *run_args = (struct RunThreadArgs *)arg;
+ SuspendedThreadsListMac suspended_threads_list;
+
+ thread_array_t threads;
+ mach_msg_type_number_t num_threads;
+ kern_return_t err = task_threads(mach_task_self(), &threads, &num_threads);
+ if (err != KERN_SUCCESS) {
+ VReport(1, "Failed to get threads for task (errno %d).\n", err);
+ return;
+ }
+
+ thread_t thread_self = mach_thread_self();
+ for (unsigned int i = 0; i < num_threads; ++i) {
+ if (threads[i] == thread_self) continue;
+
+ thread_suspend(threads[i]);
+ suspended_threads_list.Append(threads[i]);
+ }
+
+ run_args->callback(suspended_threads_list, run_args->argument);
+
+ uptr num_suspended = suspended_threads_list.ThreadCount();
+ for (unsigned int i = 0; i < num_suspended; ++i) {
+ thread_resume(suspended_threads_list.GetThread(i));
+ }
+}
+
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {
+ struct RunThreadArgs arg = {callback, argument};
+ pthread_t run_thread = (pthread_t)internal_start_thread(RunThread, &arg);
+ internal_join_thread(run_thread);
+}
+
+#if defined(__x86_64__)
+typedef x86_thread_state64_t regs_struct;
+
+#define SP_REG __rsp
+
+#elif defined(__aarch64__)
+typedef arm_thread_state64_t regs_struct;
+
+# if __DARWIN_UNIX03
+# define SP_REG __sp
+# else
+# define SP_REG sp
+# endif
+
+#elif defined(__i386)
+typedef x86_thread_state32_t regs_struct;
+
+#define SP_REG __esp
+
+#else
+#error "Unsupported architecture"
+#endif
+
+tid_t SuspendedThreadsListMac::GetThreadID(uptr index) const {
+ CHECK_LT(index, threads_.size());
+ return threads_[index].tid;
+}
+
+thread_t SuspendedThreadsListMac::GetThread(uptr index) const {
+ CHECK_LT(index, threads_.size());
+ return threads_[index].thread;
+}
+
+uptr SuspendedThreadsListMac::ThreadCount() const {
+ return threads_.size();
+}
+
+bool SuspendedThreadsListMac::ContainsThread(thread_t thread) const {
+ for (uptr i = 0; i < threads_.size(); i++) {
+ if (threads_[i].thread == thread) return true;
+ }
+ return false;
+}
+
+void SuspendedThreadsListMac::Append(thread_t thread) {
+ thread_identifier_info_data_t info;
+ mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT;
+ kern_return_t err = thread_info(thread, THREAD_IDENTIFIER_INFO,
+ (thread_info_t)&info, &info_count);
+ if (err != KERN_SUCCESS) {
+ VReport(1, "Error - unable to get thread ident for a thread\n");
+ return;
+ }
+ threads_.push_back({info.thread_id, thread});
+}
+
+PtraceRegistersStatus SuspendedThreadsListMac::GetRegistersAndSP(
+ uptr index, uptr *buffer, uptr *sp) const {
+ thread_t thread = GetThread(index);
+ regs_struct regs;
+ int err;
+ mach_msg_type_number_t reg_count = MACHINE_THREAD_STATE_COUNT;
+ err = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)®s,
+ ®_count);
+ if (err != KERN_SUCCESS) {
+ VReport(1, "Error - unable to get registers for a thread\n");
+ // KERN_INVALID_ARGUMENT indicates that either the flavor is invalid,
+ // or the thread does not exist. The other possible error case,
+ // MIG_ARRAY_TOO_LARGE, means that the state is too large, but it's
+ // still safe to proceed.
+ return err == KERN_INVALID_ARGUMENT ? REGISTERS_UNAVAILABLE_FATAL
+ : REGISTERS_UNAVAILABLE;
+ }
+
+ internal_memcpy(buffer, ®s, sizeof(regs));
+ *sp = regs.SP_REG;
+
+ // On x86_64 and aarch64, we must account for the stack redzone, which is 128
+ // bytes.
+ if (SANITIZER_WORDSIZE == 64) *sp -= 128;
+
+ return REGISTERS_AVAILABLE;
+}
+
+uptr SuspendedThreadsListMac::RegisterCount() const {
+ return MACHINE_THREAD_STATE_COUNT;
+}
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC && (defined(__x86_64__) || defined(__aarch64__)) ||
+ // defined(__i386))
--- /dev/null
+//===-- sanitizer_stoptheworld_netbsd_libcdep.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// See sanitizer_stoptheworld.h for details.
+// This implementation was inspired by Markus Gutschke's linuxthreads.cc.
+//
+// This is a NetBSD variation of Linux stoptheworld implementation
+// See sanitizer_stoptheworld_linux_libcdep.cpp for code comments.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#if SANITIZER_NETBSD
+
+#include "sanitizer_stoptheworld.h"
+
+#include "sanitizer_atomic.h"
+#include "sanitizer_platform_limits_posix.h"
+
+#include <sys/types.h>
+
+#include <sys/ptrace.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <machine/reg.h>
+
+#include <elf.h>
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <stddef.h>
+
+#define internal_sigaction_norestorer internal_sigaction
+
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+
+namespace __sanitizer {
+
+class SuspendedThreadsListNetBSD : public SuspendedThreadsList {
+ public:
+ SuspendedThreadsListNetBSD() { thread_ids_.reserve(1024); }
+
+ tid_t GetThreadID(uptr index) const;
+ uptr ThreadCount() const;
+ bool ContainsTid(tid_t thread_id) const;
+ void Append(tid_t tid);
+
+ PtraceRegistersStatus GetRegistersAndSP(uptr index, uptr *buffer,
+ uptr *sp) const;
+ uptr RegisterCount() const;
+
+ private:
+ InternalMmapVector<tid_t> thread_ids_;
+};
+
+struct TracerThreadArgument {
+ StopTheWorldCallback callback;
+ void *callback_argument;
+ BlockingMutex mutex;
+ atomic_uintptr_t done;
+ uptr parent_pid;
+};
+
+class ThreadSuspender {
+ public:
+ explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg)
+ : arg(arg), pid_(pid) {
+ CHECK_GE(pid, 0);
+ }
+ bool SuspendAllThreads();
+ void ResumeAllThreads();
+ void KillAllThreads();
+ SuspendedThreadsListNetBSD &suspended_threads_list() {
+ return suspended_threads_list_;
+ }
+ TracerThreadArgument *arg;
+
+ private:
+ SuspendedThreadsListNetBSD suspended_threads_list_;
+ pid_t pid_;
+};
+
+void ThreadSuspender::ResumeAllThreads() {
+ int pterrno;
+ if (!internal_iserror(internal_ptrace(PT_DETACH, pid_, (void *)(uptr)1, 0),
+ &pterrno)) {
+ VReport(2, "Detached from process %d.\n", pid_);
+ } else {
+ VReport(1, "Could not detach from process %d (errno %d).\n", pid_, pterrno);
+ }
+}
+
+void ThreadSuspender::KillAllThreads() {
+ internal_ptrace(PT_KILL, pid_, nullptr, 0);
+}
+
+bool ThreadSuspender::SuspendAllThreads() {
+ int pterrno;
+ if (internal_iserror(internal_ptrace(PT_ATTACH, pid_, nullptr, 0),
+ &pterrno)) {
+ Printf("Could not attach to process %d (errno %d).\n", pid_, pterrno);
+ return false;
+ }
+
+ int status;
+ uptr waitpid_status;
+ HANDLE_EINTR(waitpid_status, internal_waitpid(pid_, &status, 0));
+
+ VReport(2, "Attached to process %d.\n", pid_);
+
+ struct ptrace_lwpinfo pl;
+ int val;
+ pl.pl_lwpid = 0;
+ while ((val = ptrace(PT_LWPINFO, pid_, (void *)&pl, sizeof(pl))) != -1 &&
+ pl.pl_lwpid != 0) {
+ suspended_threads_list_.Append(pl.pl_lwpid);
+ VReport(2, "Appended thread %d in process %d.\n", pl.pl_lwpid, pid_);
+ }
+ return true;
+}
+
+// Pointer to the ThreadSuspender instance for use in signal handler.
+static ThreadSuspender *thread_suspender_instance = nullptr;
+
+// Synchronous signals that should not be blocked.
+static const int kSyncSignals[] = {SIGABRT, SIGILL, SIGFPE, SIGSEGV,
+ SIGBUS, SIGXCPU, SIGXFSZ};
+
+static void TracerThreadDieCallback() {
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst && stoptheworld_tracer_pid == internal_getpid()) {
+ inst->KillAllThreads();
+ thread_suspender_instance = nullptr;
+ }
+}
+
+// Signal handler to wake up suspended threads when the tracer thread dies.
+static void TracerThreadSignalHandler(int signum, __sanitizer_siginfo *siginfo,
+ void *uctx) {
+ SignalContext ctx(siginfo, uctx);
+ Printf("Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", signum,
+ ctx.addr, ctx.pc, ctx.sp);
+ ThreadSuspender *inst = thread_suspender_instance;
+ if (inst) {
+ if (signum == SIGABRT)
+ inst->KillAllThreads();
+ else
+ inst->ResumeAllThreads();
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&inst->arg->done, 1, memory_order_relaxed);
+ }
+ internal__exit((signum == SIGABRT) ? 1 : 2);
+}
+
+// Size of alternative stack for signal handlers in the tracer thread.
+static const int kHandlerStackSize = 8192;
+
+// This function will be run as a cloned task.
+static int TracerThread(void *argument) {
+ TracerThreadArgument *tracer_thread_argument =
+ (TracerThreadArgument *)argument;
+
+ // Check if parent is already dead.
+ if (internal_getppid() != tracer_thread_argument->parent_pid)
+ internal__exit(4);
+
+ // Wait for the parent thread to finish preparations.
+ tracer_thread_argument->mutex.Lock();
+ tracer_thread_argument->mutex.Unlock();
+
+ RAW_CHECK(AddDieCallback(TracerThreadDieCallback));
+
+ ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument);
+ // Global pointer for the signal handler.
+ thread_suspender_instance = &thread_suspender;
+
+ // Alternate stack for signal handling.
+ InternalMmapVector<char> handler_stack_memory(kHandlerStackSize);
+ stack_t handler_stack;
+ internal_memset(&handler_stack, 0, sizeof(handler_stack));
+ handler_stack.ss_sp = handler_stack_memory.data();
+ handler_stack.ss_size = kHandlerStackSize;
+ internal_sigaltstack(&handler_stack, nullptr);
+
+ // Install our handler for synchronous signals. Other signals should be
+ // blocked by the mask we inherited from the parent thread.
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++) {
+ __sanitizer_sigaction act;
+ internal_memset(&act, 0, sizeof(act));
+ act.sigaction = TracerThreadSignalHandler;
+ act.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ internal_sigaction_norestorer(kSyncSignals[i], &act, 0);
+ }
+
+ int exit_code = 0;
+ if (!thread_suspender.SuspendAllThreads()) {
+ VReport(1, "Failed suspending threads.\n");
+ exit_code = 3;
+ } else {
+ tracer_thread_argument->callback(thread_suspender.suspended_threads_list(),
+ tracer_thread_argument->callback_argument);
+ thread_suspender.ResumeAllThreads();
+ exit_code = 0;
+ }
+ RAW_CHECK(RemoveDieCallback(TracerThreadDieCallback));
+ thread_suspender_instance = nullptr;
+ atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed);
+ return exit_code;
+}
+
+class ScopedStackSpaceWithGuard {
+ public:
+ explicit ScopedStackSpaceWithGuard(uptr stack_size) {
+ stack_size_ = stack_size;
+ guard_size_ = GetPageSizeCached();
+ // FIXME: Omitting MAP_STACK here works in current kernels but might break
+ // in the future.
+ guard_start_ =
+ (uptr)MmapOrDie(stack_size_ + guard_size_, "ScopedStackWithGuard");
+ CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_));
+ }
+ ~ScopedStackSpaceWithGuard() {
+ UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_);
+ }
+ void *Bottom() const {
+ return (void *)(guard_start_ + stack_size_ + guard_size_);
+ }
+
+ private:
+ uptr stack_size_;
+ uptr guard_size_;
+ uptr guard_start_;
+};
+
+static __sanitizer_sigset_t blocked_sigset;
+static __sanitizer_sigset_t old_sigset;
+
+struct ScopedSetTracerPID {
+ explicit ScopedSetTracerPID(uptr tracer_pid) {
+ stoptheworld_tracer_pid = tracer_pid;
+ stoptheworld_tracer_ppid = internal_getpid();
+ }
+ ~ScopedSetTracerPID() {
+ stoptheworld_tracer_pid = 0;
+ stoptheworld_tracer_ppid = 0;
+ }
+};
+
+void StopTheWorld(StopTheWorldCallback callback, void *argument) {
+ // Prepare the arguments for TracerThread.
+ struct TracerThreadArgument tracer_thread_argument;
+ tracer_thread_argument.callback = callback;
+ tracer_thread_argument.callback_argument = argument;
+ tracer_thread_argument.parent_pid = internal_getpid();
+ atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed);
+ const uptr kTracerStackSize = 2 * 1024 * 1024;
+ ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize);
+
+ tracer_thread_argument.mutex.Lock();
+
+ internal_sigfillset(&blocked_sigset);
+ for (uptr i = 0; i < ARRAY_SIZE(kSyncSignals); i++)
+ internal_sigdelset(&blocked_sigset, kSyncSignals[i]);
+ int rv = internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset);
+ CHECK_EQ(rv, 0);
+ uptr tracer_pid = internal_clone(TracerThread, tracer_stack.Bottom(),
+ CLONE_VM | CLONE_FS | CLONE_FILES,
+ &tracer_thread_argument);
+ internal_sigprocmask(SIG_SETMASK, &old_sigset, 0);
+ int local_errno = 0;
+ if (internal_iserror(tracer_pid, &local_errno)) {
+ VReport(1, "Failed spawning a tracer thread (errno %d).\n", local_errno);
+ tracer_thread_argument.mutex.Unlock();
+ } else {
+ ScopedSetTracerPID scoped_set_tracer_pid(tracer_pid);
+
+ tracer_thread_argument.mutex.Unlock();
+
+ while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0)
+ sched_yield();
+
+ for (;;) {
+ uptr waitpid_status = internal_waitpid(tracer_pid, nullptr, __WALL);
+ if (!internal_iserror(waitpid_status, &local_errno))
+ break;
+ if (local_errno == EINTR)
+ continue;
+ VReport(1, "Waiting on the tracer thread failed (errno %d).\n",
+ local_errno);
+ break;
+ }
+ }
+}
+
+tid_t SuspendedThreadsListNetBSD::GetThreadID(uptr index) const {
+ CHECK_LT(index, thread_ids_.size());
+ return thread_ids_[index];
+}
+
+uptr SuspendedThreadsListNetBSD::ThreadCount() const {
+ return thread_ids_.size();
+}
+
+bool SuspendedThreadsListNetBSD::ContainsTid(tid_t thread_id) const {
+ for (uptr i = 0; i < thread_ids_.size(); i++) {
+ if (thread_ids_[i] == thread_id)
+ return true;
+ }
+ return false;
+}
+
+void SuspendedThreadsListNetBSD::Append(tid_t tid) {
+ thread_ids_.push_back(tid);
+}
+
+PtraceRegistersStatus SuspendedThreadsListNetBSD::GetRegistersAndSP(
+ uptr index, uptr *buffer, uptr *sp) const {
+ lwpid_t tid = GetThreadID(index);
+ pid_t ppid = internal_getppid();
+ struct reg regs;
+ int pterrno;
+ bool isErr =
+ internal_iserror(internal_ptrace(PT_GETREGS, ppid, ®s, tid), &pterrno);
+ if (isErr) {
+ VReport(1,
+ "Could not get registers from process %d thread %d (errno %d).\n",
+ ppid, tid, pterrno);
+ return pterrno == ESRCH ? REGISTERS_UNAVAILABLE_FATAL
+ : REGISTERS_UNAVAILABLE;
+ }
+
+ *sp = PTRACE_REG_SP(®s);
+ internal_memcpy(buffer, ®s, sizeof(regs));
+
+ return REGISTERS_AVAILABLE;
+}
+
+uptr SuspendedThreadsListNetBSD::RegisterCount() const {
+ return sizeof(struct reg) / sizeof(uptr);
+}
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-//===-- sanitizer_suppressions.cc -----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Suppression parsing/matching code.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_suppressions.h"
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_common.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_file.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_placement_new.h"
-
-namespace __sanitizer {
-
-SuppressionContext::SuppressionContext(const char *suppression_types[],
- int suppression_types_num)
- : suppression_types_(suppression_types),
- suppression_types_num_(suppression_types_num),
- can_parse_(true) {
- CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
- internal_memset(has_suppression_type_, 0, suppression_types_num_);
-}
-
-static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
- /*out*/char *new_file_path,
- uptr new_file_path_size) {
- InternalScopedString exec(kMaxPathLength);
- if (ReadBinaryNameCached(exec.data(), exec.size())) {
- const char *file_name_pos = StripModuleName(exec.data());
- uptr path_to_exec_len = file_name_pos - exec.data();
- internal_strncat(new_file_path, exec.data(),
- Min(path_to_exec_len, new_file_path_size - 1));
- internal_strncat(new_file_path, file_path,
- new_file_path_size - internal_strlen(new_file_path) - 1);
- return true;
- }
- return false;
-}
-
-void SuppressionContext::ParseFromFile(const char *filename) {
- if (filename[0] == '\0')
- return;
-
-#if !SANITIZER_FUCHSIA
- // If we cannot find the file, check if its location is relative to
- // the location of the executable.
- InternalScopedString new_file_path(kMaxPathLength);
- if (!FileExists(filename) && !IsAbsolutePath(filename) &&
- GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(),
- new_file_path.size())) {
- filename = new_file_path.data();
- }
-#endif // !SANITIZER_FUCHSIA
-
- // Read the file.
- VPrintf(1, "%s: reading suppressions file at %s\n",
- SanitizerToolName, filename);
- char *file_contents;
- uptr buffer_size;
- uptr contents_size;
- if (!ReadFileToBuffer(filename, &file_contents, &buffer_size,
- &contents_size)) {
- Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
- filename);
- Die();
- }
-
- Parse(file_contents);
-}
-
-bool SuppressionContext::Match(const char *str, const char *type,
- Suppression **s) {
- can_parse_ = false;
- if (!HasSuppressionType(type))
- return false;
- for (uptr i = 0; i < suppressions_.size(); i++) {
- Suppression &cur = suppressions_[i];
- if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
- *s = &cur;
- return true;
- }
- }
- return false;
-}
-
-static const char *StripPrefix(const char *str, const char *prefix) {
- while (str && *str == *prefix) {
- str++;
- prefix++;
- }
- if (!*prefix)
- return str;
- return 0;
-}
-
-void SuppressionContext::Parse(const char *str) {
- // Context must not mutate once Match has been called.
- CHECK(can_parse_);
- const char *line = str;
- while (line) {
- while (line[0] == ' ' || line[0] == '\t')
- line++;
- const char *end = internal_strchr(line, '\n');
- if (end == 0)
- end = line + internal_strlen(line);
- if (line != end && line[0] != '#') {
- const char *end2 = end;
- while (line != end2 &&
- (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r'))
- end2--;
- int type;
- for (type = 0; type < suppression_types_num_; type++) {
- const char *next_char = StripPrefix(line, suppression_types_[type]);
- if (next_char && *next_char == ':') {
- line = ++next_char;
- break;
- }
- }
- if (type == suppression_types_num_) {
- Printf("%s: failed to parse suppressions\n", SanitizerToolName);
- Die();
- }
- Suppression s;
- s.type = suppression_types_[type];
- s.templ = (char*)InternalAlloc(end2 - line + 1);
- internal_memcpy(s.templ, line, end2 - line);
- s.templ[end2 - line] = 0;
- suppressions_.push_back(s);
- has_suppression_type_[type] = true;
- }
- if (end[0] == 0)
- break;
- line = end + 1;
- }
-}
-
-uptr SuppressionContext::SuppressionCount() const {
- return suppressions_.size();
-}
-
-bool SuppressionContext::HasSuppressionType(const char *type) const {
- for (int i = 0; i < suppression_types_num_; i++) {
- if (0 == internal_strcmp(type, suppression_types_[i]))
- return has_suppression_type_[i];
- }
- return false;
-}
-
-const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
- CHECK_LT(i, suppressions_.size());
- return &suppressions_[i];
-}
-
-void SuppressionContext::GetMatched(
- InternalMmapVector<Suppression *> *matched) {
- for (uptr i = 0; i < suppressions_.size(); i++)
- if (atomic_load_relaxed(&suppressions_[i].hit_count))
- matched->push_back(&suppressions_[i]);
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_suppressions.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Suppression parsing/matching code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_suppressions.h"
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_file.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+
+namespace __sanitizer {
+
+SuppressionContext::SuppressionContext(const char *suppression_types[],
+ int suppression_types_num)
+ : suppression_types_(suppression_types),
+ suppression_types_num_(suppression_types_num),
+ can_parse_(true) {
+ CHECK_LE(suppression_types_num_, kMaxSuppressionTypes);
+ internal_memset(has_suppression_type_, 0, suppression_types_num_);
+}
+
+#if !SANITIZER_FUCHSIA
+static bool GetPathAssumingFileIsRelativeToExec(const char *file_path,
+ /*out*/char *new_file_path,
+ uptr new_file_path_size) {
+ InternalScopedString exec(kMaxPathLength);
+ if (ReadBinaryNameCached(exec.data(), exec.size())) {
+ const char *file_name_pos = StripModuleName(exec.data());
+ uptr path_to_exec_len = file_name_pos - exec.data();
+ internal_strncat(new_file_path, exec.data(),
+ Min(path_to_exec_len, new_file_path_size - 1));
+ internal_strncat(new_file_path, file_path,
+ new_file_path_size - internal_strlen(new_file_path) - 1);
+ return true;
+ }
+ return false;
+}
+
+static const char *FindFile(const char *file_path,
+ /*out*/char *new_file_path,
+ uptr new_file_path_size) {
+ // If we cannot find the file, check if its location is relative to
+ // the location of the executable.
+ if (!FileExists(file_path) && !IsAbsolutePath(file_path) &&
+ GetPathAssumingFileIsRelativeToExec(file_path, new_file_path,
+ new_file_path_size)) {
+ return new_file_path;
+ }
+ return file_path;
+}
+#else
+static const char *FindFile(const char *file_path, char *, uptr) {
+ return file_path;
+}
+#endif
+
+void SuppressionContext::ParseFromFile(const char *filename) {
+ if (filename[0] == '\0')
+ return;
+
+ InternalScopedString new_file_path(kMaxPathLength);
+ filename = FindFile(filename, new_file_path.data(), new_file_path.size());
+
+ // Read the file.
+ VPrintf(1, "%s: reading suppressions file at %s\n",
+ SanitizerToolName, filename);
+ char *file_contents;
+ uptr buffer_size;
+ uptr contents_size;
+ if (!ReadFileToBuffer(filename, &file_contents, &buffer_size,
+ &contents_size)) {
+ Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName,
+ filename);
+ Die();
+ }
+
+ Parse(file_contents);
+}
+
+bool SuppressionContext::Match(const char *str, const char *type,
+ Suppression **s) {
+ can_parse_ = false;
+ if (!HasSuppressionType(type))
+ return false;
+ for (uptr i = 0; i < suppressions_.size(); i++) {
+ Suppression &cur = suppressions_[i];
+ if (0 == internal_strcmp(cur.type, type) && TemplateMatch(cur.templ, str)) {
+ *s = &cur;
+ return true;
+ }
+ }
+ return false;
+}
+
+static const char *StripPrefix(const char *str, const char *prefix) {
+ while (*str && *str == *prefix) {
+ str++;
+ prefix++;
+ }
+ if (!*prefix)
+ return str;
+ return 0;
+}
+
+void SuppressionContext::Parse(const char *str) {
+ // Context must not mutate once Match has been called.
+ CHECK(can_parse_);
+ const char *line = str;
+ while (line) {
+ while (line[0] == ' ' || line[0] == '\t')
+ line++;
+ const char *end = internal_strchr(line, '\n');
+ if (end == 0)
+ end = line + internal_strlen(line);
+ if (line != end && line[0] != '#') {
+ const char *end2 = end;
+ while (line != end2 &&
+ (end2[-1] == ' ' || end2[-1] == '\t' || end2[-1] == '\r'))
+ end2--;
+ int type;
+ for (type = 0; type < suppression_types_num_; type++) {
+ const char *next_char = StripPrefix(line, suppression_types_[type]);
+ if (next_char && *next_char == ':') {
+ line = ++next_char;
+ break;
+ }
+ }
+ if (type == suppression_types_num_) {
+ Printf("%s: failed to parse suppressions\n", SanitizerToolName);
+ Die();
+ }
+ Suppression s;
+ s.type = suppression_types_[type];
+ s.templ = (char*)InternalAlloc(end2 - line + 1);
+ internal_memcpy(s.templ, line, end2 - line);
+ s.templ[end2 - line] = 0;
+ suppressions_.push_back(s);
+ has_suppression_type_[type] = true;
+ }
+ if (end[0] == 0)
+ break;
+ line = end + 1;
+ }
+}
+
+uptr SuppressionContext::SuppressionCount() const {
+ return suppressions_.size();
+}
+
+bool SuppressionContext::HasSuppressionType(const char *type) const {
+ for (int i = 0; i < suppression_types_num_; i++) {
+ if (0 == internal_strcmp(type, suppression_types_[i]))
+ return has_suppression_type_[i];
+ }
+ return false;
+}
+
+const Suppression *SuppressionContext::SuppressionAt(uptr i) const {
+ CHECK_LT(i, suppressions_.size());
+ return &suppressions_[i];
+}
+
+void SuppressionContext::GetMatched(
+ InternalMmapVector<Suppression *> *matched) {
+ for (uptr i = 0; i < suppressions_.size(); i++)
+ if (atomic_load_relaxed(&suppressions_[i].hit_count))
+ matched->push_back(&suppressions_[i]);
+}
+
+} // namespace __sanitizer
//===-- sanitizer_suppressions.h --------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_symbolizer.cc -------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_platform.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_symbolizer_internal.h"
-
-namespace __sanitizer {
-
-AddressInfo::AddressInfo() {
- internal_memset(this, 0, sizeof(AddressInfo));
- function_offset = kUnknown;
-}
-
-void AddressInfo::Clear() {
- InternalFree(module);
- InternalFree(function);
- InternalFree(file);
- internal_memset(this, 0, sizeof(AddressInfo));
- function_offset = kUnknown;
-}
-
-void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset,
- ModuleArch mod_arch) {
- module = internal_strdup(mod_name);
- module_offset = mod_offset;
- module_arch = mod_arch;
-}
-
-SymbolizedStack::SymbolizedStack() : next(nullptr), info() {}
-
-SymbolizedStack *SymbolizedStack::New(uptr addr) {
- void *mem = InternalAlloc(sizeof(SymbolizedStack));
- SymbolizedStack *res = new(mem) SymbolizedStack();
- res->info.address = addr;
- return res;
-}
-
-void SymbolizedStack::ClearAll() {
- info.Clear();
- if (next)
- next->ClearAll();
- InternalFree(this);
-}
-
-DataInfo::DataInfo() {
- internal_memset(this, 0, sizeof(DataInfo));
-}
-
-void DataInfo::Clear() {
- InternalFree(module);
- InternalFree(file);
- InternalFree(name);
- internal_memset(this, 0, sizeof(DataInfo));
-}
-
-Symbolizer *Symbolizer::symbolizer_;
-StaticSpinMutex Symbolizer::init_mu_;
-LowLevelAllocator Symbolizer::symbolizer_allocator_;
-
-void Symbolizer::InvalidateModuleList() {
- modules_fresh_ = false;
-}
-
-void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
- Symbolizer::EndSymbolizationHook end_hook) {
- CHECK(start_hook_ == 0 && end_hook_ == 0);
- start_hook_ = start_hook;
- end_hook_ = end_hook;
-}
-
-const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) {
- mu_->CheckLocked();
-
- // 'str' will be the same string multiple times in a row, optimize this case.
- if (last_match_ && !internal_strcmp(last_match_, str))
- return last_match_;
-
- // FIXME: this is linear search.
- // We should optimize this further if this turns out to be a bottleneck later.
- for (uptr i = 0; i < storage_.size(); ++i) {
- if (!internal_strcmp(storage_[i], str)) {
- last_match_ = storage_[i];
- return last_match_;
- }
- }
- last_match_ = internal_strdup(str);
- storage_.push_back(last_match_);
- return last_match_;
-}
-
-Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
- : module_names_(&mu_), modules_(), modules_fresh_(false), tools_(tools),
- start_hook_(0), end_hook_(0) {}
-
-Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
- : sym_(sym) {
- if (sym_->start_hook_)
- sym_->start_hook_();
-}
-
-Symbolizer::SymbolizerScope::~SymbolizerScope() {
- if (sym_->end_hook_)
- sym_->end_hook_();
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_symbolizer.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_platform.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+AddressInfo::AddressInfo() {
+ internal_memset(this, 0, sizeof(AddressInfo));
+ function_offset = kUnknown;
+}
+
+void AddressInfo::Clear() {
+ InternalFree(module);
+ InternalFree(function);
+ InternalFree(file);
+ internal_memset(this, 0, sizeof(AddressInfo));
+ function_offset = kUnknown;
+}
+
+void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset,
+ ModuleArch mod_arch) {
+ module = internal_strdup(mod_name);
+ module_offset = mod_offset;
+ module_arch = mod_arch;
+}
+
+SymbolizedStack::SymbolizedStack() : next(nullptr), info() {}
+
+SymbolizedStack *SymbolizedStack::New(uptr addr) {
+ void *mem = InternalAlloc(sizeof(SymbolizedStack));
+ SymbolizedStack *res = new(mem) SymbolizedStack();
+ res->info.address = addr;
+ return res;
+}
+
+void SymbolizedStack::ClearAll() {
+ info.Clear();
+ if (next)
+ next->ClearAll();
+ InternalFree(this);
+}
+
+DataInfo::DataInfo() {
+ internal_memset(this, 0, sizeof(DataInfo));
+}
+
+void DataInfo::Clear() {
+ InternalFree(module);
+ InternalFree(file);
+ InternalFree(name);
+ internal_memset(this, 0, sizeof(DataInfo));
+}
+
+void FrameInfo::Clear() {
+ InternalFree(module);
+ for (LocalInfo &local : locals) {
+ InternalFree(local.function_name);
+ InternalFree(local.name);
+ InternalFree(local.decl_file);
+ }
+ locals.clear();
+}
+
+Symbolizer *Symbolizer::symbolizer_;
+StaticSpinMutex Symbolizer::init_mu_;
+LowLevelAllocator Symbolizer::symbolizer_allocator_;
+
+void Symbolizer::InvalidateModuleList() {
+ modules_fresh_ = false;
+}
+
+void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook,
+ Symbolizer::EndSymbolizationHook end_hook) {
+ CHECK(start_hook_ == 0 && end_hook_ == 0);
+ start_hook_ = start_hook;
+ end_hook_ = end_hook;
+}
+
+const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) {
+ mu_->CheckLocked();
+
+ // 'str' will be the same string multiple times in a row, optimize this case.
+ if (last_match_ && !internal_strcmp(last_match_, str))
+ return last_match_;
+
+ // FIXME: this is linear search.
+ // We should optimize this further if this turns out to be a bottleneck later.
+ for (uptr i = 0; i < storage_.size(); ++i) {
+ if (!internal_strcmp(storage_[i], str)) {
+ last_match_ = storage_[i];
+ return last_match_;
+ }
+ }
+ last_match_ = internal_strdup(str);
+ storage_.push_back(last_match_);
+ return last_match_;
+}
+
+Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools)
+ : module_names_(&mu_), modules_(), modules_fresh_(false), tools_(tools),
+ start_hook_(0), end_hook_(0) {}
+
+Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym)
+ : sym_(sym) {
+ if (sym_->start_hook_)
+ sym_->start_hook_();
+}
+
+Symbolizer::SymbolizerScope::~SymbolizerScope() {
+ if (sym_->end_hook_)
+ sym_->end_hook_();
+}
+
+} // namespace __sanitizer
//===-- sanitizer_symbolizer.h ----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#include "sanitizer_common.h"
#include "sanitizer_mutex.h"
+#include "sanitizer_vector.h"
namespace __sanitizer {
void Clear();
};
+struct LocalInfo {
+ char *function_name = nullptr;
+ char *name = nullptr;
+ char *decl_file = nullptr;
+ unsigned decl_line = 0;
+
+ bool has_frame_offset = false;
+ bool has_size = false;
+ bool has_tag_offset = false;
+
+ sptr frame_offset;
+ uptr size;
+ uptr tag_offset;
+
+ void Clear();
+};
+
+struct FrameInfo {
+ char *module;
+ uptr module_offset;
+ ModuleArch module_arch;
+
+ InternalMmapVector<LocalInfo> locals;
+ void Clear();
+};
+
class SymbolizerTool;
class Symbolizer final {
// all inlined functions, if necessary).
SymbolizedStack *SymbolizePC(uptr address);
bool SymbolizeData(uptr address, DataInfo *info);
+ bool SymbolizeFrame(uptr address, FrameInfo *info);
// The module names Symbolizer returns are stable and unique for every given
// module. It is safe to store and compare them as pointers.
//===-- sanitizer_symbolizer_fuchsia.h -----------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// One frame in a backtrace (printed on a line by itself).
constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}";
+// Dump trigger element.
+#define FORMAT_DUMPFILE "{{{dumpfile:%s:%s}}}"
+
} // namespace __sanitizer
#endif // SANITIZER_SYMBOLIZER_FUCHSIA_H
//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#include "sanitizer_symbolizer.h"
#include "sanitizer_file.h"
+#include "sanitizer_vector.h"
namespace __sanitizer {
UNIMPLEMENTED();
}
+ virtual bool SymbolizeFrame(uptr addr, FrameInfo *info) {
+ return false;
+ }
+
virtual void Flush() {}
// Return nullptr to fallback to the default platform-specific demangler.
const char *SendCommand(const char *command);
protected:
+ /// The maximum number of arguments required to invoke a tool process.
+ static const unsigned kArgVMax = 6;
+
+ // Customizable by subclasses.
+ virtual bool StartSymbolizerSubprocess();
+ virtual bool ReadFromSymbolizer(char *buffer, uptr max_length);
+
+ private:
virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const {
UNIMPLEMENTED();
}
- /// The maximum number of arguments required to invoke a tool process.
- enum { kArgVMax = 6 };
-
/// Fill in an argv array to invoke the child process.
virtual void GetArgV(const char *path_to_binary,
const char *(&argv)[kArgVMax]) const {
UNIMPLEMENTED();
}
- virtual bool ReadFromSymbolizer(char *buffer, uptr max_length);
-
- private:
bool Restart();
const char *SendCommandImpl(const char *command);
bool WriteToSymbolizer(const char *buffer, uptr length);
- bool StartSymbolizerSubprocess();
const char *path_;
fd_t input_fd_;
explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator);
bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
-
bool SymbolizeData(uptr addr, DataInfo *info) override;
+ bool SymbolizeFrame(uptr addr, FrameInfo *info) override;
private:
- const char *FormatAndSendCommand(bool is_data, const char *module_name,
- uptr module_offset, ModuleArch arch);
+ const char *FormatAndSendCommand(const char *command_prefix,
+ const char *module_name, uptr module_offset,
+ ModuleArch arch);
LLVMSymbolizerProcess *symbolizer_process_;
static const uptr kBufferSize = 16 * 1024;
+++ /dev/null
-//===-- sanitizer_symbolizer_libbacktrace.cc ------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-// Libbacktrace implementation of symbolizer parts.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer.h"
-#include "sanitizer_symbolizer_libbacktrace.h"
-
-#if SANITIZER_LIBBACKTRACE
-# include "backtrace-supported.h"
-# if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC
-# include "backtrace.h"
-# if SANITIZER_CP_DEMANGLE
-# undef ARRAY_SIZE
-# include "demangle.h"
-# endif
-# else
-# define SANITIZER_LIBBACKTRACE 0
-# endif
-#endif
-
-namespace __sanitizer {
-
-static char *DemangleAlloc(const char *name, bool always_alloc);
-
-#if SANITIZER_LIBBACKTRACE
-
-namespace {
-
-# if SANITIZER_CP_DEMANGLE
-struct CplusV3DemangleData {
- char *buf;
- uptr size, allocated;
-};
-
-extern "C" {
-static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) {
- CplusV3DemangleData *data = (CplusV3DemangleData *)vdata;
- uptr needed = data->size + l + 1;
- if (needed > data->allocated) {
- data->allocated *= 2;
- if (needed > data->allocated)
- data->allocated = needed;
- char *buf = (char *)InternalAlloc(data->allocated);
- if (data->buf) {
- internal_memcpy(buf, data->buf, data->size);
- InternalFree(data->buf);
- }
- data->buf = buf;
- }
- internal_memcpy(data->buf + data->size, s, l);
- data->buf[data->size + l] = '\0';
- data->size += l;
-}
-} // extern "C"
-
-char *CplusV3Demangle(const char *name) {
- CplusV3DemangleData data;
- data.buf = 0;
- data.size = 0;
- data.allocated = 0;
- if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI,
- CplusV3DemangleCallback, &data)) {
- if (data.size + 64 > data.allocated)
- return data.buf;
- char *buf = internal_strdup(data.buf);
- InternalFree(data.buf);
- return buf;
- }
- if (data.buf)
- InternalFree(data.buf);
- return 0;
-}
-# endif // SANITIZER_CP_DEMANGLE
-
-struct SymbolizeCodeCallbackArg {
- SymbolizedStack *first;
- SymbolizedStack *last;
- uptr frames_symbolized;
-
- AddressInfo *get_new_frame(uintptr_t addr) {
- CHECK(last);
- if (frames_symbolized > 0) {
- SymbolizedStack *cur = SymbolizedStack::New(addr);
- AddressInfo *info = &cur->info;
- info->FillModuleInfo(first->info.module, first->info.module_offset,
- first->info.module_arch);
- last->next = cur;
- last = cur;
- }
- CHECK_EQ(addr, first->info.address);
- CHECK_EQ(addr, last->info.address);
- return &last->info;
- }
-};
-
-extern "C" {
-static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,
- const char *filename, int lineno,
- const char *function) {
- SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
- if (function) {
- AddressInfo *info = cdata->get_new_frame(addr);
- info->function = DemangleAlloc(function, /*always_alloc*/ true);
- if (filename)
- info->file = internal_strdup(filename);
- info->line = lineno;
- cdata->frames_symbolized++;
- }
- return 0;
-}
-
-static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,
- const char *symname, uintptr_t, uintptr_t) {
- SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
- if (symname) {
- AddressInfo *info = cdata->get_new_frame(addr);
- info->function = DemangleAlloc(symname, /*always_alloc*/ true);
- cdata->frames_symbolized++;
- }
-}
-
-static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname,
- uintptr_t symval, uintptr_t symsize) {
- DataInfo *info = (DataInfo *)vdata;
- if (symname && symval) {
- info->name = DemangleAlloc(symname, /*always_alloc*/ true);
- info->start = symval;
- info->size = symsize;
- }
-}
-
-static void ErrorCallback(void *, const char *, int) {}
-} // extern "C"
-
-} // namespace
-
-LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
- // State created in backtrace_create_state is leaked.
- void *state = (void *)(backtrace_create_state("/proc/self/exe", 0,
- ErrorCallback, NULL));
- if (!state)
- return 0;
- return new(*alloc) LibbacktraceSymbolizer(state);
-}
-
-bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
- SymbolizeCodeCallbackArg data;
- data.first = stack;
- data.last = stack;
- data.frames_symbolized = 0;
- backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
- ErrorCallback, &data);
- if (data.frames_symbolized > 0)
- return true;
- backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
- ErrorCallback, &data);
- return (data.frames_symbolized > 0);
-}
-
-bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
- backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback,
- ErrorCallback, info);
- return true;
-}
-
-#else // SANITIZER_LIBBACKTRACE
-
-LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
- return 0;
-}
-
-bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
- (void)state_;
- return false;
-}
-
-bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
- return false;
-}
-
-#endif // SANITIZER_LIBBACKTRACE
-
-static char *DemangleAlloc(const char *name, bool always_alloc) {
-#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
- if (char *demangled = CplusV3Demangle(name))
- return demangled;
-#endif
- if (always_alloc)
- return internal_strdup(name);
- return 0;
-}
-
-const char *LibbacktraceSymbolizer::Demangle(const char *name) {
- return DemangleAlloc(name, /*always_alloc*/ false);
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_symbolizer_libbacktrace.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Libbacktrace implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer.h"
+#include "sanitizer_symbolizer_libbacktrace.h"
+
+#if SANITIZER_LIBBACKTRACE
+# include "backtrace-supported.h"
+# if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC
+# include "backtrace.h"
+# if SANITIZER_CP_DEMANGLE
+# undef ARRAY_SIZE
+# include "demangle.h"
+# endif
+# else
+# define SANITIZER_LIBBACKTRACE 0
+# endif
+#endif
+
+namespace __sanitizer {
+
+static char *DemangleAlloc(const char *name, bool always_alloc);
+
+#if SANITIZER_LIBBACKTRACE
+
+namespace {
+
+# if SANITIZER_CP_DEMANGLE
+struct CplusV3DemangleData {
+ char *buf;
+ uptr size, allocated;
+};
+
+extern "C" {
+static void CplusV3DemangleCallback(const char *s, size_t l, void *vdata) {
+ CplusV3DemangleData *data = (CplusV3DemangleData *)vdata;
+ uptr needed = data->size + l + 1;
+ if (needed > data->allocated) {
+ data->allocated *= 2;
+ if (needed > data->allocated)
+ data->allocated = needed;
+ char *buf = (char *)InternalAlloc(data->allocated);
+ if (data->buf) {
+ internal_memcpy(buf, data->buf, data->size);
+ InternalFree(data->buf);
+ }
+ data->buf = buf;
+ }
+ internal_memcpy(data->buf + data->size, s, l);
+ data->buf[data->size + l] = '\0';
+ data->size += l;
+}
+} // extern "C"
+
+char *CplusV3Demangle(const char *name) {
+ CplusV3DemangleData data;
+ data.buf = 0;
+ data.size = 0;
+ data.allocated = 0;
+ if (cplus_demangle_v3_callback(name, DMGL_PARAMS | DMGL_ANSI,
+ CplusV3DemangleCallback, &data)) {
+ if (data.size + 64 > data.allocated)
+ return data.buf;
+ char *buf = internal_strdup(data.buf);
+ InternalFree(data.buf);
+ return buf;
+ }
+ if (data.buf)
+ InternalFree(data.buf);
+ return 0;
+}
+# endif // SANITIZER_CP_DEMANGLE
+
+struct SymbolizeCodeCallbackArg {
+ SymbolizedStack *first;
+ SymbolizedStack *last;
+ uptr frames_symbolized;
+
+ AddressInfo *get_new_frame(uintptr_t addr) {
+ CHECK(last);
+ if (frames_symbolized > 0) {
+ SymbolizedStack *cur = SymbolizedStack::New(addr);
+ AddressInfo *info = &cur->info;
+ info->FillModuleInfo(first->info.module, first->info.module_offset,
+ first->info.module_arch);
+ last->next = cur;
+ last = cur;
+ }
+ CHECK_EQ(addr, first->info.address);
+ CHECK_EQ(addr, last->info.address);
+ return &last->info;
+ }
+};
+
+extern "C" {
+static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr,
+ const char *filename, int lineno,
+ const char *function) {
+ SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
+ if (function) {
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(function, /*always_alloc*/ true);
+ if (filename)
+ info->file = internal_strdup(filename);
+ info->line = lineno;
+ cdata->frames_symbolized++;
+ }
+ return 0;
+}
+
+static void SymbolizeCodeCallback(void *vdata, uintptr_t addr,
+ const char *symname, uintptr_t, uintptr_t) {
+ SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata;
+ if (symname) {
+ AddressInfo *info = cdata->get_new_frame(addr);
+ info->function = DemangleAlloc(symname, /*always_alloc*/ true);
+ cdata->frames_symbolized++;
+ }
+}
+
+static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname,
+ uintptr_t symval, uintptr_t symsize) {
+ DataInfo *info = (DataInfo *)vdata;
+ if (symname && symval) {
+ info->name = DemangleAlloc(symname, /*always_alloc*/ true);
+ info->start = symval;
+ info->size = symsize;
+ }
+}
+
+static void ErrorCallback(void *, const char *, int) {}
+} // extern "C"
+
+} // namespace
+
+LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
+ // State created in backtrace_create_state is leaked.
+ void *state = (void *)(backtrace_create_state("/proc/self/exe", 0,
+ ErrorCallback, NULL));
+ if (!state)
+ return 0;
+ return new(*alloc) LibbacktraceSymbolizer(state);
+}
+
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ SymbolizeCodeCallbackArg data;
+ data.first = stack;
+ data.last = stack;
+ data.frames_symbolized = 0;
+ backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback,
+ ErrorCallback, &data);
+ if (data.frames_symbolized > 0)
+ return true;
+ backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback,
+ ErrorCallback, &data);
+ return (data.frames_symbolized > 0);
+}
+
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeDataCallback,
+ ErrorCallback, info);
+ return true;
+}
+
+#else // SANITIZER_LIBBACKTRACE
+
+LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) {
+ return 0;
+}
+
+bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ (void)state_;
+ return false;
+}
+
+bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ return false;
+}
+
+#endif // SANITIZER_LIBBACKTRACE
+
+static char *DemangleAlloc(const char *name, bool always_alloc) {
+#if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE
+ if (char *demangled = CplusV3Demangle(name))
+ return demangled;
+#endif
+ if (always_alloc)
+ return internal_strdup(name);
+ return 0;
+}
+
+const char *LibbacktraceSymbolizer::Demangle(const char *name) {
+ return DemangleAlloc(name, /*always_alloc*/ false);
+}
+
+} // namespace __sanitizer
//===-- sanitizer_symbolizer_libbacktrace.h ---------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_symbolizer_libcdep.cc -----------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_symbolizer_internal.h"
-
-namespace __sanitizer {
-
-Symbolizer *Symbolizer::GetOrInit() {
- SpinMutexLock l(&init_mu_);
- if (symbolizer_)
- return symbolizer_;
- symbolizer_ = PlatformInit();
- CHECK(symbolizer_);
- return symbolizer_;
-}
-
-// See sanitizer_symbolizer_markup.cc.
-#if !SANITIZER_SYMBOLIZER_MARKUP
-
-const char *ExtractToken(const char *str, const char *delims, char **result) {
- uptr prefix_len = internal_strcspn(str, delims);
- *result = (char*)InternalAlloc(prefix_len + 1);
- internal_memcpy(*result, str, prefix_len);
- (*result)[prefix_len] = '\0';
- const char *prefix_end = str + prefix_len;
- if (*prefix_end != '\0') prefix_end++;
- return prefix_end;
-}
-
-const char *ExtractInt(const char *str, const char *delims, int *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (int)internal_atoll(buff);
- }
- InternalFree(buff);
- return ret;
-}
-
-const char *ExtractUptr(const char *str, const char *delims, uptr *result) {
- char *buff;
- const char *ret = ExtractToken(str, delims, &buff);
- if (buff != 0) {
- *result = (uptr)internal_atoll(buff);
- }
- InternalFree(buff);
- return ret;
-}
-
-const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
- char **result) {
- const char *found_delimiter = internal_strstr(str, delimiter);
- uptr prefix_len =
- found_delimiter ? found_delimiter - str : internal_strlen(str);
- *result = (char *)InternalAlloc(prefix_len + 1);
- internal_memcpy(*result, str, prefix_len);
- (*result)[prefix_len] = '\0';
- const char *prefix_end = str + prefix_len;
- if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
- return prefix_end;
-}
-
-SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
- BlockingMutexLock l(&mu_);
- const char *module_name;
- uptr module_offset;
- ModuleArch arch;
- SymbolizedStack *res = SymbolizedStack::New(addr);
- if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,
- &arch))
- return res;
- // Always fill data about module name and offset.
- res->info.FillModuleInfo(module_name, module_offset, arch);
- for (auto &tool : tools_) {
- SymbolizerScope sym_scope(this);
- if (tool.SymbolizePC(addr, res)) {
- return res;
- }
- }
- return res;
-}
-
-bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
- BlockingMutexLock l(&mu_);
- const char *module_name;
- uptr module_offset;
- ModuleArch arch;
- if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,
- &arch))
- return false;
- info->Clear();
- info->module = internal_strdup(module_name);
- info->module_offset = module_offset;
- info->module_arch = arch;
- for (auto &tool : tools_) {
- SymbolizerScope sym_scope(this);
- if (tool.SymbolizeData(addr, info)) {
- return true;
- }
- }
- return true;
-}
-
-bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) {
- BlockingMutexLock l(&mu_);
- const char *internal_module_name = nullptr;
- ModuleArch arch;
- if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
- module_address, &arch))
- return false;
-
- if (module_name)
- *module_name = module_names_.GetOwnedCopy(internal_module_name);
- return true;
-}
-
-void Symbolizer::Flush() {
- BlockingMutexLock l(&mu_);
- for (auto &tool : tools_) {
- SymbolizerScope sym_scope(this);
- tool.Flush();
- }
-}
-
-const char *Symbolizer::Demangle(const char *name) {
- BlockingMutexLock l(&mu_);
- for (auto &tool : tools_) {
- SymbolizerScope sym_scope(this);
- if (const char *demangled = tool.Demangle(name))
- return demangled;
- }
- return PlatformDemangle(name);
-}
-
-bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
- const char **module_name,
- uptr *module_offset,
- ModuleArch *module_arch) {
- const LoadedModule *module = FindModuleForAddress(address);
- if (module == nullptr)
- return false;
- *module_name = module->full_name();
- *module_offset = address - module->base_address();
- *module_arch = module->arch();
- return true;
-}
-
-void Symbolizer::RefreshModules() {
- modules_.init();
- fallback_modules_.fallbackInit();
- RAW_CHECK(modules_.size() > 0);
- modules_fresh_ = true;
-}
-
-static const LoadedModule *SearchForModule(const ListOfModules &modules,
- uptr address) {
- for (uptr i = 0; i < modules.size(); i++) {
- if (modules[i].containsAddress(address)) {
- return &modules[i];
- }
- }
- return nullptr;
-}
-
-const LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
- bool modules_were_reloaded = false;
- if (!modules_fresh_) {
- RefreshModules();
- modules_were_reloaded = true;
- }
- const LoadedModule *module = SearchForModule(modules_, address);
- if (module) return module;
-
- // dlopen/dlclose interceptors invalidate the module list, but when
- // interception is disabled, we need to retry if the lookup fails in
- // case the module list changed.
-#if !SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
- if (!modules_were_reloaded) {
- RefreshModules();
- module = SearchForModule(modules_, address);
- if (module) return module;
- }
-#endif
-
- if (fallback_modules_.size()) {
- module = SearchForModule(fallback_modules_, address);
- }
- return module;
-}
-
-// For now we assume the following protocol:
-// For each request of the form
-// <module_name> <module_offset>
-// passed to STDIN, external symbolizer prints to STDOUT response:
-// <function_name>
-// <file_name>:<line_number>:<column_number>
-// <function_name>
-// <file_name>:<line_number>:<column_number>
-// ...
-// <empty line>
-class LLVMSymbolizerProcess : public SymbolizerProcess {
- public:
- explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
-
- private:
- bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
- // Empty line marks the end of llvm-symbolizer output.
- return length >= 2 && buffer[length - 1] == '\n' &&
- buffer[length - 2] == '\n';
- }
-
- // When adding a new architecture, don't forget to also update
- // script/asan_symbolize.py and sanitizer_common.h.
- void GetArgV(const char *path_to_binary,
- const char *(&argv)[kArgVMax]) const override {
-#if defined(__x86_64h__)
- const char* const kSymbolizerArch = "--default-arch=x86_64h";
-#elif defined(__x86_64__)
- const char* const kSymbolizerArch = "--default-arch=x86_64";
-#elif defined(__i386__)
- const char* const kSymbolizerArch = "--default-arch=i386";
-#elif defined(__aarch64__)
- const char* const kSymbolizerArch = "--default-arch=arm64";
-#elif defined(__arm__)
- const char* const kSymbolizerArch = "--default-arch=arm";
-#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- const char* const kSymbolizerArch = "--default-arch=powerpc64";
-#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
- const char* const kSymbolizerArch = "--default-arch=powerpc64le";
-#elif defined(__s390x__)
- const char* const kSymbolizerArch = "--default-arch=s390x";
-#elif defined(__s390__)
- const char* const kSymbolizerArch = "--default-arch=s390";
-#else
- const char* const kSymbolizerArch = "--default-arch=unknown";
-#endif
-
- const char *const inline_flag = common_flags()->symbolize_inline_frames
- ? "--inlining=true"
- : "--inlining=false";
- int i = 0;
- argv[i++] = path_to_binary;
- argv[i++] = inline_flag;
- argv[i++] = kSymbolizerArch;
- argv[i++] = nullptr;
- }
-};
-
-LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
- : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
-
-// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on
-// Windows, so extract tokens from the right hand side first. The column info is
-// also optional.
-static const char *ParseFileLineInfo(AddressInfo *info, const char *str) {
- char *file_line_info = 0;
- str = ExtractToken(str, "\n", &file_line_info);
- CHECK(file_line_info);
-
- if (uptr size = internal_strlen(file_line_info)) {
- char *back = file_line_info + size - 1;
- for (int i = 0; i < 2; ++i) {
- while (back > file_line_info && IsDigit(*back)) --back;
- if (*back != ':' || !IsDigit(back[1])) break;
- info->column = info->line;
- info->line = internal_atoll(back + 1);
- // Truncate the string at the colon to keep only filename.
- *back = '\0';
- --back;
- }
- ExtractToken(file_line_info, "", &info->file);
- }
-
- InternalFree(file_line_info);
- return str;
-}
-
-// Parses one or more two-line strings in the following format:
-// <function_name>
-// <file_name>:<line_number>[:<column_number>]
-// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
-// them use the same output format.
-void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
- bool top_frame = true;
- SymbolizedStack *last = res;
- while (true) {
- char *function_name = 0;
- str = ExtractToken(str, "\n", &function_name);
- CHECK(function_name);
- if (function_name[0] == '\0') {
- // There are no more frames.
- InternalFree(function_name);
- break;
- }
- SymbolizedStack *cur;
- if (top_frame) {
- cur = res;
- top_frame = false;
- } else {
- cur = SymbolizedStack::New(res->info.address);
- cur->info.FillModuleInfo(res->info.module, res->info.module_offset,
- res->info.module_arch);
- last->next = cur;
- last = cur;
- }
-
- AddressInfo *info = &cur->info;
- info->function = function_name;
- str = ParseFileLineInfo(info, str);
-
- // Functions and filenames can be "??", in which case we write 0
- // to address info to mark that names are unknown.
- if (0 == internal_strcmp(info->function, "??")) {
- InternalFree(info->function);
- info->function = 0;
- }
- if (0 == internal_strcmp(info->file, "??")) {
- InternalFree(info->file);
- info->file = 0;
- }
- }
-}
-
-// Parses a two-line string in the following format:
-// <symbol_name>
-// <start_address> <size>
-// Used by LLVMSymbolizer and InternalSymbolizer.
-void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
- str = ExtractToken(str, "\n", &info->name);
- str = ExtractUptr(str, " ", &info->start);
- str = ExtractUptr(str, "\n", &info->size);
-}
-
-bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
- AddressInfo *info = &stack->info;
- const char *buf = FormatAndSendCommand(
- /*is_data*/ false, info->module, info->module_offset, info->module_arch);
- if (buf) {
- ParseSymbolizePCOutput(buf, stack);
- return true;
- }
- return false;
-}
-
-bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
- const char *buf = FormatAndSendCommand(
- /*is_data*/ true, info->module, info->module_offset, info->module_arch);
- if (buf) {
- ParseSymbolizeDataOutput(buf, info);
- info->start += (addr - info->module_offset); // Add the base address.
- return true;
- }
- return false;
-}
-
-const char *LLVMSymbolizer::FormatAndSendCommand(bool is_data,
- const char *module_name,
- uptr module_offset,
- ModuleArch arch) {
- CHECK(module_name);
- const char *is_data_str = is_data ? "DATA " : "";
- if (arch == kModuleArchUnknown) {
- if (internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", is_data_str,
- module_name,
- module_offset) >= static_cast<int>(kBufferSize)) {
- Report("WARNING: Command buffer too small");
- return nullptr;
- }
- } else {
- if (internal_snprintf(buffer_, kBufferSize, "%s\"%s:%s\" 0x%zx\n",
- is_data_str, module_name, ModuleArchToString(arch),
- module_offset) >= static_cast<int>(kBufferSize)) {
- Report("WARNING: Command buffer too small");
- return nullptr;
- }
- }
- return symbolizer_process_->SendCommand(buffer_);
-}
-
-SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
- : path_(path),
- input_fd_(kInvalidFd),
- output_fd_(kInvalidFd),
- times_restarted_(0),
- failed_to_start_(false),
- reported_invalid_path_(false),
- use_forkpty_(use_forkpty) {
- CHECK(path_);
- CHECK_NE(path_[0], '\0');
-}
-
-static bool IsSameModule(const char* path) {
- if (const char* ProcessName = GetProcessName()) {
- if (const char* SymbolizerName = StripModuleName(path)) {
- return !internal_strcmp(ProcessName, SymbolizerName);
- }
- }
- return false;
-}
-
-const char *SymbolizerProcess::SendCommand(const char *command) {
- if (failed_to_start_)
- return nullptr;
- if (IsSameModule(path_)) {
- Report("WARNING: Symbolizer was blocked from starting itself!\n");
- failed_to_start_ = true;
- return nullptr;
- }
- for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
- // Start or restart symbolizer if we failed to send command to it.
- if (const char *res = SendCommandImpl(command))
- return res;
- Restart();
- }
- if (!failed_to_start_) {
- Report("WARNING: Failed to use and restart external symbolizer!\n");
- failed_to_start_ = true;
- }
- return 0;
-}
-
-const char *SymbolizerProcess::SendCommandImpl(const char *command) {
- if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
- return 0;
- if (!WriteToSymbolizer(command, internal_strlen(command)))
- return 0;
- if (!ReadFromSymbolizer(buffer_, kBufferSize))
- return 0;
- return buffer_;
-}
-
-bool SymbolizerProcess::Restart() {
- if (input_fd_ != kInvalidFd)
- CloseFile(input_fd_);
- if (output_fd_ != kInvalidFd)
- CloseFile(output_fd_);
- return StartSymbolizerSubprocess();
-}
-
-bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
- if (max_length == 0)
- return true;
- uptr read_len = 0;
- while (true) {
- uptr just_read = 0;
- bool success = ReadFromFile(input_fd_, buffer + read_len,
- max_length - read_len - 1, &just_read);
- // We can't read 0 bytes, as we don't expect external symbolizer to close
- // its stdout.
- if (!success || just_read == 0) {
- Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
- return false;
- }
- read_len += just_read;
- if (ReachedEndOfOutput(buffer, read_len))
- break;
- if (read_len + 1 == max_length) {
- Report("WARNING: Symbolizer buffer too small\n");
- read_len = 0;
- break;
- }
- }
- buffer[read_len] = '\0';
- return true;
-}
-
-bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
- if (length == 0)
- return true;
- uptr write_len = 0;
- bool success = WriteToFile(output_fd_, buffer, length, &write_len);
- if (!success || write_len != length) {
- Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
- return false;
- }
- return true;
-}
-
-#endif // !SANITIZER_SYMBOLIZER_MARKUP
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_symbolizer_libcdep.cpp ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+Symbolizer *Symbolizer::GetOrInit() {
+ SpinMutexLock l(&init_mu_);
+ if (symbolizer_)
+ return symbolizer_;
+ symbolizer_ = PlatformInit();
+ CHECK(symbolizer_);
+ return symbolizer_;
+}
+
+// See sanitizer_symbolizer_markup.cpp.
+#if !SANITIZER_SYMBOLIZER_MARKUP
+
+const char *ExtractToken(const char *str, const char *delims, char **result) {
+ uptr prefix_len = internal_strcspn(str, delims);
+ *result = (char*)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end++;
+ return prefix_end;
+}
+
+const char *ExtractInt(const char *str, const char *delims, int *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (int)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractUptr(const char *str, const char *delims, uptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (uptr)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractSptr(const char *str, const char *delims, sptr *result) {
+ char *buff;
+ const char *ret = ExtractToken(str, delims, &buff);
+ if (buff != 0) {
+ *result = (sptr)internal_atoll(buff);
+ }
+ InternalFree(buff);
+ return ret;
+}
+
+const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter,
+ char **result) {
+ const char *found_delimiter = internal_strstr(str, delimiter);
+ uptr prefix_len =
+ found_delimiter ? found_delimiter - str : internal_strlen(str);
+ *result = (char *)InternalAlloc(prefix_len + 1);
+ internal_memcpy(*result, str, prefix_len);
+ (*result)[prefix_len] = '\0';
+ const char *prefix_end = str + prefix_len;
+ if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter);
+ return prefix_end;
+}
+
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ ModuleArch arch;
+ SymbolizedStack *res = SymbolizedStack::New(addr);
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,
+ &arch))
+ return res;
+ // Always fill data about module name and offset.
+ res->info.FillModuleInfo(module_name, module_offset, arch);
+ for (auto &tool : tools_) {
+ SymbolizerScope sym_scope(this);
+ if (tool.SymbolizePC(addr, res)) {
+ return res;
+ }
+ }
+ return res;
+}
+
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ uptr module_offset;
+ ModuleArch arch;
+ if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset,
+ &arch))
+ return false;
+ info->Clear();
+ info->module = internal_strdup(module_name);
+ info->module_offset = module_offset;
+ info->module_arch = arch;
+ for (auto &tool : tools_) {
+ SymbolizerScope sym_scope(this);
+ if (tool.SymbolizeData(addr, info)) {
+ return true;
+ }
+ }
+ return true;
+}
+
+bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {
+ BlockingMutexLock l(&mu_);
+ const char *module_name;
+ if (!FindModuleNameAndOffsetForAddress(
+ addr, &module_name, &info->module_offset, &info->module_arch))
+ return false;
+ info->module = internal_strdup(module_name);
+ for (auto &tool : tools_) {
+ SymbolizerScope sym_scope(this);
+ if (tool.SymbolizeFrame(addr, info)) {
+ return true;
+ }
+ }
+ return true;
+}
+
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address) {
+ BlockingMutexLock l(&mu_);
+ const char *internal_module_name = nullptr;
+ ModuleArch arch;
+ if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name,
+ module_address, &arch))
+ return false;
+
+ if (module_name)
+ *module_name = module_names_.GetOwnedCopy(internal_module_name);
+ return true;
+}
+
+void Symbolizer::Flush() {
+ BlockingMutexLock l(&mu_);
+ for (auto &tool : tools_) {
+ SymbolizerScope sym_scope(this);
+ tool.Flush();
+ }
+}
+
+const char *Symbolizer::Demangle(const char *name) {
+ BlockingMutexLock l(&mu_);
+ for (auto &tool : tools_) {
+ SymbolizerScope sym_scope(this);
+ if (const char *demangled = tool.Demangle(name))
+ return demangled;
+ }
+ return PlatformDemangle(name);
+}
+
+bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address,
+ const char **module_name,
+ uptr *module_offset,
+ ModuleArch *module_arch) {
+ const LoadedModule *module = FindModuleForAddress(address);
+ if (module == nullptr)
+ return false;
+ *module_name = module->full_name();
+ *module_offset = address - module->base_address();
+ *module_arch = module->arch();
+ return true;
+}
+
+void Symbolizer::RefreshModules() {
+ modules_.init();
+ fallback_modules_.fallbackInit();
+ RAW_CHECK(modules_.size() > 0);
+ modules_fresh_ = true;
+}
+
+static const LoadedModule *SearchForModule(const ListOfModules &modules,
+ uptr address) {
+ for (uptr i = 0; i < modules.size(); i++) {
+ if (modules[i].containsAddress(address)) {
+ return &modules[i];
+ }
+ }
+ return nullptr;
+}
+
+const LoadedModule *Symbolizer::FindModuleForAddress(uptr address) {
+ bool modules_were_reloaded = false;
+ if (!modules_fresh_) {
+ RefreshModules();
+ modules_were_reloaded = true;
+ }
+ const LoadedModule *module = SearchForModule(modules_, address);
+ if (module) return module;
+
+ // dlopen/dlclose interceptors invalidate the module list, but when
+ // interception is disabled, we need to retry if the lookup fails in
+ // case the module list changed.
+#if !SANITIZER_INTERCEPT_DLOPEN_DLCLOSE
+ if (!modules_were_reloaded) {
+ RefreshModules();
+ module = SearchForModule(modules_, address);
+ if (module) return module;
+ }
+#endif
+
+ if (fallback_modules_.size()) {
+ module = SearchForModule(fallback_modules_, address);
+ }
+ return module;
+}
+
+// For now we assume the following protocol:
+// For each request of the form
+// <module_name> <module_offset>
+// passed to STDIN, external symbolizer prints to STDOUT response:
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// <function_name>
+// <file_name>:<line_number>:<column_number>
+// ...
+// <empty line>
+class LLVMSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {}
+
+ private:
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ // Empty line marks the end of llvm-symbolizer output.
+ return length >= 2 && buffer[length - 1] == '\n' &&
+ buffer[length - 2] == '\n';
+ }
+
+ // When adding a new architecture, don't forget to also update
+ // script/asan_symbolize.py and sanitizer_common.h.
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+#if defined(__x86_64h__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64h";
+#elif defined(__x86_64__)
+ const char* const kSymbolizerArch = "--default-arch=x86_64";
+#elif defined(__i386__)
+ const char* const kSymbolizerArch = "--default-arch=i386";
+#elif defined(__aarch64__)
+ const char* const kSymbolizerArch = "--default-arch=arm64";
+#elif defined(__arm__)
+ const char* const kSymbolizerArch = "--default-arch=arm";
+#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ const char* const kSymbolizerArch = "--default-arch=powerpc64";
+#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ const char* const kSymbolizerArch = "--default-arch=powerpc64le";
+#elif defined(__s390x__)
+ const char* const kSymbolizerArch = "--default-arch=s390x";
+#elif defined(__s390__)
+ const char* const kSymbolizerArch = "--default-arch=s390";
+#else
+ const char* const kSymbolizerArch = "--default-arch=unknown";
+#endif
+
+ const char *const inline_flag = common_flags()->symbolize_inline_frames
+ ? "--inlining=true"
+ : "--inlining=false";
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = inline_flag;
+ argv[i++] = kSymbolizerArch;
+ argv[i++] = nullptr;
+ }
+};
+
+LLVMSymbolizer::LLVMSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {}
+
+// Parse a <file>:<line>[:<column>] buffer. The file path may contain colons on
+// Windows, so extract tokens from the right hand side first. The column info is
+// also optional.
+static const char *ParseFileLineInfo(AddressInfo *info, const char *str) {
+ char *file_line_info = 0;
+ str = ExtractToken(str, "\n", &file_line_info);
+ CHECK(file_line_info);
+
+ if (uptr size = internal_strlen(file_line_info)) {
+ char *back = file_line_info + size - 1;
+ for (int i = 0; i < 2; ++i) {
+ while (back > file_line_info && IsDigit(*back)) --back;
+ if (*back != ':' || !IsDigit(back[1])) break;
+ info->column = info->line;
+ info->line = internal_atoll(back + 1);
+ // Truncate the string at the colon to keep only filename.
+ *back = '\0';
+ --back;
+ }
+ ExtractToken(file_line_info, "", &info->file);
+ }
+
+ InternalFree(file_line_info);
+ return str;
+}
+
+// Parses one or more two-line strings in the following format:
+// <function_name>
+// <file_name>:<line_number>[:<column_number>]
+// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of
+// them use the same output format.
+void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) {
+ bool top_frame = true;
+ SymbolizedStack *last = res;
+ while (true) {
+ char *function_name = 0;
+ str = ExtractToken(str, "\n", &function_name);
+ CHECK(function_name);
+ if (function_name[0] == '\0') {
+ // There are no more frames.
+ InternalFree(function_name);
+ break;
+ }
+ SymbolizedStack *cur;
+ if (top_frame) {
+ cur = res;
+ top_frame = false;
+ } else {
+ cur = SymbolizedStack::New(res->info.address);
+ cur->info.FillModuleInfo(res->info.module, res->info.module_offset,
+ res->info.module_arch);
+ last->next = cur;
+ last = cur;
+ }
+
+ AddressInfo *info = &cur->info;
+ info->function = function_name;
+ str = ParseFileLineInfo(info, str);
+
+ // Functions and filenames can be "??", in which case we write 0
+ // to address info to mark that names are unknown.
+ if (0 == internal_strcmp(info->function, "??")) {
+ InternalFree(info->function);
+ info->function = 0;
+ }
+ if (0 == internal_strcmp(info->file, "??")) {
+ InternalFree(info->file);
+ info->file = 0;
+ }
+ }
+}
+
+// Parses a two-line string in the following format:
+// <symbol_name>
+// <start_address> <size>
+// Used by LLVMSymbolizer and InternalSymbolizer.
+void ParseSymbolizeDataOutput(const char *str, DataInfo *info) {
+ str = ExtractToken(str, "\n", &info->name);
+ str = ExtractUptr(str, " ", &info->start);
+ str = ExtractUptr(str, "\n", &info->size);
+}
+
+static void ParseSymbolizeFrameOutput(const char *str,
+ InternalMmapVector<LocalInfo> *locals) {
+ if (internal_strncmp(str, "??", 2) == 0)
+ return;
+
+ while (*str) {
+ LocalInfo local;
+ str = ExtractToken(str, "\n", &local.function_name);
+ str = ExtractToken(str, "\n", &local.name);
+
+ AddressInfo addr;
+ str = ParseFileLineInfo(&addr, str);
+ local.decl_file = addr.file;
+ local.decl_line = addr.line;
+
+ local.has_frame_offset = internal_strncmp(str, "??", 2) != 0;
+ str = ExtractSptr(str, " ", &local.frame_offset);
+
+ local.has_size = internal_strncmp(str, "??", 2) != 0;
+ str = ExtractUptr(str, " ", &local.size);
+
+ local.has_tag_offset = internal_strncmp(str, "??", 2) != 0;
+ str = ExtractUptr(str, "\n", &local.tag_offset);
+
+ locals->push_back(local);
+ }
+}
+
+bool LLVMSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ AddressInfo *info = &stack->info;
+ const char *buf = FormatAndSendCommand(
+ "CODE", info->module, info->module_offset, info->module_arch);
+ if (buf) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+}
+
+bool LLVMSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ const char *buf = FormatAndSendCommand(
+ "DATA", info->module, info->module_offset, info->module_arch);
+ if (buf) {
+ ParseSymbolizeDataOutput(buf, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ return true;
+ }
+ return false;
+}
+
+bool LLVMSymbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) {
+ const char *buf = FormatAndSendCommand(
+ "FRAME", info->module, info->module_offset, info->module_arch);
+ if (buf) {
+ ParseSymbolizeFrameOutput(buf, &info->locals);
+ return true;
+ }
+ return false;
+}
+
+const char *LLVMSymbolizer::FormatAndSendCommand(const char *command_prefix,
+ const char *module_name,
+ uptr module_offset,
+ ModuleArch arch) {
+ CHECK(module_name);
+ if (arch == kModuleArchUnknown) {
+ if (internal_snprintf(buffer_, kBufferSize, "%s \"%s\" 0x%zx\n",
+ command_prefix, module_name,
+ module_offset) >= static_cast<int>(kBufferSize)) {
+ Report("WARNING: Command buffer too small");
+ return nullptr;
+ }
+ } else {
+ if (internal_snprintf(buffer_, kBufferSize, "%s \"%s:%s\" 0x%zx\n",
+ command_prefix, module_name, ModuleArchToString(arch),
+ module_offset) >= static_cast<int>(kBufferSize)) {
+ Report("WARNING: Command buffer too small");
+ return nullptr;
+ }
+ }
+ return symbolizer_process_->SendCommand(buffer_);
+}
+
+SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty)
+ : path_(path),
+ input_fd_(kInvalidFd),
+ output_fd_(kInvalidFd),
+ times_restarted_(0),
+ failed_to_start_(false),
+ reported_invalid_path_(false),
+ use_forkpty_(use_forkpty) {
+ CHECK(path_);
+ CHECK_NE(path_[0], '\0');
+}
+
+static bool IsSameModule(const char* path) {
+ if (const char* ProcessName = GetProcessName()) {
+ if (const char* SymbolizerName = StripModuleName(path)) {
+ return !internal_strcmp(ProcessName, SymbolizerName);
+ }
+ }
+ return false;
+}
+
+const char *SymbolizerProcess::SendCommand(const char *command) {
+ if (failed_to_start_)
+ return nullptr;
+ if (IsSameModule(path_)) {
+ Report("WARNING: Symbolizer was blocked from starting itself!\n");
+ failed_to_start_ = true;
+ return nullptr;
+ }
+ for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) {
+ // Start or restart symbolizer if we failed to send command to it.
+ if (const char *res = SendCommandImpl(command))
+ return res;
+ Restart();
+ }
+ if (!failed_to_start_) {
+ Report("WARNING: Failed to use and restart external symbolizer!\n");
+ failed_to_start_ = true;
+ }
+ return 0;
+}
+
+const char *SymbolizerProcess::SendCommandImpl(const char *command) {
+ if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd)
+ return 0;
+ if (!WriteToSymbolizer(command, internal_strlen(command)))
+ return 0;
+ if (!ReadFromSymbolizer(buffer_, kBufferSize))
+ return 0;
+ return buffer_;
+}
+
+bool SymbolizerProcess::Restart() {
+ if (input_fd_ != kInvalidFd)
+ CloseFile(input_fd_);
+ if (output_fd_ != kInvalidFd)
+ CloseFile(output_fd_);
+ return StartSymbolizerSubprocess();
+}
+
+bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) {
+ if (max_length == 0)
+ return true;
+ uptr read_len = 0;
+ while (true) {
+ uptr just_read = 0;
+ bool success = ReadFromFile(input_fd_, buffer + read_len,
+ max_length - read_len - 1, &just_read);
+ // We can't read 0 bytes, as we don't expect external symbolizer to close
+ // its stdout.
+ if (!success || just_read == 0) {
+ Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_);
+ return false;
+ }
+ read_len += just_read;
+ if (ReachedEndOfOutput(buffer, read_len))
+ break;
+ if (read_len + 1 == max_length) {
+ Report("WARNING: Symbolizer buffer too small\n");
+ read_len = 0;
+ break;
+ }
+ }
+ buffer[read_len] = '\0';
+ return true;
+}
+
+bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) {
+ if (length == 0)
+ return true;
+ uptr write_len = 0;
+ bool success = WriteToFile(output_fd_, buffer, length, &write_len);
+ if (!success || write_len != length) {
+ Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_);
+ return false;
+ }
+ return true;
+}
+
+#endif // !SANITIZER_SYMBOLIZER_MARKUP
+
+} // namespace __sanitizer
+++ /dev/null
-//===-- sanitizer_symbolizer_mac.cc ---------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries.
-//
-// Implementation of Mac-specific "atos" symbolizer.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_mac.h"
-#include "sanitizer_symbolizer_mac.h"
-
-#include <dlfcn.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <util.h>
-
-namespace __sanitizer {
-
-bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
- Dl_info info;
- int result = dladdr((const void *)addr, &info);
- if (!result) return false;
- const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
- if (!demangled) return false;
- stack->info.function = internal_strdup(demangled);
- return true;
-}
-
-bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
- Dl_info info;
- int result = dladdr((const void *)addr, &info);
- if (!result) return false;
- const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
- datainfo->name = internal_strdup(demangled);
- datainfo->start = (uptr)info.dli_saddr;
- return true;
-}
-
-class AtosSymbolizerProcess : public SymbolizerProcess {
- public:
- explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
- : SymbolizerProcess(path, /*use_forkpty*/ true) {
- // Put the string command line argument in the object so that it outlives
- // the call to GetArgV.
- internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
- }
-
- private:
- bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
- return (length >= 1 && buffer[length - 1] == '\n');
- }
-
- void GetArgV(const char *path_to_binary,
- const char *(&argv)[kArgVMax]) const override {
- int i = 0;
- argv[i++] = path_to_binary;
- argv[i++] = "-p";
- argv[i++] = &pid_str_[0];
- if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
- // On Mavericks atos prints a deprecation warning which we suppress by
- // passing -d. The warning isn't present on other OSX versions, even the
- // newer ones.
- argv[i++] = "-d";
- }
- argv[i++] = nullptr;
- }
-
- char pid_str_[16];
-};
-
-static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
- char **out_module, char **out_file, uptr *line,
- uptr *start_address) {
- // Trim ending newlines.
- char *trim;
- ExtractTokenUpToDelimiter(str, "\n", &trim);
-
- // The line from `atos` is in one of these formats:
- // myfunction (in library.dylib) (sourcefile.c:17)
- // myfunction (in library.dylib) + 0x1fe
- // myfunction (in library.dylib) + 15
- // 0xdeadbeef (in library.dylib) + 0x1fe
- // 0xdeadbeef (in library.dylib) + 15
- // 0xdeadbeef (in library.dylib)
- // 0xdeadbeef
-
- const char *rest = trim;
- char *symbol_name;
- rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
- if (rest[0] == '\0') {
- InternalFree(symbol_name);
- InternalFree(trim);
- return false;
- }
-
- if (internal_strncmp(symbol_name, "0x", 2) != 0)
- *out_name = symbol_name;
- else
- InternalFree(symbol_name);
- rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
-
- if (rest[0] == '(') {
- if (out_file) {
- rest++;
- rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
- char *extracted_line_number;
- rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
- if (line) *line = (uptr)internal_atoll(extracted_line_number);
- InternalFree(extracted_line_number);
- }
- } else if (rest[0] == '+') {
- rest += 2;
- uptr offset = internal_atoll(rest);
- if (start_address) *start_address = addr - offset;
- }
-
- InternalFree(trim);
- return true;
-}
-
-AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
- : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
-
-bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
- if (!process_) return false;
- if (addr == 0) return false;
- char command[32];
- internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
- const char *buf = process_->SendCommand(command);
- if (!buf) return false;
- uptr line;
- if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
- &stack->info.file, &line, nullptr)) {
- process_ = nullptr;
- return false;
- }
- stack->info.line = (int)line;
- return true;
-}
-
-bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
- if (!process_) return false;
- char command[32];
- internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
- const char *buf = process_->SendCommand(command);
- if (!buf) return false;
- if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
- nullptr, &info->start)) {
- process_ = nullptr;
- return false;
- }
- return true;
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- sanitizer_symbolizer_mac.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Implementation of Mac-specific "atos" symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_mac.h"
+#include "sanitizer_symbolizer_mac.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <util.h>
+
+namespace __sanitizer {
+
+bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ Dl_info info;
+ int result = dladdr((const void *)addr, &info);
+ if (!result) return false;
+ const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
+ if (!demangled) return false;
+ stack->info.function = internal_strdup(demangled);
+ return true;
+}
+
+bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *datainfo) {
+ Dl_info info;
+ int result = dladdr((const void *)addr, &info);
+ if (!result) return false;
+ const char *demangled = DemangleSwiftAndCXX(info.dli_sname);
+ datainfo->name = internal_strdup(demangled);
+ datainfo->start = (uptr)info.dli_saddr;
+ return true;
+}
+
+class AtosSymbolizerProcess : public SymbolizerProcess {
+ public:
+ explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid)
+ : SymbolizerProcess(path, /*use_forkpty*/ true) {
+ // Put the string command line argument in the object so that it outlives
+ // the call to GetArgV.
+ internal_snprintf(pid_str_, sizeof(pid_str_), "%d", parent_pid);
+ }
+
+ private:
+ virtual bool StartSymbolizerSubprocess() override {
+ // Configure sandbox before starting atos process.
+ return SymbolizerProcess::StartSymbolizerSubprocess();
+ }
+
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override {
+ return (length >= 1 && buffer[length - 1] == '\n');
+ }
+
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = "-p";
+ argv[i++] = &pid_str_[0];
+ if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) {
+ // On Mavericks atos prints a deprecation warning which we suppress by
+ // passing -d. The warning isn't present on other OSX versions, even the
+ // newer ones.
+ argv[i++] = "-d";
+ }
+ argv[i++] = nullptr;
+ }
+
+ char pid_str_[16];
+};
+
+static bool ParseCommandOutput(const char *str, uptr addr, char **out_name,
+ char **out_module, char **out_file, uptr *line,
+ uptr *start_address) {
+ // Trim ending newlines.
+ char *trim;
+ ExtractTokenUpToDelimiter(str, "\n", &trim);
+
+ // The line from `atos` is in one of these formats:
+ // myfunction (in library.dylib) (sourcefile.c:17)
+ // myfunction (in library.dylib) + 0x1fe
+ // myfunction (in library.dylib) + 15
+ // 0xdeadbeef (in library.dylib) + 0x1fe
+ // 0xdeadbeef (in library.dylib) + 15
+ // 0xdeadbeef (in library.dylib)
+ // 0xdeadbeef
+
+ const char *rest = trim;
+ char *symbol_name;
+ rest = ExtractTokenUpToDelimiter(rest, " (in ", &symbol_name);
+ if (rest[0] == '\0') {
+ InternalFree(symbol_name);
+ InternalFree(trim);
+ return false;
+ }
+
+ if (internal_strncmp(symbol_name, "0x", 2) != 0)
+ *out_name = symbol_name;
+ else
+ InternalFree(symbol_name);
+ rest = ExtractTokenUpToDelimiter(rest, ") ", out_module);
+
+ if (rest[0] == '(') {
+ if (out_file) {
+ rest++;
+ rest = ExtractTokenUpToDelimiter(rest, ":", out_file);
+ char *extracted_line_number;
+ rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number);
+ if (line) *line = (uptr)internal_atoll(extracted_line_number);
+ InternalFree(extracted_line_number);
+ }
+ } else if (rest[0] == '+') {
+ rest += 2;
+ uptr offset = internal_atoll(rest);
+ if (start_address) *start_address = addr - offset;
+ }
+
+ InternalFree(trim);
+ return true;
+}
+
+AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator)
+ : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {}
+
+bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) {
+ if (!process_) return false;
+ if (addr == 0) return false;
+ char command[32];
+ internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+ const char *buf = process_->SendCommand(command);
+ if (!buf) return false;
+ uptr line;
+ if (!ParseCommandOutput(buf, addr, &stack->info.function, &stack->info.module,
+ &stack->info.file, &line, nullptr)) {
+ process_ = nullptr;
+ return false;
+ }
+ stack->info.line = (int)line;
+ return true;
+}
+
+bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ if (!process_) return false;
+ char command[32];
+ internal_snprintf(command, sizeof(command), "0x%zx\n", addr);
+ const char *buf = process_->SendCommand(command);
+ if (!buf) return false;
+ if (!ParseCommandOutput(buf, addr, &info->name, &info->module, nullptr,
+ nullptr, &info->start)) {
+ process_ = nullptr;
+ return false;
+ }
+ return true;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_MAC
//===-- sanitizer_symbolizer_mac.h ------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_symbolizer_markup.cc ------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between various sanitizers' runtime libraries.
-//
-// Implementation of offline markup symbolizer.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_SYMBOLIZER_MARKUP
-
-#if SANITIZER_FUCHSIA
-#include "sanitizer_symbolizer_fuchsia.h"
-#elif SANITIZER_RTEMS
-#include "sanitizer_symbolizer_rtems.h"
-#endif
-#include "sanitizer_stacktrace.h"
-#include "sanitizer_symbolizer.h"
-
-#include <limits.h>
-#include <unwind.h>
-
-namespace __sanitizer {
-
-// This generic support for offline symbolizing is based on the
-// Fuchsia port. We don't do any actual symbolization per se.
-// Instead, we emit text containing raw addresses and raw linkage
-// symbol names, embedded in Fuchsia's symbolization markup format.
-// Fuchsia's logging infrastructure emits enough information about
-// process memory layout that a post-processing filter can do the
-// symbolization and pretty-print the markup. See the spec at:
-// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
-
-// This is used by UBSan for type names, and by ASan for global variable names.
-// It's expected to return a static buffer that will be reused on each call.
-const char *Symbolizer::Demangle(const char *name) {
- static char buffer[kFormatDemangleMax];
- internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
- return buffer;
-}
-
-// This is used mostly for suppression matching. Making it work
-// would enable "interceptor_via_lib" suppressions. It's also used
-// once in UBSan to say "in module ..." in a message that also
-// includes an address in the module, so post-processing can already
-// pretty-print that so as to indicate the module.
-bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
- uptr *module_address) {
- return false;
-}
-
-// This is used in some places for suppression checking, which we
-// don't really support for Fuchsia. It's also used in UBSan to
-// identify a PC location to a function name, so we always fill in
-// the function member with a string containing markup around the PC
-// value.
-// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
-// to render stack frames, but that should be changed to use
-// RenderStackFrame.
-SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
- SymbolizedStack *s = SymbolizedStack::New(addr);
- char buffer[kFormatFunctionMax];
- internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr);
- s->info.function = internal_strdup(buffer);
- return s;
-}
-
-// Always claim we succeeded, so that RenderDataInfo will be called.
-bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
- info->Clear();
- info->start = addr;
- return true;
-}
-
-// We ignore the format argument to __sanitizer_symbolize_global.
-void RenderData(InternalScopedString *buffer, const char *format,
- const DataInfo *DI, const char *strip_path_prefix) {
- buffer->append(kFormatData, DI->start);
-}
-
-// We don't support the stack_trace_format flag at all.
-void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
- const AddressInfo &info, bool vs_style,
- const char *strip_path_prefix, const char *strip_func_prefix) {
- buffer->append(kFormatFrame, frame_no, info.address);
-}
-
-Symbolizer *Symbolizer::PlatformInit() {
- return new (symbolizer_allocator_) Symbolizer({});
-}
-
-void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
-
-void StartReportDeadlySignal() {}
-void ReportDeadlySignal(const SignalContext &sig, u32 tid,
- UnwindSignalStackCallbackType unwind,
- const void *unwind_context) {}
-
-#if SANITIZER_CAN_SLOW_UNWIND
-struct UnwindTraceArg {
- BufferedStackTrace *stack;
- u32 max_depth;
-};
-
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
- UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param);
- CHECK_LT(arg->stack->size, arg->max_depth);
- uptr pc = _Unwind_GetIP(ctx);
- if (pc < PAGE_SIZE) return _URC_NORMAL_STOP;
- arg->stack->trace_buffer[arg->stack->size++] = pc;
- return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP
- : _URC_NO_REASON);
-}
-
-void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
- CHECK_GE(max_depth, 2);
- size = 0;
- UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
- _Unwind_Backtrace(Unwind_Trace, &arg);
- CHECK_GT(size, 0);
- // We need to pop a few frames so that pc is on top.
- uptr to_pop = LocatePcInTrace(pc);
- // trace_buffer[0] belongs to the current function so we always pop it,
- // unless there is only 1 frame in the stack trace (1 frame is always better
- // than 0!).
- PopStackFrames(Min(to_pop, static_cast<uptr>(1)));
- trace_buffer[0] = pc;
-}
-
-void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- u32 max_depth) {
- CHECK_NE(context, nullptr);
- UNREACHABLE("signal context doesn't exist");
-}
-#endif // SANITIZER_CAN_SLOW_UNWIND
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_SYMBOLIZER_MARKUP
--- /dev/null
+//===-- sanitizer_symbolizer_markup.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between various sanitizers' runtime libraries.
+//
+// Implementation of offline markup symbolizer.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_SYMBOLIZER_MARKUP
+
+#if SANITIZER_FUCHSIA
+#include "sanitizer_symbolizer_fuchsia.h"
+#elif SANITIZER_RTEMS
+#include "sanitizer_symbolizer_rtems.h"
+#endif
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h"
+
+#include <limits.h>
+#include <unwind.h>
+
+namespace __sanitizer {
+
+// This generic support for offline symbolizing is based on the
+// Fuchsia port. We don't do any actual symbolization per se.
+// Instead, we emit text containing raw addresses and raw linkage
+// symbol names, embedded in Fuchsia's symbolization markup format.
+// Fuchsia's logging infrastructure emits enough information about
+// process memory layout that a post-processing filter can do the
+// symbolization and pretty-print the markup. See the spec at:
+// https://fuchsia.googlesource.com/zircon/+/master/docs/symbolizer_markup.md
+
+// This is used by UBSan for type names, and by ASan for global variable names.
+// It's expected to return a static buffer that will be reused on each call.
+const char *Symbolizer::Demangle(const char *name) {
+ static char buffer[kFormatDemangleMax];
+ internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name);
+ return buffer;
+}
+
+// This is used mostly for suppression matching. Making it work
+// would enable "interceptor_via_lib" suppressions. It's also used
+// once in UBSan to say "in module ..." in a message that also
+// includes an address in the module, so post-processing can already
+// pretty-print that so as to indicate the module.
+bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name,
+ uptr *module_address) {
+ return false;
+}
+
+// This is used in some places for suppression checking, which we
+// don't really support for Fuchsia. It's also used in UBSan to
+// identify a PC location to a function name, so we always fill in
+// the function member with a string containing markup around the PC
+// value.
+// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan
+// to render stack frames, but that should be changed to use
+// RenderStackFrame.
+SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) {
+ SymbolizedStack *s = SymbolizedStack::New(addr);
+ char buffer[kFormatFunctionMax];
+ internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr);
+ s->info.function = internal_strdup(buffer);
+ return s;
+}
+
+// Always claim we succeeded, so that RenderDataInfo will be called.
+bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) {
+ info->Clear();
+ info->start = addr;
+ return true;
+}
+
+// We ignore the format argument to __sanitizer_symbolize_global.
+void RenderData(InternalScopedString *buffer, const char *format,
+ const DataInfo *DI, const char *strip_path_prefix) {
+ buffer->append(kFormatData, DI->start);
+}
+
+// We don't support the stack_trace_format flag at all.
+void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
+ const AddressInfo &info, bool vs_style,
+ const char *strip_path_prefix, const char *strip_func_prefix) {
+ buffer->append(kFormatFrame, frame_no, info.address);
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+ return new (symbolizer_allocator_) Symbolizer({});
+}
+
+void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); }
+
+void StartReportDeadlySignal() {}
+void ReportDeadlySignal(const SignalContext &sig, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {}
+
+#if SANITIZER_CAN_SLOW_UNWIND
+struct UnwindTraceArg {
+ BufferedStackTrace *stack;
+ u32 max_depth;
+};
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+ UnwindTraceArg *arg = static_cast<UnwindTraceArg *>(param);
+ CHECK_LT(arg->stack->size, arg->max_depth);
+ uptr pc = _Unwind_GetIP(ctx);
+ if (pc < PAGE_SIZE) return _URC_NORMAL_STOP;
+ arg->stack->trace_buffer[arg->stack->size++] = pc;
+ return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP
+ : _URC_NO_REASON);
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ size = 0;
+ UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
+ _Unwind_Backtrace(Unwind_Trace, &arg);
+ CHECK_GT(size, 0);
+ // We need to pop a few frames so that pc is on top.
+ uptr to_pop = LocatePcInTrace(pc);
+ // trace_buffer[0] belongs to the current function so we always pop it,
+ // unless there is only 1 frame in the stack trace (1 frame is always better
+ // than 0!).
+ PopStackFrames(Min(to_pop, static_cast<uptr>(1)));
+ trace_buffer[0] = pc;
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
+ CHECK(context);
+ CHECK_GE(max_depth, 2);
+ UNREACHABLE("signal context doesn't exist");
+}
+#endif // SANITIZER_CAN_SLOW_UNWIND
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_SYMBOLIZER_MARKUP
+++ /dev/null
-//===-- sanitizer_symbolizer_posix_libcdep.cc -----------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-// POSIX-specific implementation of symbolizer parts.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_POSIX
-#include "sanitizer_allocator_internal.h"
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_internal_defs.h"
-#include "sanitizer_linux.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_posix.h"
-#include "sanitizer_procmaps.h"
-#include "sanitizer_symbolizer_internal.h"
-#include "sanitizer_symbolizer_libbacktrace.h"
-#include "sanitizer_symbolizer_mac.h"
-
-#include <dlfcn.h> // for dlsym()
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#if SANITIZER_MAC
-#include <util.h> // for forkpty()
-#endif // SANITIZER_MAC
-
-// C++ demangling function, as required by Itanium C++ ABI. This is weak,
-// because we do not require a C++ ABI library to be linked to a program
-// using sanitizers; if it's not present, we'll just use the mangled name.
-namespace __cxxabiv1 {
- extern "C" SANITIZER_WEAK_ATTRIBUTE
- char *__cxa_demangle(const char *mangled, char *buffer,
- size_t *length, int *status);
-}
-
-namespace __sanitizer {
-
-// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
-const char *DemangleCXXABI(const char *name) {
- // FIXME: __cxa_demangle aggressively insists on allocating memory.
- // There's not much we can do about that, short of providing our
- // own demangler (libc++abi's implementation could be adapted so that
- // it does not allocate). For now, we just call it anyway, and we leak
- // the returned value.
- if (&__cxxabiv1::__cxa_demangle)
- if (const char *demangled_name =
- __cxxabiv1::__cxa_demangle(name, 0, 0, 0))
- return demangled_name;
-
- return name;
-}
-
-// As of now, there are no headers for the Swift runtime. Once they are
-// present, we will weakly link since we do not require Swift runtime to be
-// linked.
-typedef char *(*swift_demangle_ft)(const char *mangledName,
- size_t mangledNameLength, char *outputBuffer,
- size_t *outputBufferSize, uint32_t flags);
-static swift_demangle_ft swift_demangle_f;
-
-// This must not happen lazily at symbolication time, because dlsym uses
-// malloc and thread-local storage, which is not a good thing to do during
-// symbolication.
-static void InitializeSwiftDemangler() {
- swift_demangle_f = (swift_demangle_ft)dlsym(RTLD_DEFAULT, "swift_demangle");
- (void)dlerror(); // Cleanup error message in case of failure
-}
-
-// Attempts to demangle a Swift name. The demangler will return nullptr if a
-// non-Swift name is passed in.
-const char *DemangleSwift(const char *name) {
- if (!name) return nullptr;
-
- // Check if we are dealing with a Swift mangled name first.
- if (name[0] != '_' || name[1] != 'T') {
- return nullptr;
- }
-
- if (swift_demangle_f)
- return swift_demangle_f(name, internal_strlen(name), 0, 0, 0);
-
- return nullptr;
-}
-
-const char *DemangleSwiftAndCXX(const char *name) {
- if (!name) return nullptr;
- if (const char *swift_demangled_name = DemangleSwift(name))
- return swift_demangled_name;
- return DemangleCXXABI(name);
-}
-
-static bool CreateTwoHighNumberedPipes(int *infd_, int *outfd_) {
- int *infd = NULL;
- int *outfd = NULL;
- // The client program may close its stdin and/or stdout and/or stderr
- // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
- // In this case the communication between the forked processes may be
- // broken if either the parent or the child tries to close or duplicate
- // these descriptors. The loop below produces two pairs of file
- // descriptors, each greater than 2 (stderr).
- int sock_pair[5][2];
- for (int i = 0; i < 5; i++) {
- if (pipe(sock_pair[i]) == -1) {
- for (int j = 0; j < i; j++) {
- internal_close(sock_pair[j][0]);
- internal_close(sock_pair[j][1]);
- }
- return false;
- } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
- if (infd == NULL) {
- infd = sock_pair[i];
- } else {
- outfd = sock_pair[i];
- for (int j = 0; j < i; j++) {
- if (sock_pair[j] == infd) continue;
- internal_close(sock_pair[j][0]);
- internal_close(sock_pair[j][1]);
- }
- break;
- }
- }
- }
- CHECK(infd);
- CHECK(outfd);
- infd_[0] = infd[0];
- infd_[1] = infd[1];
- outfd_[0] = outfd[0];
- outfd_[1] = outfd[1];
- return true;
-}
-
-bool SymbolizerProcess::StartSymbolizerSubprocess() {
- if (!FileExists(path_)) {
- if (!reported_invalid_path_) {
- Report("WARNING: invalid path to external symbolizer!\n");
- reported_invalid_path_ = true;
- }
- return false;
- }
-
- int pid = -1;
-
- int infd[2];
- internal_memset(&infd, 0, sizeof(infd));
- int outfd[2];
- internal_memset(&outfd, 0, sizeof(outfd));
- if (!CreateTwoHighNumberedPipes(infd, outfd)) {
- Report("WARNING: Can't create a socket pair to start "
- "external symbolizer (errno: %d)\n", errno);
- return false;
- }
-
- if (use_forkpty_) {
-#if SANITIZER_MAC
- fd_t fd = kInvalidFd;
-
- // forkpty redirects stdout and stderr into a single stream, so we would
- // receive error messages as standard replies. To avoid that, let's dup
- // stderr and restore it in the child.
- int saved_stderr = dup(STDERR_FILENO);
- CHECK_GE(saved_stderr, 0);
-
- // We only need one pipe, for stdin of the child.
- close(outfd[0]);
- close(outfd[1]);
-
- // Use forkpty to disable buffering in the new terminal.
- pid = internal_forkpty(&fd);
- if (pid == -1) {
- // forkpty() failed.
- Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
- errno);
- return false;
- } else if (pid == 0) {
- // Child subprocess.
-
- // infd[0] is the child's reading end.
- close(infd[1]);
-
- // Set up stdin to read from the pipe.
- CHECK_GE(dup2(infd[0], STDIN_FILENO), 0);
- close(infd[0]);
-
- // Restore stderr.
- CHECK_GE(dup2(saved_stderr, STDERR_FILENO), 0);
- close(saved_stderr);
-
- const char *argv[kArgVMax];
- GetArgV(path_, argv);
- execv(path_, const_cast<char **>(&argv[0]));
- internal__exit(1);
- }
-
- // Input for the child, infd[1] is our writing end.
- output_fd_ = infd[1];
- close(infd[0]);
-
- // Continue execution in parent process.
- input_fd_ = fd;
-
- close(saved_stderr);
-
- // Disable echo in the new terminal, disable CR.
- struct termios termflags;
- tcgetattr(fd, &termflags);
- termflags.c_oflag &= ~ONLCR;
- termflags.c_lflag &= ~ECHO;
- tcsetattr(fd, TCSANOW, &termflags);
-#else // SANITIZER_MAC
- UNIMPLEMENTED();
-#endif // SANITIZER_MAC
- } else {
- const char *argv[kArgVMax];
- GetArgV(path_, argv);
- pid = StartSubprocess(path_, argv, /* stdin */ outfd[0],
- /* stdout */ infd[1]);
- if (pid < 0) {
- internal_close(infd[0]);
- internal_close(outfd[1]);
- return false;
- }
-
- input_fd_ = infd[0];
- output_fd_ = outfd[1];
- }
-
- CHECK_GT(pid, 0);
-
- // Check that symbolizer subprocess started successfully.
- SleepForMillis(kSymbolizerStartupTimeMillis);
- if (!IsProcessRunning(pid)) {
- // Either waitpid failed, or child has already exited.
- Report("WARNING: external symbolizer didn't start up correctly!\n");
- return false;
- }
-
- return true;
-}
-
-class Addr2LineProcess : public SymbolizerProcess {
- public:
- Addr2LineProcess(const char *path, const char *module_name)
- : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
-
- const char *module_name() const { return module_name_; }
-
- private:
- void GetArgV(const char *path_to_binary,
- const char *(&argv)[kArgVMax]) const override {
- int i = 0;
- argv[i++] = path_to_binary;
- argv[i++] = "-iCfe";
- argv[i++] = module_name_;
- argv[i++] = nullptr;
- }
-
- bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
-
- bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
- if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
- return false;
- // The returned buffer is empty when output is valid, but exceeds
- // max_length.
- if (*buffer == '\0')
- return true;
- // We should cut out output_terminator_ at the end of given buffer,
- // appended by addr2line to mark the end of its meaningful output.
- // We cannot scan buffer from it's beginning, because it is legal for it
- // to start with output_terminator_ in case given offset is invalid. So,
- // scanning from second character.
- char *garbage = internal_strstr(buffer + 1, output_terminator_);
- // This should never be NULL since buffer must end up with
- // output_terminator_.
- CHECK(garbage);
- // Trim the buffer.
- garbage[0] = '\0';
- return true;
- }
-
- const char *module_name_; // Owned, leaked.
- static const char output_terminator_[];
-};
-
-const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
-
-bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
- uptr length) const {
- const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
- // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
- // should consist at least of two pairs of lines:
- // 1. First one, corresponding to given offset to be symbolized
- // (may be equal to output_terminator_, if offset is not valid).
- // 2. Second one for output_terminator_, itself to mark the end of output.
- if (length <= kTerminatorLen) return false;
- // Addr2Line output should end up with output_terminator_.
- return !internal_memcmp(buffer + length - kTerminatorLen,
- output_terminator_, kTerminatorLen);
-}
-
-class Addr2LinePool : public SymbolizerTool {
- public:
- explicit Addr2LinePool(const char *addr2line_path,
- LowLevelAllocator *allocator)
- : addr2line_path_(addr2line_path), allocator_(allocator) {
- addr2line_pool_.reserve(16);
- }
-
- bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
- if (const char *buf =
- SendCommand(stack->info.module, stack->info.module_offset)) {
- ParseSymbolizePCOutput(buf, stack);
- return true;
- }
- return false;
- }
-
- bool SymbolizeData(uptr addr, DataInfo *info) override {
- return false;
- }
-
- private:
- const char *SendCommand(const char *module_name, uptr module_offset) {
- Addr2LineProcess *addr2line = 0;
- for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
- if (0 ==
- internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
- addr2line = addr2line_pool_[i];
- break;
- }
- }
- if (!addr2line) {
- addr2line =
- new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
- addr2line_pool_.push_back(addr2line);
- }
- CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
- char buffer[kBufferSize];
- internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
- module_offset, dummy_address_);
- return addr2line->SendCommand(buffer);
- }
-
- static const uptr kBufferSize = 64;
- const char *addr2line_path_;
- LowLevelAllocator *allocator_;
- InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
- static const uptr dummy_address_ =
- FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
-};
-
-#if SANITIZER_SUPPORTS_WEAK_HOOKS
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,
- char *Buffer, int MaxLength);
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,
- char *Buffer, int MaxLength);
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-void __sanitizer_symbolize_flush();
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
-int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
- int MaxLength);
-} // extern "C"
-
-class InternalSymbolizer : public SymbolizerTool {
- public:
- static InternalSymbolizer *get(LowLevelAllocator *alloc) {
- if (__sanitizer_symbolize_code != 0 &&
- __sanitizer_symbolize_data != 0) {
- return new(*alloc) InternalSymbolizer();
- }
- return 0;
- }
-
- bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
- bool result = __sanitizer_symbolize_code(
- stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
- if (result) ParseSymbolizePCOutput(buffer_, stack);
- return result;
- }
-
- bool SymbolizeData(uptr addr, DataInfo *info) override {
- bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
- buffer_, kBufferSize);
- if (result) {
- ParseSymbolizeDataOutput(buffer_, info);
- info->start += (addr - info->module_offset); // Add the base address.
- }
- return result;
- }
-
- void Flush() override {
- if (__sanitizer_symbolize_flush)
- __sanitizer_symbolize_flush();
- }
-
- const char *Demangle(const char *name) override {
- if (__sanitizer_symbolize_demangle) {
- for (uptr res_length = 1024;
- res_length <= InternalSizeClassMap::kMaxSize;) {
- char *res_buff = static_cast<char*>(InternalAlloc(res_length));
- uptr req_length =
- __sanitizer_symbolize_demangle(name, res_buff, res_length);
- if (req_length > res_length) {
- res_length = req_length + 1;
- InternalFree(res_buff);
- continue;
- }
- return res_buff;
- }
- }
- return name;
- }
-
- private:
- InternalSymbolizer() { }
-
- static const int kBufferSize = 16 * 1024;
- char buffer_[kBufferSize];
-};
-#else // SANITIZER_SUPPORTS_WEAK_HOOKS
-
-class InternalSymbolizer : public SymbolizerTool {
- public:
- static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
-};
-
-#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
-
-const char *Symbolizer::PlatformDemangle(const char *name) {
- return DemangleSwiftAndCXX(name);
-}
-
-static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
- const char *path = common_flags()->external_symbolizer_path;
- const char *binary_name = path ? StripModuleName(path) : "";
- if (path && path[0] == '\0') {
- VReport(2, "External symbolizer is explicitly disabled.\n");
- return nullptr;
- } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
- VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
- return new(*allocator) LLVMSymbolizer(path, allocator);
- } else if (!internal_strcmp(binary_name, "atos")) {
-#if SANITIZER_MAC
- VReport(2, "Using atos at user-specified path: %s\n", path);
- return new(*allocator) AtosSymbolizer(path, allocator);
-#else // SANITIZER_MAC
- Report("ERROR: Using `atos` is only supported on Darwin.\n");
- Die();
-#endif // SANITIZER_MAC
- } else if (!internal_strcmp(binary_name, "addr2line")) {
- VReport(2, "Using addr2line at user-specified path: %s\n", path);
- return new(*allocator) Addr2LinePool(path, allocator);
- } else if (path) {
- Report("ERROR: External symbolizer path is set to '%s' which isn't "
- "a known symbolizer. Please set the path to the llvm-symbolizer "
- "binary or other known tool.\n", path);
- Die();
- }
-
- // Otherwise symbolizer program is unknown, let's search $PATH
- CHECK(path == nullptr);
-#if SANITIZER_MAC
- if (const char *found_path = FindPathToBinary("atos")) {
- VReport(2, "Using atos found at: %s\n", found_path);
- return new(*allocator) AtosSymbolizer(found_path, allocator);
- }
-#endif // SANITIZER_MAC
- if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
- VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
- return new(*allocator) LLVMSymbolizer(found_path, allocator);
- }
- if (common_flags()->allow_addr2line) {
- if (const char *found_path = FindPathToBinary("addr2line")) {
- VReport(2, "Using addr2line found at: %s\n", found_path);
- return new(*allocator) Addr2LinePool(found_path, allocator);
- }
- }
- return nullptr;
-}
-
-static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
- LowLevelAllocator *allocator) {
- if (!common_flags()->symbolize) {
- VReport(2, "Symbolizer is disabled.\n");
- return;
- }
- if (IsAllocatorOutOfMemory()) {
- VReport(2, "Cannot use internal symbolizer: out of memory\n");
- } else if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
- VReport(2, "Using internal symbolizer.\n");
- list->push_back(tool);
- return;
- }
- if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
- VReport(2, "Using libbacktrace symbolizer.\n");
- list->push_back(tool);
- return;
- }
-
- if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
- list->push_back(tool);
- }
-
-#if SANITIZER_MAC
- VReport(2, "Using dladdr symbolizer.\n");
- list->push_back(new(*allocator) DlAddrSymbolizer());
-#endif // SANITIZER_MAC
-}
-
-Symbolizer *Symbolizer::PlatformInit() {
- IntrusiveList<SymbolizerTool> list;
- list.clear();
- ChooseSymbolizerTools(&list, &symbolizer_allocator_);
- return new(symbolizer_allocator_) Symbolizer(list);
-}
-
-void Symbolizer::LateInitialize() {
- Symbolizer::GetOrInit();
- InitializeSwiftDemangler();
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_POSIX
--- /dev/null
+//===-- sanitizer_symbolizer_posix_libcdep.cpp ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// POSIX-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_POSIX
+#include "sanitizer_allocator_internal.h"
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_internal_defs.h"
+#include "sanitizer_linux.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_posix.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_symbolizer_internal.h"
+#include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
+
+#include <dlfcn.h> // for dlsym()
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#if SANITIZER_MAC
+#include <util.h> // for forkpty()
+#endif // SANITIZER_MAC
+
+// C++ demangling function, as required by Itanium C++ ABI. This is weak,
+// because we do not require a C++ ABI library to be linked to a program
+// using sanitizers; if it's not present, we'll just use the mangled name.
+namespace __cxxabiv1 {
+ extern "C" SANITIZER_WEAK_ATTRIBUTE
+ char *__cxa_demangle(const char *mangled, char *buffer,
+ size_t *length, int *status);
+}
+
+namespace __sanitizer {
+
+// Attempts to demangle the name via __cxa_demangle from __cxxabiv1.
+const char *DemangleCXXABI(const char *name) {
+ // FIXME: __cxa_demangle aggressively insists on allocating memory.
+ // There's not much we can do about that, short of providing our
+ // own demangler (libc++abi's implementation could be adapted so that
+ // it does not allocate). For now, we just call it anyway, and we leak
+ // the returned value.
+ if (&__cxxabiv1::__cxa_demangle)
+ if (const char *demangled_name =
+ __cxxabiv1::__cxa_demangle(name, 0, 0, 0))
+ return demangled_name;
+
+ return name;
+}
+
+// As of now, there are no headers for the Swift runtime. Once they are
+// present, we will weakly link since we do not require Swift runtime to be
+// linked.
+typedef char *(*swift_demangle_ft)(const char *mangledName,
+ size_t mangledNameLength, char *outputBuffer,
+ size_t *outputBufferSize, uint32_t flags);
+static swift_demangle_ft swift_demangle_f;
+
+// This must not happen lazily at symbolication time, because dlsym uses
+// malloc and thread-local storage, which is not a good thing to do during
+// symbolication.
+static void InitializeSwiftDemangler() {
+ swift_demangle_f = (swift_demangle_ft)dlsym(RTLD_DEFAULT, "swift_demangle");
+ (void)dlerror(); // Cleanup error message in case of failure
+}
+
+// Attempts to demangle a Swift name. The demangler will return nullptr if a
+// non-Swift name is passed in.
+const char *DemangleSwift(const char *name) {
+ if (!name) return nullptr;
+
+ // Check if we are dealing with a Swift mangled name first.
+ if (name[0] != '_' || name[1] != 'T') {
+ return nullptr;
+ }
+
+ if (swift_demangle_f)
+ return swift_demangle_f(name, internal_strlen(name), 0, 0, 0);
+
+ return nullptr;
+}
+
+const char *DemangleSwiftAndCXX(const char *name) {
+ if (!name) return nullptr;
+ if (const char *swift_demangled_name = DemangleSwift(name))
+ return swift_demangled_name;
+ return DemangleCXXABI(name);
+}
+
+static bool CreateTwoHighNumberedPipes(int *infd_, int *outfd_) {
+ int *infd = NULL;
+ int *outfd = NULL;
+ // The client program may close its stdin and/or stdout and/or stderr
+ // thus allowing socketpair to reuse file descriptors 0, 1 or 2.
+ // In this case the communication between the forked processes may be
+ // broken if either the parent or the child tries to close or duplicate
+ // these descriptors. The loop below produces two pairs of file
+ // descriptors, each greater than 2 (stderr).
+ int sock_pair[5][2];
+ for (int i = 0; i < 5; i++) {
+ if (pipe(sock_pair[i]) == -1) {
+ for (int j = 0; j < i; j++) {
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ return false;
+ } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) {
+ if (infd == NULL) {
+ infd = sock_pair[i];
+ } else {
+ outfd = sock_pair[i];
+ for (int j = 0; j < i; j++) {
+ if (sock_pair[j] == infd) continue;
+ internal_close(sock_pair[j][0]);
+ internal_close(sock_pair[j][1]);
+ }
+ break;
+ }
+ }
+ }
+ CHECK(infd);
+ CHECK(outfd);
+ infd_[0] = infd[0];
+ infd_[1] = infd[1];
+ outfd_[0] = outfd[0];
+ outfd_[1] = outfd[1];
+ return true;
+}
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ if (!FileExists(path_)) {
+ if (!reported_invalid_path_) {
+ Report("WARNING: invalid path to external symbolizer!\n");
+ reported_invalid_path_ = true;
+ }
+ return false;
+ }
+
+ int pid = -1;
+
+ int infd[2];
+ internal_memset(&infd, 0, sizeof(infd));
+ int outfd[2];
+ internal_memset(&outfd, 0, sizeof(outfd));
+ if (!CreateTwoHighNumberedPipes(infd, outfd)) {
+ Report("WARNING: Can't create a socket pair to start "
+ "external symbolizer (errno: %d)\n", errno);
+ return false;
+ }
+
+ if (use_forkpty_) {
+#if SANITIZER_MAC
+ fd_t fd = kInvalidFd;
+
+ // forkpty redirects stdout and stderr into a single stream, so we would
+ // receive error messages as standard replies. To avoid that, let's dup
+ // stderr and restore it in the child.
+ int saved_stderr = dup(STDERR_FILENO);
+ CHECK_GE(saved_stderr, 0);
+
+ // We only need one pipe, for stdin of the child.
+ close(outfd[0]);
+ close(outfd[1]);
+
+ // Use forkpty to disable buffering in the new terminal.
+ pid = internal_forkpty(&fd);
+ if (pid == -1) {
+ // forkpty() failed.
+ Report("WARNING: failed to fork external symbolizer (errno: %d)\n",
+ errno);
+ return false;
+ } else if (pid == 0) {
+ // Child subprocess.
+
+ // infd[0] is the child's reading end.
+ close(infd[1]);
+
+ // Set up stdin to read from the pipe.
+ CHECK_GE(dup2(infd[0], STDIN_FILENO), 0);
+ close(infd[0]);
+
+ // Restore stderr.
+ CHECK_GE(dup2(saved_stderr, STDERR_FILENO), 0);
+ close(saved_stderr);
+
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ execv(path_, const_cast<char **>(&argv[0]));
+ internal__exit(1);
+ }
+
+ // Input for the child, infd[1] is our writing end.
+ output_fd_ = infd[1];
+ close(infd[0]);
+
+ // Continue execution in parent process.
+ input_fd_ = fd;
+
+ close(saved_stderr);
+
+ // Disable echo in the new terminal, disable CR.
+ struct termios termflags;
+ tcgetattr(fd, &termflags);
+ termflags.c_oflag &= ~ONLCR;
+ termflags.c_lflag &= ~ECHO;
+ tcsetattr(fd, TCSANOW, &termflags);
+#else // SANITIZER_MAC
+ UNIMPLEMENTED();
+#endif // SANITIZER_MAC
+ } else {
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ pid = StartSubprocess(path_, argv, /* stdin */ outfd[0],
+ /* stdout */ infd[1]);
+ if (pid < 0) {
+ internal_close(infd[0]);
+ internal_close(outfd[1]);
+ return false;
+ }
+
+ input_fd_ = infd[0];
+ output_fd_ = outfd[1];
+ }
+
+ CHECK_GT(pid, 0);
+
+ // Check that symbolizer subprocess started successfully.
+ SleepForMillis(kSymbolizerStartupTimeMillis);
+ if (!IsProcessRunning(pid)) {
+ // Either waitpid failed, or child has already exited.
+ Report("WARNING: external symbolizer didn't start up correctly!\n");
+ return false;
+ }
+
+ return true;
+}
+
+class Addr2LineProcess : public SymbolizerProcess {
+ public:
+ Addr2LineProcess(const char *path, const char *module_name)
+ : SymbolizerProcess(path), module_name_(internal_strdup(module_name)) {}
+
+ const char *module_name() const { return module_name_; }
+
+ private:
+ void GetArgV(const char *path_to_binary,
+ const char *(&argv)[kArgVMax]) const override {
+ int i = 0;
+ argv[i++] = path_to_binary;
+ argv[i++] = "-iCfe";
+ argv[i++] = module_name_;
+ argv[i++] = nullptr;
+ }
+
+ bool ReachedEndOfOutput(const char *buffer, uptr length) const override;
+
+ bool ReadFromSymbolizer(char *buffer, uptr max_length) override {
+ if (!SymbolizerProcess::ReadFromSymbolizer(buffer, max_length))
+ return false;
+ // The returned buffer is empty when output is valid, but exceeds
+ // max_length.
+ if (*buffer == '\0')
+ return true;
+ // We should cut out output_terminator_ at the end of given buffer,
+ // appended by addr2line to mark the end of its meaningful output.
+ // We cannot scan buffer from it's beginning, because it is legal for it
+ // to start with output_terminator_ in case given offset is invalid. So,
+ // scanning from second character.
+ char *garbage = internal_strstr(buffer + 1, output_terminator_);
+ // This should never be NULL since buffer must end up with
+ // output_terminator_.
+ CHECK(garbage);
+ // Trim the buffer.
+ garbage[0] = '\0';
+ return true;
+ }
+
+ const char *module_name_; // Owned, leaked.
+ static const char output_terminator_[];
+};
+
+const char Addr2LineProcess::output_terminator_[] = "??\n??:0\n";
+
+bool Addr2LineProcess::ReachedEndOfOutput(const char *buffer,
+ uptr length) const {
+ const size_t kTerminatorLen = sizeof(output_terminator_) - 1;
+ // Skip, if we read just kTerminatorLen bytes, because Addr2Line output
+ // should consist at least of two pairs of lines:
+ // 1. First one, corresponding to given offset to be symbolized
+ // (may be equal to output_terminator_, if offset is not valid).
+ // 2. Second one for output_terminator_, itself to mark the end of output.
+ if (length <= kTerminatorLen) return false;
+ // Addr2Line output should end up with output_terminator_.
+ return !internal_memcmp(buffer + length - kTerminatorLen,
+ output_terminator_, kTerminatorLen);
+}
+
+class Addr2LinePool : public SymbolizerTool {
+ public:
+ explicit Addr2LinePool(const char *addr2line_path,
+ LowLevelAllocator *allocator)
+ : addr2line_path_(addr2line_path), allocator_(allocator) {
+ addr2line_pool_.reserve(16);
+ }
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ if (const char *buf =
+ SendCommand(stack->info.module, stack->info.module_offset)) {
+ ParseSymbolizePCOutput(buf, stack);
+ return true;
+ }
+ return false;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+
+ private:
+ const char *SendCommand(const char *module_name, uptr module_offset) {
+ Addr2LineProcess *addr2line = 0;
+ for (uptr i = 0; i < addr2line_pool_.size(); ++i) {
+ if (0 ==
+ internal_strcmp(module_name, addr2line_pool_[i]->module_name())) {
+ addr2line = addr2line_pool_[i];
+ break;
+ }
+ }
+ if (!addr2line) {
+ addr2line =
+ new(*allocator_) Addr2LineProcess(addr2line_path_, module_name);
+ addr2line_pool_.push_back(addr2line);
+ }
+ CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name()));
+ char buffer[kBufferSize];
+ internal_snprintf(buffer, kBufferSize, "0x%zx\n0x%zx\n",
+ module_offset, dummy_address_);
+ return addr2line->SendCommand(buffer);
+ }
+
+ static const uptr kBufferSize = 64;
+ const char *addr2line_path_;
+ LowLevelAllocator *allocator_;
+ InternalMmapVector<Addr2LineProcess*> addr2line_pool_;
+ static const uptr dummy_address_ =
+ FIRST_32_SECOND_64(UINT32_MAX, UINT64_MAX);
+};
+
+#if SANITIZER_SUPPORTS_WEAK_HOOKS
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+bool __sanitizer_symbolize_code(const char *ModuleName, u64 ModuleOffset,
+ char *Buffer, int MaxLength);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+bool __sanitizer_symbolize_data(const char *ModuleName, u64 ModuleOffset,
+ char *Buffer, int MaxLength);
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+void __sanitizer_symbolize_flush();
+SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
+int __sanitizer_symbolize_demangle(const char *Name, char *Buffer,
+ int MaxLength);
+} // extern "C"
+
+class InternalSymbolizer : public SymbolizerTool {
+ public:
+ static InternalSymbolizer *get(LowLevelAllocator *alloc) {
+ if (__sanitizer_symbolize_code != 0 &&
+ __sanitizer_symbolize_data != 0) {
+ return new(*alloc) InternalSymbolizer();
+ }
+ return 0;
+ }
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override {
+ bool result = __sanitizer_symbolize_code(
+ stack->info.module, stack->info.module_offset, buffer_, kBufferSize);
+ if (result) ParseSymbolizePCOutput(buffer_, stack);
+ return result;
+ }
+
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ bool result = __sanitizer_symbolize_data(info->module, info->module_offset,
+ buffer_, kBufferSize);
+ if (result) {
+ ParseSymbolizeDataOutput(buffer_, info);
+ info->start += (addr - info->module_offset); // Add the base address.
+ }
+ return result;
+ }
+
+ void Flush() override {
+ if (__sanitizer_symbolize_flush)
+ __sanitizer_symbolize_flush();
+ }
+
+ const char *Demangle(const char *name) override {
+ if (__sanitizer_symbolize_demangle) {
+ for (uptr res_length = 1024;
+ res_length <= InternalSizeClassMap::kMaxSize;) {
+ char *res_buff = static_cast<char*>(InternalAlloc(res_length));
+ uptr req_length =
+ __sanitizer_symbolize_demangle(name, res_buff, res_length);
+ if (req_length > res_length) {
+ res_length = req_length + 1;
+ InternalFree(res_buff);
+ continue;
+ }
+ return res_buff;
+ }
+ }
+ return name;
+ }
+
+ private:
+ InternalSymbolizer() { }
+
+ static const int kBufferSize = 16 * 1024;
+ char buffer_[kBufferSize];
+};
+#else // SANITIZER_SUPPORTS_WEAK_HOOKS
+
+class InternalSymbolizer : public SymbolizerTool {
+ public:
+ static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; }
+};
+
+#endif // SANITIZER_SUPPORTS_WEAK_HOOKS
+
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return DemangleSwiftAndCXX(name);
+}
+
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+ const char *path = common_flags()->external_symbolizer_path;
+ const char *binary_name = path ? StripModuleName(path) : "";
+ if (path && path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ return nullptr;
+ } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+ return new(*allocator) LLVMSymbolizer(path, allocator);
+ } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+ VReport(2, "Using atos at user-specified path: %s\n", path);
+ return new(*allocator) AtosSymbolizer(path, allocator);
+#else // SANITIZER_MAC
+ Report("ERROR: Using `atos` is only supported on Darwin.\n");
+ Die();
+#endif // SANITIZER_MAC
+ } else if (!internal_strcmp(binary_name, "addr2line")) {
+ VReport(2, "Using addr2line at user-specified path: %s\n", path);
+ return new(*allocator) Addr2LinePool(path, allocator);
+ } else if (path) {
+ Report("ERROR: External symbolizer path is set to '%s' which isn't "
+ "a known symbolizer. Please set the path to the llvm-symbolizer "
+ "binary or other known tool.\n", path);
+ Die();
+ }
+
+ // Otherwise symbolizer program is unknown, let's search $PATH
+ CHECK(path == nullptr);
+#if SANITIZER_MAC
+ if (const char *found_path = FindPathToBinary("atos")) {
+ VReport(2, "Using atos found at: %s\n", found_path);
+ return new(*allocator) AtosSymbolizer(found_path, allocator);
+ }
+#endif // SANITIZER_MAC
+ if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+ VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+ return new(*allocator) LLVMSymbolizer(found_path, allocator);
+ }
+ if (common_flags()->allow_addr2line) {
+ if (const char *found_path = FindPathToBinary("addr2line")) {
+ VReport(2, "Using addr2line found at: %s\n", found_path);
+ return new(*allocator) Addr2LinePool(found_path, allocator);
+ }
+ }
+ return nullptr;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
+ }
+ if (IsAllocatorOutOfMemory()) {
+ VReport(2, "Cannot use internal symbolizer: out of memory\n");
+ } else if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
+ VReport(2, "Using internal symbolizer.\n");
+ list->push_back(tool);
+ return;
+ }
+ if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
+ VReport(2, "Using libbacktrace symbolizer.\n");
+ list->push_back(tool);
+ return;
+ }
+
+ if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+ list->push_back(tool);
+ }
+
+#if SANITIZER_MAC
+ VReport(2, "Using dladdr symbolizer.\n");
+ list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif // SANITIZER_MAC
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+ return new(symbolizer_allocator_) Symbolizer(list);
+}
+
+void Symbolizer::LateInitialize() {
+ Symbolizer::GetOrInit();
+ InitializeSwiftDemangler();
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_POSIX
+++ /dev/null
-//===-- sanitizer_symbolizer_report.cc ------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// This file is shared between AddressSanitizer and other sanitizer run-time
-/// libraries and implements symbolized reports related functions.
-///
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_flags.h"
-#include "sanitizer_procmaps.h"
-#include "sanitizer_report_decorator.h"
-#include "sanitizer_stacktrace.h"
-#include "sanitizer_stacktrace_printer.h"
-#include "sanitizer_symbolizer.h"
-
-#if SANITIZER_POSIX
-# include "sanitizer_posix.h"
-# include <sys/mman.h>
-#endif
-
-namespace __sanitizer {
-
-#if !SANITIZER_GO
-void ReportErrorSummary(const char *error_type, const AddressInfo &info,
- const char *alt_tool_name) {
- if (!common_flags()->print_summary) return;
- InternalScopedString buff(kMaxSummaryLength);
- buff.append("%s ", error_type);
- RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- ReportErrorSummary(buff.data(), alt_tool_name);
-}
-#endif
-
-#if !SANITIZER_FUCHSIA
-
-bool ReportFile::SupportsColors() {
- SpinMutexLock l(mu);
- ReopenIfNecessary();
- return SupportsColoredOutput(fd);
-}
-
-static INLINE bool ReportSupportsColors() {
- return report_file.SupportsColors();
-}
-
-#else // SANITIZER_FUCHSIA
-
-// Fuchsia's logs always go through post-processing that handles colorization.
-static INLINE bool ReportSupportsColors() { return true; }
-
-#endif // !SANITIZER_FUCHSIA
-
-bool ColorizeReports() {
- // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
- // printing on Windows.
- if (SANITIZER_WINDOWS)
- return false;
-
- const char *flag = common_flags()->color;
- return internal_strcmp(flag, "always") == 0 ||
- (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors());
-}
-
-void ReportErrorSummary(const char *error_type, const StackTrace *stack,
- const char *alt_tool_name) {
-#if !SANITIZER_GO
- if (!common_flags()->print_summary)
- return;
- if (stack->size == 0) {
- ReportErrorSummary(error_type);
- return;
- }
- // Currently, we include the first stack frame into the report summary.
- // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
- uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
- SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
- ReportErrorSummary(error_type, frame->info, alt_tool_name);
- frame->ClearAll();
-#endif
-}
-
-void ReportMmapWriteExec(int prot) {
-#if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID)
- if ((prot & (PROT_WRITE | PROT_EXEC)) != (PROT_WRITE | PROT_EXEC))
- return;
-
- ScopedErrorReportLock l;
- SanitizerCommonDecorator d;
-
- InternalMmapVector<BufferedStackTrace> stack_buffer(1);
- BufferedStackTrace *stack = stack_buffer.data();
- stack->Reset();
- uptr top = 0;
- uptr bottom = 0;
- GET_CALLER_PC_BP_SP;
- (void)sp;
- bool fast = common_flags()->fast_unwind_on_fatal;
- if (fast)
- GetThreadStackTopAndBottom(false, &top, &bottom);
- stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, fast);
-
- Printf("%s", d.Warning());
- Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName);
- Printf("%s", d.Default());
-
- stack->Print();
- ReportErrorSummary("w-and-x-usage", stack);
-#endif
-}
-
-#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_GO
-void StartReportDeadlySignal() {
- // Write the first message using fd=2, just in case.
- // It may actually fail to write in case stderr is closed.
- CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName));
- static const char kDeadlySignal[] = ":DEADLYSIGNAL\n";
- CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1);
-}
-
-static void MaybeReportNonExecRegion(uptr pc) {
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
- MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
- MemoryMappedSegment segment;
- while (proc_maps.Next(&segment)) {
- if (pc >= segment.start && pc < segment.end && !segment.IsExecutable())
- Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n");
- }
-#endif
-}
-
-static void PrintMemoryByte(InternalScopedString *str, const char *before,
- u8 byte) {
- SanitizerCommonDecorator d;
- str->append("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15,
- d.Default());
-}
-
-static void MaybeDumpInstructionBytes(uptr pc) {
- if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
- return;
- InternalScopedString str(1024);
- str.append("First 16 instruction bytes at pc: ");
- if (IsAccessibleMemoryRange(pc, 16)) {
- for (int i = 0; i < 16; ++i) {
- PrintMemoryByte(&str, "", ((u8 *)pc)[i]);
- }
- str.append("\n");
- } else {
- str.append("unaccessible\n");
- }
- Report("%s", str.data());
-}
-
-static void MaybeDumpRegisters(void *context) {
- if (!common_flags()->dump_registers) return;
- SignalContext::DumpAllRegisters(context);
-}
-
-static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid,
- UnwindSignalStackCallbackType unwind,
- const void *unwind_context) {
- SanitizerCommonDecorator d;
- Printf("%s", d.Warning());
- static const char kDescription[] = "stack-overflow";
- Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n",
- SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc,
- (void *)sig.bp, (void *)sig.sp, tid);
- Printf("%s", d.Default());
- InternalMmapVector<BufferedStackTrace> stack_buffer(1);
- BufferedStackTrace *stack = stack_buffer.data();
- stack->Reset();
- unwind(sig, unwind_context, stack);
- stack->Print();
- ReportErrorSummary(kDescription, stack);
-}
-
-static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
- UnwindSignalStackCallbackType unwind,
- const void *unwind_context) {
- SanitizerCommonDecorator d;
- Printf("%s", d.Warning());
- const char *description = sig.Describe();
- Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n",
- SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc,
- (void *)sig.bp, (void *)sig.sp, tid);
- Printf("%s", d.Default());
- if (sig.pc < GetPageSizeCached())
- Report("Hint: pc points to the zero page.\n");
- if (sig.is_memory_access) {
- const char *access_type =
- sig.write_flag == SignalContext::WRITE
- ? "WRITE"
- : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
- Report("The signal is caused by a %s memory access.\n", access_type);
- if (sig.addr < GetPageSizeCached())
- Report("Hint: address points to the zero page.\n");
- }
- MaybeReportNonExecRegion(sig.pc);
- InternalMmapVector<BufferedStackTrace> stack_buffer(1);
- BufferedStackTrace *stack = stack_buffer.data();
- stack->Reset();
- unwind(sig, unwind_context, stack);
- stack->Print();
- MaybeDumpInstructionBytes(sig.pc);
- MaybeDumpRegisters(sig.context);
- Printf("%s can not provide additional info.\n", SanitizerToolName);
- ReportErrorSummary(description, stack);
-}
-
-void ReportDeadlySignal(const SignalContext &sig, u32 tid,
- UnwindSignalStackCallbackType unwind,
- const void *unwind_context) {
- if (sig.IsStackOverflow())
- ReportStackOverflowImpl(sig, tid, unwind, unwind_context);
- else
- ReportDeadlySignalImpl(sig, tid, unwind, unwind_context);
-}
-
-void HandleDeadlySignal(void *siginfo, void *context, u32 tid,
- UnwindSignalStackCallbackType unwind,
- const void *unwind_context) {
- StartReportDeadlySignal();
- ScopedErrorReportLock rl;
- SignalContext sig(siginfo, context);
- ReportDeadlySignal(sig, tid, unwind, unwind_context);
- Report("ABORTING\n");
- Die();
-}
-
-#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO
-
-static atomic_uintptr_t reporting_thread = {0};
-static StaticSpinMutex CommonSanitizerReportMutex;
-
-ScopedErrorReportLock::ScopedErrorReportLock() {
- uptr current = GetThreadSelf();
- for (;;) {
- uptr expected = 0;
- if (atomic_compare_exchange_strong(&reporting_thread, &expected, current,
- memory_order_relaxed)) {
- // We've claimed reporting_thread so proceed.
- CommonSanitizerReportMutex.Lock();
- return;
- }
-
- if (expected == current) {
- // This is either asynch signal or nested error during error reporting.
- // Fail simple to avoid deadlocks in Report().
-
- // Can't use Report() here because of potential deadlocks in nested
- // signal handlers.
- CatastrophicErrorWrite(SanitizerToolName,
- internal_strlen(SanitizerToolName));
- static const char msg[] = ": nested bug in the same thread, aborting.\n";
- CatastrophicErrorWrite(msg, sizeof(msg) - 1);
-
- internal__exit(common_flags()->exitcode);
- }
-
- internal_sched_yield();
- }
-}
-
-ScopedErrorReportLock::~ScopedErrorReportLock() {
- CommonSanitizerReportMutex.Unlock();
- atomic_store_relaxed(&reporting_thread, 0);
-}
-
-void ScopedErrorReportLock::CheckLocked() {
- CommonSanitizerReportMutex.CheckLocked();
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_symbolizer_report.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file is shared between AddressSanitizer and other sanitizer run-time
+/// libraries and implements symbolized reports related functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+#include "sanitizer_flags.h"
+#include "sanitizer_procmaps.h"
+#include "sanitizer_report_decorator.h"
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_stacktrace_printer.h"
+#include "sanitizer_symbolizer.h"
+
+#if SANITIZER_POSIX
+# include "sanitizer_posix.h"
+# include <sys/mman.h>
+#endif
+
+namespace __sanitizer {
+
+#if !SANITIZER_GO
+void ReportErrorSummary(const char *error_type, const AddressInfo &info,
+ const char *alt_tool_name) {
+ if (!common_flags()->print_summary) return;
+ InternalScopedString buff(kMaxSummaryLength);
+ buff.append("%s ", error_type);
+ RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ ReportErrorSummary(buff.data(), alt_tool_name);
+}
+#endif
+
+#if !SANITIZER_FUCHSIA
+
+bool ReportFile::SupportsColors() {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ return SupportsColoredOutput(fd);
+}
+
+static INLINE bool ReportSupportsColors() {
+ return report_file.SupportsColors();
+}
+
+#else // SANITIZER_FUCHSIA
+
+// Fuchsia's logs always go through post-processing that handles colorization.
+static INLINE bool ReportSupportsColors() { return true; }
+
+#endif // !SANITIZER_FUCHSIA
+
+bool ColorizeReports() {
+ // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
+ // printing on Windows.
+ if (SANITIZER_WINDOWS)
+ return false;
+
+ const char *flag = common_flags()->color;
+ return internal_strcmp(flag, "always") == 0 ||
+ (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors());
+}
+
+void ReportErrorSummary(const char *error_type, const StackTrace *stack,
+ const char *alt_tool_name) {
+#if !SANITIZER_GO
+ if (!common_flags()->print_summary)
+ return;
+ if (stack->size == 0) {
+ ReportErrorSummary(error_type);
+ return;
+ }
+ // Currently, we include the first stack frame into the report summary.
+ // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc).
+ uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
+ SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc);
+ ReportErrorSummary(error_type, frame->info, alt_tool_name);
+ frame->ClearAll();
+#endif
+}
+
+void ReportMmapWriteExec(int prot) {
+#if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID)
+ if ((prot & (PROT_WRITE | PROT_EXEC)) != (PROT_WRITE | PROT_EXEC))
+ return;
+
+ ScopedErrorReportLock l;
+ SanitizerCommonDecorator d;
+
+ InternalMmapVector<BufferedStackTrace> stack_buffer(1);
+ BufferedStackTrace *stack = stack_buffer.data();
+ stack->Reset();
+ uptr top = 0;
+ uptr bottom = 0;
+ GET_CALLER_PC_BP_SP;
+ (void)sp;
+ bool fast = common_flags()->fast_unwind_on_fatal;
+ if (StackTrace::WillUseFastUnwind(fast)) {
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, true);
+ } else
+ stack->Unwind(kStackTraceMax, pc, 0, nullptr, 0, 0, false);
+
+ Printf("%s", d.Warning());
+ Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName);
+ Printf("%s", d.Default());
+
+ stack->Print();
+ ReportErrorSummary("w-and-x-usage", stack);
+#endif
+}
+
+#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_GO
+void StartReportDeadlySignal() {
+ // Write the first message using fd=2, just in case.
+ // It may actually fail to write in case stderr is closed.
+ CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName));
+ static const char kDeadlySignal[] = ":DEADLYSIGNAL\n";
+ CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1);
+}
+
+static void MaybeReportNonExecRegion(uptr pc) {
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
+ MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
+ MemoryMappedSegment segment;
+ while (proc_maps.Next(&segment)) {
+ if (pc >= segment.start && pc < segment.end && !segment.IsExecutable())
+ Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n");
+ }
+#endif
+}
+
+static void PrintMemoryByte(InternalScopedString *str, const char *before,
+ u8 byte) {
+ SanitizerCommonDecorator d;
+ str->append("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15,
+ d.Default());
+}
+
+static void MaybeDumpInstructionBytes(uptr pc) {
+ if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
+ return;
+ InternalScopedString str(1024);
+ str.append("First 16 instruction bytes at pc: ");
+ if (IsAccessibleMemoryRange(pc, 16)) {
+ for (int i = 0; i < 16; ++i) {
+ PrintMemoryByte(&str, "", ((u8 *)pc)[i]);
+ }
+ str.append("\n");
+ } else {
+ str.append("unaccessible\n");
+ }
+ Report("%s", str.data());
+}
+
+static void MaybeDumpRegisters(void *context) {
+ if (!common_flags()->dump_registers) return;
+ SignalContext::DumpAllRegisters(context);
+}
+
+static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {
+ SanitizerCommonDecorator d;
+ Printf("%s", d.Warning());
+ static const char kDescription[] = "stack-overflow";
+ Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n",
+ SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc,
+ (void *)sig.bp, (void *)sig.sp, tid);
+ Printf("%s", d.Default());
+ InternalMmapVector<BufferedStackTrace> stack_buffer(1);
+ BufferedStackTrace *stack = stack_buffer.data();
+ stack->Reset();
+ unwind(sig, unwind_context, stack);
+ stack->Print();
+ ReportErrorSummary(kDescription, stack);
+}
+
+static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {
+ SanitizerCommonDecorator d;
+ Printf("%s", d.Warning());
+ const char *description = sig.Describe();
+ Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n",
+ SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc,
+ (void *)sig.bp, (void *)sig.sp, tid);
+ Printf("%s", d.Default());
+ if (sig.pc < GetPageSizeCached())
+ Report("Hint: pc points to the zero page.\n");
+ if (sig.is_memory_access) {
+ const char *access_type =
+ sig.write_flag == SignalContext::WRITE
+ ? "WRITE"
+ : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
+ Report("The signal is caused by a %s memory access.\n", access_type);
+ if (sig.addr < GetPageSizeCached())
+ Report("Hint: address points to the zero page.\n");
+ }
+ MaybeReportNonExecRegion(sig.pc);
+ InternalMmapVector<BufferedStackTrace> stack_buffer(1);
+ BufferedStackTrace *stack = stack_buffer.data();
+ stack->Reset();
+ unwind(sig, unwind_context, stack);
+ stack->Print();
+ MaybeDumpInstructionBytes(sig.pc);
+ MaybeDumpRegisters(sig.context);
+ Printf("%s can not provide additional info.\n", SanitizerToolName);
+ ReportErrorSummary(description, stack);
+}
+
+void ReportDeadlySignal(const SignalContext &sig, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {
+ if (sig.IsStackOverflow())
+ ReportStackOverflowImpl(sig, tid, unwind, unwind_context);
+ else
+ ReportDeadlySignalImpl(sig, tid, unwind, unwind_context);
+}
+
+void HandleDeadlySignal(void *siginfo, void *context, u32 tid,
+ UnwindSignalStackCallbackType unwind,
+ const void *unwind_context) {
+ StartReportDeadlySignal();
+ ScopedErrorReportLock rl;
+ SignalContext sig(siginfo, context);
+ ReportDeadlySignal(sig, tid, unwind, unwind_context);
+ Report("ABORTING\n");
+ Die();
+}
+
+#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO
+
+static atomic_uintptr_t reporting_thread = {0};
+static StaticSpinMutex CommonSanitizerReportMutex;
+
+ScopedErrorReportLock::ScopedErrorReportLock() {
+ uptr current = GetThreadSelf();
+ for (;;) {
+ uptr expected = 0;
+ if (atomic_compare_exchange_strong(&reporting_thread, &expected, current,
+ memory_order_relaxed)) {
+ // We've claimed reporting_thread so proceed.
+ CommonSanitizerReportMutex.Lock();
+ return;
+ }
+
+ if (expected == current) {
+ // This is either asynch signal or nested error during error reporting.
+ // Fail simple to avoid deadlocks in Report().
+
+ // Can't use Report() here because of potential deadlocks in nested
+ // signal handlers.
+ CatastrophicErrorWrite(SanitizerToolName,
+ internal_strlen(SanitizerToolName));
+ static const char msg[] = ": nested bug in the same thread, aborting.\n";
+ CatastrophicErrorWrite(msg, sizeof(msg) - 1);
+
+ internal__exit(common_flags()->exitcode);
+ }
+
+ internal_sched_yield();
+ }
+}
+
+ScopedErrorReportLock::~ScopedErrorReportLock() {
+ CommonSanitizerReportMutex.Unlock();
+ atomic_store_relaxed(&reporting_thread, 0);
+}
+
+void ScopedErrorReportLock::CheckLocked() {
+ CommonSanitizerReportMutex.CheckLocked();
+}
+
+} // namespace __sanitizer
//===-- sanitizer_symbolizer_rtems.h -----------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_symbolizer_win.cc ---------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries.
-// Windows-specific implementation of symbolizer parts.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-
-#include "sanitizer_dbghelp.h"
-#include "sanitizer_symbolizer_internal.h"
-
-namespace __sanitizer {
-
-decltype(::StackWalk64) *StackWalk64;
-decltype(::SymCleanup) *SymCleanup;
-decltype(::SymFromAddr) *SymFromAddr;
-decltype(::SymFunctionTableAccess64) *SymFunctionTableAccess64;
-decltype(::SymGetLineFromAddr64) *SymGetLineFromAddr64;
-decltype(::SymGetModuleBase64) *SymGetModuleBase64;
-decltype(::SymGetSearchPathW) *SymGetSearchPathW;
-decltype(::SymInitialize) *SymInitialize;
-decltype(::SymSetOptions) *SymSetOptions;
-decltype(::SymSetSearchPathW) *SymSetSearchPathW;
-decltype(::UnDecorateSymbolName) *UnDecorateSymbolName;
-
-namespace {
-
-class WinSymbolizerTool : public SymbolizerTool {
- public:
- bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
- bool SymbolizeData(uptr addr, DataInfo *info) override {
- return false;
- }
- const char *Demangle(const char *name) override;
-};
-
-bool is_dbghelp_initialized = false;
-
-bool TrySymInitialize() {
- SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
- return SymInitialize(GetCurrentProcess(), 0, TRUE);
- // FIXME: We don't call SymCleanup() on exit yet - should we?
-}
-
-} // namespace
-
-// Initializes DbgHelp library, if it's not yet initialized. Calls to this
-// function should be synchronized with respect to other calls to DbgHelp API
-// (e.g. from WinSymbolizerTool).
-void InitializeDbgHelpIfNeeded() {
- if (is_dbghelp_initialized)
- return;
-
- HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
- CHECK(dbghelp && "failed to load dbghelp.dll");
-
-#define DBGHELP_IMPORT(name) \
- do { \
- name = \
- reinterpret_cast<decltype(::name) *>(GetProcAddress(dbghelp, #name)); \
- CHECK(name != nullptr); \
- } while (0)
- DBGHELP_IMPORT(StackWalk64);
- DBGHELP_IMPORT(SymCleanup);
- DBGHELP_IMPORT(SymFromAddr);
- DBGHELP_IMPORT(SymFunctionTableAccess64);
- DBGHELP_IMPORT(SymGetLineFromAddr64);
- DBGHELP_IMPORT(SymGetModuleBase64);
- DBGHELP_IMPORT(SymGetSearchPathW);
- DBGHELP_IMPORT(SymInitialize);
- DBGHELP_IMPORT(SymSetOptions);
- DBGHELP_IMPORT(SymSetSearchPathW);
- DBGHELP_IMPORT(UnDecorateSymbolName);
-#undef DBGHELP_IMPORT
-
- if (!TrySymInitialize()) {
- // OK, maybe the client app has called SymInitialize already.
- // That's a bit unfortunate for us as all the DbgHelp functions are
- // single-threaded and we can't coordinate with the app.
- // FIXME: Can we stop the other threads at this point?
- // Anyways, we have to reconfigure stuff to make sure that SymInitialize
- // has all the appropriate options set.
- // Cross our fingers and reinitialize DbgHelp.
- Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
- Report("*** Most likely this means that the app is already ***\n");
- Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
- Report("*** Due to technical reasons, symbolization might crash ***\n");
- Report("*** or produce wrong results. ***\n");
- SymCleanup(GetCurrentProcess());
- TrySymInitialize();
- }
- is_dbghelp_initialized = true;
-
- // When an executable is run from a location different from the one where it
- // was originally built, we may not see the nearby PDB files.
- // To work around this, let's append the directory of the main module
- // to the symbol search path. All the failures below are not fatal.
- const size_t kSymPathSize = 2048;
- static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
- if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
- Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
- return;
- }
- size_t sz = wcslen(path_buffer);
- if (sz) {
- CHECK_EQ(0, wcscat_s(path_buffer, L";"));
- sz++;
- }
- DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
- if (res == 0 || res == MAX_PATH) {
- Report("*** WARNING: Failed to getting the EXE directory ***\n");
- return;
- }
- // Write the zero character in place of the last backslash to get the
- // directory of the main module at the end of path_buffer.
- wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
- CHECK_NE(last_bslash, 0);
- *last_bslash = L'\0';
- if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
- Report("*** WARNING: Failed to SymSetSearchPathW\n");
- return;
- }
-}
-
-bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
- InitializeDbgHelpIfNeeded();
-
- // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
- char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
- PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
- symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
- symbol->MaxNameLen = MAX_SYM_NAME;
- DWORD64 offset = 0;
- BOOL got_objname = SymFromAddr(GetCurrentProcess(),
- (DWORD64)addr, &offset, symbol);
- if (!got_objname)
- return false;
-
- DWORD unused;
- IMAGEHLP_LINE64 line_info;
- line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
- &unused, &line_info);
- frame->info.function = internal_strdup(symbol->Name);
- frame->info.function_offset = (uptr)offset;
- if (got_fileline) {
- frame->info.file = internal_strdup(line_info.FileName);
- frame->info.line = line_info.LineNumber;
- }
- // Only consider this a successful symbolization attempt if we got file info.
- // Otherwise, try llvm-symbolizer.
- return got_fileline;
-}
-
-const char *WinSymbolizerTool::Demangle(const char *name) {
- CHECK(is_dbghelp_initialized);
- static char demangle_buffer[1000];
- if (name[0] == '\01' &&
- UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
- UNDNAME_NAME_ONLY))
- return demangle_buffer;
- else
- return name;
-}
-
-const char *Symbolizer::PlatformDemangle(const char *name) {
- return name;
-}
-
-namespace {
-struct ScopedHandle {
- ScopedHandle() : h_(nullptr) {}
- explicit ScopedHandle(HANDLE h) : h_(h) {}
- ~ScopedHandle() {
- if (h_)
- ::CloseHandle(h_);
- }
- HANDLE get() { return h_; }
- HANDLE *receive() { return &h_; }
- HANDLE release() {
- HANDLE h = h_;
- h_ = nullptr;
- return h;
- }
- HANDLE h_;
-};
-} // namespace
-
-bool SymbolizerProcess::StartSymbolizerSubprocess() {
- // Create inherited pipes for stdin and stdout.
- ScopedHandle stdin_read, stdin_write;
- ScopedHandle stdout_read, stdout_write;
- SECURITY_ATTRIBUTES attrs;
- attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
- attrs.bInheritHandle = TRUE;
- attrs.lpSecurityDescriptor = nullptr;
- if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
- !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
- VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
- SanitizerToolName, path_, GetLastError());
- return false;
- }
-
- // Don't inherit the writing end of stdin or the reading end of stdout.
- if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
- !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
- VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
- SanitizerToolName, path_, GetLastError());
- return false;
- }
-
- // Compute the command line. Wrap double quotes around everything.
- const char *argv[kArgVMax];
- GetArgV(path_, argv);
- InternalScopedString command_line(kMaxPathLength * 3);
- for (int i = 0; argv[i]; i++) {
- const char *arg = argv[i];
- int arglen = internal_strlen(arg);
- // Check that tool command lines are simple and that complete escaping is
- // unnecessary.
- CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
- CHECK(!internal_strstr(arg, "\\\\") &&
- "double backslashes in args unsupported");
- CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
- "args ending in backslash and empty args unsupported");
- command_line.append("\"%s\" ", arg);
- }
- VReport(3, "Launching symbolizer command: %s\n", command_line.data());
-
- // Launch llvm-symbolizer with stdin and stdout redirected.
- STARTUPINFOA si;
- memset(&si, 0, sizeof(si));
- si.cb = sizeof(si);
- si.dwFlags |= STARTF_USESTDHANDLES;
- si.hStdInput = stdin_read.get();
- si.hStdOutput = stdout_write.get();
- PROCESS_INFORMATION pi;
- memset(&pi, 0, sizeof(pi));
- if (!CreateProcessA(path_, // Executable
- command_line.data(), // Command line
- nullptr, // Process handle not inheritable
- nullptr, // Thread handle not inheritable
- TRUE, // Set handle inheritance to TRUE
- 0, // Creation flags
- nullptr, // Use parent's environment block
- nullptr, // Use parent's starting directory
- &si, &pi)) {
- VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
- SanitizerToolName, path_, GetLastError());
- return false;
- }
-
- // Process creation succeeded, so transfer handle ownership into the fields.
- input_fd_ = stdout_read.release();
- output_fd_ = stdin_write.release();
-
- // The llvm-symbolizer process is responsible for quitting itself when the
- // stdin pipe is closed, so we don't need these handles. Close them to prevent
- // leaks. If we ever want to try to kill the symbolizer process from the
- // parent, we'll want to hang on to these handles.
- CloseHandle(pi.hProcess);
- CloseHandle(pi.hThread);
- return true;
-}
-
-static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
- LowLevelAllocator *allocator) {
- if (!common_flags()->symbolize) {
- VReport(2, "Symbolizer is disabled.\n");
- return;
- }
-
- // Add llvm-symbolizer in case the binary has dwarf.
- const char *user_path = common_flags()->external_symbolizer_path;
- const char *path =
- user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
- if (path) {
- VReport(2, "Using llvm-symbolizer at %spath: %s\n",
- user_path ? "user-specified " : "", path);
- list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
- } else {
- if (user_path && user_path[0] == '\0') {
- VReport(2, "External symbolizer is explicitly disabled.\n");
- } else {
- VReport(2, "External symbolizer is not present.\n");
- }
- }
-
- // Add the dbghelp based symbolizer.
- list->push_back(new(*allocator) WinSymbolizerTool());
-}
-
-Symbolizer *Symbolizer::PlatformInit() {
- IntrusiveList<SymbolizerTool> list;
- list.clear();
- ChooseSymbolizerTools(&list, &symbolizer_allocator_);
-
- return new(symbolizer_allocator_) Symbolizer(list);
-}
-
-void Symbolizer::LateInitialize() {
- Symbolizer::GetOrInit();
-}
-
-} // namespace __sanitizer
-
-#endif // _WIN32
--- /dev/null
+//===-- sanitizer_symbolizer_win.cpp --------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries.
+// Windows-specific implementation of symbolizer parts.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+
+#include "sanitizer_dbghelp.h"
+#include "sanitizer_symbolizer_internal.h"
+
+namespace __sanitizer {
+
+decltype(::StackWalk64) *StackWalk64;
+decltype(::SymCleanup) *SymCleanup;
+decltype(::SymFromAddr) *SymFromAddr;
+decltype(::SymFunctionTableAccess64) *SymFunctionTableAccess64;
+decltype(::SymGetLineFromAddr64) *SymGetLineFromAddr64;
+decltype(::SymGetModuleBase64) *SymGetModuleBase64;
+decltype(::SymGetSearchPathW) *SymGetSearchPathW;
+decltype(::SymInitialize) *SymInitialize;
+decltype(::SymSetOptions) *SymSetOptions;
+decltype(::SymSetSearchPathW) *SymSetSearchPathW;
+decltype(::UnDecorateSymbolName) *UnDecorateSymbolName;
+
+namespace {
+
+class WinSymbolizerTool : public SymbolizerTool {
+ public:
+ // The constructor is provided to avoid synthesized memsets.
+ WinSymbolizerTool() {}
+
+ bool SymbolizePC(uptr addr, SymbolizedStack *stack) override;
+ bool SymbolizeData(uptr addr, DataInfo *info) override {
+ return false;
+ }
+ const char *Demangle(const char *name) override;
+};
+
+bool is_dbghelp_initialized = false;
+
+bool TrySymInitialize() {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
+ return SymInitialize(GetCurrentProcess(), 0, TRUE);
+ // FIXME: We don't call SymCleanup() on exit yet - should we?
+}
+
+} // namespace
+
+// Initializes DbgHelp library, if it's not yet initialized. Calls to this
+// function should be synchronized with respect to other calls to DbgHelp API
+// (e.g. from WinSymbolizerTool).
+void InitializeDbgHelpIfNeeded() {
+ if (is_dbghelp_initialized)
+ return;
+
+ HMODULE dbghelp = LoadLibraryA("dbghelp.dll");
+ CHECK(dbghelp && "failed to load dbghelp.dll");
+
+#define DBGHELP_IMPORT(name) \
+ do { \
+ name = \
+ reinterpret_cast<decltype(::name) *>(GetProcAddress(dbghelp, #name)); \
+ CHECK(name != nullptr); \
+ } while (0)
+ DBGHELP_IMPORT(StackWalk64);
+ DBGHELP_IMPORT(SymCleanup);
+ DBGHELP_IMPORT(SymFromAddr);
+ DBGHELP_IMPORT(SymFunctionTableAccess64);
+ DBGHELP_IMPORT(SymGetLineFromAddr64);
+ DBGHELP_IMPORT(SymGetModuleBase64);
+ DBGHELP_IMPORT(SymGetSearchPathW);
+ DBGHELP_IMPORT(SymInitialize);
+ DBGHELP_IMPORT(SymSetOptions);
+ DBGHELP_IMPORT(SymSetSearchPathW);
+ DBGHELP_IMPORT(UnDecorateSymbolName);
+#undef DBGHELP_IMPORT
+
+ if (!TrySymInitialize()) {
+ // OK, maybe the client app has called SymInitialize already.
+ // That's a bit unfortunate for us as all the DbgHelp functions are
+ // single-threaded and we can't coordinate with the app.
+ // FIXME: Can we stop the other threads at this point?
+ // Anyways, we have to reconfigure stuff to make sure that SymInitialize
+ // has all the appropriate options set.
+ // Cross our fingers and reinitialize DbgHelp.
+ Report("*** WARNING: Failed to initialize DbgHelp! ***\n");
+ Report("*** Most likely this means that the app is already ***\n");
+ Report("*** using DbgHelp, possibly with incompatible flags. ***\n");
+ Report("*** Due to technical reasons, symbolization might crash ***\n");
+ Report("*** or produce wrong results. ***\n");
+ SymCleanup(GetCurrentProcess());
+ TrySymInitialize();
+ }
+ is_dbghelp_initialized = true;
+
+ // When an executable is run from a location different from the one where it
+ // was originally built, we may not see the nearby PDB files.
+ // To work around this, let's append the directory of the main module
+ // to the symbol search path. All the failures below are not fatal.
+ const size_t kSymPathSize = 2048;
+ static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
+ if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
+ Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
+ return;
+ }
+ size_t sz = wcslen(path_buffer);
+ if (sz) {
+ CHECK_EQ(0, wcscat_s(path_buffer, L";"));
+ sz++;
+ }
+ DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
+ if (res == 0 || res == MAX_PATH) {
+ Report("*** WARNING: Failed to getting the EXE directory ***\n");
+ return;
+ }
+ // Write the zero character in place of the last backslash to get the
+ // directory of the main module at the end of path_buffer.
+ wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
+ CHECK_NE(last_bslash, 0);
+ *last_bslash = L'\0';
+ if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
+ Report("*** WARNING: Failed to SymSetSearchPathW\n");
+ return;
+ }
+}
+
+bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) {
+ InitializeDbgHelpIfNeeded();
+
+ // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
+ char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
+ PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+ symbol->MaxNameLen = MAX_SYM_NAME;
+ DWORD64 offset = 0;
+ BOOL got_objname = SymFromAddr(GetCurrentProcess(),
+ (DWORD64)addr, &offset, symbol);
+ if (!got_objname)
+ return false;
+
+ DWORD unused;
+ IMAGEHLP_LINE64 line_info;
+ line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
+ &unused, &line_info);
+ frame->info.function = internal_strdup(symbol->Name);
+ frame->info.function_offset = (uptr)offset;
+ if (got_fileline) {
+ frame->info.file = internal_strdup(line_info.FileName);
+ frame->info.line = line_info.LineNumber;
+ }
+ // Only consider this a successful symbolization attempt if we got file info.
+ // Otherwise, try llvm-symbolizer.
+ return got_fileline;
+}
+
+const char *WinSymbolizerTool::Demangle(const char *name) {
+ CHECK(is_dbghelp_initialized);
+ static char demangle_buffer[1000];
+ if (name[0] == '\01' &&
+ UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
+ UNDNAME_NAME_ONLY))
+ return demangle_buffer;
+ else
+ return name;
+}
+
+const char *Symbolizer::PlatformDemangle(const char *name) {
+ return name;
+}
+
+namespace {
+struct ScopedHandle {
+ ScopedHandle() : h_(nullptr) {}
+ explicit ScopedHandle(HANDLE h) : h_(h) {}
+ ~ScopedHandle() {
+ if (h_)
+ ::CloseHandle(h_);
+ }
+ HANDLE get() { return h_; }
+ HANDLE *receive() { return &h_; }
+ HANDLE release() {
+ HANDLE h = h_;
+ h_ = nullptr;
+ return h;
+ }
+ HANDLE h_;
+};
+} // namespace
+
+bool SymbolizerProcess::StartSymbolizerSubprocess() {
+ // Create inherited pipes for stdin and stdout.
+ ScopedHandle stdin_read, stdin_write;
+ ScopedHandle stdout_read, stdout_write;
+ SECURITY_ATTRIBUTES attrs;
+ attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ attrs.bInheritHandle = TRUE;
+ attrs.lpSecurityDescriptor = nullptr;
+ if (!::CreatePipe(stdin_read.receive(), stdin_write.receive(), &attrs, 0) ||
+ !::CreatePipe(stdout_read.receive(), stdout_write.receive(), &attrs, 0)) {
+ VReport(2, "WARNING: %s CreatePipe failed (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Don't inherit the writing end of stdin or the reading end of stdout.
+ if (!SetHandleInformation(stdin_write.get(), HANDLE_FLAG_INHERIT, 0) ||
+ !SetHandleInformation(stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
+ VReport(2, "WARNING: %s SetHandleInformation failed (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Compute the command line. Wrap double quotes around everything.
+ const char *argv[kArgVMax];
+ GetArgV(path_, argv);
+ InternalScopedString command_line(kMaxPathLength * 3);
+ for (int i = 0; argv[i]; i++) {
+ const char *arg = argv[i];
+ int arglen = internal_strlen(arg);
+ // Check that tool command lines are simple and that complete escaping is
+ // unnecessary.
+ CHECK(!internal_strchr(arg, '"') && "quotes in args unsupported");
+ CHECK(!internal_strstr(arg, "\\\\") &&
+ "double backslashes in args unsupported");
+ CHECK(arglen > 0 && arg[arglen - 1] != '\\' &&
+ "args ending in backslash and empty args unsupported");
+ command_line.append("\"%s\" ", arg);
+ }
+ VReport(3, "Launching symbolizer command: %s\n", command_line.data());
+
+ // Launch llvm-symbolizer with stdin and stdout redirected.
+ STARTUPINFOA si;
+ memset(&si, 0, sizeof(si));
+ si.cb = sizeof(si);
+ si.dwFlags |= STARTF_USESTDHANDLES;
+ si.hStdInput = stdin_read.get();
+ si.hStdOutput = stdout_write.get();
+ PROCESS_INFORMATION pi;
+ memset(&pi, 0, sizeof(pi));
+ if (!CreateProcessA(path_, // Executable
+ command_line.data(), // Command line
+ nullptr, // Process handle not inheritable
+ nullptr, // Thread handle not inheritable
+ TRUE, // Set handle inheritance to TRUE
+ 0, // Creation flags
+ nullptr, // Use parent's environment block
+ nullptr, // Use parent's starting directory
+ &si, &pi)) {
+ VReport(2, "WARNING: %s failed to create process for %s (error code: %d)\n",
+ SanitizerToolName, path_, GetLastError());
+ return false;
+ }
+
+ // Process creation succeeded, so transfer handle ownership into the fields.
+ input_fd_ = stdout_read.release();
+ output_fd_ = stdin_write.release();
+
+ // The llvm-symbolizer process is responsible for quitting itself when the
+ // stdin pipe is closed, so we don't need these handles. Close them to prevent
+ // leaks. If we ever want to try to kill the symbolizer process from the
+ // parent, we'll want to hang on to these handles.
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ return true;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+ LowLevelAllocator *allocator) {
+ if (!common_flags()->symbolize) {
+ VReport(2, "Symbolizer is disabled.\n");
+ return;
+ }
+
+ // Add llvm-symbolizer in case the binary has dwarf.
+ const char *user_path = common_flags()->external_symbolizer_path;
+ const char *path =
+ user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe");
+ if (path) {
+ VReport(2, "Using llvm-symbolizer at %spath: %s\n",
+ user_path ? "user-specified " : "", path);
+ list->push_back(new(*allocator) LLVMSymbolizer(path, allocator));
+ } else {
+ if (user_path && user_path[0] == '\0') {
+ VReport(2, "External symbolizer is explicitly disabled.\n");
+ } else {
+ VReport(2, "External symbolizer is not present.\n");
+ }
+ }
+
+ // Add the dbghelp based symbolizer.
+ list->push_back(new(*allocator) WinSymbolizerTool());
+}
+
+Symbolizer *Symbolizer::PlatformInit() {
+ IntrusiveList<SymbolizerTool> list;
+ list.clear();
+ ChooseSymbolizerTools(&list, &symbolizer_allocator_);
+
+ return new(symbolizer_allocator_) Symbolizer(list);
+}
+
+void Symbolizer::LateInitialize() {
+ Symbolizer::GetOrInit();
+}
+
+} // namespace __sanitizer
+
+#endif // _WIN32
//===-- sanitizer_syscall_generic.inc ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_syscall_linux_aarch64.inc --------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_syscall_linux_arm.inc -------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_syscall_linux_x86_64.inc ----------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_syscalls_netbsd.inc ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// DO NOT EDIT! THIS FILE HAS BEEN GENERATED!
//
// Generated with: generate_netbsd_syscalls.awk
-// Generated date: 2018-03-03
-// Generated from: syscalls.master,v 1.291 2018/01/06 16:41:23 kamil Exp
+// Generated date: 2018-10-30
+// Generated from: syscalls.master,v 1.293 2018/07/31 13:00:13 rjs Exp
//
//===----------------------------------------------------------------------===//
POST_SYSCALL(fpathconf)(long long res, long long fd_, long long name_) {
/* Nothing to do */
}
-/* syscall 193 has been skipped */
+PRE_SYSCALL(getsockopt2)
+(long long s_, long long level_, long long name_, void *val_, void *avalsize_) {
+ /* TODO */
+}
+POST_SYSCALL(getsockopt2)
+(long long res, long long s_, long long level_, long long name_, void *val_,
+ void *avalsize_) {
+ /* TODO */
+}
PRE_SYSCALL(getrlimit)(long long which_, void *rlp_) {
PRE_WRITE(rlp_, struct_rlimit_sz);
}
PRE_READ(nsa_, sizeof(__sanitizer_sigaction));
}
}
-PRE_SYSCALL(pmc_get_info)(long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
-POST_SYSCALL(pmc_get_info)
-(long long res, long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
-PRE_SYSCALL(pmc_control)(long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
-POST_SYSCALL(pmc_control)
-(long long res, long long ctr_, long long op_, void *args_) {
- /* TODO */
-}
+/* syscall 341 has been skipped */
+/* syscall 342 has been skipped */
PRE_SYSCALL(rasctl)(void *addr_, long long len_, long long op_) {
/* Nothing to do */
}
PRE_SYSCALL(sendmmsg)
(long long s_, void *mmsg_, long long vlen_, long long flags_) {
struct __sanitizer_mmsghdr *mmsg = (struct __sanitizer_mmsghdr *)mmsg_;
- unsigned int vlen = (vlen_ > 1024 ? 1024 : vlen_);
if (mmsg) {
- PRE_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * vlen);
+ PRE_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) *
+ (vlen_ > 1024 ? 1024 : vlen_));
}
}
POST_SYSCALL(sendmmsg)
(long long res, long long s_, void *mmsg_, long long vlen_, long long flags_) {
struct __sanitizer_mmsghdr *mmsg = (struct __sanitizer_mmsghdr *)mmsg_;
- unsigned int vlen = (vlen_ > 1024 ? 1024 : vlen_);
if (res >= 0) {
if (mmsg) {
- POST_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) * vlen);
+ POST_READ(mmsg, sizeof(struct __sanitizer_mmsghdr) *
+ (vlen_ > 1024 ? 1024 : vlen_));
}
}
}
+++ /dev/null
-//===-- sanitizer_termination.cc --------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// This file contains the Sanitizer termination functions CheckFailed and Die,
-/// and the callback functionalities associated with them.
-///
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common.h"
-#include "sanitizer_libc.h"
-
-namespace __sanitizer {
-
-static const int kMaxNumOfInternalDieCallbacks = 5;
-static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
-
-bool AddDieCallback(DieCallbackType callback) {
- for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
- if (InternalDieCallbacks[i] == nullptr) {
- InternalDieCallbacks[i] = callback;
- return true;
- }
- }
- return false;
-}
-
-bool RemoveDieCallback(DieCallbackType callback) {
- for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
- if (InternalDieCallbacks[i] == callback) {
- internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
- sizeof(InternalDieCallbacks[0]) *
- (kMaxNumOfInternalDieCallbacks - i - 1));
- InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
- return true;
- }
- }
- return false;
-}
-
-static DieCallbackType UserDieCallback;
-void SetUserDieCallback(DieCallbackType callback) {
- UserDieCallback = callback;
-}
-
-void NORETURN Die() {
- if (UserDieCallback)
- UserDieCallback();
- for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
- if (InternalDieCallbacks[i])
- InternalDieCallbacks[i]();
- }
- if (common_flags()->abort_on_error)
- Abort();
- internal__exit(common_flags()->exitcode);
-}
-
-static CheckFailedCallbackType CheckFailedCallback;
-void SetCheckFailedCallback(CheckFailedCallbackType callback) {
- CheckFailedCallback = callback;
-}
-
-const int kSecondsToSleepWhenRecursiveCheckFailed = 2;
-
-void NORETURN CheckFailed(const char *file, int line, const char *cond,
- u64 v1, u64 v2) {
- static atomic_uint32_t num_calls;
- if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) > 10) {
- SleepForSeconds(kSecondsToSleepWhenRecursiveCheckFailed);
- Trap();
- }
-
- if (CheckFailedCallback) {
- CheckFailedCallback(file, line, cond, v1, v2);
- }
- Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
- v1, v2);
- Die();
-}
-
-} // namespace __sanitizer
-
-using namespace __sanitizer; // NOLINT
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_set_death_callback(void (*callback)(void)) {
- SetUserDieCallback(callback);
-}
-} // extern "C"
--- /dev/null
+//===-- sanitizer_termination.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file contains the Sanitizer termination functions CheckFailed and Die,
+/// and the callback functionalities associated with them.
+///
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common.h"
+#include "sanitizer_libc.h"
+
+namespace __sanitizer {
+
+static const int kMaxNumOfInternalDieCallbacks = 5;
+static DieCallbackType InternalDieCallbacks[kMaxNumOfInternalDieCallbacks];
+
+bool AddDieCallback(DieCallbackType callback) {
+ for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+ if (InternalDieCallbacks[i] == nullptr) {
+ InternalDieCallbacks[i] = callback;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool RemoveDieCallback(DieCallbackType callback) {
+ for (int i = 0; i < kMaxNumOfInternalDieCallbacks; i++) {
+ if (InternalDieCallbacks[i] == callback) {
+ internal_memmove(&InternalDieCallbacks[i], &InternalDieCallbacks[i + 1],
+ sizeof(InternalDieCallbacks[0]) *
+ (kMaxNumOfInternalDieCallbacks - i - 1));
+ InternalDieCallbacks[kMaxNumOfInternalDieCallbacks - 1] = nullptr;
+ return true;
+ }
+ }
+ return false;
+}
+
+static DieCallbackType UserDieCallback;
+void SetUserDieCallback(DieCallbackType callback) {
+ UserDieCallback = callback;
+}
+
+void NORETURN Die() {
+ if (UserDieCallback)
+ UserDieCallback();
+ for (int i = kMaxNumOfInternalDieCallbacks - 1; i >= 0; i--) {
+ if (InternalDieCallbacks[i])
+ InternalDieCallbacks[i]();
+ }
+ if (common_flags()->abort_on_error)
+ Abort();
+ internal__exit(common_flags()->exitcode);
+}
+
+static CheckFailedCallbackType CheckFailedCallback;
+void SetCheckFailedCallback(CheckFailedCallbackType callback) {
+ CheckFailedCallback = callback;
+}
+
+const int kSecondsToSleepWhenRecursiveCheckFailed = 2;
+
+void NORETURN CheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ static atomic_uint32_t num_calls;
+ if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) > 10) {
+ SleepForSeconds(kSecondsToSleepWhenRecursiveCheckFailed);
+ Trap();
+ }
+
+ if (CheckFailedCallback) {
+ CheckFailedCallback(file, line, cond, v1, v2);
+ }
+ Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond,
+ v1, v2);
+ Die();
+}
+
+} // namespace __sanitizer
+
+using namespace __sanitizer; // NOLINT
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_set_death_callback(void (*callback)(void)) {
+ SetUserDieCallback(callback);
+}
+} // extern "C"
+++ /dev/null
-//===-- sanitizer_thread_registry.cc --------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between sanitizer tools.
-//
-// General thread bookkeeping functionality.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_thread_registry.h"
-
-namespace __sanitizer {
-
-ThreadContextBase::ThreadContextBase(u32 tid)
- : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
- status(ThreadStatusInvalid),
- detached(false), workerthread(false), parent_tid(0), next(0) {
- name[0] = '\0';
- atomic_store(&thread_destroyed, 0, memory_order_release);
-}
-
-ThreadContextBase::~ThreadContextBase() {
- // ThreadContextBase should never be deleted.
- CHECK(0);
-}
-
-void ThreadContextBase::SetName(const char *new_name) {
- name[0] = '\0';
- if (new_name) {
- internal_strncpy(name, new_name, sizeof(name));
- name[sizeof(name) - 1] = '\0';
- }
-}
-
-void ThreadContextBase::SetDead() {
- CHECK(status == ThreadStatusRunning ||
- status == ThreadStatusFinished);
- status = ThreadStatusDead;
- user_id = 0;
- OnDead();
-}
-
-void ThreadContextBase::SetDestroyed() {
- atomic_store(&thread_destroyed, 1, memory_order_release);
-}
-
-bool ThreadContextBase::GetDestroyed() {
- return !!atomic_load(&thread_destroyed, memory_order_acquire);
-}
-
-void ThreadContextBase::SetJoined(void *arg) {
- // FIXME(dvyukov): print message and continue (it's user error).
- CHECK_EQ(false, detached);
- CHECK_EQ(ThreadStatusFinished, status);
- status = ThreadStatusDead;
- user_id = 0;
- OnJoined(arg);
-}
-
-void ThreadContextBase::SetFinished() {
- // ThreadRegistry::FinishThread calls here in ThreadStatusCreated state
- // for a thread that never actually started. In that case the thread
- // should go to ThreadStatusFinished regardless of whether it was created
- // as detached.
- if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished;
- OnFinished();
-}
-
-void ThreadContextBase::SetStarted(tid_t _os_id, bool _workerthread,
- void *arg) {
- status = ThreadStatusRunning;
- os_id = _os_id;
- workerthread = _workerthread;
- OnStarted(arg);
-}
-
-void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
- bool _detached, u32 _parent_tid, void *arg) {
- status = ThreadStatusCreated;
- user_id = _user_id;
- unique_id = _unique_id;
- detached = _detached;
- // Parent tid makes no sense for the main thread.
- if (tid != 0)
- parent_tid = _parent_tid;
- OnCreated(arg);
-}
-
-void ThreadContextBase::Reset() {
- status = ThreadStatusInvalid;
- SetName(0);
- atomic_store(&thread_destroyed, 0, memory_order_release);
- OnReset();
-}
-
-// ThreadRegistry implementation.
-
-const u32 ThreadRegistry::kUnknownTid = ~0U;
-
-ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
- u32 thread_quarantine_size, u32 max_reuse)
- : context_factory_(factory),
- max_threads_(max_threads),
- thread_quarantine_size_(thread_quarantine_size),
- max_reuse_(max_reuse),
- mtx_(),
- n_contexts_(0),
- total_threads_(0),
- alive_threads_(0),
- max_alive_threads_(0),
- running_threads_(0) {
- threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
- "ThreadRegistry");
- dead_threads_.clear();
- invalid_threads_.clear();
-}
-
-void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
- uptr *alive) {
- BlockingMutexLock l(&mtx_);
- if (total) *total = n_contexts_;
- if (running) *running = running_threads_;
- if (alive) *alive = alive_threads_;
-}
-
-uptr ThreadRegistry::GetMaxAliveThreads() {
- BlockingMutexLock l(&mtx_);
- return max_alive_threads_;
-}
-
-u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
- void *arg) {
- BlockingMutexLock l(&mtx_);
- u32 tid = kUnknownTid;
- ThreadContextBase *tctx = QuarantinePop();
- if (tctx) {
- tid = tctx->tid;
- } else if (n_contexts_ < max_threads_) {
- // Allocate new thread context and tid.
- tid = n_contexts_++;
- tctx = context_factory_(tid);
- threads_[tid] = tctx;
- } else {
-#if !SANITIZER_GO
- Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
- SanitizerToolName, max_threads_);
-#else
- Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
- " dying\n", max_threads_);
-#endif
- Die();
- }
- CHECK_NE(tctx, 0);
- CHECK_NE(tid, kUnknownTid);
- CHECK_LT(tid, max_threads_);
- CHECK_EQ(tctx->status, ThreadStatusInvalid);
- alive_threads_++;
- if (max_alive_threads_ < alive_threads_) {
- max_alive_threads_++;
- CHECK_EQ(alive_threads_, max_alive_threads_);
- }
- tctx->SetCreated(user_id, total_threads_++, detached,
- parent_tid, arg);
- return tid;
-}
-
-void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
- void *arg) {
- CheckLocked();
- for (u32 tid = 0; tid < n_contexts_; tid++) {
- ThreadContextBase *tctx = threads_[tid];
- if (tctx == 0)
- continue;
- cb(tctx, arg);
- }
-}
-
-u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
- BlockingMutexLock l(&mtx_);
- for (u32 tid = 0; tid < n_contexts_; tid++) {
- ThreadContextBase *tctx = threads_[tid];
- if (tctx != 0 && cb(tctx, arg))
- return tctx->tid;
- }
- return kUnknownTid;
-}
-
-ThreadContextBase *
-ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
- CheckLocked();
- for (u32 tid = 0; tid < n_contexts_; tid++) {
- ThreadContextBase *tctx = threads_[tid];
- if (tctx != 0 && cb(tctx, arg))
- return tctx;
- }
- return 0;
-}
-
-static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
- void *arg) {
- return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
- tctx->status != ThreadStatusDead);
-}
-
-ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
- return FindThreadContextLocked(FindThreadContextByOsIdCallback,
- (void *)os_id);
-}
-
-void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
- BlockingMutexLock l(&mtx_);
- CHECK_LT(tid, n_contexts_);
- ThreadContextBase *tctx = threads_[tid];
- CHECK_NE(tctx, 0);
- CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning,
- tctx->status);
- tctx->SetName(name);
-}
-
-void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
- BlockingMutexLock l(&mtx_);
- for (u32 tid = 0; tid < n_contexts_; tid++) {
- ThreadContextBase *tctx = threads_[tid];
- if (tctx != 0 && tctx->user_id == user_id &&
- tctx->status != ThreadStatusInvalid) {
- tctx->SetName(name);
- return;
- }
- }
-}
-
-void ThreadRegistry::DetachThread(u32 tid, void *arg) {
- BlockingMutexLock l(&mtx_);
- CHECK_LT(tid, n_contexts_);
- ThreadContextBase *tctx = threads_[tid];
- CHECK_NE(tctx, 0);
- if (tctx->status == ThreadStatusInvalid) {
- Report("%s: Detach of non-existent thread\n", SanitizerToolName);
- return;
- }
- tctx->OnDetached(arg);
- if (tctx->status == ThreadStatusFinished) {
- tctx->SetDead();
- QuarantinePush(tctx);
- } else {
- tctx->detached = true;
- }
-}
-
-void ThreadRegistry::JoinThread(u32 tid, void *arg) {
- bool destroyed = false;
- do {
- {
- BlockingMutexLock l(&mtx_);
- CHECK_LT(tid, n_contexts_);
- ThreadContextBase *tctx = threads_[tid];
- CHECK_NE(tctx, 0);
- if (tctx->status == ThreadStatusInvalid) {
- Report("%s: Join of non-existent thread\n", SanitizerToolName);
- return;
- }
- if ((destroyed = tctx->GetDestroyed())) {
- tctx->SetJoined(arg);
- QuarantinePush(tctx);
- }
- }
- if (!destroyed)
- internal_sched_yield();
- } while (!destroyed);
-}
-
-// Normally this is called when the thread is about to exit. If
-// called in ThreadStatusCreated state, then this thread was never
-// really started. We just did CreateThread for a prospective new
-// thread before trying to create it, and then failed to actually
-// create it, and so never called StartThread.
-void ThreadRegistry::FinishThread(u32 tid) {
- BlockingMutexLock l(&mtx_);
- CHECK_GT(alive_threads_, 0);
- alive_threads_--;
- CHECK_LT(tid, n_contexts_);
- ThreadContextBase *tctx = threads_[tid];
- CHECK_NE(tctx, 0);
- bool dead = tctx->detached;
- if (tctx->status == ThreadStatusRunning) {
- CHECK_GT(running_threads_, 0);
- running_threads_--;
- } else {
- // The thread never really existed.
- CHECK_EQ(tctx->status, ThreadStatusCreated);
- dead = true;
- }
- tctx->SetFinished();
- if (dead) {
- tctx->SetDead();
- QuarantinePush(tctx);
- }
- tctx->SetDestroyed();
-}
-
-void ThreadRegistry::StartThread(u32 tid, tid_t os_id, bool workerthread,
- void *arg) {
- BlockingMutexLock l(&mtx_);
- running_threads_++;
- CHECK_LT(tid, n_contexts_);
- ThreadContextBase *tctx = threads_[tid];
- CHECK_NE(tctx, 0);
- CHECK_EQ(ThreadStatusCreated, tctx->status);
- tctx->SetStarted(os_id, workerthread, arg);
-}
-
-void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
- if (tctx->tid == 0)
- return; // Don't reuse the main thread. It's a special snowflake.
- dead_threads_.push_back(tctx);
- if (dead_threads_.size() <= thread_quarantine_size_)
- return;
- tctx = dead_threads_.front();
- dead_threads_.pop_front();
- CHECK_EQ(tctx->status, ThreadStatusDead);
- tctx->Reset();
- tctx->reuse_count++;
- if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)
- return;
- invalid_threads_.push_back(tctx);
-}
-
-ThreadContextBase *ThreadRegistry::QuarantinePop() {
- if (invalid_threads_.size() == 0)
- return 0;
- ThreadContextBase *tctx = invalid_threads_.front();
- invalid_threads_.pop_front();
- return tctx;
-}
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_thread_registry.cpp -------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizer tools.
+//
+// General thread bookkeeping functionality.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_thread_registry.h"
+
+namespace __sanitizer {
+
+ThreadContextBase::ThreadContextBase(u32 tid)
+ : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
+ status(ThreadStatusInvalid), detached(false),
+ thread_type(ThreadType::Regular), parent_tid(0), next(0) {
+ name[0] = '\0';
+ atomic_store(&thread_destroyed, 0, memory_order_release);
+}
+
+ThreadContextBase::~ThreadContextBase() {
+ // ThreadContextBase should never be deleted.
+ CHECK(0);
+}
+
+void ThreadContextBase::SetName(const char *new_name) {
+ name[0] = '\0';
+ if (new_name) {
+ internal_strncpy(name, new_name, sizeof(name));
+ name[sizeof(name) - 1] = '\0';
+ }
+}
+
+void ThreadContextBase::SetDead() {
+ CHECK(status == ThreadStatusRunning ||
+ status == ThreadStatusFinished);
+ status = ThreadStatusDead;
+ user_id = 0;
+ OnDead();
+}
+
+void ThreadContextBase::SetDestroyed() {
+ atomic_store(&thread_destroyed, 1, memory_order_release);
+}
+
+bool ThreadContextBase::GetDestroyed() {
+ return !!atomic_load(&thread_destroyed, memory_order_acquire);
+}
+
+void ThreadContextBase::SetJoined(void *arg) {
+ // FIXME(dvyukov): print message and continue (it's user error).
+ CHECK_EQ(false, detached);
+ CHECK_EQ(ThreadStatusFinished, status);
+ status = ThreadStatusDead;
+ user_id = 0;
+ OnJoined(arg);
+}
+
+void ThreadContextBase::SetFinished() {
+ // ThreadRegistry::FinishThread calls here in ThreadStatusCreated state
+ // for a thread that never actually started. In that case the thread
+ // should go to ThreadStatusFinished regardless of whether it was created
+ // as detached.
+ if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished;
+ OnFinished();
+}
+
+void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type,
+ void *arg) {
+ status = ThreadStatusRunning;
+ os_id = _os_id;
+ thread_type = _thread_type;
+ OnStarted(arg);
+}
+
+void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
+ bool _detached, u32 _parent_tid, void *arg) {
+ status = ThreadStatusCreated;
+ user_id = _user_id;
+ unique_id = _unique_id;
+ detached = _detached;
+ // Parent tid makes no sense for the main thread.
+ if (tid != 0)
+ parent_tid = _parent_tid;
+ OnCreated(arg);
+}
+
+void ThreadContextBase::Reset() {
+ status = ThreadStatusInvalid;
+ SetName(0);
+ atomic_store(&thread_destroyed, 0, memory_order_release);
+ OnReset();
+}
+
+// ThreadRegistry implementation.
+
+const u32 ThreadRegistry::kUnknownTid = ~0U;
+
+ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
+ u32 thread_quarantine_size, u32 max_reuse)
+ : context_factory_(factory),
+ max_threads_(max_threads),
+ thread_quarantine_size_(thread_quarantine_size),
+ max_reuse_(max_reuse),
+ mtx_(),
+ n_contexts_(0),
+ total_threads_(0),
+ alive_threads_(0),
+ max_alive_threads_(0),
+ running_threads_(0) {
+ threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
+ "ThreadRegistry");
+ dead_threads_.clear();
+ invalid_threads_.clear();
+}
+
+void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
+ uptr *alive) {
+ BlockingMutexLock l(&mtx_);
+ if (total) *total = n_contexts_;
+ if (running) *running = running_threads_;
+ if (alive) *alive = alive_threads_;
+}
+
+uptr ThreadRegistry::GetMaxAliveThreads() {
+ BlockingMutexLock l(&mtx_);
+ return max_alive_threads_;
+}
+
+u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
+ void *arg) {
+ BlockingMutexLock l(&mtx_);
+ u32 tid = kUnknownTid;
+ ThreadContextBase *tctx = QuarantinePop();
+ if (tctx) {
+ tid = tctx->tid;
+ } else if (n_contexts_ < max_threads_) {
+ // Allocate new thread context and tid.
+ tid = n_contexts_++;
+ tctx = context_factory_(tid);
+ threads_[tid] = tctx;
+ } else {
+#if !SANITIZER_GO
+ Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
+ SanitizerToolName, max_threads_);
+#else
+ Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
+ " dying\n", max_threads_);
+#endif
+ Die();
+ }
+ CHECK_NE(tctx, 0);
+ CHECK_NE(tid, kUnknownTid);
+ CHECK_LT(tid, max_threads_);
+ CHECK_EQ(tctx->status, ThreadStatusInvalid);
+ alive_threads_++;
+ if (max_alive_threads_ < alive_threads_) {
+ max_alive_threads_++;
+ CHECK_EQ(alive_threads_, max_alive_threads_);
+ }
+ tctx->SetCreated(user_id, total_threads_++, detached,
+ parent_tid, arg);
+ return tid;
+}
+
+void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
+ void *arg) {
+ CheckLocked();
+ for (u32 tid = 0; tid < n_contexts_; tid++) {
+ ThreadContextBase *tctx = threads_[tid];
+ if (tctx == 0)
+ continue;
+ cb(tctx, arg);
+ }
+}
+
+u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
+ BlockingMutexLock l(&mtx_);
+ for (u32 tid = 0; tid < n_contexts_; tid++) {
+ ThreadContextBase *tctx = threads_[tid];
+ if (tctx != 0 && cb(tctx, arg))
+ return tctx->tid;
+ }
+ return kUnknownTid;
+}
+
+ThreadContextBase *
+ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
+ CheckLocked();
+ for (u32 tid = 0; tid < n_contexts_; tid++) {
+ ThreadContextBase *tctx = threads_[tid];
+ if (tctx != 0 && cb(tctx, arg))
+ return tctx;
+ }
+ return 0;
+}
+
+static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
+ void *arg) {
+ return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
+ tctx->status != ThreadStatusDead);
+}
+
+ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
+ return FindThreadContextLocked(FindThreadContextByOsIdCallback,
+ (void *)os_id);
+}
+
+void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
+ BlockingMutexLock l(&mtx_);
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning,
+ tctx->status);
+ tctx->SetName(name);
+}
+
+void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
+ BlockingMutexLock l(&mtx_);
+ for (u32 tid = 0; tid < n_contexts_; tid++) {
+ ThreadContextBase *tctx = threads_[tid];
+ if (tctx != 0 && tctx->user_id == user_id &&
+ tctx->status != ThreadStatusInvalid) {
+ tctx->SetName(name);
+ return;
+ }
+ }
+}
+
+void ThreadRegistry::DetachThread(u32 tid, void *arg) {
+ BlockingMutexLock l(&mtx_);
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ if (tctx->status == ThreadStatusInvalid) {
+ Report("%s: Detach of non-existent thread\n", SanitizerToolName);
+ return;
+ }
+ tctx->OnDetached(arg);
+ if (tctx->status == ThreadStatusFinished) {
+ tctx->SetDead();
+ QuarantinePush(tctx);
+ } else {
+ tctx->detached = true;
+ }
+}
+
+void ThreadRegistry::JoinThread(u32 tid, void *arg) {
+ bool destroyed = false;
+ do {
+ {
+ BlockingMutexLock l(&mtx_);
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ if (tctx->status == ThreadStatusInvalid) {
+ Report("%s: Join of non-existent thread\n", SanitizerToolName);
+ return;
+ }
+ if ((destroyed = tctx->GetDestroyed())) {
+ tctx->SetJoined(arg);
+ QuarantinePush(tctx);
+ }
+ }
+ if (!destroyed)
+ internal_sched_yield();
+ } while (!destroyed);
+}
+
+// Normally this is called when the thread is about to exit. If
+// called in ThreadStatusCreated state, then this thread was never
+// really started. We just did CreateThread for a prospective new
+// thread before trying to create it, and then failed to actually
+// create it, and so never called StartThread.
+void ThreadRegistry::FinishThread(u32 tid) {
+ BlockingMutexLock l(&mtx_);
+ CHECK_GT(alive_threads_, 0);
+ alive_threads_--;
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ bool dead = tctx->detached;
+ if (tctx->status == ThreadStatusRunning) {
+ CHECK_GT(running_threads_, 0);
+ running_threads_--;
+ } else {
+ // The thread never really existed.
+ CHECK_EQ(tctx->status, ThreadStatusCreated);
+ dead = true;
+ }
+ tctx->SetFinished();
+ if (dead) {
+ tctx->SetDead();
+ QuarantinePush(tctx);
+ }
+ tctx->SetDestroyed();
+}
+
+void ThreadRegistry::StartThread(u32 tid, tid_t os_id, ThreadType thread_type,
+ void *arg) {
+ BlockingMutexLock l(&mtx_);
+ running_threads_++;
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ CHECK_EQ(ThreadStatusCreated, tctx->status);
+ tctx->SetStarted(os_id, thread_type, arg);
+}
+
+void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
+ if (tctx->tid == 0)
+ return; // Don't reuse the main thread. It's a special snowflake.
+ dead_threads_.push_back(tctx);
+ if (dead_threads_.size() <= thread_quarantine_size_)
+ return;
+ tctx = dead_threads_.front();
+ dead_threads_.pop_front();
+ CHECK_EQ(tctx->status, ThreadStatusDead);
+ tctx->Reset();
+ tctx->reuse_count++;
+ if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)
+ return;
+ invalid_threads_.push_back(tctx);
+}
+
+ThreadContextBase *ThreadRegistry::QuarantinePop() {
+ if (invalid_threads_.size() == 0)
+ return 0;
+ ThreadContextBase *tctx = invalid_threads_.front();
+ invalid_threads_.pop_front();
+ return tctx;
+}
+
+void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
+ BlockingMutexLock l(&mtx_);
+ CHECK_LT(tid, n_contexts_);
+ ThreadContextBase *tctx = threads_[tid];
+ CHECK_NE(tctx, 0);
+ CHECK_NE(tctx->status, ThreadStatusInvalid);
+ CHECK_NE(tctx->status, ThreadStatusDead);
+ CHECK_EQ(tctx->user_id, 0);
+ tctx->user_id = user_id;
+}
+
+} // namespace __sanitizer
//===-- sanitizer_thread_registry.h -----------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
ThreadStatusDead // Joined, but some info is still available.
};
+enum class ThreadType {
+ Regular, // Normal thread
+ Worker, // macOS Grand Central Dispatch (GCD) worker thread
+ Fiber, // Fiber
+};
+
// Generic thread context. Specific sanitizer tools may inherit from it.
// If thread is dead, context may optionally be reused for a new thread.
class ThreadContextBase {
ThreadStatus status;
bool detached;
- bool workerthread;
+ ThreadType thread_type;
u32 parent_tid;
ThreadContextBase *next; // For storing thread contexts in a list.
void SetDead();
void SetJoined(void *arg);
void SetFinished();
- void SetStarted(tid_t _os_id, bool _workerthread, void *arg);
+ void SetStarted(tid_t _os_id, ThreadType _thread_type, void *arg);
void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
u32 _parent_tid, void *arg);
void Reset();
void DetachThread(u32 tid, void *arg);
void JoinThread(u32 tid, void *arg);
void FinishThread(u32 tid);
- void StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg);
+ void StartThread(u32 tid, tid_t os_id, ThreadType thread_type, void *arg);
+ void SetThreadUserId(u32 tid, uptr user_id);
private:
const ThreadContextFactory context_factory_;
+++ /dev/null
-//===-- sanitizer_tls_get_addr.cc -----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Handle the __tls_get_addr call.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_tls_get_addr.h"
-
-#include "sanitizer_flags.h"
-#include "sanitizer_platform_interceptors.h"
-
-namespace __sanitizer {
-#if SANITIZER_INTERCEPT_TLS_GET_ADDR
-
-// The actual parameter that comes to __tls_get_addr
-// is a pointer to a struct with two words in it:
-struct TlsGetAddrParam {
- uptr dso_id;
- uptr offset;
-};
-
-// Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
-// which has such header.
-struct Glibc_2_19_tls_header {
- uptr size;
- uptr start;
-};
-
-// This must be static TLS
-__attribute__((tls_model("initial-exec")))
-static __thread DTLS dtls;
-
-// Make sure we properly destroy the DTLS objects:
-// this counter should never get too large.
-static atomic_uintptr_t number_of_live_dtls;
-
-static const uptr kDestroyedThread = -1;
-
-static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) {
- if (!size) return;
- VReport(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv, size);
- UnmapOrDie(dtv, size * sizeof(DTLS::DTV));
- atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed);
-}
-
-static inline void DTLS_Resize(uptr new_size) {
- if (dtls.dtv_size >= new_size) return;
- new_size = RoundUpToPowerOfTwo(new_size);
- new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV));
- DTLS::DTV *new_dtv =
- (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize");
- uptr num_live_dtls =
- atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed);
- VReport(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls, num_live_dtls);
- CHECK_LT(num_live_dtls, 1 << 20);
- uptr old_dtv_size = dtls.dtv_size;
- DTLS::DTV *old_dtv = dtls.dtv;
- if (old_dtv_size)
- internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV));
- dtls.dtv = new_dtv;
- dtls.dtv_size = new_size;
- if (old_dtv_size)
- DTLS_Deallocate(old_dtv, old_dtv_size);
-}
-
-void DTLS_Destroy() {
- if (!common_flags()->intercept_tls_get_addr) return;
- VReport(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls, dtls.dtv_size);
- uptr s = dtls.dtv_size;
- dtls.dtv_size = kDestroyedThread; // Do this before unmap for AS-safety.
- DTLS_Deallocate(dtls.dtv, s);
-}
-
-#if defined(__powerpc64__) || defined(__mips__)
-// This is glibc's TLS_DTV_OFFSET:
-// "Dynamic thread vector pointers point 0x8000 past the start of each
-// TLS block."
-static const uptr kDtvOffset = 0x8000;
-#else
-static const uptr kDtvOffset = 0;
-#endif
-
-DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
- uptr static_tls_begin, uptr static_tls_end) {
- if (!common_flags()->intercept_tls_get_addr) return 0;
- TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
- uptr dso_id = arg->dso_id;
- if (dtls.dtv_size == kDestroyedThread) return 0;
- DTLS_Resize(dso_id + 1);
- if (dtls.dtv[dso_id].beg) return 0;
- uptr tls_size = 0;
- uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
- VReport(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
- "num_live_dtls %zd\n",
- arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg,
- atomic_load(&number_of_live_dtls, memory_order_relaxed));
- if (dtls.last_memalign_ptr == tls_beg) {
- tls_size = dtls.last_memalign_size;
- VReport(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
- tls_beg, tls_size);
- } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
- // This is the static TLS block which was initialized / unpoisoned at thread
- // creation.
- VReport(2, "__tls_get_addr: static tls: %p\n", tls_beg);
- tls_size = 0;
- } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
- // We may want to check gnu_get_libc_version().
- Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
- tls_size = header->size;
- tls_beg = header->start;
- VReport(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n",
- tls_beg, tls_size);
- } else {
- VReport(2, "__tls_get_addr: Can't guess glibc version\n");
- // This may happen inside the DTOR of main thread, so just ignore it.
- tls_size = 0;
- }
- dtls.dtv[dso_id].beg = tls_beg;
- dtls.dtv[dso_id].size = tls_size;
- return dtls.dtv + dso_id;
-}
-
-void DTLS_on_libc_memalign(void *ptr, uptr size) {
- if (!common_flags()->intercept_tls_get_addr) return;
- VReport(2, "DTLS_on_libc_memalign: %p %p\n", ptr, size);
- dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
- dtls.last_memalign_size = size;
-}
-
-DTLS *DTLS_Get() { return &dtls; }
-
-bool DTLSInDestruction(DTLS *dtls) {
- return dtls->dtv_size == kDestroyedThread;
-}
-
-#else
-void DTLS_on_libc_memalign(void *ptr, uptr size) {}
-DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res,
- unsigned long, unsigned long) { return 0; }
-DTLS *DTLS_Get() { return 0; }
-void DTLS_Destroy() {}
-bool DTLSInDestruction(DTLS *dtls) {
- UNREACHABLE("dtls is unsupported on this platform!");
-}
-
-#endif // SANITIZER_INTERCEPT_TLS_GET_ADDR
-
-} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_tls_get_addr.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Handle the __tls_get_addr call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_tls_get_addr.h"
+
+#include "sanitizer_flags.h"
+#include "sanitizer_platform_interceptors.h"
+
+namespace __sanitizer {
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+// The actual parameter that comes to __tls_get_addr
+// is a pointer to a struct with two words in it:
+struct TlsGetAddrParam {
+ uptr dso_id;
+ uptr offset;
+};
+
+// Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
+// which has such header.
+struct Glibc_2_19_tls_header {
+ uptr size;
+ uptr start;
+};
+
+// This must be static TLS
+__attribute__((tls_model("initial-exec")))
+static __thread DTLS dtls;
+
+// Make sure we properly destroy the DTLS objects:
+// this counter should never get too large.
+static atomic_uintptr_t number_of_live_dtls;
+
+static const uptr kDestroyedThread = -1;
+
+static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) {
+ if (!size) return;
+ VReport(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv, size);
+ UnmapOrDie(dtv, size * sizeof(DTLS::DTV));
+ atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed);
+}
+
+static inline void DTLS_Resize(uptr new_size) {
+ if (dtls.dtv_size >= new_size) return;
+ new_size = RoundUpToPowerOfTwo(new_size);
+ new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV));
+ DTLS::DTV *new_dtv =
+ (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize");
+ uptr num_live_dtls =
+ atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed);
+ VReport(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls, num_live_dtls);
+ CHECK_LT(num_live_dtls, 1 << 20);
+ uptr old_dtv_size = dtls.dtv_size;
+ DTLS::DTV *old_dtv = dtls.dtv;
+ if (old_dtv_size)
+ internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV));
+ dtls.dtv = new_dtv;
+ dtls.dtv_size = new_size;
+ if (old_dtv_size)
+ DTLS_Deallocate(old_dtv, old_dtv_size);
+}
+
+void DTLS_Destroy() {
+ if (!common_flags()->intercept_tls_get_addr) return;
+ VReport(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls, dtls.dtv_size);
+ uptr s = dtls.dtv_size;
+ dtls.dtv_size = kDestroyedThread; // Do this before unmap for AS-safety.
+ DTLS_Deallocate(dtls.dtv, s);
+}
+
+#if defined(__powerpc64__) || defined(__mips__)
+// This is glibc's TLS_DTV_OFFSET:
+// "Dynamic thread vector pointers point 0x8000 past the start of each
+// TLS block."
+static const uptr kDtvOffset = 0x8000;
+#else
+static const uptr kDtvOffset = 0;
+#endif
+
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res,
+ uptr static_tls_begin, uptr static_tls_end) {
+ if (!common_flags()->intercept_tls_get_addr) return 0;
+ TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void);
+ uptr dso_id = arg->dso_id;
+ if (dtls.dtv_size == kDestroyedThread) return 0;
+ DTLS_Resize(dso_id + 1);
+ if (dtls.dtv[dso_id].beg) return 0;
+ uptr tls_size = 0;
+ uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset;
+ VReport(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
+ "num_live_dtls %zd\n",
+ arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg,
+ atomic_load(&number_of_live_dtls, memory_order_relaxed));
+ if (dtls.last_memalign_ptr == tls_beg) {
+ tls_size = dtls.last_memalign_size;
+ VReport(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
+ tls_beg, tls_size);
+ } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) {
+ // This is the static TLS block which was initialized / unpoisoned at thread
+ // creation.
+ VReport(2, "__tls_get_addr: static tls: %p\n", tls_beg);
+ tls_size = 0;
+ } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) {
+ // We may want to check gnu_get_libc_version().
+ Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1;
+ tls_size = header->size;
+ tls_beg = header->start;
+ VReport(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n",
+ tls_beg, tls_size);
+ } else {
+ VReport(2, "__tls_get_addr: Can't guess glibc version\n");
+ // This may happen inside the DTOR of main thread, so just ignore it.
+ tls_size = 0;
+ }
+ dtls.dtv[dso_id].beg = tls_beg;
+ dtls.dtv[dso_id].size = tls_size;
+ return dtls.dtv + dso_id;
+}
+
+void DTLS_on_libc_memalign(void *ptr, uptr size) {
+ if (!common_flags()->intercept_tls_get_addr) return;
+ VReport(2, "DTLS_on_libc_memalign: %p %p\n", ptr, size);
+ dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr);
+ dtls.last_memalign_size = size;
+}
+
+DTLS *DTLS_Get() { return &dtls; }
+
+bool DTLSInDestruction(DTLS *dtls) {
+ return dtls->dtv_size == kDestroyedThread;
+}
+
+#else
+void DTLS_on_libc_memalign(void *ptr, uptr size) {}
+DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res,
+ unsigned long, unsigned long) { return 0; }
+DTLS *DTLS_Get() { return 0; }
+void DTLS_Destroy() {}
+bool DTLSInDestruction(DTLS *dtls) {
+ UNREACHABLE("dtls is unsupported on this platform!");
+}
+
+#endif // SANITIZER_INTERCEPT_TLS_GET_ADDR
+
+} // namespace __sanitizer
//===-- sanitizer_tls_get_addr.h --------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
uptr dtv_size;
DTV *dtv; // dtv_size elements, allocated by MmapOrDie.
- // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cc
+ // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cpp
uptr last_memalign_size;
uptr last_memalign_ptr;
};
--- /dev/null
+//===-- sanitizer_type_traits.cpp -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements a subset of C++ type traits. This is so we can avoid depending
+// on system C++ headers.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_type_traits.h"
+
+namespace __sanitizer {
+
+const bool true_type::value;
+const bool false_type::value;
+
+} // namespace __sanitizer
--- /dev/null
+//===-- sanitizer_type_traits.h ---------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implements a subset of C++ type traits. This is so we can avoid depending
+// on system C++ headers.
+//
+//===----------------------------------------------------------------------===//
+#ifndef SANITIZER_TYPE_TRAITS_H
+#define SANITIZER_TYPE_TRAITS_H
+
+namespace __sanitizer {
+
+struct true_type {
+ static const bool value = true;
+};
+
+struct false_type {
+ static const bool value = false;
+};
+
+// is_same<T, U>
+//
+// Type trait to compare if types are the same.
+// E.g.
+//
+// ```
+// is_same<int,int>::value - True
+// is_same<int,char>::value - False
+// ```
+template <typename T, typename U>
+struct is_same : public false_type {};
+
+template <typename T>
+struct is_same<T, T> : public true_type {};
+
+// conditional<B, T, F>
+//
+// Defines type as T if B is true or as F otherwise.
+// E.g. the following is true
+//
+// ```
+// is_same<int, conditional<true, int, double>::type>::value
+// is_same<double, conditional<false, int, double>::type>::value
+// ```
+template <bool B, class T, class F>
+struct conditional {
+ using type = T;
+};
+
+template <class T, class F>
+struct conditional<false, T, F> {
+ using type = F;
+};
+
+} // namespace __sanitizer
+
+#endif
+++ /dev/null
-//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file contains the unwind.h-based (aka "slow") stack unwinding routines
-// available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
- SANITIZER_SOLARIS
-#include "sanitizer_common.h"
-#include "sanitizer_stacktrace.h"
-
-#if SANITIZER_ANDROID
-#include <dlfcn.h> // for dlopen()
-#endif
-
-#if SANITIZER_FREEBSD
-#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
-#endif
-#include <unwind.h>
-
-namespace __sanitizer {
-
-//------------------------- SlowUnwindStack -----------------------------------
-
-typedef struct {
- uptr absolute_pc;
- uptr stack_top;
- uptr stack_size;
-} backtrace_frame_t;
-
-extern "C" {
-typedef void *(*acquire_my_map_info_list_func)();
-typedef void (*release_my_map_info_list_func)(void *map);
-typedef sptr (*unwind_backtrace_signal_arch_func)(
- void *siginfo, void *sigcontext, void *map_info_list,
- backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
-acquire_my_map_info_list_func acquire_my_map_info_list;
-release_my_map_info_list_func release_my_map_info_list;
-unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
-} // extern "C"
-
-#if SANITIZER_ANDROID
-void SanitizerInitializeUnwinder() {
- if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
-
- // Pre-lollipop Android can not unwind through signal handler frames with
- // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
- // workarounds.
- void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
- if (!p) {
- VReport(1,
- "Failed to open libcorkscrew.so. You may see broken stack traces "
- "in SEGV reports.");
- return;
- }
- acquire_my_map_info_list =
- (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
- release_my_map_info_list =
- (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
- unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
- p, "unwind_backtrace_signal_arch");
- if (!acquire_my_map_info_list || !release_my_map_info_list ||
- !unwind_backtrace_signal_arch) {
- VReport(1,
- "Failed to find one of the required symbols in libcorkscrew.so. "
- "You may see broken stack traces in SEGV reports.");
- acquire_my_map_info_list = 0;
- unwind_backtrace_signal_arch = 0;
- release_my_map_info_list = 0;
- }
-}
-#endif
-
-#if defined(__arm__) && !SANITIZER_NETBSD
-// NetBSD uses dwarf EH
-#define UNWIND_STOP _URC_END_OF_STACK
-#define UNWIND_CONTINUE _URC_NO_REASON
-#else
-#define UNWIND_STOP _URC_NORMAL_STOP
-#define UNWIND_CONTINUE _URC_NO_REASON
-#endif
-
-uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
-#if defined(__arm__) && !SANITIZER_MAC
- uptr val;
- _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
- 15 /* r15 = PC */, _UVRSD_UINT32, &val);
- CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
- // Clear the Thumb bit.
- return val & ~(uptr)1;
-#else
- return _Unwind_GetIP(ctx);
-#endif
-}
-
-struct UnwindTraceArg {
- BufferedStackTrace *stack;
- u32 max_depth;
-};
-
-_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
- UnwindTraceArg *arg = (UnwindTraceArg*)param;
- CHECK_LT(arg->stack->size, arg->max_depth);
- uptr pc = Unwind_GetIP(ctx);
- const uptr kPageSize = GetPageSizeCached();
- // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
- // x86_64) is invalid and stop unwinding here. If we're adding support for
- // a platform where this isn't true, we need to reconsider this check.
- if (pc < kPageSize) return UNWIND_STOP;
- arg->stack->trace_buffer[arg->stack->size++] = pc;
- if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
- return UNWIND_CONTINUE;
-}
-
-void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
- CHECK_GE(max_depth, 2);
- size = 0;
- UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
- _Unwind_Backtrace(Unwind_Trace, &arg);
- // We need to pop a few frames so that pc is on top.
- uptr to_pop = LocatePcInTrace(pc);
- // trace_buffer[0] belongs to the current function so we always pop it,
- // unless there is only 1 frame in the stack trace (1 frame is always better
- // than 0!).
- // 1-frame stacks don't normally happen, but this depends on the actual
- // unwinder implementation (libgcc, libunwind, etc) which is outside of our
- // control.
- if (to_pop == 0 && size > 1)
- to_pop = 1;
- PopStackFrames(to_pop);
-#if defined(__GNUC__) && defined(__sparc__)
- // __builtin_return_address returns the address of the call instruction
- // on the SPARC and not the return address, so we need to compensate.
- trace_buffer[0] = GetNextInstructionPc(pc);
-#else
- trace_buffer[0] = pc;
-#endif
-}
-
-void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- u32 max_depth) {
- CHECK_GE(max_depth, 2);
- if (!unwind_backtrace_signal_arch) {
- SlowUnwindStack(pc, max_depth);
- return;
- }
-
- void *map = acquire_my_map_info_list();
- CHECK(map);
- InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
- // siginfo argument appears to be unused.
- sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
- frames.data(),
- /* ignore_depth */ 0, max_depth);
- release_my_map_info_list(map);
- if (res < 0) return;
- CHECK_LE((uptr)res, kStackTraceMax);
-
- size = 0;
- // +2 compensate for libcorkscrew unwinder returning addresses of call
- // instructions instead of raw return addresses.
- for (sptr i = 0; i < res; ++i)
- trace_buffer[size++] = frames[i].absolute_pc + 2;
-}
-
-} // namespace __sanitizer
-
-#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
- // SANITIZER_SOLARIS
--- /dev/null
+//===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the unwind.h-based (aka "slow") stack unwinding routines
+// available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
+ SANITIZER_SOLARIS
+#include "sanitizer_common.h"
+#include "sanitizer_stacktrace.h"
+
+#if SANITIZER_ANDROID
+#include <dlfcn.h> // for dlopen()
+#endif
+
+#if SANITIZER_FREEBSD
+#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
+#endif
+#include <unwind.h>
+
+namespace __sanitizer {
+
+//---------------------------- UnwindSlow --------------------------------------
+
+typedef struct {
+ uptr absolute_pc;
+ uptr stack_top;
+ uptr stack_size;
+} backtrace_frame_t;
+
+extern "C" {
+typedef void *(*acquire_my_map_info_list_func)();
+typedef void (*release_my_map_info_list_func)(void *map);
+typedef sptr (*unwind_backtrace_signal_arch_func)(
+ void *siginfo, void *sigcontext, void *map_info_list,
+ backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
+acquire_my_map_info_list_func acquire_my_map_info_list;
+release_my_map_info_list_func release_my_map_info_list;
+unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
+} // extern "C"
+
+#if SANITIZER_ANDROID
+void SanitizerInitializeUnwinder() {
+ if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
+
+ // Pre-lollipop Android can not unwind through signal handler frames with
+ // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
+ // workarounds.
+ void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
+ if (!p) {
+ VReport(1,
+ "Failed to open libcorkscrew.so. You may see broken stack traces "
+ "in SEGV reports.");
+ return;
+ }
+ acquire_my_map_info_list =
+ (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
+ release_my_map_info_list =
+ (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
+ unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
+ p, "unwind_backtrace_signal_arch");
+ if (!acquire_my_map_info_list || !release_my_map_info_list ||
+ !unwind_backtrace_signal_arch) {
+ VReport(1,
+ "Failed to find one of the required symbols in libcorkscrew.so. "
+ "You may see broken stack traces in SEGV reports.");
+ acquire_my_map_info_list = 0;
+ unwind_backtrace_signal_arch = 0;
+ release_my_map_info_list = 0;
+ }
+}
+#endif
+
+#if defined(__arm__) && !SANITIZER_NETBSD
+// NetBSD uses dwarf EH
+#define UNWIND_STOP _URC_END_OF_STACK
+#define UNWIND_CONTINUE _URC_NO_REASON
+#else
+#define UNWIND_STOP _URC_NORMAL_STOP
+#define UNWIND_CONTINUE _URC_NO_REASON
+#endif
+
+uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
+#if defined(__arm__) && !SANITIZER_MAC
+ uptr val;
+ _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
+ 15 /* r15 = PC */, _UVRSD_UINT32, &val);
+ CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
+ // Clear the Thumb bit.
+ return val & ~(uptr)1;
+#else
+ return (uptr)_Unwind_GetIP(ctx);
+#endif
+}
+
+struct UnwindTraceArg {
+ BufferedStackTrace *stack;
+ u32 max_depth;
+};
+
+_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
+ UnwindTraceArg *arg = (UnwindTraceArg*)param;
+ CHECK_LT(arg->stack->size, arg->max_depth);
+ uptr pc = Unwind_GetIP(ctx);
+ const uptr kPageSize = GetPageSizeCached();
+ // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
+ // x86_64) is invalid and stop unwinding here. If we're adding support for
+ // a platform where this isn't true, we need to reconsider this check.
+ if (pc < kPageSize) return UNWIND_STOP;
+ arg->stack->trace_buffer[arg->stack->size++] = pc;
+ if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
+ return UNWIND_CONTINUE;
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ size = 0;
+ UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
+ _Unwind_Backtrace(Unwind_Trace, &arg);
+ // We need to pop a few frames so that pc is on top.
+ uptr to_pop = LocatePcInTrace(pc);
+ // trace_buffer[0] belongs to the current function so we always pop it,
+ // unless there is only 1 frame in the stack trace (1 frame is always better
+ // than 0!).
+ // 1-frame stacks don't normally happen, but this depends on the actual
+ // unwinder implementation (libgcc, libunwind, etc) which is outside of our
+ // control.
+ if (to_pop == 0 && size > 1)
+ to_pop = 1;
+ PopStackFrames(to_pop);
+#if defined(__GNUC__) && defined(__sparc__)
+ // __builtin_return_address returns the address of the call instruction
+ // on the SPARC and not the return address, so we need to compensate.
+ trace_buffer[0] = GetNextInstructionPc(pc);
+#else
+ trace_buffer[0] = pc;
+#endif
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
+ CHECK(context);
+ CHECK_GE(max_depth, 2);
+ if (!unwind_backtrace_signal_arch) {
+ UnwindSlow(pc, max_depth);
+ return;
+ }
+
+ void *map = acquire_my_map_info_list();
+ CHECK(map);
+ InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
+ // siginfo argument appears to be unused.
+ sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
+ frames.data(),
+ /* ignore_depth */ 0, max_depth);
+ release_my_map_info_list(map);
+ if (res < 0) return;
+ CHECK_LE((uptr)res, kStackTraceMax);
+
+ size = 0;
+ // +2 compensate for libcorkscrew unwinder returning addresses of call
+ // instructions instead of raw return addresses.
+ for (sptr i = 0; i < res; ++i)
+ trace_buffer[size++] = frames[i].absolute_pc + 2;
+}
+
+} // namespace __sanitizer
+
+#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
+ // SANITIZER_SOLARIS
+++ /dev/null
-//===-- sanitizer_unwind_win.cc -------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-/// Sanitizer unwind Windows specific functions.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-
-#define WIN32_LEAN_AND_MEAN
-#define NOGDI
-#include <windows.h>
-
-#include "sanitizer_dbghelp.h" // for StackWalk64
-#include "sanitizer_stacktrace.h"
-#include "sanitizer_symbolizer.h" // for InitializeDbgHelpIfNeeded
-
-using namespace __sanitizer;
-
-#if !SANITIZER_GO
-void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
- CHECK_GE(max_depth, 2);
- // FIXME: CaptureStackBackTrace might be too slow for us.
- // FIXME: Compare with StackWalk64.
- // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
- size = CaptureStackBackTrace(1, Min(max_depth, kStackTraceMax),
- (void **)&trace_buffer[0], 0);
- if (size == 0)
- return;
-
- // Skip the RTL frames by searching for the PC in the stacktrace.
- uptr pc_location = LocatePcInTrace(pc);
- PopStackFrames(pc_location);
-}
-
-void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- u32 max_depth) {
- CONTEXT ctx = *(CONTEXT *)context;
- STACKFRAME64 stack_frame;
- memset(&stack_frame, 0, sizeof(stack_frame));
-
- InitializeDbgHelpIfNeeded();
-
- size = 0;
-#if defined(_WIN64)
- int machine_type = IMAGE_FILE_MACHINE_AMD64;
- stack_frame.AddrPC.Offset = ctx.Rip;
- stack_frame.AddrFrame.Offset = ctx.Rbp;
- stack_frame.AddrStack.Offset = ctx.Rsp;
-#else
- int machine_type = IMAGE_FILE_MACHINE_I386;
- stack_frame.AddrPC.Offset = ctx.Eip;
- stack_frame.AddrFrame.Offset = ctx.Ebp;
- stack_frame.AddrStack.Offset = ctx.Esp;
-#endif
- stack_frame.AddrPC.Mode = AddrModeFlat;
- stack_frame.AddrFrame.Mode = AddrModeFlat;
- stack_frame.AddrStack.Mode = AddrModeFlat;
- while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
- &stack_frame, &ctx, NULL, SymFunctionTableAccess64,
- SymGetModuleBase64, NULL) &&
- size < Min(max_depth, kStackTraceMax)) {
- trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
- }
-}
-#endif // #if !SANITIZER_GO
-
-#endif // SANITIZER_WINDOWS
--- /dev/null
+//===-- sanitizer_unwind_win.cpp ------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// Sanitizer unwind Windows specific functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#include <windows.h>
+
+#include "sanitizer_dbghelp.h" // for StackWalk64
+#include "sanitizer_stacktrace.h"
+#include "sanitizer_symbolizer.h" // for InitializeDbgHelpIfNeeded
+
+using namespace __sanitizer;
+
+#if !SANITIZER_GO
+void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
+ CHECK_GE(max_depth, 2);
+ // FIXME: CaptureStackBackTrace might be too slow for us.
+ // FIXME: Compare with StackWalk64.
+ // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
+ size = CaptureStackBackTrace(1, Min(max_depth, kStackTraceMax),
+ (void **)&trace_buffer[0], 0);
+ if (size == 0)
+ return;
+
+ // Skip the RTL frames by searching for the PC in the stacktrace.
+ uptr pc_location = LocatePcInTrace(pc);
+ PopStackFrames(pc_location);
+}
+
+void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
+ CHECK(context);
+ CHECK_GE(max_depth, 2);
+ CONTEXT ctx = *(CONTEXT *)context;
+ STACKFRAME64 stack_frame;
+ memset(&stack_frame, 0, sizeof(stack_frame));
+
+ InitializeDbgHelpIfNeeded();
+
+ size = 0;
+#if defined(_WIN64)
+ int machine_type = IMAGE_FILE_MACHINE_AMD64;
+ stack_frame.AddrPC.Offset = ctx.Rip;
+ stack_frame.AddrFrame.Offset = ctx.Rbp;
+ stack_frame.AddrStack.Offset = ctx.Rsp;
+#else
+ int machine_type = IMAGE_FILE_MACHINE_I386;
+ stack_frame.AddrPC.Offset = ctx.Eip;
+ stack_frame.AddrFrame.Offset = ctx.Ebp;
+ stack_frame.AddrStack.Offset = ctx.Esp;
+#endif
+ stack_frame.AddrPC.Mode = AddrModeFlat;
+ stack_frame.AddrFrame.Mode = AddrModeFlat;
+ stack_frame.AddrStack.Mode = AddrModeFlat;
+ while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(),
+ &stack_frame, &ctx, NULL, SymFunctionTableAccess64,
+ SymGetModuleBase64, NULL) &&
+ size < Min(max_depth, kStackTraceMax)) {
+ trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
+ }
+}
+#endif // #if !SANITIZER_GO
+
+#endif // SANITIZER_WINDOWS
//===-- sanitizer_vector.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_win.cc --------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is shared between AddressSanitizer and ThreadSanitizer
-// run-time libraries and implements windows-specific functions from
-// sanitizer_libc.h.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-
-#define WIN32_LEAN_AND_MEAN
-#define NOGDI
-#include <windows.h>
-#include <io.h>
-#include <psapi.h>
-#include <stdlib.h>
-
-#include "sanitizer_common.h"
-#include "sanitizer_file.h"
-#include "sanitizer_libc.h"
-#include "sanitizer_mutex.h"
-#include "sanitizer_placement_new.h"
-#include "sanitizer_win_defs.h"
-
-#if defined(PSAPI_VERSION) && PSAPI_VERSION == 1
-#pragma comment(lib, "psapi")
-#endif
-
-// A macro to tell the compiler that this part of the code cannot be reached,
-// if the compiler supports this feature. Since we're using this in
-// code that is called when terminating the process, the expansion of the
-// macro should not terminate the process to avoid infinite recursion.
-#if defined(__clang__)
-# define BUILTIN_UNREACHABLE() __builtin_unreachable()
-#elif defined(__GNUC__) && \
- (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
-# define BUILTIN_UNREACHABLE() __builtin_unreachable()
-#elif defined(_MSC_VER)
-# define BUILTIN_UNREACHABLE() __assume(0)
-#else
-# define BUILTIN_UNREACHABLE()
-#endif
-
-namespace __sanitizer {
-
-#include "sanitizer_syscall_generic.inc"
-
-// --------------------- sanitizer_common.h
-uptr GetPageSize() {
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return si.dwPageSize;
-}
-
-uptr GetMmapGranularity() {
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return si.dwAllocationGranularity;
-}
-
-uptr GetMaxUserVirtualAddress() {
- SYSTEM_INFO si;
- GetSystemInfo(&si);
- return (uptr)si.lpMaximumApplicationAddress;
-}
-
-uptr GetMaxVirtualAddress() {
- return GetMaxUserVirtualAddress();
-}
-
-bool FileExists(const char *filename) {
- return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES;
-}
-
-uptr internal_getpid() {
- return GetProcessId(GetCurrentProcess());
-}
-
-// In contrast to POSIX, on Windows GetCurrentThreadId()
-// returns a system-unique identifier.
-tid_t GetTid() {
- return GetCurrentThreadId();
-}
-
-uptr GetThreadSelf() {
- return GetTid();
-}
-
-#if !SANITIZER_GO
-void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
- uptr *stack_bottom) {
- CHECK(stack_top);
- CHECK(stack_bottom);
- MEMORY_BASIC_INFORMATION mbi;
- CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
- // FIXME: is it possible for the stack to not be a single allocation?
- // Are these values what ASan expects to get (reserved, not committed;
- // including stack guard page) ?
- *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
- *stack_bottom = (uptr)mbi.AllocationBase;
-}
-#endif // #if !SANITIZER_GO
-
-void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
- void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (rv == 0)
- ReportMmapFailureAndDie(size, mem_type, "allocate",
- GetLastError(), raw_report);
- return rv;
-}
-
-void UnmapOrDie(void *addr, uptr size) {
- if (!size || !addr)
- return;
-
- MEMORY_BASIC_INFORMATION mbi;
- CHECK(VirtualQuery(addr, &mbi, sizeof(mbi)));
-
- // MEM_RELEASE can only be used to unmap whole regions previously mapped with
- // VirtualAlloc. So we first try MEM_RELEASE since it is better, and if that
- // fails try MEM_DECOMMIT.
- if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
- if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
- Report("ERROR: %s failed to "
- "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
- SanitizerToolName, size, size, addr, GetLastError());
- CHECK("unable to unmap" && 0);
- }
- }
-}
-
-static void *ReturnNullptrOnOOMOrDie(uptr size, const char *mem_type,
- const char *mmap_type) {
- error_t last_error = GetLastError();
- if (last_error == ERROR_NOT_ENOUGH_MEMORY)
- return nullptr;
- ReportMmapFailureAndDie(size, mem_type, mmap_type, last_error);
-}
-
-void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
- void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (rv == 0)
- return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate");
- return rv;
-}
-
-// We want to map a chunk of address space aligned to 'alignment'.
-void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
- const char *mem_type) {
- CHECK(IsPowerOfTwo(size));
- CHECK(IsPowerOfTwo(alignment));
-
- // Windows will align our allocations to at least 64K.
- alignment = Max(alignment, GetMmapGranularity());
-
- uptr mapped_addr =
- (uptr)VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- if (!mapped_addr)
- return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned");
-
- // If we got it right on the first try, return. Otherwise, unmap it and go to
- // the slow path.
- if (IsAligned(mapped_addr, alignment))
- return (void*)mapped_addr;
- if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0)
- ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError());
-
- // If we didn't get an aligned address, overallocate, find an aligned address,
- // unmap, and try to allocate at that aligned address.
- int retries = 0;
- const int kMaxRetries = 10;
- for (; retries < kMaxRetries &&
- (mapped_addr == 0 || !IsAligned(mapped_addr, alignment));
- retries++) {
- // Overallocate size + alignment bytes.
- mapped_addr =
- (uptr)VirtualAlloc(0, size + alignment, MEM_RESERVE, PAGE_NOACCESS);
- if (!mapped_addr)
- return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned");
-
- // Find the aligned address.
- uptr aligned_addr = RoundUpTo(mapped_addr, alignment);
-
- // Free the overallocation.
- if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0)
- ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError());
-
- // Attempt to allocate exactly the number of bytes we need at the aligned
- // address. This may fail for a number of reasons, in which case we continue
- // the loop.
- mapped_addr = (uptr)VirtualAlloc((void *)aligned_addr, size,
- MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
- }
-
- // Fail if we can't make this work quickly.
- if (retries == kMaxRetries && mapped_addr == 0)
- return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned");
-
- return (void *)mapped_addr;
-}
-
-bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
- // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
- // but on Win64 it does.
- (void)name; // unsupported
-#if !SANITIZER_GO && SANITIZER_WINDOWS64
- // On asan/Windows64, use MEM_COMMIT would result in error
- // 1455:ERROR_COMMITMENT_LIMIT.
- // Asan uses exception handler to commit page on demand.
- void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_READWRITE);
-#else
- void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT,
- PAGE_READWRITE);
-#endif
- if (p == 0) {
- Report("ERROR: %s failed to "
- "allocate %p (%zd) bytes at %p (error code: %d)\n",
- SanitizerToolName, size, size, fixed_addr, GetLastError());
- return false;
- }
- return true;
-}
-
-// Memory space mapped by 'MmapFixedOrDie' must have been reserved by
-// 'MmapFixedNoAccess'.
-void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
- void *p = VirtualAlloc((LPVOID)fixed_addr, size,
- MEM_COMMIT, PAGE_READWRITE);
- if (p == 0) {
- char mem_type[30];
- internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
- fixed_addr);
- ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
- }
- return p;
-}
-
-// Uses fixed_addr for now.
-// Will use offset instead once we've implemented this function for real.
-uptr ReservedAddressRange::Map(uptr fixed_addr, uptr size) {
- return reinterpret_cast<uptr>(MmapFixedOrDieOnFatalError(fixed_addr, size));
-}
-
-uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr size) {
- return reinterpret_cast<uptr>(MmapFixedOrDie(fixed_addr, size));
-}
-
-void ReservedAddressRange::Unmap(uptr addr, uptr size) {
- // Only unmap if it covers the entire range.
- CHECK((addr == reinterpret_cast<uptr>(base_)) && (size == size_));
- // We unmap the whole range, just null out the base.
- base_ = nullptr;
- size_ = 0;
- UnmapOrDie(reinterpret_cast<void*>(addr), size);
-}
-
-void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size) {
- void *p = VirtualAlloc((LPVOID)fixed_addr, size,
- MEM_COMMIT, PAGE_READWRITE);
- if (p == 0) {
- char mem_type[30];
- internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
- fixed_addr);
- return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate");
- }
- return p;
-}
-
-void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
- // FIXME: make this really NoReserve?
- return MmapOrDie(size, mem_type);
-}
-
-uptr ReservedAddressRange::Init(uptr size, const char *name, uptr fixed_addr) {
- base_ = fixed_addr ? MmapFixedNoAccess(fixed_addr, size) : MmapNoAccess(size);
- size_ = size;
- name_ = name;
- (void)os_handle_; // unsupported
- return reinterpret_cast<uptr>(base_);
-}
-
-
-void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
- (void)name; // unsupported
- void *res = VirtualAlloc((LPVOID)fixed_addr, size,
- MEM_RESERVE, PAGE_NOACCESS);
- if (res == 0)
- Report("WARNING: %s failed to "
- "mprotect %p (%zd) bytes at %p (error code: %d)\n",
- SanitizerToolName, size, size, fixed_addr, GetLastError());
- return res;
-}
-
-void *MmapNoAccess(uptr size) {
- void *res = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_NOACCESS);
- if (res == 0)
- Report("WARNING: %s failed to "
- "mprotect %p (%zd) bytes (error code: %d)\n",
- SanitizerToolName, size, size, GetLastError());
- return res;
-}
-
-bool MprotectNoAccess(uptr addr, uptr size) {
- DWORD old_protection;
- return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
-}
-
-void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
- // This is almost useless on 32-bits.
- // FIXME: add madvise-analog when we move to 64-bits.
-}
-
-bool NoHugePagesInRegion(uptr addr, uptr size) {
- // FIXME: probably similar to ReleaseMemoryToOS.
- return true;
-}
-
-bool DontDumpShadowMemory(uptr addr, uptr length) {
- // This is almost useless on 32-bits.
- // FIXME: add madvise-analog when we move to 64-bits.
- return true;
-}
-
-uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
- uptr *largest_gap_found,
- uptr *max_occupied_addr) {
- uptr address = 0;
- while (true) {
- MEMORY_BASIC_INFORMATION info;
- if (!::VirtualQuery((void*)address, &info, sizeof(info)))
- return 0;
-
- if (info.State == MEM_FREE) {
- uptr shadow_address = RoundUpTo((uptr)info.BaseAddress + left_padding,
- alignment);
- if (shadow_address + size < (uptr)info.BaseAddress + info.RegionSize)
- return shadow_address;
- }
-
- // Move to the next region.
- address = (uptr)info.BaseAddress + info.RegionSize;
- }
- return 0;
-}
-
-bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
- MEMORY_BASIC_INFORMATION mbi;
- CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi)));
- return mbi.Protect == PAGE_NOACCESS &&
- (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end;
-}
-
-void *MapFileToMemory(const char *file_name, uptr *buff_size) {
- UNIMPLEMENTED();
-}
-
-void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
- UNIMPLEMENTED();
-}
-
-static const int kMaxEnvNameLength = 128;
-static const DWORD kMaxEnvValueLength = 32767;
-
-namespace {
-
-struct EnvVariable {
- char name[kMaxEnvNameLength];
- char value[kMaxEnvValueLength];
-};
-
-} // namespace
-
-static const int kEnvVariables = 5;
-static EnvVariable env_vars[kEnvVariables];
-static int num_env_vars;
-
-const char *GetEnv(const char *name) {
- // Note: this implementation caches the values of the environment variables
- // and limits their quantity.
- for (int i = 0; i < num_env_vars; i++) {
- if (0 == internal_strcmp(name, env_vars[i].name))
- return env_vars[i].value;
- }
- CHECK_LT(num_env_vars, kEnvVariables);
- DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
- kMaxEnvValueLength);
- if (rv > 0 && rv < kMaxEnvValueLength) {
- CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
- internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
- num_env_vars++;
- return env_vars[num_env_vars - 1].value;
- }
- return 0;
-}
-
-const char *GetPwd() {
- UNIMPLEMENTED();
-}
-
-u32 GetUid() {
- UNIMPLEMENTED();
-}
-
-namespace {
-struct ModuleInfo {
- const char *filepath;
- uptr base_address;
- uptr end_address;
-};
-
-#if !SANITIZER_GO
-int CompareModulesBase(const void *pl, const void *pr) {
- const ModuleInfo *l = (const ModuleInfo *)pl, *r = (const ModuleInfo *)pr;
- if (l->base_address < r->base_address)
- return -1;
- return l->base_address > r->base_address;
-}
-#endif
-} // namespace
-
-#if !SANITIZER_GO
-void DumpProcessMap() {
- Report("Dumping process modules:\n");
- ListOfModules modules;
- modules.init();
- uptr num_modules = modules.size();
-
- InternalMmapVector<ModuleInfo> module_infos(num_modules);
- for (size_t i = 0; i < num_modules; ++i) {
- module_infos[i].filepath = modules[i].full_name();
- module_infos[i].base_address = modules[i].ranges().front()->beg;
- module_infos[i].end_address = modules[i].ranges().back()->end;
- }
- qsort(module_infos.data(), num_modules, sizeof(ModuleInfo),
- CompareModulesBase);
-
- for (size_t i = 0; i < num_modules; ++i) {
- const ModuleInfo &mi = module_infos[i];
- if (mi.end_address != 0) {
- Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
- mi.filepath[0] ? mi.filepath : "[no name]");
- } else if (mi.filepath[0]) {
- Printf("\t??\?-??? %s\n", mi.filepath);
- } else {
- Printf("\t???\n");
- }
- }
-}
-#endif
-
-void PrintModuleMap() { }
-
-void DisableCoreDumperIfNecessary() {
- // Do nothing.
-}
-
-void ReExec() {
- UNIMPLEMENTED();
-}
-
-void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
-
-bool StackSizeIsUnlimited() {
- UNIMPLEMENTED();
-}
-
-void SetStackSizeLimitInBytes(uptr limit) {
- UNIMPLEMENTED();
-}
-
-bool AddressSpaceIsUnlimited() {
- UNIMPLEMENTED();
-}
-
-void SetAddressSpaceUnlimited() {
- UNIMPLEMENTED();
-}
-
-bool IsPathSeparator(const char c) {
- return c == '\\' || c == '/';
-}
-
-bool IsAbsolutePath(const char *path) {
- UNIMPLEMENTED();
-}
-
-void SleepForSeconds(int seconds) {
- Sleep(seconds * 1000);
-}
-
-void SleepForMillis(int millis) {
- Sleep(millis);
-}
-
-u64 NanoTime() {
- static LARGE_INTEGER frequency = {};
- LARGE_INTEGER counter;
- if (UNLIKELY(frequency.QuadPart == 0)) {
- QueryPerformanceFrequency(&frequency);
- CHECK_NE(frequency.QuadPart, 0);
- }
- QueryPerformanceCounter(&counter);
- counter.QuadPart *= 1000ULL * 1000000ULL;
- counter.QuadPart /= frequency.QuadPart;
- return counter.QuadPart;
-}
-
-u64 MonotonicNanoTime() { return NanoTime(); }
-
-void Abort() {
- internal__exit(3);
-}
-
-#if !SANITIZER_GO
-// Read the file to extract the ImageBase field from the PE header. If ASLR is
-// disabled and this virtual address is available, the loader will typically
-// load the image at this address. Therefore, we call it the preferred base. Any
-// addresses in the DWARF typically assume that the object has been loaded at
-// this address.
-static uptr GetPreferredBase(const char *modname) {
- fd_t fd = OpenFile(modname, RdOnly, nullptr);
- if (fd == kInvalidFd)
- return 0;
- FileCloser closer(fd);
-
- // Read just the DOS header.
- IMAGE_DOS_HEADER dos_header;
- uptr bytes_read;
- if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) ||
- bytes_read != sizeof(dos_header))
- return 0;
-
- // The file should start with the right signature.
- if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
- return 0;
-
- // The layout at e_lfanew is:
- // "PE\0\0"
- // IMAGE_FILE_HEADER
- // IMAGE_OPTIONAL_HEADER
- // Seek to e_lfanew and read all that data.
- char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)];
- if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) ==
- INVALID_SET_FILE_POINTER)
- return 0;
- if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) ||
- bytes_read != sizeof(buf))
- return 0;
-
- // Check for "PE\0\0" before the PE header.
- char *pe_sig = &buf[0];
- if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0)
- return 0;
-
- // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted.
- IMAGE_OPTIONAL_HEADER *pe_header =
- (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER));
-
- // Check for more magic in the PE header.
- if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
- return 0;
-
- // Finally, return the ImageBase.
- return (uptr)pe_header->ImageBase;
-}
-
-void ListOfModules::init() {
- clearOrInit();
- HANDLE cur_process = GetCurrentProcess();
-
- // Query the list of modules. Start by assuming there are no more than 256
- // modules and retry if that's not sufficient.
- HMODULE *hmodules = 0;
- uptr modules_buffer_size = sizeof(HMODULE) * 256;
- DWORD bytes_required;
- while (!hmodules) {
- hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
- CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
- &bytes_required));
- if (bytes_required > modules_buffer_size) {
- // Either there turned out to be more than 256 hmodules, or new hmodules
- // could have loaded since the last try. Retry.
- UnmapOrDie(hmodules, modules_buffer_size);
- hmodules = 0;
- modules_buffer_size = bytes_required;
- }
- }
-
- // |num_modules| is the number of modules actually present,
- size_t num_modules = bytes_required / sizeof(HMODULE);
- for (size_t i = 0; i < num_modules; ++i) {
- HMODULE handle = hmodules[i];
- MODULEINFO mi;
- if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi)))
- continue;
-
- // Get the UTF-16 path and convert to UTF-8.
- wchar_t modname_utf16[kMaxPathLength];
- int modname_utf16_len =
- GetModuleFileNameW(handle, modname_utf16, kMaxPathLength);
- if (modname_utf16_len == 0)
- modname_utf16[0] = '\0';
- char module_name[kMaxPathLength];
- int module_name_len =
- ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1,
- &module_name[0], kMaxPathLength, NULL, NULL);
- module_name[module_name_len] = '\0';
-
- uptr base_address = (uptr)mi.lpBaseOfDll;
- uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
-
- // Adjust the base address of the module so that we get a VA instead of an
- // RVA when computing the module offset. This helps llvm-symbolizer find the
- // right DWARF CU. In the common case that the image is loaded at it's
- // preferred address, we will now print normal virtual addresses.
- uptr preferred_base = GetPreferredBase(&module_name[0]);
- uptr adjusted_base = base_address - preferred_base;
-
- LoadedModule cur_module;
- cur_module.set(module_name, adjusted_base);
- // We add the whole module as one single address range.
- cur_module.addAddressRange(base_address, end_address, /*executable*/ true,
- /*writable*/ true);
- modules_.push_back(cur_module);
- }
- UnmapOrDie(hmodules, modules_buffer_size);
-}
-
-void ListOfModules::fallbackInit() { clear(); }
-
-// We can't use atexit() directly at __asan_init time as the CRT is not fully
-// initialized at this point. Place the functions into a vector and use
-// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
-InternalMmapVectorNoCtor<void (*)(void)> atexit_functions;
-
-int Atexit(void (*function)(void)) {
- atexit_functions.push_back(function);
- return 0;
-}
-
-static int RunAtexit() {
- int ret = 0;
- for (uptr i = 0; i < atexit_functions.size(); ++i) {
- ret |= atexit(atexit_functions[i]);
- }
- return ret;
-}
-
-#pragma section(".CRT$XID", long, read) // NOLINT
-__declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit;
-#endif
-
-// ------------------ sanitizer_libc.h
-fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) {
- // FIXME: Use the wide variants to handle Unicode filenames.
- fd_t res;
- if (mode == RdOnly) {
- res = CreateFileA(filename, GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
- } else if (mode == WrOnly) {
- res = CreateFileA(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
- FILE_ATTRIBUTE_NORMAL, nullptr);
- } else {
- UNIMPLEMENTED();
- }
- CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd);
- CHECK(res != kStderrFd || kStderrFd == kInvalidFd);
- if (res == kInvalidFd && last_error)
- *last_error = GetLastError();
- return res;
-}
-
-void CloseFile(fd_t fd) {
- CloseHandle(fd);
-}
-
-bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
- error_t *error_p) {
- CHECK(fd != kInvalidFd);
-
- // bytes_read can't be passed directly to ReadFile:
- // uptr is unsigned long long on 64-bit Windows.
- unsigned long num_read_long;
-
- bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr);
- if (!success && error_p)
- *error_p = GetLastError();
- if (bytes_read)
- *bytes_read = num_read_long;
- return success;
-}
-
-bool SupportsColoredOutput(fd_t fd) {
- // FIXME: support colored output.
- return false;
-}
-
-bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
- error_t *error_p) {
- CHECK(fd != kInvalidFd);
-
- // Handle null optional parameters.
- error_t dummy_error;
- error_p = error_p ? error_p : &dummy_error;
- uptr dummy_bytes_written;
- bytes_written = bytes_written ? bytes_written : &dummy_bytes_written;
-
- // Initialize output parameters in case we fail.
- *error_p = 0;
- *bytes_written = 0;
-
- // Map the conventional Unix fds 1 and 2 to Windows handles. They might be
- // closed, in which case this will fail.
- if (fd == kStdoutFd || fd == kStderrFd) {
- fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
- if (fd == 0) {
- *error_p = ERROR_INVALID_HANDLE;
- return false;
- }
- }
-
- DWORD bytes_written_32;
- if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) {
- *error_p = GetLastError();
- return false;
- } else {
- *bytes_written = bytes_written_32;
- return true;
- }
-}
-
-bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) {
- UNIMPLEMENTED();
-}
-
-uptr internal_sched_yield() {
- Sleep(0);
- return 0;
-}
-
-void internal__exit(int exitcode) {
- // ExitProcess runs some finalizers, so use TerminateProcess to avoid that.
- // The debugger doesn't stop on TerminateProcess like it does on ExitProcess,
- // so add our own breakpoint here.
- if (::IsDebuggerPresent())
- __debugbreak();
- TerminateProcess(GetCurrentProcess(), exitcode);
- BUILTIN_UNREACHABLE();
-}
-
-uptr internal_ftruncate(fd_t fd, uptr size) {
- UNIMPLEMENTED();
-}
-
-uptr GetRSS() {
- PROCESS_MEMORY_COUNTERS counters;
- if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
- return 0;
- return counters.WorkingSetSize;
-}
-
-void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
-void internal_join_thread(void *th) { }
-
-// ---------------------- BlockingMutex ---------------- {{{1
-
-BlockingMutex::BlockingMutex() {
- CHECK(sizeof(SRWLOCK) <= sizeof(opaque_storage_));
- internal_memset(this, 0, sizeof(*this));
-}
-
-void BlockingMutex::Lock() {
- AcquireSRWLockExclusive((PSRWLOCK)opaque_storage_);
- CHECK_EQ(owner_, 0);
- owner_ = GetThreadSelf();
-}
-
-void BlockingMutex::Unlock() {
- CheckLocked();
- owner_ = 0;
- ReleaseSRWLockExclusive((PSRWLOCK)opaque_storage_);
-}
-
-void BlockingMutex::CheckLocked() {
- CHECK_EQ(owner_, GetThreadSelf());
-}
-
-uptr GetTlsSize() {
- return 0;
-}
-
-void InitTlsSize() {
-}
-
-void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
- uptr *tls_addr, uptr *tls_size) {
-#if SANITIZER_GO
- *stk_addr = 0;
- *stk_size = 0;
- *tls_addr = 0;
- *tls_size = 0;
-#else
- uptr stack_top, stack_bottom;
- GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
- *stk_addr = stack_bottom;
- *stk_size = stack_top - stack_bottom;
- *tls_addr = 0;
- *tls_size = 0;
-#endif
-}
-
-void ReportFile::Write(const char *buffer, uptr length) {
- SpinMutexLock l(mu);
- ReopenIfNecessary();
- if (!WriteToFile(fd, buffer, length)) {
- // stderr may be closed, but we may be able to print to the debugger
- // instead. This is the case when launching a program from Visual Studio,
- // and the following routine should write to its console.
- OutputDebugStringA(buffer);
- }
-}
-
-void SetAlternateSignalStack() {
- // FIXME: Decide what to do on Windows.
-}
-
-void UnsetAlternateSignalStack() {
- // FIXME: Decide what to do on Windows.
-}
-
-void InstallDeadlySignalHandlers(SignalHandlerType handler) {
- (void)handler;
- // FIXME: Decide what to do on Windows.
-}
-
-HandleSignalMode GetHandleSignalMode(int signum) {
- // FIXME: Decide what to do on Windows.
- return kHandleSignalNo;
-}
-
-// Check based on flags if we should handle this exception.
-bool IsHandledDeadlyException(DWORD exceptionCode) {
- switch (exceptionCode) {
- case EXCEPTION_ACCESS_VIOLATION:
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- case EXCEPTION_STACK_OVERFLOW:
- case EXCEPTION_DATATYPE_MISALIGNMENT:
- case EXCEPTION_IN_PAGE_ERROR:
- return common_flags()->handle_segv;
- case EXCEPTION_ILLEGAL_INSTRUCTION:
- case EXCEPTION_PRIV_INSTRUCTION:
- case EXCEPTION_BREAKPOINT:
- return common_flags()->handle_sigill;
- case EXCEPTION_FLT_DENORMAL_OPERAND:
- case EXCEPTION_FLT_DIVIDE_BY_ZERO:
- case EXCEPTION_FLT_INEXACT_RESULT:
- case EXCEPTION_FLT_INVALID_OPERATION:
- case EXCEPTION_FLT_OVERFLOW:
- case EXCEPTION_FLT_STACK_CHECK:
- case EXCEPTION_FLT_UNDERFLOW:
- case EXCEPTION_INT_DIVIDE_BY_ZERO:
- case EXCEPTION_INT_OVERFLOW:
- return common_flags()->handle_sigfpe;
- }
- return false;
-}
-
-bool IsAccessibleMemoryRange(uptr beg, uptr size) {
- SYSTEM_INFO si;
- GetNativeSystemInfo(&si);
- uptr page_size = si.dwPageSize;
- uptr page_mask = ~(page_size - 1);
-
- for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask;
- page <= end;) {
- MEMORY_BASIC_INFORMATION info;
- if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info))
- return false;
-
- if (info.Protect == 0 || info.Protect == PAGE_NOACCESS ||
- info.Protect == PAGE_EXECUTE)
- return false;
-
- if (info.RegionSize == 0)
- return false;
-
- page += info.RegionSize;
- }
-
- return true;
-}
-
-bool SignalContext::IsStackOverflow() const {
- return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW;
-}
-
-void SignalContext::InitPcSpBp() {
- EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
- CONTEXT *context_record = (CONTEXT *)context;
-
- pc = (uptr)exception_record->ExceptionAddress;
-#ifdef _WIN64
- bp = (uptr)context_record->Rbp;
- sp = (uptr)context_record->Rsp;
-#else
- bp = (uptr)context_record->Ebp;
- sp = (uptr)context_record->Esp;
-#endif
-}
-
-uptr SignalContext::GetAddress() const {
- EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
- return exception_record->ExceptionInformation[1];
-}
-
-bool SignalContext::IsMemoryAccess() const {
- return GetWriteFlag() != SignalContext::UNKNOWN;
-}
-
-SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
- EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
- // The contents of this array are documented at
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx
- // The first element indicates read as 0, write as 1, or execute as 8. The
- // second element is the faulting address.
- switch (exception_record->ExceptionInformation[0]) {
- case 0:
- return SignalContext::READ;
- case 1:
- return SignalContext::WRITE;
- case 8:
- return SignalContext::UNKNOWN;
- }
- return SignalContext::UNKNOWN;
-}
-
-void SignalContext::DumpAllRegisters(void *context) {
- // FIXME: Implement this.
-}
-
-int SignalContext::GetType() const {
- return static_cast<const EXCEPTION_RECORD *>(siginfo)->ExceptionCode;
-}
-
-const char *SignalContext::Describe() const {
- unsigned code = GetType();
- // Get the string description of the exception if this is a known deadly
- // exception.
- switch (code) {
- case EXCEPTION_ACCESS_VIOLATION:
- return "access-violation";
- case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
- return "array-bounds-exceeded";
- case EXCEPTION_STACK_OVERFLOW:
- return "stack-overflow";
- case EXCEPTION_DATATYPE_MISALIGNMENT:
- return "datatype-misalignment";
- case EXCEPTION_IN_PAGE_ERROR:
- return "in-page-error";
- case EXCEPTION_ILLEGAL_INSTRUCTION:
- return "illegal-instruction";
- case EXCEPTION_PRIV_INSTRUCTION:
- return "priv-instruction";
- case EXCEPTION_BREAKPOINT:
- return "breakpoint";
- case EXCEPTION_FLT_DENORMAL_OPERAND:
- return "flt-denormal-operand";
- case EXCEPTION_FLT_DIVIDE_BY_ZERO:
- return "flt-divide-by-zero";
- case EXCEPTION_FLT_INEXACT_RESULT:
- return "flt-inexact-result";
- case EXCEPTION_FLT_INVALID_OPERATION:
- return "flt-invalid-operation";
- case EXCEPTION_FLT_OVERFLOW:
- return "flt-overflow";
- case EXCEPTION_FLT_STACK_CHECK:
- return "flt-stack-check";
- case EXCEPTION_FLT_UNDERFLOW:
- return "flt-underflow";
- case EXCEPTION_INT_DIVIDE_BY_ZERO:
- return "int-divide-by-zero";
- case EXCEPTION_INT_OVERFLOW:
- return "int-overflow";
- }
- return "unknown exception";
-}
-
-uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
- // FIXME: Actually implement this function.
- CHECK_GT(buf_len, 0);
- buf[0] = 0;
- return 0;
-}
-
-uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
- return ReadBinaryName(buf, buf_len);
-}
-
-void CheckVMASize() {
- // Do nothing.
-}
-
-void MaybeReexec() {
- // No need to re-exec on Windows.
-}
-
-void CheckASLR() {
- // Do nothing
-}
-
-char **GetArgv() {
- // FIXME: Actually implement this function.
- return 0;
-}
-
-pid_t StartSubprocess(const char *program, const char *const argv[],
- fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
- // FIXME: implement on this platform
- // Should be implemented based on
- // SymbolizerProcess::StarAtSymbolizerSubprocess
- // from lib/sanitizer_common/sanitizer_symbolizer_win.cc.
- return -1;
-}
-
-bool IsProcessRunning(pid_t pid) {
- // FIXME: implement on this platform.
- return false;
-}
-
-int WaitForProcess(pid_t pid) { return -1; }
-
-// FIXME implement on this platform.
-void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { }
-
-void CheckNoDeepBind(const char *filename, int flag) {
- // Do nothing.
-}
-
-// FIXME: implement on this platform.
-bool GetRandom(void *buffer, uptr length, bool blocking) {
- UNIMPLEMENTED();
-}
-
-u32 GetNumberOfCPUs() {
- SYSTEM_INFO sysinfo = {};
- GetNativeSystemInfo(&sysinfo);
- return sysinfo.dwNumberOfProcessors;
-}
-
-} // namespace __sanitizer
-
-#endif // _WIN32
--- /dev/null
+//===-- sanitizer_win.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between AddressSanitizer and ThreadSanitizer
+// run-time libraries and implements windows-specific functions from
+// sanitizer_libc.h.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+
+#define WIN32_LEAN_AND_MEAN
+#define NOGDI
+#include <windows.h>
+#include <io.h>
+#include <psapi.h>
+#include <stdlib.h>
+
+#include "sanitizer_common.h"
+#include "sanitizer_file.h"
+#include "sanitizer_libc.h"
+#include "sanitizer_mutex.h"
+#include "sanitizer_placement_new.h"
+#include "sanitizer_win_defs.h"
+
+#if defined(PSAPI_VERSION) && PSAPI_VERSION == 1
+#pragma comment(lib, "psapi")
+#endif
+#if SANITIZER_WIN_TRACE
+#include <traceloggingprovider.h>
+// Windows trace logging provider init
+#pragma comment(lib, "advapi32.lib")
+TRACELOGGING_DECLARE_PROVIDER(g_asan_provider);
+// GUID must be the same in utils/AddressSanitizerLoggingProvider.wprp
+TRACELOGGING_DEFINE_PROVIDER(g_asan_provider, "AddressSanitizerLoggingProvider",
+ (0x6c6c766d, 0x3846, 0x4e6a, 0xa4, 0xfb, 0x5b,
+ 0x53, 0x0b, 0xd0, 0xf3, 0xfa));
+#else
+#define TraceLoggingUnregister(x)
+#endif
+
+// A macro to tell the compiler that this part of the code cannot be reached,
+// if the compiler supports this feature. Since we're using this in
+// code that is called when terminating the process, the expansion of the
+// macro should not terminate the process to avoid infinite recursion.
+#if defined(__clang__)
+# define BUILTIN_UNREACHABLE() __builtin_unreachable()
+#elif defined(__GNUC__) && \
+ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
+# define BUILTIN_UNREACHABLE() __builtin_unreachable()
+#elif defined(_MSC_VER)
+# define BUILTIN_UNREACHABLE() __assume(0)
+#else
+# define BUILTIN_UNREACHABLE()
+#endif
+
+namespace __sanitizer {
+
+#include "sanitizer_syscall_generic.inc"
+
+// --------------------- sanitizer_common.h
+uptr GetPageSize() {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwPageSize;
+}
+
+uptr GetMmapGranularity() {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return si.dwAllocationGranularity;
+}
+
+uptr GetMaxUserVirtualAddress() {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ return (uptr)si.lpMaximumApplicationAddress;
+}
+
+uptr GetMaxVirtualAddress() {
+ return GetMaxUserVirtualAddress();
+}
+
+bool FileExists(const char *filename) {
+ return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES;
+}
+
+uptr internal_getpid() {
+ return GetProcessId(GetCurrentProcess());
+}
+
+// In contrast to POSIX, on Windows GetCurrentThreadId()
+// returns a system-unique identifier.
+tid_t GetTid() {
+ return GetCurrentThreadId();
+}
+
+uptr GetThreadSelf() {
+ return GetTid();
+}
+
+#if !SANITIZER_GO
+void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
+ uptr *stack_bottom) {
+ CHECK(stack_top);
+ CHECK(stack_bottom);
+ MEMORY_BASIC_INFORMATION mbi;
+ CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
+ // FIXME: is it possible for the stack to not be a single allocation?
+ // Are these values what ASan expects to get (reserved, not committed;
+ // including stack guard page) ?
+ *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
+ *stack_bottom = (uptr)mbi.AllocationBase;
+}
+#endif // #if !SANITIZER_GO
+
+void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
+ void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ if (rv == 0)
+ ReportMmapFailureAndDie(size, mem_type, "allocate",
+ GetLastError(), raw_report);
+ return rv;
+}
+
+void UnmapOrDie(void *addr, uptr size) {
+ if (!size || !addr)
+ return;
+
+ MEMORY_BASIC_INFORMATION mbi;
+ CHECK(VirtualQuery(addr, &mbi, sizeof(mbi)));
+
+ // MEM_RELEASE can only be used to unmap whole regions previously mapped with
+ // VirtualAlloc. So we first try MEM_RELEASE since it is better, and if that
+ // fails try MEM_DECOMMIT.
+ if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
+ if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
+ Report("ERROR: %s failed to "
+ "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n",
+ SanitizerToolName, size, size, addr, GetLastError());
+ CHECK("unable to unmap" && 0);
+ }
+ }
+}
+
+static void *ReturnNullptrOnOOMOrDie(uptr size, const char *mem_type,
+ const char *mmap_type) {
+ error_t last_error = GetLastError();
+ if (last_error == ERROR_NOT_ENOUGH_MEMORY)
+ return nullptr;
+ ReportMmapFailureAndDie(size, mem_type, mmap_type, last_error);
+}
+
+void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
+ void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ if (rv == 0)
+ return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate");
+ return rv;
+}
+
+// We want to map a chunk of address space aligned to 'alignment'.
+void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
+ const char *mem_type) {
+ CHECK(IsPowerOfTwo(size));
+ CHECK(IsPowerOfTwo(alignment));
+
+ // Windows will align our allocations to at least 64K.
+ alignment = Max(alignment, GetMmapGranularity());
+
+ uptr mapped_addr =
+ (uptr)VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ if (!mapped_addr)
+ return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned");
+
+ // If we got it right on the first try, return. Otherwise, unmap it and go to
+ // the slow path.
+ if (IsAligned(mapped_addr, alignment))
+ return (void*)mapped_addr;
+ if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0)
+ ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError());
+
+ // If we didn't get an aligned address, overallocate, find an aligned address,
+ // unmap, and try to allocate at that aligned address.
+ int retries = 0;
+ const int kMaxRetries = 10;
+ for (; retries < kMaxRetries &&
+ (mapped_addr == 0 || !IsAligned(mapped_addr, alignment));
+ retries++) {
+ // Overallocate size + alignment bytes.
+ mapped_addr =
+ (uptr)VirtualAlloc(0, size + alignment, MEM_RESERVE, PAGE_NOACCESS);
+ if (!mapped_addr)
+ return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned");
+
+ // Find the aligned address.
+ uptr aligned_addr = RoundUpTo(mapped_addr, alignment);
+
+ // Free the overallocation.
+ if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0)
+ ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError());
+
+ // Attempt to allocate exactly the number of bytes we need at the aligned
+ // address. This may fail for a number of reasons, in which case we continue
+ // the loop.
+ mapped_addr = (uptr)VirtualAlloc((void *)aligned_addr, size,
+ MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ }
+
+ // Fail if we can't make this work quickly.
+ if (retries == kMaxRetries && mapped_addr == 0)
+ return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned");
+
+ return (void *)mapped_addr;
+}
+
+bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) {
+ // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
+ // but on Win64 it does.
+ (void)name; // unsupported
+#if !SANITIZER_GO && SANITIZER_WINDOWS64
+ // On asan/Windows64, use MEM_COMMIT would result in error
+ // 1455:ERROR_COMMITMENT_LIMIT.
+ // Asan uses exception handler to commit page on demand.
+ void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_READWRITE);
+#else
+ void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT,
+ PAGE_READWRITE);
+#endif
+ if (p == 0) {
+ Report("ERROR: %s failed to "
+ "allocate %p (%zd) bytes at %p (error code: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, GetLastError());
+ return false;
+ }
+ return true;
+}
+
+// Memory space mapped by 'MmapFixedOrDie' must have been reserved by
+// 'MmapFixedNoAccess'.
+void *MmapFixedOrDie(uptr fixed_addr, uptr size, const char *name) {
+ void *p = VirtualAlloc((LPVOID)fixed_addr, size,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (p == 0) {
+ char mem_type[30];
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
+ fixed_addr);
+ ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError());
+ }
+ return p;
+}
+
+// Uses fixed_addr for now.
+// Will use offset instead once we've implemented this function for real.
+uptr ReservedAddressRange::Map(uptr fixed_addr, uptr size, const char *name) {
+ return reinterpret_cast<uptr>(MmapFixedOrDieOnFatalError(fixed_addr, size));
+}
+
+uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr size,
+ const char *name) {
+ return reinterpret_cast<uptr>(MmapFixedOrDie(fixed_addr, size));
+}
+
+void ReservedAddressRange::Unmap(uptr addr, uptr size) {
+ // Only unmap if it covers the entire range.
+ CHECK((addr == reinterpret_cast<uptr>(base_)) && (size == size_));
+ // We unmap the whole range, just null out the base.
+ base_ = nullptr;
+ size_ = 0;
+ UnmapOrDie(reinterpret_cast<void*>(addr), size);
+}
+
+void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size, const char *name) {
+ void *p = VirtualAlloc((LPVOID)fixed_addr, size,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (p == 0) {
+ char mem_type[30];
+ internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx",
+ fixed_addr);
+ return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate");
+ }
+ return p;
+}
+
+void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
+ // FIXME: make this really NoReserve?
+ return MmapOrDie(size, mem_type);
+}
+
+uptr ReservedAddressRange::Init(uptr size, const char *name, uptr fixed_addr) {
+ base_ = fixed_addr ? MmapFixedNoAccess(fixed_addr, size) : MmapNoAccess(size);
+ size_ = size;
+ name_ = name;
+ (void)os_handle_; // unsupported
+ return reinterpret_cast<uptr>(base_);
+}
+
+
+void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
+ (void)name; // unsupported
+ void *res = VirtualAlloc((LPVOID)fixed_addr, size,
+ MEM_RESERVE, PAGE_NOACCESS);
+ if (res == 0)
+ Report("WARNING: %s failed to "
+ "mprotect %p (%zd) bytes at %p (error code: %d)\n",
+ SanitizerToolName, size, size, fixed_addr, GetLastError());
+ return res;
+}
+
+void *MmapNoAccess(uptr size) {
+ void *res = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_NOACCESS);
+ if (res == 0)
+ Report("WARNING: %s failed to "
+ "mprotect %p (%zd) bytes (error code: %d)\n",
+ SanitizerToolName, size, size, GetLastError());
+ return res;
+}
+
+bool MprotectNoAccess(uptr addr, uptr size) {
+ DWORD old_protection;
+ return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection);
+}
+
+void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
+ // This is almost useless on 32-bits.
+ // FIXME: add madvise-analog when we move to 64-bits.
+}
+
+void SetShadowRegionHugePageMode(uptr addr, uptr size) {
+ // FIXME: probably similar to ReleaseMemoryToOS.
+}
+
+bool DontDumpShadowMemory(uptr addr, uptr length) {
+ // This is almost useless on 32-bits.
+ // FIXME: add madvise-analog when we move to 64-bits.
+ return true;
+}
+
+uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding,
+ uptr *largest_gap_found,
+ uptr *max_occupied_addr) {
+ uptr address = 0;
+ while (true) {
+ MEMORY_BASIC_INFORMATION info;
+ if (!::VirtualQuery((void*)address, &info, sizeof(info)))
+ return 0;
+
+ if (info.State == MEM_FREE) {
+ uptr shadow_address = RoundUpTo((uptr)info.BaseAddress + left_padding,
+ alignment);
+ if (shadow_address + size < (uptr)info.BaseAddress + info.RegionSize)
+ return shadow_address;
+ }
+
+ // Move to the next region.
+ address = (uptr)info.BaseAddress + info.RegionSize;
+ }
+ return 0;
+}
+
+bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
+ MEMORY_BASIC_INFORMATION mbi;
+ CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi)));
+ return mbi.Protect == PAGE_NOACCESS &&
+ (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end;
+}
+
+void *MapFileToMemory(const char *file_name, uptr *buff_size) {
+ UNIMPLEMENTED();
+}
+
+void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) {
+ UNIMPLEMENTED();
+}
+
+static const int kMaxEnvNameLength = 128;
+static const DWORD kMaxEnvValueLength = 32767;
+
+namespace {
+
+struct EnvVariable {
+ char name[kMaxEnvNameLength];
+ char value[kMaxEnvValueLength];
+};
+
+} // namespace
+
+static const int kEnvVariables = 5;
+static EnvVariable env_vars[kEnvVariables];
+static int num_env_vars;
+
+const char *GetEnv(const char *name) {
+ // Note: this implementation caches the values of the environment variables
+ // and limits their quantity.
+ for (int i = 0; i < num_env_vars; i++) {
+ if (0 == internal_strcmp(name, env_vars[i].name))
+ return env_vars[i].value;
+ }
+ CHECK_LT(num_env_vars, kEnvVariables);
+ DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
+ kMaxEnvValueLength);
+ if (rv > 0 && rv < kMaxEnvValueLength) {
+ CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
+ internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
+ num_env_vars++;
+ return env_vars[num_env_vars - 1].value;
+ }
+ return 0;
+}
+
+const char *GetPwd() {
+ UNIMPLEMENTED();
+}
+
+u32 GetUid() {
+ UNIMPLEMENTED();
+}
+
+namespace {
+struct ModuleInfo {
+ const char *filepath;
+ uptr base_address;
+ uptr end_address;
+};
+
+#if !SANITIZER_GO
+int CompareModulesBase(const void *pl, const void *pr) {
+ const ModuleInfo *l = (const ModuleInfo *)pl, *r = (const ModuleInfo *)pr;
+ if (l->base_address < r->base_address)
+ return -1;
+ return l->base_address > r->base_address;
+}
+#endif
+} // namespace
+
+#if !SANITIZER_GO
+void DumpProcessMap() {
+ Report("Dumping process modules:\n");
+ ListOfModules modules;
+ modules.init();
+ uptr num_modules = modules.size();
+
+ InternalMmapVector<ModuleInfo> module_infos(num_modules);
+ for (size_t i = 0; i < num_modules; ++i) {
+ module_infos[i].filepath = modules[i].full_name();
+ module_infos[i].base_address = modules[i].ranges().front()->beg;
+ module_infos[i].end_address = modules[i].ranges().back()->end;
+ }
+ qsort(module_infos.data(), num_modules, sizeof(ModuleInfo),
+ CompareModulesBase);
+
+ for (size_t i = 0; i < num_modules; ++i) {
+ const ModuleInfo &mi = module_infos[i];
+ if (mi.end_address != 0) {
+ Printf("\t%p-%p %s\n", mi.base_address, mi.end_address,
+ mi.filepath[0] ? mi.filepath : "[no name]");
+ } else if (mi.filepath[0]) {
+ Printf("\t??\?-??? %s\n", mi.filepath);
+ } else {
+ Printf("\t???\n");
+ }
+ }
+}
+#endif
+
+void PrintModuleMap() { }
+
+void DisableCoreDumperIfNecessary() {
+ // Do nothing.
+}
+
+void ReExec() {
+ UNIMPLEMENTED();
+}
+
+void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {}
+
+bool StackSizeIsUnlimited() {
+ UNIMPLEMENTED();
+}
+
+void SetStackSizeLimitInBytes(uptr limit) {
+ UNIMPLEMENTED();
+}
+
+bool AddressSpaceIsUnlimited() {
+ UNIMPLEMENTED();
+}
+
+void SetAddressSpaceUnlimited() {
+ UNIMPLEMENTED();
+}
+
+bool IsPathSeparator(const char c) {
+ return c == '\\' || c == '/';
+}
+
+static bool IsAlpha(char c) {
+ c = ToLower(c);
+ return c >= 'a' && c <= 'z';
+}
+
+bool IsAbsolutePath(const char *path) {
+ return path != nullptr && IsAlpha(path[0]) && path[1] == ':' &&
+ IsPathSeparator(path[2]);
+}
+
+void SleepForSeconds(int seconds) {
+ Sleep(seconds * 1000);
+}
+
+void SleepForMillis(int millis) {
+ Sleep(millis);
+}
+
+u64 NanoTime() {
+ static LARGE_INTEGER frequency = {};
+ LARGE_INTEGER counter;
+ if (UNLIKELY(frequency.QuadPart == 0)) {
+ QueryPerformanceFrequency(&frequency);
+ CHECK_NE(frequency.QuadPart, 0);
+ }
+ QueryPerformanceCounter(&counter);
+ counter.QuadPart *= 1000ULL * 1000000ULL;
+ counter.QuadPart /= frequency.QuadPart;
+ return counter.QuadPart;
+}
+
+u64 MonotonicNanoTime() { return NanoTime(); }
+
+void Abort() {
+ internal__exit(3);
+}
+
+#if !SANITIZER_GO
+// Read the file to extract the ImageBase field from the PE header. If ASLR is
+// disabled and this virtual address is available, the loader will typically
+// load the image at this address. Therefore, we call it the preferred base. Any
+// addresses in the DWARF typically assume that the object has been loaded at
+// this address.
+static uptr GetPreferredBase(const char *modname) {
+ fd_t fd = OpenFile(modname, RdOnly, nullptr);
+ if (fd == kInvalidFd)
+ return 0;
+ FileCloser closer(fd);
+
+ // Read just the DOS header.
+ IMAGE_DOS_HEADER dos_header;
+ uptr bytes_read;
+ if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) ||
+ bytes_read != sizeof(dos_header))
+ return 0;
+
+ // The file should start with the right signature.
+ if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
+ return 0;
+
+ // The layout at e_lfanew is:
+ // "PE\0\0"
+ // IMAGE_FILE_HEADER
+ // IMAGE_OPTIONAL_HEADER
+ // Seek to e_lfanew and read all that data.
+ char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)];
+ if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) ==
+ INVALID_SET_FILE_POINTER)
+ return 0;
+ if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) ||
+ bytes_read != sizeof(buf))
+ return 0;
+
+ // Check for "PE\0\0" before the PE header.
+ char *pe_sig = &buf[0];
+ if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0)
+ return 0;
+
+ // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted.
+ IMAGE_OPTIONAL_HEADER *pe_header =
+ (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER));
+
+ // Check for more magic in the PE header.
+ if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return 0;
+
+ // Finally, return the ImageBase.
+ return (uptr)pe_header->ImageBase;
+}
+
+void ListOfModules::init() {
+ clearOrInit();
+ HANDLE cur_process = GetCurrentProcess();
+
+ // Query the list of modules. Start by assuming there are no more than 256
+ // modules and retry if that's not sufficient.
+ HMODULE *hmodules = 0;
+ uptr modules_buffer_size = sizeof(HMODULE) * 256;
+ DWORD bytes_required;
+ while (!hmodules) {
+ hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__);
+ CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size,
+ &bytes_required));
+ if (bytes_required > modules_buffer_size) {
+ // Either there turned out to be more than 256 hmodules, or new hmodules
+ // could have loaded since the last try. Retry.
+ UnmapOrDie(hmodules, modules_buffer_size);
+ hmodules = 0;
+ modules_buffer_size = bytes_required;
+ }
+ }
+
+ // |num_modules| is the number of modules actually present,
+ size_t num_modules = bytes_required / sizeof(HMODULE);
+ for (size_t i = 0; i < num_modules; ++i) {
+ HMODULE handle = hmodules[i];
+ MODULEINFO mi;
+ if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi)))
+ continue;
+
+ // Get the UTF-16 path and convert to UTF-8.
+ wchar_t modname_utf16[kMaxPathLength];
+ int modname_utf16_len =
+ GetModuleFileNameW(handle, modname_utf16, kMaxPathLength);
+ if (modname_utf16_len == 0)
+ modname_utf16[0] = '\0';
+ char module_name[kMaxPathLength];
+ int module_name_len =
+ ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1,
+ &module_name[0], kMaxPathLength, NULL, NULL);
+ module_name[module_name_len] = '\0';
+
+ uptr base_address = (uptr)mi.lpBaseOfDll;
+ uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage;
+
+ // Adjust the base address of the module so that we get a VA instead of an
+ // RVA when computing the module offset. This helps llvm-symbolizer find the
+ // right DWARF CU. In the common case that the image is loaded at it's
+ // preferred address, we will now print normal virtual addresses.
+ uptr preferred_base = GetPreferredBase(&module_name[0]);
+ uptr adjusted_base = base_address - preferred_base;
+
+ LoadedModule cur_module;
+ cur_module.set(module_name, adjusted_base);
+ // We add the whole module as one single address range.
+ cur_module.addAddressRange(base_address, end_address, /*executable*/ true,
+ /*writable*/ true);
+ modules_.push_back(cur_module);
+ }
+ UnmapOrDie(hmodules, modules_buffer_size);
+}
+
+void ListOfModules::fallbackInit() { clear(); }
+
+// We can't use atexit() directly at __asan_init time as the CRT is not fully
+// initialized at this point. Place the functions into a vector and use
+// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers).
+InternalMmapVectorNoCtor<void (*)(void)> atexit_functions;
+
+int Atexit(void (*function)(void)) {
+ atexit_functions.push_back(function);
+ return 0;
+}
+
+static int RunAtexit() {
+ TraceLoggingUnregister(g_asan_provider);
+ int ret = 0;
+ for (uptr i = 0; i < atexit_functions.size(); ++i) {
+ ret |= atexit(atexit_functions[i]);
+ }
+ return ret;
+}
+
+#pragma section(".CRT$XID", long, read) // NOLINT
+__declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit;
+#endif
+
+// ------------------ sanitizer_libc.h
+fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) {
+ // FIXME: Use the wide variants to handle Unicode filenames.
+ fd_t res;
+ if (mode == RdOnly) {
+ res = CreateFileA(filename, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+ } else if (mode == WrOnly) {
+ res = CreateFileA(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, nullptr);
+ } else {
+ UNIMPLEMENTED();
+ }
+ CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd);
+ CHECK(res != kStderrFd || kStderrFd == kInvalidFd);
+ if (res == kInvalidFd && last_error)
+ *last_error = GetLastError();
+ return res;
+}
+
+void CloseFile(fd_t fd) {
+ CloseHandle(fd);
+}
+
+bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
+
+ // bytes_read can't be passed directly to ReadFile:
+ // uptr is unsigned long long on 64-bit Windows.
+ unsigned long num_read_long;
+
+ bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr);
+ if (!success && error_p)
+ *error_p = GetLastError();
+ if (bytes_read)
+ *bytes_read = num_read_long;
+ return success;
+}
+
+bool SupportsColoredOutput(fd_t fd) {
+ // FIXME: support colored output.
+ return false;
+}
+
+bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written,
+ error_t *error_p) {
+ CHECK(fd != kInvalidFd);
+
+ // Handle null optional parameters.
+ error_t dummy_error;
+ error_p = error_p ? error_p : &dummy_error;
+ uptr dummy_bytes_written;
+ bytes_written = bytes_written ? bytes_written : &dummy_bytes_written;
+
+ // Initialize output parameters in case we fail.
+ *error_p = 0;
+ *bytes_written = 0;
+
+ // Map the conventional Unix fds 1 and 2 to Windows handles. They might be
+ // closed, in which case this will fail.
+ if (fd == kStdoutFd || fd == kStderrFd) {
+ fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
+ if (fd == 0) {
+ *error_p = ERROR_INVALID_HANDLE;
+ return false;
+ }
+ }
+
+ DWORD bytes_written_32;
+ if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) {
+ *error_p = GetLastError();
+ return false;
+ } else {
+ *bytes_written = bytes_written_32;
+ return true;
+ }
+}
+
+uptr internal_sched_yield() {
+ Sleep(0);
+ return 0;
+}
+
+void internal__exit(int exitcode) {
+ TraceLoggingUnregister(g_asan_provider);
+ // ExitProcess runs some finalizers, so use TerminateProcess to avoid that.
+ // The debugger doesn't stop on TerminateProcess like it does on ExitProcess,
+ // so add our own breakpoint here.
+ if (::IsDebuggerPresent())
+ __debugbreak();
+ TerminateProcess(GetCurrentProcess(), exitcode);
+ BUILTIN_UNREACHABLE();
+}
+
+uptr internal_ftruncate(fd_t fd, uptr size) {
+ UNIMPLEMENTED();
+}
+
+uptr GetRSS() {
+ PROCESS_MEMORY_COUNTERS counters;
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
+ return 0;
+ return counters.WorkingSetSize;
+}
+
+void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; }
+void internal_join_thread(void *th) { }
+
+// ---------------------- BlockingMutex ---------------- {{{1
+
+BlockingMutex::BlockingMutex() {
+ CHECK(sizeof(SRWLOCK) <= sizeof(opaque_storage_));
+ internal_memset(this, 0, sizeof(*this));
+}
+
+void BlockingMutex::Lock() {
+ AcquireSRWLockExclusive((PSRWLOCK)opaque_storage_);
+ CHECK_EQ(owner_, 0);
+ owner_ = GetThreadSelf();
+}
+
+void BlockingMutex::Unlock() {
+ CheckLocked();
+ owner_ = 0;
+ ReleaseSRWLockExclusive((PSRWLOCK)opaque_storage_);
+}
+
+void BlockingMutex::CheckLocked() {
+ CHECK_EQ(owner_, GetThreadSelf());
+}
+
+uptr GetTlsSize() {
+ return 0;
+}
+
+void InitTlsSize() {
+}
+
+void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
+ uptr *tls_addr, uptr *tls_size) {
+#if SANITIZER_GO
+ *stk_addr = 0;
+ *stk_size = 0;
+ *tls_addr = 0;
+ *tls_size = 0;
+#else
+ uptr stack_top, stack_bottom;
+ GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
+ *stk_addr = stack_bottom;
+ *stk_size = stack_top - stack_bottom;
+ *tls_addr = 0;
+ *tls_size = 0;
+#endif
+}
+
+void ReportFile::Write(const char *buffer, uptr length) {
+ SpinMutexLock l(mu);
+ ReopenIfNecessary();
+ if (!WriteToFile(fd, buffer, length)) {
+ // stderr may be closed, but we may be able to print to the debugger
+ // instead. This is the case when launching a program from Visual Studio,
+ // and the following routine should write to its console.
+ OutputDebugStringA(buffer);
+ }
+}
+
+void SetAlternateSignalStack() {
+ // FIXME: Decide what to do on Windows.
+}
+
+void UnsetAlternateSignalStack() {
+ // FIXME: Decide what to do on Windows.
+}
+
+void InstallDeadlySignalHandlers(SignalHandlerType handler) {
+ (void)handler;
+ // FIXME: Decide what to do on Windows.
+}
+
+HandleSignalMode GetHandleSignalMode(int signum) {
+ // FIXME: Decide what to do on Windows.
+ return kHandleSignalNo;
+}
+
+// Check based on flags if we should handle this exception.
+bool IsHandledDeadlyException(DWORD exceptionCode) {
+ switch (exceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ case EXCEPTION_STACK_OVERFLOW:
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ case EXCEPTION_IN_PAGE_ERROR:
+ return common_flags()->handle_segv;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ case EXCEPTION_PRIV_INSTRUCTION:
+ case EXCEPTION_BREAKPOINT:
+ return common_flags()->handle_sigill;
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_STACK_CHECK:
+ case EXCEPTION_FLT_UNDERFLOW:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_OVERFLOW:
+ return common_flags()->handle_sigfpe;
+ }
+ return false;
+}
+
+bool IsAccessibleMemoryRange(uptr beg, uptr size) {
+ SYSTEM_INFO si;
+ GetNativeSystemInfo(&si);
+ uptr page_size = si.dwPageSize;
+ uptr page_mask = ~(page_size - 1);
+
+ for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask;
+ page <= end;) {
+ MEMORY_BASIC_INFORMATION info;
+ if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info))
+ return false;
+
+ if (info.Protect == 0 || info.Protect == PAGE_NOACCESS ||
+ info.Protect == PAGE_EXECUTE)
+ return false;
+
+ if (info.RegionSize == 0)
+ return false;
+
+ page += info.RegionSize;
+ }
+
+ return true;
+}
+
+bool SignalContext::IsStackOverflow() const {
+ return (DWORD)GetType() == EXCEPTION_STACK_OVERFLOW;
+}
+
+void SignalContext::InitPcSpBp() {
+ EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
+ CONTEXT *context_record = (CONTEXT *)context;
+
+ pc = (uptr)exception_record->ExceptionAddress;
+#ifdef _WIN64
+ bp = (uptr)context_record->Rbp;
+ sp = (uptr)context_record->Rsp;
+#else
+ bp = (uptr)context_record->Ebp;
+ sp = (uptr)context_record->Esp;
+#endif
+}
+
+uptr SignalContext::GetAddress() const {
+ EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
+ return exception_record->ExceptionInformation[1];
+}
+
+bool SignalContext::IsMemoryAccess() const {
+ return GetWriteFlag() != SignalContext::UNKNOWN;
+}
+
+SignalContext::WriteFlag SignalContext::GetWriteFlag() const {
+ EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo;
+ // The contents of this array are documented at
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx
+ // The first element indicates read as 0, write as 1, or execute as 8. The
+ // second element is the faulting address.
+ switch (exception_record->ExceptionInformation[0]) {
+ case 0:
+ return SignalContext::READ;
+ case 1:
+ return SignalContext::WRITE;
+ case 8:
+ return SignalContext::UNKNOWN;
+ }
+ return SignalContext::UNKNOWN;
+}
+
+void SignalContext::DumpAllRegisters(void *context) {
+ // FIXME: Implement this.
+}
+
+int SignalContext::GetType() const {
+ return static_cast<const EXCEPTION_RECORD *>(siginfo)->ExceptionCode;
+}
+
+const char *SignalContext::Describe() const {
+ unsigned code = GetType();
+ // Get the string description of the exception if this is a known deadly
+ // exception.
+ switch (code) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ return "access-violation";
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ return "array-bounds-exceeded";
+ case EXCEPTION_STACK_OVERFLOW:
+ return "stack-overflow";
+ case EXCEPTION_DATATYPE_MISALIGNMENT:
+ return "datatype-misalignment";
+ case EXCEPTION_IN_PAGE_ERROR:
+ return "in-page-error";
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ return "illegal-instruction";
+ case EXCEPTION_PRIV_INSTRUCTION:
+ return "priv-instruction";
+ case EXCEPTION_BREAKPOINT:
+ return "breakpoint";
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ return "flt-denormal-operand";
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ return "flt-divide-by-zero";
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ return "flt-inexact-result";
+ case EXCEPTION_FLT_INVALID_OPERATION:
+ return "flt-invalid-operation";
+ case EXCEPTION_FLT_OVERFLOW:
+ return "flt-overflow";
+ case EXCEPTION_FLT_STACK_CHECK:
+ return "flt-stack-check";
+ case EXCEPTION_FLT_UNDERFLOW:
+ return "flt-underflow";
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ return "int-divide-by-zero";
+ case EXCEPTION_INT_OVERFLOW:
+ return "int-overflow";
+ }
+ return "unknown exception";
+}
+
+uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) {
+ // FIXME: Actually implement this function.
+ CHECK_GT(buf_len, 0);
+ buf[0] = 0;
+ return 0;
+}
+
+uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) {
+ return ReadBinaryName(buf, buf_len);
+}
+
+void CheckVMASize() {
+ // Do nothing.
+}
+
+void InitializePlatformEarly() {
+ // Do nothing.
+}
+
+void MaybeReexec() {
+ // No need to re-exec on Windows.
+}
+
+void CheckASLR() {
+ // Do nothing
+}
+
+void CheckMPROTECT() {
+ // Do nothing
+}
+
+char **GetArgv() {
+ // FIXME: Actually implement this function.
+ return 0;
+}
+
+char **GetEnviron() {
+ // FIXME: Actually implement this function.
+ return 0;
+}
+
+pid_t StartSubprocess(const char *program, const char *const argv[],
+ fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
+ // FIXME: implement on this platform
+ // Should be implemented based on
+ // SymbolizerProcess::StarAtSymbolizerSubprocess
+ // from lib/sanitizer_common/sanitizer_symbolizer_win.cpp.
+ return -1;
+}
+
+bool IsProcessRunning(pid_t pid) {
+ // FIXME: implement on this platform.
+ return false;
+}
+
+int WaitForProcess(pid_t pid) { return -1; }
+
+// FIXME implement on this platform.
+void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { }
+
+void CheckNoDeepBind(const char *filename, int flag) {
+ // Do nothing.
+}
+
+// FIXME: implement on this platform.
+bool GetRandom(void *buffer, uptr length, bool blocking) {
+ UNIMPLEMENTED();
+}
+
+u32 GetNumberOfCPUs() {
+ SYSTEM_INFO sysinfo = {};
+ GetNativeSystemInfo(&sysinfo);
+ return sysinfo.dwNumberOfProcessors;
+}
+
+#if SANITIZER_WIN_TRACE
+// TODO(mcgov): Rename this project-wide to PlatformLogInit
+void AndroidLogInit(void) {
+ HRESULT hr = TraceLoggingRegister(g_asan_provider);
+ if (!SUCCEEDED(hr))
+ return;
+}
+
+void SetAbortMessage(const char *) {}
+
+void LogFullErrorReport(const char *buffer) {
+ if (common_flags()->log_to_syslog) {
+ InternalMmapVector<wchar_t> filename;
+ DWORD filename_length = 0;
+ do {
+ filename.resize(filename.size() + 0x100);
+ filename_length =
+ GetModuleFileNameW(NULL, filename.begin(), filename.size());
+ } while (filename_length >= filename.size());
+ TraceLoggingWrite(g_asan_provider, "AsanReportEvent",
+ TraceLoggingValue(filename.begin(), "ExecutableName"),
+ TraceLoggingValue(buffer, "AsanReportContents"));
+ }
+}
+#endif // SANITIZER_WIN_TRACE
+
+} // namespace __sanitizer
+
+#endif // _WIN32
//===-- sanitizer_win.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- sanitizer_win_defs.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- sanitizer_win_dll_thunk.cc ----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// This file defines a family of thunks that should be statically linked into
-// the DLLs that have instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-
-#ifdef SANITIZER_DLL_THUNK
-#include "sanitizer_win_defs.h"
-#include "sanitizer_win_dll_thunk.h"
-#include "interception/interception.h"
-
-extern "C" {
-void *WINAPI GetModuleHandleA(const char *module_name);
-void abort();
-}
-
-namespace __sanitizer {
-uptr dllThunkGetRealAddrOrDie(const char *name) {
- uptr ret =
- __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
- if (!ret)
- abort();
- return ret;
-}
-
-int dllThunkIntercept(const char* main_function, uptr dll_function) {
- uptr wrapper = dllThunkGetRealAddrOrDie(main_function);
- if (!__interception::OverrideFunction(dll_function, wrapper, 0))
- abort();
- return 0;
-}
-
-int dllThunkInterceptWhenPossible(const char* main_function,
- const char* default_function, uptr dll_function) {
- uptr wrapper = __interception::InternalGetProcAddress(
- (void *)GetModuleHandleA(0), main_function);
- if (!wrapper)
- wrapper = dllThunkGetRealAddrOrDie(default_function);
- if (!__interception::OverrideFunction(dll_function, wrapper, 0))
- abort();
- return 0;
-}
-} // namespace __sanitizer
-
-// Include Sanitizer Common interface.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_common_interface.inc"
-
-#pragma section(".DLLTH$A", read) // NOLINT
-#pragma section(".DLLTH$Z", read) // NOLINT
-
-typedef void (*DllThunkCB)();
-extern "C" {
-__declspec(allocate(".DLLTH$A")) DllThunkCB __start_dll_thunk;
-__declspec(allocate(".DLLTH$Z")) DllThunkCB __stop_dll_thunk;
-}
-
-// Disable compiler warnings that show up if we declare our own version
-// of a compiler intrinsic (e.g. strlen).
-#pragma warning(disable: 4391)
-#pragma warning(disable: 4392)
-
-extern "C" int __dll_thunk_init() {
- static bool flag = false;
- // __dll_thunk_init is expected to be called by only one thread.
- if (flag) return 0;
- flag = true;
-
- for (DllThunkCB *it = &__start_dll_thunk; it < &__stop_dll_thunk; ++it)
- if (*it)
- (*it)();
-
- // In DLLs, the callbacks are expected to return 0,
- // otherwise CRT initialization fails.
- return 0;
-}
-
-// We want to call dll_thunk_init before C/C++ initializers / constructors are
-// executed, otherwise functions like memset might be invoked.
-#pragma section(".CRT$XIB", long, read) // NOLINT
-__declspec(allocate(".CRT$XIB")) int (*__dll_thunk_preinit)() =
- __dll_thunk_init;
-
-static void WINAPI dll_thunk_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == /*DLL_PROCESS_ATTACH=*/1) __dll_thunk_init();
-}
-
-#pragma section(".CRT$XLAB", long, read) // NOLINT
-__declspec(allocate(".CRT$XLAB")) void (WINAPI *__dll_thunk_tls_init)(void *,
- unsigned long, void *) = dll_thunk_thread_init;
-
-#endif // SANITIZER_DLL_THUNK
--- /dev/null
+//===-- sanitizer_win_dll_thunk.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This file defines a family of thunks that should be statically linked into
+// the DLLs that have instrumentation in order to delegate the calls to the
+// shared runtime that lives in the main binary.
+// See https://github.com/google/sanitizers/issues/209 for the details.
+//===----------------------------------------------------------------------===//
+
+#ifdef SANITIZER_DLL_THUNK
+#include "sanitizer_win_defs.h"
+#include "sanitizer_win_dll_thunk.h"
+#include "interception/interception.h"
+
+extern "C" {
+void *WINAPI GetModuleHandleA(const char *module_name);
+void abort();
+}
+
+namespace __sanitizer {
+uptr dllThunkGetRealAddrOrDie(const char *name) {
+ uptr ret =
+ __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
+ if (!ret)
+ abort();
+ return ret;
+}
+
+int dllThunkIntercept(const char* main_function, uptr dll_function) {
+ uptr wrapper = dllThunkGetRealAddrOrDie(main_function);
+ if (!__interception::OverrideFunction(dll_function, wrapper, 0))
+ abort();
+ return 0;
+}
+
+int dllThunkInterceptWhenPossible(const char* main_function,
+ const char* default_function, uptr dll_function) {
+ uptr wrapper = __interception::InternalGetProcAddress(
+ (void *)GetModuleHandleA(0), main_function);
+ if (!wrapper)
+ wrapper = dllThunkGetRealAddrOrDie(default_function);
+ if (!__interception::OverrideFunction(dll_function, wrapper, 0))
+ abort();
+ return 0;
+}
+} // namespace __sanitizer
+
+// Include Sanitizer Common interface.
+#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "sanitizer_common_interface.inc"
+
+#pragma section(".DLLTH$A", read) // NOLINT
+#pragma section(".DLLTH$Z", read) // NOLINT
+
+typedef void (*DllThunkCB)();
+extern "C" {
+__declspec(allocate(".DLLTH$A")) DllThunkCB __start_dll_thunk;
+__declspec(allocate(".DLLTH$Z")) DllThunkCB __stop_dll_thunk;
+}
+
+// Disable compiler warnings that show up if we declare our own version
+// of a compiler intrinsic (e.g. strlen).
+#pragma warning(disable: 4391)
+#pragma warning(disable: 4392)
+
+extern "C" int __dll_thunk_init() {
+ static bool flag = false;
+ // __dll_thunk_init is expected to be called by only one thread.
+ if (flag) return 0;
+ flag = true;
+
+ for (DllThunkCB *it = &__start_dll_thunk; it < &__stop_dll_thunk; ++it)
+ if (*it)
+ (*it)();
+
+ // In DLLs, the callbacks are expected to return 0,
+ // otherwise CRT initialization fails.
+ return 0;
+}
+
+// We want to call dll_thunk_init before C/C++ initializers / constructors are
+// executed, otherwise functions like memset might be invoked.
+#pragma section(".CRT$XIB", long, read) // NOLINT
+__declspec(allocate(".CRT$XIB")) int (*__dll_thunk_preinit)() =
+ __dll_thunk_init;
+
+static void WINAPI dll_thunk_thread_init(void *mod, unsigned long reason,
+ void *reserved) {
+ if (reason == /*DLL_PROCESS_ATTACH=*/1) __dll_thunk_init();
+}
+
+#pragma section(".CRT$XLAB", long, read) // NOLINT
+__declspec(allocate(".CRT$XLAB")) void (WINAPI *__dll_thunk_tls_init)(void *,
+ unsigned long, void *) = dll_thunk_thread_init;
+
+#endif // SANITIZER_DLL_THUNK
//===-- sanitizer_win_dll_thunk.h -----------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This header provide helper macros to delegate calls to the shared runtime
+++ /dev/null
-//===-- santizer_win_dynamic_runtime_thunk.cc -----------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines things that need to be present in the application modules
-// to interact with Sanitizer Common, when it is included in a dll.
-//
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_win_defs.h"
-// Define weak alias for all weak functions imported from sanitizer common.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "sanitizer_common_interface.inc"
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
--- /dev/null
+//===-- santizer_win_dynamic_runtime_thunk.cpp ----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines things that need to be present in the application modules
+// to interact with Sanitizer Common, when it is included in a dll.
+//
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
+#define SANITIZER_IMPORT_INTERFACE 1
+#include "sanitizer_win_defs.h"
+// Define weak alias for all weak functions imported from sanitizer common.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
+#include "sanitizer_common_interface.inc"
+#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+
+namespace __sanitizer {
+// Add one, otherwise unused, external symbol to this object file so that the
+// Visual C++ linker includes it and reads the .drective section.
+void ForceWholeArchiveIncludeForSanitizerCommon() {}
+}
+++ /dev/null
-//===-- sanitizer_win_weak_interception.cc --------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// This module should be included in the sanitizer when it is implemented as a
-// shared library on Windows (dll), in order to delegate the calls of weak
-// functions to the implementation in the main executable when a strong
-// definition is provided.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_platform.h"
-#if SANITIZER_WINDOWS && SANITIZER_DYNAMIC
-#include "sanitizer_win_weak_interception.h"
-#include "sanitizer_allocator_interface.h"
-#include "sanitizer_interface_internal.h"
-#include "sanitizer_win_defs.h"
-#include "interception/interception.h"
-
-extern "C" {
-void *WINAPI GetModuleHandleA(const char *module_name);
-void abort();
-}
-
-namespace __sanitizer {
-// Try to get a pointer to real_function in the main module and override
-// dll_function with that pointer. If the function isn't found, nothing changes.
-int interceptWhenPossible(uptr dll_function, const char *real_function) {
- uptr real = __interception::InternalGetProcAddress(
- (void *)GetModuleHandleA(0), real_function);
- if (real && !__interception::OverrideFunction((uptr)dll_function, real, 0))
- abort();
- return 0;
-}
-} // namespace __sanitizer
-
-// Declare weak hooks.
-extern "C" {
-void __sanitizer_weak_hook_memcmp(uptr called_pc, const void *s1,
- const void *s2, uptr n, int result);
-void __sanitizer_weak_hook_strcmp(uptr called_pc, const char *s1,
- const char *s2, int result);
-void __sanitizer_weak_hook_strncmp(uptr called_pc, const char *s1,
- const char *s2, uptr n, int result);
-void __sanitizer_weak_hook_strstr(uptr called_pc, const char *s1,
- const char *s2, char *result);
-}
-
-// Include Sanitizer Common interface.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "sanitizer_common_interface.inc"
-
-#pragma section(".WEAK$A", read) // NOLINT
-#pragma section(".WEAK$Z", read) // NOLINT
-
-typedef void (*InterceptCB)();
-extern "C" {
-__declspec(allocate(".WEAK$A")) InterceptCB __start_weak_list;
-__declspec(allocate(".WEAK$Z")) InterceptCB __stop_weak_list;
-}
-
-static int weak_intercept_init() {
- static bool flag = false;
- // weak_interception_init is expected to be called by only one thread.
- if (flag) return 0;
- flag = true;
-
- for (InterceptCB *it = &__start_weak_list; it < &__stop_weak_list; ++it)
- if (*it)
- (*it)();
-
- // In DLLs, the callbacks are expected to return 0,
- // otherwise CRT initialization fails.
- return 0;
-}
-
-#pragma section(".CRT$XIB", long, read) // NOLINT
-__declspec(allocate(".CRT$XIB")) int (*__weak_intercept_preinit)() =
- weak_intercept_init;
-
-static void WINAPI weak_intercept_thread_init(void *mod, unsigned long reason,
- void *reserved) {
- if (reason == /*DLL_PROCESS_ATTACH=*/1) weak_intercept_init();
-}
-
-#pragma section(".CRT$XLAB", long, read) // NOLINT
-__declspec(allocate(".CRT$XLAB")) void(WINAPI *__weak_intercept_tls_init)(
- void *, unsigned long, void *) = weak_intercept_thread_init;
-
-#endif // SANITIZER_WINDOWS && SANITIZER_DYNAMIC
--- /dev/null
+//===-- sanitizer_win_weak_interception.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This module should be included in the sanitizer when it is implemented as a
+// shared library on Windows (dll), in order to delegate the calls of weak
+// functions to the implementation in the main executable when a strong
+// definition is provided.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_platform.h"
+#if SANITIZER_WINDOWS && SANITIZER_DYNAMIC
+#include "sanitizer_win_weak_interception.h"
+#include "sanitizer_allocator_interface.h"
+#include "sanitizer_interface_internal.h"
+#include "sanitizer_win_defs.h"
+#include "interception/interception.h"
+
+extern "C" {
+void *WINAPI GetModuleHandleA(const char *module_name);
+void abort();
+}
+
+namespace __sanitizer {
+// Try to get a pointer to real_function in the main module and override
+// dll_function with that pointer. If the function isn't found, nothing changes.
+int interceptWhenPossible(uptr dll_function, const char *real_function) {
+ uptr real = __interception::InternalGetProcAddress(
+ (void *)GetModuleHandleA(0), real_function);
+ if (real && !__interception::OverrideFunction((uptr)dll_function, real, 0))
+ abort();
+ return 0;
+}
+} // namespace __sanitizer
+
+// Declare weak hooks.
+extern "C" {
+void __sanitizer_weak_hook_memcmp(uptr called_pc, const void *s1,
+ const void *s2, uptr n, int result);
+void __sanitizer_weak_hook_strcmp(uptr called_pc, const char *s1,
+ const char *s2, int result);
+void __sanitizer_weak_hook_strncmp(uptr called_pc, const char *s1,
+ const char *s2, uptr n, int result);
+void __sanitizer_weak_hook_strstr(uptr called_pc, const char *s1,
+ const char *s2, char *result);
+}
+
+// Include Sanitizer Common interface.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "sanitizer_common_interface.inc"
+
+#pragma section(".WEAK$A", read) // NOLINT
+#pragma section(".WEAK$Z", read) // NOLINT
+
+typedef void (*InterceptCB)();
+extern "C" {
+__declspec(allocate(".WEAK$A")) InterceptCB __start_weak_list;
+__declspec(allocate(".WEAK$Z")) InterceptCB __stop_weak_list;
+}
+
+static int weak_intercept_init() {
+ static bool flag = false;
+ // weak_interception_init is expected to be called by only one thread.
+ if (flag) return 0;
+ flag = true;
+
+ for (InterceptCB *it = &__start_weak_list; it < &__stop_weak_list; ++it)
+ if (*it)
+ (*it)();
+
+ // In DLLs, the callbacks are expected to return 0,
+ // otherwise CRT initialization fails.
+ return 0;
+}
+
+#pragma section(".CRT$XIB", long, read) // NOLINT
+__declspec(allocate(".CRT$XIB")) int (*__weak_intercept_preinit)() =
+ weak_intercept_init;
+
+static void WINAPI weak_intercept_thread_init(void *mod, unsigned long reason,
+ void *reserved) {
+ if (reason == /*DLL_PROCESS_ATTACH=*/1) weak_intercept_init();
+}
+
+#pragma section(".CRT$XLAB", long, read) // NOLINT
+__declspec(allocate(".CRT$XLAB")) void(WINAPI *__weak_intercept_tls_init)(
+ void *, unsigned long, void *) = weak_intercept_thread_init;
+
+#endif // SANITIZER_WINDOWS && SANITIZER_DYNAMIC
//===-- sanitizer_win_weak_interception.h ---------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This header provide helper macros to delegate calls of weak functions to the
nodist_toolexeclib_HEADERS = libtsan_preinit.o
tsan_files = \
- tsan_clock.cc \
- tsan_debugging.cc \
- tsan_external.cc \
- tsan_fd.cc \
- tsan_flags.cc \
- tsan_ignoreset.cc \
- tsan_interceptors.cc \
- tsan_interceptors_mac.cc \
- tsan_interface_ann.cc \
- tsan_interface_atomic.cc \
- tsan_interface.cc \
- tsan_interface_java.cc \
- tsan_libdispatch_mac.cc \
- tsan_malloc_mac.cc \
- tsan_md5.cc \
- tsan_mman.cc \
- tsan_mutex.cc \
- tsan_mutexset.cc \
- tsan_new_delete.cc \
- tsan_platform_linux.cc \
- tsan_platform_mac.cc \
- tsan_platform_posix.cc \
- tsan_platform_windows.cc \
- tsan_report.cc \
- tsan_rtl.cc \
- tsan_rtl_mutex.cc \
- tsan_rtl_proc.cc \
- tsan_rtl_report.cc \
- tsan_rtl_thread.cc \
- tsan_stack_trace.cc \
- tsan_stat.cc \
- tsan_suppressions.cc \
- tsan_symbolize.cc \
- tsan_sync.cc
+ tsan_clock.cpp \
+ tsan_debugging.cpp \
+ tsan_external.cpp \
+ tsan_fd.cpp \
+ tsan_flags.cpp \
+ tsan_ignoreset.cpp \
+ tsan_interceptors.cpp \
+ tsan_interceptors_mac.cpp \
+ tsan_interface_ann.cpp \
+ tsan_interface_atomic.cpp \
+ tsan_interface.cpp \
+ tsan_interface_java.cpp \
+ tsan_malloc_mac.cpp \
+ tsan_md5.cpp \
+ tsan_mman.cpp \
+ tsan_mutex.cpp \
+ tsan_mutexset.cpp \
+ tsan_new_delete.cpp \
+ tsan_platform_linux.cpp \
+ tsan_platform_mac.cpp \
+ tsan_platform_posix.cpp \
+ tsan_platform_windows.cpp \
+ tsan_report.cpp \
+ tsan_rtl.cpp \
+ tsan_rtl_mutex.cpp \
+ tsan_rtl_proc.cpp \
+ tsan_rtl_report.cpp \
+ tsan_rtl_thread.cpp \
+ tsan_stack_trace.cpp \
+ tsan_stat.cpp \
+ tsan_suppressions.cpp \
+ tsan_symbolize.cpp \
+ tsan_sync.cpp
libtsan_la_SOURCES = $(tsan_files)
EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S tsan_rtl_mips64.S tsan_rtl_ppc64.S
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
tsan_fd.lo tsan_flags.lo tsan_ignoreset.lo \
tsan_interceptors.lo tsan_interceptors_mac.lo \
tsan_interface_ann.lo tsan_interface_atomic.lo \
- tsan_interface.lo tsan_interface_java.lo \
- tsan_libdispatch_mac.lo tsan_malloc_mac.lo tsan_md5.lo \
- tsan_mman.lo tsan_mutex.lo tsan_mutexset.lo tsan_new_delete.lo \
- tsan_platform_linux.lo tsan_platform_mac.lo \
+ tsan_interface.lo tsan_interface_java.lo tsan_malloc_mac.lo \
+ tsan_md5.lo tsan_mman.lo tsan_mutex.lo tsan_mutexset.lo \
+ tsan_new_delete.lo tsan_platform_linux.lo tsan_platform_mac.lo \
tsan_platform_posix.lo tsan_platform_windows.lo tsan_report.lo \
tsan_rtl.lo tsan_rtl_mutex.lo tsan_rtl_proc.lo \
tsan_rtl_report.lo tsan_rtl_thread.lo tsan_stack_trace.lo \
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/tsan_clock.Plo \
+ ./$(DEPDIR)/tsan_debugging.Plo ./$(DEPDIR)/tsan_external.Plo \
+ ./$(DEPDIR)/tsan_fd.Plo ./$(DEPDIR)/tsan_flags.Plo \
+ ./$(DEPDIR)/tsan_ignoreset.Plo \
+ ./$(DEPDIR)/tsan_interceptors.Plo \
+ ./$(DEPDIR)/tsan_interceptors_mac.Plo \
+ ./$(DEPDIR)/tsan_interface.Plo \
+ ./$(DEPDIR)/tsan_interface_ann.Plo \
+ ./$(DEPDIR)/tsan_interface_atomic.Plo \
+ ./$(DEPDIR)/tsan_interface_java.Plo \
+ ./$(DEPDIR)/tsan_malloc_mac.Plo ./$(DEPDIR)/tsan_md5.Plo \
+ ./$(DEPDIR)/tsan_mman.Plo ./$(DEPDIR)/tsan_mutex.Plo \
+ ./$(DEPDIR)/tsan_mutexset.Plo ./$(DEPDIR)/tsan_new_delete.Plo \
+ ./$(DEPDIR)/tsan_platform_linux.Plo \
+ ./$(DEPDIR)/tsan_platform_mac.Plo \
+ ./$(DEPDIR)/tsan_platform_posix.Plo \
+ ./$(DEPDIR)/tsan_platform_windows.Plo \
+ ./$(DEPDIR)/tsan_report.Plo ./$(DEPDIR)/tsan_rtl.Plo \
+ ./$(DEPDIR)/tsan_rtl_aarch64.Plo \
+ ./$(DEPDIR)/tsan_rtl_amd64.Plo ./$(DEPDIR)/tsan_rtl_mips64.Plo \
+ ./$(DEPDIR)/tsan_rtl_mutex.Plo ./$(DEPDIR)/tsan_rtl_ppc64.Plo \
+ ./$(DEPDIR)/tsan_rtl_proc.Plo ./$(DEPDIR)/tsan_rtl_report.Plo \
+ ./$(DEPDIR)/tsan_rtl_thread.Plo \
+ ./$(DEPDIR)/tsan_stack_trace.Plo ./$(DEPDIR)/tsan_stat.Plo \
+ ./$(DEPDIR)/tsan_suppressions.Plo \
+ ./$(DEPDIR)/tsan_symbolize.Plo ./$(DEPDIR)/tsan_sync.Plo
am__mv = mv -f
CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS)
-LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+LTCPPASCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CCAS) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CCASFLAGS) $(CCASFLAGS)
toolexeclib_LTLIBRARIES = libtsan.la
nodist_toolexeclib_HEADERS = libtsan_preinit.o
tsan_files = \
- tsan_clock.cc \
- tsan_debugging.cc \
- tsan_external.cc \
- tsan_fd.cc \
- tsan_flags.cc \
- tsan_ignoreset.cc \
- tsan_interceptors.cc \
- tsan_interceptors_mac.cc \
- tsan_interface_ann.cc \
- tsan_interface_atomic.cc \
- tsan_interface.cc \
- tsan_interface_java.cc \
- tsan_libdispatch_mac.cc \
- tsan_malloc_mac.cc \
- tsan_md5.cc \
- tsan_mman.cc \
- tsan_mutex.cc \
- tsan_mutexset.cc \
- tsan_new_delete.cc \
- tsan_platform_linux.cc \
- tsan_platform_mac.cc \
- tsan_platform_posix.cc \
- tsan_platform_windows.cc \
- tsan_report.cc \
- tsan_rtl.cc \
- tsan_rtl_mutex.cc \
- tsan_rtl_proc.cc \
- tsan_rtl_report.cc \
- tsan_rtl_thread.cc \
- tsan_stack_trace.cc \
- tsan_stat.cc \
- tsan_suppressions.cc \
- tsan_symbolize.cc \
- tsan_sync.cc
+ tsan_clock.cpp \
+ tsan_debugging.cpp \
+ tsan_external.cpp \
+ tsan_fd.cpp \
+ tsan_flags.cpp \
+ tsan_ignoreset.cpp \
+ tsan_interceptors.cpp \
+ tsan_interceptors_mac.cpp \
+ tsan_interface_ann.cpp \
+ tsan_interface_atomic.cpp \
+ tsan_interface.cpp \
+ tsan_interface_java.cpp \
+ tsan_malloc_mac.cpp \
+ tsan_md5.cpp \
+ tsan_mman.cpp \
+ tsan_mutex.cpp \
+ tsan_mutexset.cpp \
+ tsan_new_delete.cpp \
+ tsan_platform_linux.cpp \
+ tsan_platform_mac.cpp \
+ tsan_platform_posix.cpp \
+ tsan_platform_windows.cpp \
+ tsan_report.cpp \
+ tsan_rtl.cpp \
+ tsan_rtl_mutex.cpp \
+ tsan_rtl_proc.cpp \
+ tsan_rtl_report.cpp \
+ tsan_rtl_thread.cpp \
+ tsan_stack_trace.cpp \
+ tsan_stat.cpp \
+ tsan_suppressions.cpp \
+ tsan_symbolize.cpp \
+ tsan_sync.cpp
libtsan_la_SOURCES = $(tsan_files)
EXTRA_libtsan_la_SOURCES = tsan_rtl_amd64.S tsan_rtl_aarch64.S tsan_rtl_mips64.S tsan_rtl_ppc64.S
all: all-am
.SUFFIXES:
-.SUFFIXES: .S .cc .lo .o .obj
+.SUFFIXES: .S .cpp .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_clock.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_debugging.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_external.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_fd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_flags.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_ignoreset.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_libdispatch_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_malloc_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutexset.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_new_delete.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_posix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_windows.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_aarch64.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_amd64.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mips64.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_ppc64.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_proc.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_thread.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stack_trace.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stat.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_suppressions.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_symbolize.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_sync.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_clock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_debugging.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_external.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_fd.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_ignoreset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_atomic.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_java.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_malloc_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_md5.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mman.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_mutexset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_new_delete.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_mac.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_posix.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_platform_windows.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_report.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_aarch64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_amd64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mips64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_mutex.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_ppc64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_proc.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_report.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_rtl_thread.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stack_trace.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_stat.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_suppressions.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_symbolize.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_sync.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
.S.o:
@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CPPASCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(LTCPPASCOMPILE) -c -o $@ $<
-.cc.o:
+.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
-.cc.obj:
+.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-.cc.lo:
+.cpp.lo:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/tsan_clock.Plo
+ -rm -f ./$(DEPDIR)/tsan_debugging.Plo
+ -rm -f ./$(DEPDIR)/tsan_external.Plo
+ -rm -f ./$(DEPDIR)/tsan_fd.Plo
+ -rm -f ./$(DEPDIR)/tsan_flags.Plo
+ -rm -f ./$(DEPDIR)/tsan_ignoreset.Plo
+ -rm -f ./$(DEPDIR)/tsan_interceptors.Plo
+ -rm -f ./$(DEPDIR)/tsan_interceptors_mac.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface_ann.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface_atomic.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface_java.Plo
+ -rm -f ./$(DEPDIR)/tsan_malloc_mac.Plo
+ -rm -f ./$(DEPDIR)/tsan_md5.Plo
+ -rm -f ./$(DEPDIR)/tsan_mman.Plo
+ -rm -f ./$(DEPDIR)/tsan_mutex.Plo
+ -rm -f ./$(DEPDIR)/tsan_mutexset.Plo
+ -rm -f ./$(DEPDIR)/tsan_new_delete.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_linux.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_mac.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_posix.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_windows.Plo
+ -rm -f ./$(DEPDIR)/tsan_report.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_aarch64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_amd64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_mips64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_mutex.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_ppc64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_proc.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_report.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_thread.Plo
+ -rm -f ./$(DEPDIR)/tsan_stack_trace.Plo
+ -rm -f ./$(DEPDIR)/tsan_stat.Plo
+ -rm -f ./$(DEPDIR)/tsan_suppressions.Plo
+ -rm -f ./$(DEPDIR)/tsan_symbolize.Plo
+ -rm -f ./$(DEPDIR)/tsan_sync.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/tsan_clock.Plo
+ -rm -f ./$(DEPDIR)/tsan_debugging.Plo
+ -rm -f ./$(DEPDIR)/tsan_external.Plo
+ -rm -f ./$(DEPDIR)/tsan_fd.Plo
+ -rm -f ./$(DEPDIR)/tsan_flags.Plo
+ -rm -f ./$(DEPDIR)/tsan_ignoreset.Plo
+ -rm -f ./$(DEPDIR)/tsan_interceptors.Plo
+ -rm -f ./$(DEPDIR)/tsan_interceptors_mac.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface_ann.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface_atomic.Plo
+ -rm -f ./$(DEPDIR)/tsan_interface_java.Plo
+ -rm -f ./$(DEPDIR)/tsan_malloc_mac.Plo
+ -rm -f ./$(DEPDIR)/tsan_md5.Plo
+ -rm -f ./$(DEPDIR)/tsan_mman.Plo
+ -rm -f ./$(DEPDIR)/tsan_mutex.Plo
+ -rm -f ./$(DEPDIR)/tsan_mutexset.Plo
+ -rm -f ./$(DEPDIR)/tsan_new_delete.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_linux.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_mac.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_posix.Plo
+ -rm -f ./$(DEPDIR)/tsan_platform_windows.Plo
+ -rm -f ./$(DEPDIR)/tsan_report.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_aarch64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_amd64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_mips64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_mutex.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_ppc64.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_proc.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_report.Plo
+ -rm -f ./$(DEPDIR)/tsan_rtl_thread.Plo
+ -rm -f ./$(DEPDIR)/tsan_stack_trace.Plo
+ -rm -f ./$(DEPDIR)/tsan_stat.Plo
+ -rm -f ./$(DEPDIR)/tsan_suppressions.Plo
+ -rm -f ./$(DEPDIR)/tsan_symbolize.Plo
+ -rm -f ./$(DEPDIR)/tsan_sync.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-nodist_toolexeclibHEADERS install-pdf \
- install-pdf-am install-ps install-ps-am install-strip \
- install-toolexeclibLTLIBRARIES installcheck installcheck-am \
- installdirs maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
- uninstall-am uninstall-nodist_toolexeclibHEADERS \
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags dvi dvi-am \
+ html html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-nodist_toolexeclibHEADERS \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip install-toolexeclibLTLIBRARIES installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-nodist_toolexeclibHEADERS \
uninstall-toolexeclibLTLIBRARIES
.PRECIOUS: Makefile
+++ /dev/null
-//===-- tsan_clock.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_clock.h"
-#include "tsan_rtl.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-
-// SyncClock and ThreadClock implement vector clocks for sync variables
-// (mutexes, atomic variables, file descriptors, etc) and threads, respectively.
-// ThreadClock contains fixed-size vector clock for maximum number of threads.
-// SyncClock contains growable vector clock for currently necessary number of
-// threads.
-// Together they implement very simple model of operations, namely:
-//
-// void ThreadClock::acquire(const SyncClock *src) {
-// for (int i = 0; i < kMaxThreads; i++)
-// clock[i] = max(clock[i], src->clock[i]);
-// }
-//
-// void ThreadClock::release(SyncClock *dst) const {
-// for (int i = 0; i < kMaxThreads; i++)
-// dst->clock[i] = max(dst->clock[i], clock[i]);
-// }
-//
-// void ThreadClock::ReleaseStore(SyncClock *dst) const {
-// for (int i = 0; i < kMaxThreads; i++)
-// dst->clock[i] = clock[i];
-// }
-//
-// void ThreadClock::acq_rel(SyncClock *dst) {
-// acquire(dst);
-// release(dst);
-// }
-//
-// Conformance to this model is extensively verified in tsan_clock_test.cc.
-// However, the implementation is significantly more complex. The complexity
-// allows to implement important classes of use cases in O(1) instead of O(N).
-//
-// The use cases are:
-// 1. Singleton/once atomic that has a single release-store operation followed
-// by zillions of acquire-loads (the acquire-load is O(1)).
-// 2. Thread-local mutex (both lock and unlock can be O(1)).
-// 3. Leaf mutex (unlock is O(1)).
-// 4. A mutex shared by 2 threads (both lock and unlock can be O(1)).
-// 5. An atomic with a single writer (writes can be O(1)).
-// The implementation dynamically adopts to workload. So if an atomic is in
-// read-only phase, these reads will be O(1); if it later switches to read/write
-// phase, the implementation will correctly handle that by switching to O(N).
-//
-// Thread-safety note: all const operations on SyncClock's are conducted under
-// a shared lock; all non-const operations on SyncClock's are conducted under
-// an exclusive lock; ThreadClock's are private to respective threads and so
-// do not need any protection.
-//
-// Description of SyncClock state:
-// clk_ - variable size vector clock, low kClkBits hold timestamp,
-// the remaining bits hold "acquired" flag (the actual value is thread's
-// reused counter);
-// if acquried == thr->reused_, then the respective thread has already
-// acquired this clock (except possibly for dirty elements).
-// dirty_ - holds up to two indeces in the vector clock that other threads
-// need to acquire regardless of "acquired" flag value;
-// release_store_tid_ - denotes that the clock state is a result of
-// release-store operation by the thread with release_store_tid_ index.
-// release_store_reused_ - reuse count of release_store_tid_.
-
-// We don't have ThreadState in these methods, so this is an ugly hack that
-// works only in C++.
-#if !SANITIZER_GO
-# define CPP_STAT_INC(typ) StatInc(cur_thread(), typ)
-#else
-# define CPP_STAT_INC(typ) (void)0
-#endif
-
-namespace __tsan {
-
-static atomic_uint32_t *ref_ptr(ClockBlock *cb) {
- return reinterpret_cast<atomic_uint32_t *>(&cb->table[ClockBlock::kRefIdx]);
-}
-
-// Drop reference to the first level block idx.
-static void UnrefClockBlock(ClockCache *c, u32 idx, uptr blocks) {
- ClockBlock *cb = ctx->clock_alloc.Map(idx);
- atomic_uint32_t *ref = ref_ptr(cb);
- u32 v = atomic_load(ref, memory_order_acquire);
- for (;;) {
- CHECK_GT(v, 0);
- if (v == 1)
- break;
- if (atomic_compare_exchange_strong(ref, &v, v - 1, memory_order_acq_rel))
- return;
- }
- // First level block owns second level blocks, so them as well.
- for (uptr i = 0; i < blocks; i++)
- ctx->clock_alloc.Free(c, cb->table[ClockBlock::kBlockIdx - i]);
- ctx->clock_alloc.Free(c, idx);
-}
-
-ThreadClock::ThreadClock(unsigned tid, unsigned reused)
- : tid_(tid)
- , reused_(reused + 1) // 0 has special meaning
- , cached_idx_()
- , cached_size_()
- , cached_blocks_() {
- CHECK_LT(tid, kMaxTidInClock);
- CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits);
- nclk_ = tid_ + 1;
- last_acquire_ = 0;
- internal_memset(clk_, 0, sizeof(clk_));
-}
-
-void ThreadClock::ResetCached(ClockCache *c) {
- if (cached_idx_) {
- UnrefClockBlock(c, cached_idx_, cached_blocks_);
- cached_idx_ = 0;
- cached_size_ = 0;
- cached_blocks_ = 0;
- }
-}
-
-void ThreadClock::acquire(ClockCache *c, SyncClock *src) {
- DCHECK_LE(nclk_, kMaxTid);
- DCHECK_LE(src->size_, kMaxTid);
- CPP_STAT_INC(StatClockAcquire);
-
- // Check if it's empty -> no need to do anything.
- const uptr nclk = src->size_;
- if (nclk == 0) {
- CPP_STAT_INC(StatClockAcquireEmpty);
- return;
- }
-
- bool acquired = false;
- for (unsigned i = 0; i < kDirtyTids; i++) {
- SyncClock::Dirty dirty = src->dirty_[i];
- unsigned tid = dirty.tid;
- if (tid != kInvalidTid) {
- if (clk_[tid] < dirty.epoch) {
- clk_[tid] = dirty.epoch;
- acquired = true;
- }
- }
- }
-
- // Check if we've already acquired src after the last release operation on src
- if (tid_ >= nclk || src->elem(tid_).reused != reused_) {
- // O(N) acquire.
- CPP_STAT_INC(StatClockAcquireFull);
- nclk_ = max(nclk_, nclk);
- u64 *dst_pos = &clk_[0];
- for (ClockElem &src_elem : *src) {
- u64 epoch = src_elem.epoch;
- if (*dst_pos < epoch) {
- *dst_pos = epoch;
- acquired = true;
- }
- dst_pos++;
- }
-
- // Remember that this thread has acquired this clock.
- if (nclk > tid_)
- src->elem(tid_).reused = reused_;
- }
-
- if (acquired) {
- CPP_STAT_INC(StatClockAcquiredSomething);
- last_acquire_ = clk_[tid_];
- ResetCached(c);
- }
-}
-
-void ThreadClock::release(ClockCache *c, SyncClock *dst) {
- DCHECK_LE(nclk_, kMaxTid);
- DCHECK_LE(dst->size_, kMaxTid);
-
- if (dst->size_ == 0) {
- // ReleaseStore will correctly set release_store_tid_,
- // which can be important for future operations.
- ReleaseStore(c, dst);
- return;
- }
-
- CPP_STAT_INC(StatClockRelease);
- // Check if we need to resize dst.
- if (dst->size_ < nclk_)
- dst->Resize(c, nclk_);
-
- // Check if we had not acquired anything from other threads
- // since the last release on dst. If so, we need to update
- // only dst->elem(tid_).
- if (dst->elem(tid_).epoch > last_acquire_) {
- UpdateCurrentThread(c, dst);
- if (dst->release_store_tid_ != tid_ ||
- dst->release_store_reused_ != reused_)
- dst->release_store_tid_ = kInvalidTid;
- return;
- }
-
- // O(N) release.
- CPP_STAT_INC(StatClockReleaseFull);
- dst->Unshare(c);
- // First, remember whether we've acquired dst.
- bool acquired = IsAlreadyAcquired(dst);
- if (acquired)
- CPP_STAT_INC(StatClockReleaseAcquired);
- // Update dst->clk_.
- dst->FlushDirty();
- uptr i = 0;
- for (ClockElem &ce : *dst) {
- ce.epoch = max(ce.epoch, clk_[i]);
- ce.reused = 0;
- i++;
- }
- // Clear 'acquired' flag in the remaining elements.
- if (nclk_ < dst->size_)
- CPP_STAT_INC(StatClockReleaseClearTail);
- for (uptr i = nclk_; i < dst->size_; i++)
- dst->elem(i).reused = 0;
- dst->release_store_tid_ = kInvalidTid;
- dst->release_store_reused_ = 0;
- // If we've acquired dst, remember this fact,
- // so that we don't need to acquire it on next acquire.
- if (acquired)
- dst->elem(tid_).reused = reused_;
-}
-
-void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) {
- DCHECK_LE(nclk_, kMaxTid);
- DCHECK_LE(dst->size_, kMaxTid);
- CPP_STAT_INC(StatClockStore);
-
- if (dst->size_ == 0 && cached_idx_ != 0) {
- // Reuse the cached clock.
- // Note: we could reuse/cache the cached clock in more cases:
- // we could update the existing clock and cache it, or replace it with the
- // currently cached clock and release the old one. And for a shared
- // existing clock, we could replace it with the currently cached;
- // or unshare, update and cache. But, for simplicity, we currnetly reuse
- // cached clock only when the target clock is empty.
- dst->tab_ = ctx->clock_alloc.Map(cached_idx_);
- dst->tab_idx_ = cached_idx_;
- dst->size_ = cached_size_;
- dst->blocks_ = cached_blocks_;
- CHECK_EQ(dst->dirty_[0].tid, kInvalidTid);
- // The cached clock is shared (immutable),
- // so this is where we store the current clock.
- dst->dirty_[0].tid = tid_;
- dst->dirty_[0].epoch = clk_[tid_];
- dst->release_store_tid_ = tid_;
- dst->release_store_reused_ = reused_;
- // Rememeber that we don't need to acquire it in future.
- dst->elem(tid_).reused = reused_;
- // Grab a reference.
- atomic_fetch_add(ref_ptr(dst->tab_), 1, memory_order_relaxed);
- return;
- }
-
- // Check if we need to resize dst.
- if (dst->size_ < nclk_)
- dst->Resize(c, nclk_);
-
- if (dst->release_store_tid_ == tid_ &&
- dst->release_store_reused_ == reused_ &&
- dst->elem(tid_).epoch > last_acquire_) {
- CPP_STAT_INC(StatClockStoreFast);
- UpdateCurrentThread(c, dst);
- return;
- }
-
- // O(N) release-store.
- CPP_STAT_INC(StatClockStoreFull);
- dst->Unshare(c);
- // Note: dst can be larger than this ThreadClock.
- // This is fine since clk_ beyond size is all zeros.
- uptr i = 0;
- for (ClockElem &ce : *dst) {
- ce.epoch = clk_[i];
- ce.reused = 0;
- i++;
- }
- for (uptr i = 0; i < kDirtyTids; i++)
- dst->dirty_[i].tid = kInvalidTid;
- dst->release_store_tid_ = tid_;
- dst->release_store_reused_ = reused_;
- // Rememeber that we don't need to acquire it in future.
- dst->elem(tid_).reused = reused_;
-
- // If the resulting clock is cachable, cache it for future release operations.
- // The clock is always cachable if we released to an empty sync object.
- if (cached_idx_ == 0 && dst->Cachable()) {
- // Grab a reference to the ClockBlock.
- atomic_uint32_t *ref = ref_ptr(dst->tab_);
- if (atomic_load(ref, memory_order_acquire) == 1)
- atomic_store_relaxed(ref, 2);
- else
- atomic_fetch_add(ref_ptr(dst->tab_), 1, memory_order_relaxed);
- cached_idx_ = dst->tab_idx_;
- cached_size_ = dst->size_;
- cached_blocks_ = dst->blocks_;
- }
-}
-
-void ThreadClock::acq_rel(ClockCache *c, SyncClock *dst) {
- CPP_STAT_INC(StatClockAcquireRelease);
- acquire(c, dst);
- ReleaseStore(c, dst);
-}
-
-// Updates only single element related to the current thread in dst->clk_.
-void ThreadClock::UpdateCurrentThread(ClockCache *c, SyncClock *dst) const {
- // Update the threads time, but preserve 'acquired' flag.
- for (unsigned i = 0; i < kDirtyTids; i++) {
- SyncClock::Dirty *dirty = &dst->dirty_[i];
- const unsigned tid = dirty->tid;
- if (tid == tid_ || tid == kInvalidTid) {
- CPP_STAT_INC(StatClockReleaseFast);
- dirty->tid = tid_;
- dirty->epoch = clk_[tid_];
- return;
- }
- }
- // Reset all 'acquired' flags, O(N).
- // We are going to touch dst elements, so we need to unshare it.
- dst->Unshare(c);
- CPP_STAT_INC(StatClockReleaseSlow);
- dst->elem(tid_).epoch = clk_[tid_];
- for (uptr i = 0; i < dst->size_; i++)
- dst->elem(i).reused = 0;
- dst->FlushDirty();
-}
-
-// Checks whether the current thread has already acquired src.
-bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const {
- if (src->elem(tid_).reused != reused_)
- return false;
- for (unsigned i = 0; i < kDirtyTids; i++) {
- SyncClock::Dirty dirty = src->dirty_[i];
- if (dirty.tid != kInvalidTid) {
- if (clk_[dirty.tid] < dirty.epoch)
- return false;
- }
- }
- return true;
-}
-
-// Sets a single element in the vector clock.
-// This function is called only from weird places like AcquireGlobal.
-void ThreadClock::set(ClockCache *c, unsigned tid, u64 v) {
- DCHECK_LT(tid, kMaxTid);
- DCHECK_GE(v, clk_[tid]);
- clk_[tid] = v;
- if (nclk_ <= tid)
- nclk_ = tid + 1;
- last_acquire_ = clk_[tid_];
- ResetCached(c);
-}
-
-void ThreadClock::DebugDump(int(*printf)(const char *s, ...)) {
- printf("clock=[");
- for (uptr i = 0; i < nclk_; i++)
- printf("%s%llu", i == 0 ? "" : ",", clk_[i]);
- printf("] tid=%u/%u last_acq=%llu", tid_, reused_, last_acquire_);
-}
-
-SyncClock::SyncClock() {
- ResetImpl();
-}
-
-SyncClock::~SyncClock() {
- // Reset must be called before dtor.
- CHECK_EQ(size_, 0);
- CHECK_EQ(blocks_, 0);
- CHECK_EQ(tab_, 0);
- CHECK_EQ(tab_idx_, 0);
-}
-
-void SyncClock::Reset(ClockCache *c) {
- if (size_)
- UnrefClockBlock(c, tab_idx_, blocks_);
- ResetImpl();
-}
-
-void SyncClock::ResetImpl() {
- tab_ = 0;
- tab_idx_ = 0;
- size_ = 0;
- blocks_ = 0;
- release_store_tid_ = kInvalidTid;
- release_store_reused_ = 0;
- for (uptr i = 0; i < kDirtyTids; i++)
- dirty_[i].tid = kInvalidTid;
-}
-
-void SyncClock::Resize(ClockCache *c, uptr nclk) {
- CPP_STAT_INC(StatClockReleaseResize);
- Unshare(c);
- if (nclk <= capacity()) {
- // Memory is already allocated, just increase the size.
- size_ = nclk;
- return;
- }
- if (size_ == 0) {
- // Grow from 0 to one-level table.
- CHECK_EQ(size_, 0);
- CHECK_EQ(blocks_, 0);
- CHECK_EQ(tab_, 0);
- CHECK_EQ(tab_idx_, 0);
- tab_idx_ = ctx->clock_alloc.Alloc(c);
- tab_ = ctx->clock_alloc.Map(tab_idx_);
- internal_memset(tab_, 0, sizeof(*tab_));
- atomic_store_relaxed(ref_ptr(tab_), 1);
- size_ = 1;
- } else if (size_ > blocks_ * ClockBlock::kClockCount) {
- u32 idx = ctx->clock_alloc.Alloc(c);
- ClockBlock *new_cb = ctx->clock_alloc.Map(idx);
- uptr top = size_ - blocks_ * ClockBlock::kClockCount;
- CHECK_LT(top, ClockBlock::kClockCount);
- const uptr move = top * sizeof(tab_->clock[0]);
- internal_memcpy(&new_cb->clock[0], tab_->clock, move);
- internal_memset(&new_cb->clock[top], 0, sizeof(*new_cb) - move);
- internal_memset(tab_->clock, 0, move);
- append_block(idx);
- }
- // At this point we have first level table allocated and all clock elements
- // are evacuated from it to a second level block.
- // Add second level tables as necessary.
- while (nclk > capacity()) {
- u32 idx = ctx->clock_alloc.Alloc(c);
- ClockBlock *cb = ctx->clock_alloc.Map(idx);
- internal_memset(cb, 0, sizeof(*cb));
- append_block(idx);
- }
- size_ = nclk;
-}
-
-// Flushes all dirty elements into the main clock array.
-void SyncClock::FlushDirty() {
- for (unsigned i = 0; i < kDirtyTids; i++) {
- Dirty *dirty = &dirty_[i];
- if (dirty->tid != kInvalidTid) {
- CHECK_LT(dirty->tid, size_);
- elem(dirty->tid).epoch = dirty->epoch;
- dirty->tid = kInvalidTid;
- }
- }
-}
-
-bool SyncClock::IsShared() const {
- if (size_ == 0)
- return false;
- atomic_uint32_t *ref = ref_ptr(tab_);
- u32 v = atomic_load(ref, memory_order_acquire);
- CHECK_GT(v, 0);
- return v > 1;
-}
-
-// Unshares the current clock if it's shared.
-// Shared clocks are immutable, so they need to be unshared before any updates.
-// Note: this does not apply to dirty entries as they are not shared.
-void SyncClock::Unshare(ClockCache *c) {
- if (!IsShared())
- return;
- // First, copy current state into old.
- SyncClock old;
- old.tab_ = tab_;
- old.tab_idx_ = tab_idx_;
- old.size_ = size_;
- old.blocks_ = blocks_;
- old.release_store_tid_ = release_store_tid_;
- old.release_store_reused_ = release_store_reused_;
- for (unsigned i = 0; i < kDirtyTids; i++)
- old.dirty_[i] = dirty_[i];
- // Then, clear current object.
- ResetImpl();
- // Allocate brand new clock in the current object.
- Resize(c, old.size_);
- // Now copy state back into this object.
- Iter old_iter(&old);
- for (ClockElem &ce : *this) {
- ce = *old_iter;
- ++old_iter;
- }
- release_store_tid_ = old.release_store_tid_;
- release_store_reused_ = old.release_store_reused_;
- for (unsigned i = 0; i < kDirtyTids; i++)
- dirty_[i] = old.dirty_[i];
- // Drop reference to old and delete if necessary.
- old.Reset(c);
-}
-
-// Can we cache this clock for future release operations?
-ALWAYS_INLINE bool SyncClock::Cachable() const {
- if (size_ == 0)
- return false;
- for (unsigned i = 0; i < kDirtyTids; i++) {
- if (dirty_[i].tid != kInvalidTid)
- return false;
- }
- return atomic_load_relaxed(ref_ptr(tab_)) == 1;
-}
-
-// elem linearizes the two-level structure into linear array.
-// Note: this is used only for one time accesses, vector operations use
-// the iterator as it is much faster.
-ALWAYS_INLINE ClockElem &SyncClock::elem(unsigned tid) const {
- DCHECK_LT(tid, size_);
- const uptr block = tid / ClockBlock::kClockCount;
- DCHECK_LE(block, blocks_);
- tid %= ClockBlock::kClockCount;
- if (block == blocks_)
- return tab_->clock[tid];
- u32 idx = get_block(block);
- ClockBlock *cb = ctx->clock_alloc.Map(idx);
- return cb->clock[tid];
-}
-
-ALWAYS_INLINE uptr SyncClock::capacity() const {
- if (size_ == 0)
- return 0;
- uptr ratio = sizeof(ClockBlock::clock[0]) / sizeof(ClockBlock::table[0]);
- // How many clock elements we can fit into the first level block.
- // +1 for ref counter.
- uptr top = ClockBlock::kClockCount - RoundUpTo(blocks_ + 1, ratio) / ratio;
- return blocks_ * ClockBlock::kClockCount + top;
-}
-
-ALWAYS_INLINE u32 SyncClock::get_block(uptr bi) const {
- DCHECK(size_);
- DCHECK_LT(bi, blocks_);
- return tab_->table[ClockBlock::kBlockIdx - bi];
-}
-
-ALWAYS_INLINE void SyncClock::append_block(u32 idx) {
- uptr bi = blocks_++;
- CHECK_EQ(get_block(bi), 0);
- tab_->table[ClockBlock::kBlockIdx - bi] = idx;
-}
-
-// Used only by tests.
-u64 SyncClock::get(unsigned tid) const {
- for (unsigned i = 0; i < kDirtyTids; i++) {
- Dirty dirty = dirty_[i];
- if (dirty.tid == tid)
- return dirty.epoch;
- }
- return elem(tid).epoch;
-}
-
-// Used only by Iter test.
-u64 SyncClock::get_clean(unsigned tid) const {
- return elem(tid).epoch;
-}
-
-void SyncClock::DebugDump(int(*printf)(const char *s, ...)) {
- printf("clock=[");
- for (uptr i = 0; i < size_; i++)
- printf("%s%llu", i == 0 ? "" : ",", elem(i).epoch);
- printf("] reused=[");
- for (uptr i = 0; i < size_; i++)
- printf("%s%llu", i == 0 ? "" : ",", elem(i).reused);
- printf("] release_store_tid=%d/%d dirty_tids=%d[%llu]/%d[%llu]",
- release_store_tid_, release_store_reused_,
- dirty_[0].tid, dirty_[0].epoch,
- dirty_[1].tid, dirty_[1].epoch);
-}
-
-void SyncClock::Iter::Next() {
- // Finished with the current block, move on to the next one.
- block_++;
- if (block_ < parent_->blocks_) {
- // Iterate over the next second level block.
- u32 idx = parent_->get_block(block_);
- ClockBlock *cb = ctx->clock_alloc.Map(idx);
- pos_ = &cb->clock[0];
- end_ = pos_ + min(parent_->size_ - block_ * ClockBlock::kClockCount,
- ClockBlock::kClockCount);
- return;
- }
- if (block_ == parent_->blocks_ &&
- parent_->size_ > parent_->blocks_ * ClockBlock::kClockCount) {
- // Iterate over elements in the first level block.
- pos_ = &parent_->tab_->clock[0];
- end_ = pos_ + min(parent_->size_ - block_ * ClockBlock::kClockCount,
- ClockBlock::kClockCount);
- return;
- }
- parent_ = nullptr; // denotes end
-}
-} // namespace __tsan
--- /dev/null
+//===-- tsan_clock.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_clock.h"
+#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+
+// SyncClock and ThreadClock implement vector clocks for sync variables
+// (mutexes, atomic variables, file descriptors, etc) and threads, respectively.
+// ThreadClock contains fixed-size vector clock for maximum number of threads.
+// SyncClock contains growable vector clock for currently necessary number of
+// threads.
+// Together they implement very simple model of operations, namely:
+//
+// void ThreadClock::acquire(const SyncClock *src) {
+// for (int i = 0; i < kMaxThreads; i++)
+// clock[i] = max(clock[i], src->clock[i]);
+// }
+//
+// void ThreadClock::release(SyncClock *dst) const {
+// for (int i = 0; i < kMaxThreads; i++)
+// dst->clock[i] = max(dst->clock[i], clock[i]);
+// }
+//
+// void ThreadClock::ReleaseStore(SyncClock *dst) const {
+// for (int i = 0; i < kMaxThreads; i++)
+// dst->clock[i] = clock[i];
+// }
+//
+// void ThreadClock::acq_rel(SyncClock *dst) {
+// acquire(dst);
+// release(dst);
+// }
+//
+// Conformance to this model is extensively verified in tsan_clock_test.cpp.
+// However, the implementation is significantly more complex. The complexity
+// allows to implement important classes of use cases in O(1) instead of O(N).
+//
+// The use cases are:
+// 1. Singleton/once atomic that has a single release-store operation followed
+// by zillions of acquire-loads (the acquire-load is O(1)).
+// 2. Thread-local mutex (both lock and unlock can be O(1)).
+// 3. Leaf mutex (unlock is O(1)).
+// 4. A mutex shared by 2 threads (both lock and unlock can be O(1)).
+// 5. An atomic with a single writer (writes can be O(1)).
+// The implementation dynamically adopts to workload. So if an atomic is in
+// read-only phase, these reads will be O(1); if it later switches to read/write
+// phase, the implementation will correctly handle that by switching to O(N).
+//
+// Thread-safety note: all const operations on SyncClock's are conducted under
+// a shared lock; all non-const operations on SyncClock's are conducted under
+// an exclusive lock; ThreadClock's are private to respective threads and so
+// do not need any protection.
+//
+// Description of SyncClock state:
+// clk_ - variable size vector clock, low kClkBits hold timestamp,
+// the remaining bits hold "acquired" flag (the actual value is thread's
+// reused counter);
+// if acquried == thr->reused_, then the respective thread has already
+// acquired this clock (except possibly for dirty elements).
+// dirty_ - holds up to two indeces in the vector clock that other threads
+// need to acquire regardless of "acquired" flag value;
+// release_store_tid_ - denotes that the clock state is a result of
+// release-store operation by the thread with release_store_tid_ index.
+// release_store_reused_ - reuse count of release_store_tid_.
+
+// We don't have ThreadState in these methods, so this is an ugly hack that
+// works only in C++.
+#if !SANITIZER_GO
+# define CPP_STAT_INC(typ) StatInc(cur_thread(), typ)
+#else
+# define CPP_STAT_INC(typ) (void)0
+#endif
+
+namespace __tsan {
+
+static atomic_uint32_t *ref_ptr(ClockBlock *cb) {
+ return reinterpret_cast<atomic_uint32_t *>(&cb->table[ClockBlock::kRefIdx]);
+}
+
+// Drop reference to the first level block idx.
+static void UnrefClockBlock(ClockCache *c, u32 idx, uptr blocks) {
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ atomic_uint32_t *ref = ref_ptr(cb);
+ u32 v = atomic_load(ref, memory_order_acquire);
+ for (;;) {
+ CHECK_GT(v, 0);
+ if (v == 1)
+ break;
+ if (atomic_compare_exchange_strong(ref, &v, v - 1, memory_order_acq_rel))
+ return;
+ }
+ // First level block owns second level blocks, so them as well.
+ for (uptr i = 0; i < blocks; i++)
+ ctx->clock_alloc.Free(c, cb->table[ClockBlock::kBlockIdx - i]);
+ ctx->clock_alloc.Free(c, idx);
+}
+
+ThreadClock::ThreadClock(unsigned tid, unsigned reused)
+ : tid_(tid)
+ , reused_(reused + 1) // 0 has special meaning
+ , cached_idx_()
+ , cached_size_()
+ , cached_blocks_() {
+ CHECK_LT(tid, kMaxTidInClock);
+ CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits);
+ nclk_ = tid_ + 1;
+ last_acquire_ = 0;
+ internal_memset(clk_, 0, sizeof(clk_));
+}
+
+void ThreadClock::ResetCached(ClockCache *c) {
+ if (cached_idx_) {
+ UnrefClockBlock(c, cached_idx_, cached_blocks_);
+ cached_idx_ = 0;
+ cached_size_ = 0;
+ cached_blocks_ = 0;
+ }
+}
+
+void ThreadClock::acquire(ClockCache *c, SyncClock *src) {
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(src->size_, kMaxTid);
+ CPP_STAT_INC(StatClockAcquire);
+
+ // Check if it's empty -> no need to do anything.
+ const uptr nclk = src->size_;
+ if (nclk == 0) {
+ CPP_STAT_INC(StatClockAcquireEmpty);
+ return;
+ }
+
+ bool acquired = false;
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ SyncClock::Dirty dirty = src->dirty_[i];
+ unsigned tid = dirty.tid;
+ if (tid != kInvalidTid) {
+ if (clk_[tid] < dirty.epoch) {
+ clk_[tid] = dirty.epoch;
+ acquired = true;
+ }
+ }
+ }
+
+ // Check if we've already acquired src after the last release operation on src
+ if (tid_ >= nclk || src->elem(tid_).reused != reused_) {
+ // O(N) acquire.
+ CPP_STAT_INC(StatClockAcquireFull);
+ nclk_ = max(nclk_, nclk);
+ u64 *dst_pos = &clk_[0];
+ for (ClockElem &src_elem : *src) {
+ u64 epoch = src_elem.epoch;
+ if (*dst_pos < epoch) {
+ *dst_pos = epoch;
+ acquired = true;
+ }
+ dst_pos++;
+ }
+
+ // Remember that this thread has acquired this clock.
+ if (nclk > tid_)
+ src->elem(tid_).reused = reused_;
+ }
+
+ if (acquired) {
+ CPP_STAT_INC(StatClockAcquiredSomething);
+ last_acquire_ = clk_[tid_];
+ ResetCached(c);
+ }
+}
+
+void ThreadClock::release(ClockCache *c, SyncClock *dst) {
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(dst->size_, kMaxTid);
+
+ if (dst->size_ == 0) {
+ // ReleaseStore will correctly set release_store_tid_,
+ // which can be important for future operations.
+ ReleaseStore(c, dst);
+ return;
+ }
+
+ CPP_STAT_INC(StatClockRelease);
+ // Check if we need to resize dst.
+ if (dst->size_ < nclk_)
+ dst->Resize(c, nclk_);
+
+ // Check if we had not acquired anything from other threads
+ // since the last release on dst. If so, we need to update
+ // only dst->elem(tid_).
+ if (dst->elem(tid_).epoch > last_acquire_) {
+ UpdateCurrentThread(c, dst);
+ if (dst->release_store_tid_ != tid_ ||
+ dst->release_store_reused_ != reused_)
+ dst->release_store_tid_ = kInvalidTid;
+ return;
+ }
+
+ // O(N) release.
+ CPP_STAT_INC(StatClockReleaseFull);
+ dst->Unshare(c);
+ // First, remember whether we've acquired dst.
+ bool acquired = IsAlreadyAcquired(dst);
+ if (acquired)
+ CPP_STAT_INC(StatClockReleaseAcquired);
+ // Update dst->clk_.
+ dst->FlushDirty();
+ uptr i = 0;
+ for (ClockElem &ce : *dst) {
+ ce.epoch = max(ce.epoch, clk_[i]);
+ ce.reused = 0;
+ i++;
+ }
+ // Clear 'acquired' flag in the remaining elements.
+ if (nclk_ < dst->size_)
+ CPP_STAT_INC(StatClockReleaseClearTail);
+ for (uptr i = nclk_; i < dst->size_; i++)
+ dst->elem(i).reused = 0;
+ dst->release_store_tid_ = kInvalidTid;
+ dst->release_store_reused_ = 0;
+ // If we've acquired dst, remember this fact,
+ // so that we don't need to acquire it on next acquire.
+ if (acquired)
+ dst->elem(tid_).reused = reused_;
+}
+
+void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) {
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(dst->size_, kMaxTid);
+ CPP_STAT_INC(StatClockStore);
+
+ if (dst->size_ == 0 && cached_idx_ != 0) {
+ // Reuse the cached clock.
+ // Note: we could reuse/cache the cached clock in more cases:
+ // we could update the existing clock and cache it, or replace it with the
+ // currently cached clock and release the old one. And for a shared
+ // existing clock, we could replace it with the currently cached;
+ // or unshare, update and cache. But, for simplicity, we currnetly reuse
+ // cached clock only when the target clock is empty.
+ dst->tab_ = ctx->clock_alloc.Map(cached_idx_);
+ dst->tab_idx_ = cached_idx_;
+ dst->size_ = cached_size_;
+ dst->blocks_ = cached_blocks_;
+ CHECK_EQ(dst->dirty_[0].tid, kInvalidTid);
+ // The cached clock is shared (immutable),
+ // so this is where we store the current clock.
+ dst->dirty_[0].tid = tid_;
+ dst->dirty_[0].epoch = clk_[tid_];
+ dst->release_store_tid_ = tid_;
+ dst->release_store_reused_ = reused_;
+ // Rememeber that we don't need to acquire it in future.
+ dst->elem(tid_).reused = reused_;
+ // Grab a reference.
+ atomic_fetch_add(ref_ptr(dst->tab_), 1, memory_order_relaxed);
+ return;
+ }
+
+ // Check if we need to resize dst.
+ if (dst->size_ < nclk_)
+ dst->Resize(c, nclk_);
+
+ if (dst->release_store_tid_ == tid_ &&
+ dst->release_store_reused_ == reused_ &&
+ dst->elem(tid_).epoch > last_acquire_) {
+ CPP_STAT_INC(StatClockStoreFast);
+ UpdateCurrentThread(c, dst);
+ return;
+ }
+
+ // O(N) release-store.
+ CPP_STAT_INC(StatClockStoreFull);
+ dst->Unshare(c);
+ // Note: dst can be larger than this ThreadClock.
+ // This is fine since clk_ beyond size is all zeros.
+ uptr i = 0;
+ for (ClockElem &ce : *dst) {
+ ce.epoch = clk_[i];
+ ce.reused = 0;
+ i++;
+ }
+ for (uptr i = 0; i < kDirtyTids; i++)
+ dst->dirty_[i].tid = kInvalidTid;
+ dst->release_store_tid_ = tid_;
+ dst->release_store_reused_ = reused_;
+ // Rememeber that we don't need to acquire it in future.
+ dst->elem(tid_).reused = reused_;
+
+ // If the resulting clock is cachable, cache it for future release operations.
+ // The clock is always cachable if we released to an empty sync object.
+ if (cached_idx_ == 0 && dst->Cachable()) {
+ // Grab a reference to the ClockBlock.
+ atomic_uint32_t *ref = ref_ptr(dst->tab_);
+ if (atomic_load(ref, memory_order_acquire) == 1)
+ atomic_store_relaxed(ref, 2);
+ else
+ atomic_fetch_add(ref_ptr(dst->tab_), 1, memory_order_relaxed);
+ cached_idx_ = dst->tab_idx_;
+ cached_size_ = dst->size_;
+ cached_blocks_ = dst->blocks_;
+ }
+}
+
+void ThreadClock::acq_rel(ClockCache *c, SyncClock *dst) {
+ CPP_STAT_INC(StatClockAcquireRelease);
+ acquire(c, dst);
+ ReleaseStore(c, dst);
+}
+
+// Updates only single element related to the current thread in dst->clk_.
+void ThreadClock::UpdateCurrentThread(ClockCache *c, SyncClock *dst) const {
+ // Update the threads time, but preserve 'acquired' flag.
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ SyncClock::Dirty *dirty = &dst->dirty_[i];
+ const unsigned tid = dirty->tid;
+ if (tid == tid_ || tid == kInvalidTid) {
+ CPP_STAT_INC(StatClockReleaseFast);
+ dirty->tid = tid_;
+ dirty->epoch = clk_[tid_];
+ return;
+ }
+ }
+ // Reset all 'acquired' flags, O(N).
+ // We are going to touch dst elements, so we need to unshare it.
+ dst->Unshare(c);
+ CPP_STAT_INC(StatClockReleaseSlow);
+ dst->elem(tid_).epoch = clk_[tid_];
+ for (uptr i = 0; i < dst->size_; i++)
+ dst->elem(i).reused = 0;
+ dst->FlushDirty();
+}
+
+// Checks whether the current thread has already acquired src.
+bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const {
+ if (src->elem(tid_).reused != reused_)
+ return false;
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ SyncClock::Dirty dirty = src->dirty_[i];
+ if (dirty.tid != kInvalidTid) {
+ if (clk_[dirty.tid] < dirty.epoch)
+ return false;
+ }
+ }
+ return true;
+}
+
+// Sets a single element in the vector clock.
+// This function is called only from weird places like AcquireGlobal.
+void ThreadClock::set(ClockCache *c, unsigned tid, u64 v) {
+ DCHECK_LT(tid, kMaxTid);
+ DCHECK_GE(v, clk_[tid]);
+ clk_[tid] = v;
+ if (nclk_ <= tid)
+ nclk_ = tid + 1;
+ last_acquire_ = clk_[tid_];
+ ResetCached(c);
+}
+
+void ThreadClock::DebugDump(int(*printf)(const char *s, ...)) {
+ printf("clock=[");
+ for (uptr i = 0; i < nclk_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i]);
+ printf("] tid=%u/%u last_acq=%llu", tid_, reused_, last_acquire_);
+}
+
+SyncClock::SyncClock() {
+ ResetImpl();
+}
+
+SyncClock::~SyncClock() {
+ // Reset must be called before dtor.
+ CHECK_EQ(size_, 0);
+ CHECK_EQ(blocks_, 0);
+ CHECK_EQ(tab_, 0);
+ CHECK_EQ(tab_idx_, 0);
+}
+
+void SyncClock::Reset(ClockCache *c) {
+ if (size_)
+ UnrefClockBlock(c, tab_idx_, blocks_);
+ ResetImpl();
+}
+
+void SyncClock::ResetImpl() {
+ tab_ = 0;
+ tab_idx_ = 0;
+ size_ = 0;
+ blocks_ = 0;
+ release_store_tid_ = kInvalidTid;
+ release_store_reused_ = 0;
+ for (uptr i = 0; i < kDirtyTids; i++)
+ dirty_[i].tid = kInvalidTid;
+}
+
+void SyncClock::Resize(ClockCache *c, uptr nclk) {
+ CPP_STAT_INC(StatClockReleaseResize);
+ Unshare(c);
+ if (nclk <= capacity()) {
+ // Memory is already allocated, just increase the size.
+ size_ = nclk;
+ return;
+ }
+ if (size_ == 0) {
+ // Grow from 0 to one-level table.
+ CHECK_EQ(size_, 0);
+ CHECK_EQ(blocks_, 0);
+ CHECK_EQ(tab_, 0);
+ CHECK_EQ(tab_idx_, 0);
+ tab_idx_ = ctx->clock_alloc.Alloc(c);
+ tab_ = ctx->clock_alloc.Map(tab_idx_);
+ internal_memset(tab_, 0, sizeof(*tab_));
+ atomic_store_relaxed(ref_ptr(tab_), 1);
+ size_ = 1;
+ } else if (size_ > blocks_ * ClockBlock::kClockCount) {
+ u32 idx = ctx->clock_alloc.Alloc(c);
+ ClockBlock *new_cb = ctx->clock_alloc.Map(idx);
+ uptr top = size_ - blocks_ * ClockBlock::kClockCount;
+ CHECK_LT(top, ClockBlock::kClockCount);
+ const uptr move = top * sizeof(tab_->clock[0]);
+ internal_memcpy(&new_cb->clock[0], tab_->clock, move);
+ internal_memset(&new_cb->clock[top], 0, sizeof(*new_cb) - move);
+ internal_memset(tab_->clock, 0, move);
+ append_block(idx);
+ }
+ // At this point we have first level table allocated and all clock elements
+ // are evacuated from it to a second level block.
+ // Add second level tables as necessary.
+ while (nclk > capacity()) {
+ u32 idx = ctx->clock_alloc.Alloc(c);
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ internal_memset(cb, 0, sizeof(*cb));
+ append_block(idx);
+ }
+ size_ = nclk;
+}
+
+// Flushes all dirty elements into the main clock array.
+void SyncClock::FlushDirty() {
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ Dirty *dirty = &dirty_[i];
+ if (dirty->tid != kInvalidTid) {
+ CHECK_LT(dirty->tid, size_);
+ elem(dirty->tid).epoch = dirty->epoch;
+ dirty->tid = kInvalidTid;
+ }
+ }
+}
+
+bool SyncClock::IsShared() const {
+ if (size_ == 0)
+ return false;
+ atomic_uint32_t *ref = ref_ptr(tab_);
+ u32 v = atomic_load(ref, memory_order_acquire);
+ CHECK_GT(v, 0);
+ return v > 1;
+}
+
+// Unshares the current clock if it's shared.
+// Shared clocks are immutable, so they need to be unshared before any updates.
+// Note: this does not apply to dirty entries as they are not shared.
+void SyncClock::Unshare(ClockCache *c) {
+ if (!IsShared())
+ return;
+ // First, copy current state into old.
+ SyncClock old;
+ old.tab_ = tab_;
+ old.tab_idx_ = tab_idx_;
+ old.size_ = size_;
+ old.blocks_ = blocks_;
+ old.release_store_tid_ = release_store_tid_;
+ old.release_store_reused_ = release_store_reused_;
+ for (unsigned i = 0; i < kDirtyTids; i++)
+ old.dirty_[i] = dirty_[i];
+ // Then, clear current object.
+ ResetImpl();
+ // Allocate brand new clock in the current object.
+ Resize(c, old.size_);
+ // Now copy state back into this object.
+ Iter old_iter(&old);
+ for (ClockElem &ce : *this) {
+ ce = *old_iter;
+ ++old_iter;
+ }
+ release_store_tid_ = old.release_store_tid_;
+ release_store_reused_ = old.release_store_reused_;
+ for (unsigned i = 0; i < kDirtyTids; i++)
+ dirty_[i] = old.dirty_[i];
+ // Drop reference to old and delete if necessary.
+ old.Reset(c);
+}
+
+// Can we cache this clock for future release operations?
+ALWAYS_INLINE bool SyncClock::Cachable() const {
+ if (size_ == 0)
+ return false;
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ if (dirty_[i].tid != kInvalidTid)
+ return false;
+ }
+ return atomic_load_relaxed(ref_ptr(tab_)) == 1;
+}
+
+// elem linearizes the two-level structure into linear array.
+// Note: this is used only for one time accesses, vector operations use
+// the iterator as it is much faster.
+ALWAYS_INLINE ClockElem &SyncClock::elem(unsigned tid) const {
+ DCHECK_LT(tid, size_);
+ const uptr block = tid / ClockBlock::kClockCount;
+ DCHECK_LE(block, blocks_);
+ tid %= ClockBlock::kClockCount;
+ if (block == blocks_)
+ return tab_->clock[tid];
+ u32 idx = get_block(block);
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ return cb->clock[tid];
+}
+
+ALWAYS_INLINE uptr SyncClock::capacity() const {
+ if (size_ == 0)
+ return 0;
+ uptr ratio = sizeof(ClockBlock::clock[0]) / sizeof(ClockBlock::table[0]);
+ // How many clock elements we can fit into the first level block.
+ // +1 for ref counter.
+ uptr top = ClockBlock::kClockCount - RoundUpTo(blocks_ + 1, ratio) / ratio;
+ return blocks_ * ClockBlock::kClockCount + top;
+}
+
+ALWAYS_INLINE u32 SyncClock::get_block(uptr bi) const {
+ DCHECK(size_);
+ DCHECK_LT(bi, blocks_);
+ return tab_->table[ClockBlock::kBlockIdx - bi];
+}
+
+ALWAYS_INLINE void SyncClock::append_block(u32 idx) {
+ uptr bi = blocks_++;
+ CHECK_EQ(get_block(bi), 0);
+ tab_->table[ClockBlock::kBlockIdx - bi] = idx;
+}
+
+// Used only by tests.
+u64 SyncClock::get(unsigned tid) const {
+ for (unsigned i = 0; i < kDirtyTids; i++) {
+ Dirty dirty = dirty_[i];
+ if (dirty.tid == tid)
+ return dirty.epoch;
+ }
+ return elem(tid).epoch;
+}
+
+// Used only by Iter test.
+u64 SyncClock::get_clean(unsigned tid) const {
+ return elem(tid).epoch;
+}
+
+void SyncClock::DebugDump(int(*printf)(const char *s, ...)) {
+ printf("clock=[");
+ for (uptr i = 0; i < size_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", elem(i).epoch);
+ printf("] reused=[");
+ for (uptr i = 0; i < size_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", elem(i).reused);
+ printf("] release_store_tid=%d/%d dirty_tids=%d[%llu]/%d[%llu]",
+ release_store_tid_, release_store_reused_,
+ dirty_[0].tid, dirty_[0].epoch,
+ dirty_[1].tid, dirty_[1].epoch);
+}
+
+void SyncClock::Iter::Next() {
+ // Finished with the current block, move on to the next one.
+ block_++;
+ if (block_ < parent_->blocks_) {
+ // Iterate over the next second level block.
+ u32 idx = parent_->get_block(block_);
+ ClockBlock *cb = ctx->clock_alloc.Map(idx);
+ pos_ = &cb->clock[0];
+ end_ = pos_ + min(parent_->size_ - block_ * ClockBlock::kClockCount,
+ ClockBlock::kClockCount);
+ return;
+ }
+ if (block_ == parent_->blocks_ &&
+ parent_->size_ > parent_->blocks_ * ClockBlock::kClockCount) {
+ // Iterate over elements in the first level block.
+ pos_ = &parent_->tab_->clock[0];
+ end_ = pos_ + min(parent_->size_ - block_ * ClockBlock::kClockCount,
+ ClockBlock::kClockCount);
+ return;
+ }
+ parent_ = nullptr; // denotes end
+}
+} // namespace __tsan
//===-- tsan_clock.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_debugging.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// TSan debugging API implementation.
-//===----------------------------------------------------------------------===//
-#include "tsan_interface.h"
-#include "tsan_report.h"
-#include "tsan_rtl.h"
-
-#include "sanitizer_common/sanitizer_stackdepot.h"
-
-using namespace __tsan;
-
-static const char *ReportTypeDescription(ReportType typ) {
- if (typ == ReportTypeRace) return "data-race";
- if (typ == ReportTypeVptrRace) return "data-race-vptr";
- if (typ == ReportTypeUseAfterFree) return "heap-use-after-free";
- if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr";
- if (typ == ReportTypeExternalRace) return "external-race";
- if (typ == ReportTypeThreadLeak) return "thread-leak";
- if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy";
- if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock";
- if (typ == ReportTypeMutexInvalidAccess) return "mutex-invalid-access";
- if (typ == ReportTypeMutexBadUnlock) return "mutex-bad-unlock";
- if (typ == ReportTypeMutexBadReadLock) return "mutex-bad-read-lock";
- if (typ == ReportTypeMutexBadReadUnlock) return "mutex-bad-read-unlock";
- if (typ == ReportTypeSignalUnsafe) return "signal-unsafe-call";
- if (typ == ReportTypeErrnoInSignal) return "errno-in-signal-handler";
- if (typ == ReportTypeDeadlock) return "lock-order-inversion";
- return "";
-}
-
-static const char *ReportLocationTypeDescription(ReportLocationType typ) {
- if (typ == ReportLocationGlobal) return "global";
- if (typ == ReportLocationHeap) return "heap";
- if (typ == ReportLocationStack) return "stack";
- if (typ == ReportLocationTLS) return "tls";
- if (typ == ReportLocationFD) return "fd";
- return "";
-}
-
-static void CopyTrace(SymbolizedStack *first_frame, void **trace,
- uptr trace_size) {
- uptr i = 0;
- for (SymbolizedStack *frame = first_frame; frame != nullptr;
- frame = frame->next) {
- trace[i++] = (void *)frame->info.address;
- if (i >= trace_size) break;
- }
-}
-
-// Meant to be called by the debugger.
-SANITIZER_INTERFACE_ATTRIBUTE
-void *__tsan_get_current_report() {
- return const_cast<ReportDesc*>(cur_thread()->current_report);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_data(void *report, const char **description, int *count,
- int *stack_count, int *mop_count, int *loc_count,
- int *mutex_count, int *thread_count,
- int *unique_tid_count, void **sleep_trace,
- uptr trace_size) {
- const ReportDesc *rep = (ReportDesc *)report;
- *description = ReportTypeDescription(rep->typ);
- *count = rep->count;
- *stack_count = rep->stacks.Size();
- *mop_count = rep->mops.Size();
- *loc_count = rep->locs.Size();
- *mutex_count = rep->mutexes.Size();
- *thread_count = rep->threads.Size();
- *unique_tid_count = rep->unique_tids.Size();
- if (rep->sleep) CopyTrace(rep->sleep->frames, sleep_trace, trace_size);
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_tag(void *report, uptr *tag) {
- const ReportDesc *rep = (ReportDesc *)report;
- *tag = rep->tag;
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_stack(void *report, uptr idx, void **trace,
- uptr trace_size) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->stacks.Size());
- ReportStack *stack = rep->stacks[idx];
- if (stack) CopyTrace(stack->frames, trace, trace_size);
- return stack ? 1 : 0;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr,
- int *size, int *write, int *atomic, void **trace,
- uptr trace_size) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->mops.Size());
- ReportMop *mop = rep->mops[idx];
- *tid = mop->tid;
- *addr = (void *)mop->addr;
- *size = mop->size;
- *write = mop->write ? 1 : 0;
- *atomic = mop->atomic ? 1 : 0;
- if (mop->stack) CopyTrace(mop->stack->frames, trace, trace_size);
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_loc(void *report, uptr idx, const char **type,
- void **addr, uptr *start, uptr *size, int *tid,
- int *fd, int *suppressable, void **trace,
- uptr trace_size) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->locs.Size());
- ReportLocation *loc = rep->locs[idx];
- *type = ReportLocationTypeDescription(loc->type);
- *addr = (void *)loc->global.start;
- *start = loc->heap_chunk_start;
- *size = loc->heap_chunk_size;
- *tid = loc->tid;
- *fd = loc->fd;
- *suppressable = loc->suppressable;
- if (loc->stack) CopyTrace(loc->stack->frames, trace, trace_size);
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_loc_object_type(void *report, uptr idx,
- const char **object_type) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->locs.Size());
- ReportLocation *loc = rep->locs[idx];
- *object_type = GetObjectTypeFromTag(loc->external_tag);
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
- int *destroyed, void **trace, uptr trace_size) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->mutexes.Size());
- ReportMutex *mutex = rep->mutexes[idx];
- *mutex_id = mutex->id;
- *addr = (void *)mutex->addr;
- *destroyed = mutex->destroyed;
- if (mutex->stack) CopyTrace(mutex->stack->frames, trace, trace_size);
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id,
- int *running, const char **name, int *parent_tid,
- void **trace, uptr trace_size) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->threads.Size());
- ReportThread *thread = rep->threads[idx];
- *tid = thread->id;
- *os_id = thread->os_id;
- *running = thread->running;
- *name = thread->name;
- *parent_tid = thread->parent_tid;
- if (thread->stack) CopyTrace(thread->stack->frames, trace, trace_size);
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) {
- const ReportDesc *rep = (ReportDesc *)report;
- CHECK_LT(idx, rep->unique_tids.Size());
- *tid = rep->unique_tids[idx];
- return 1;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-const char *__tsan_locate_address(uptr addr, char *name, uptr name_size,
- uptr *region_address_ptr,
- uptr *region_size_ptr) {
- uptr region_address = 0;
- uptr region_size = 0;
- const char *region_kind = nullptr;
- if (name && name_size > 0) name[0] = 0;
-
- if (IsMetaMem(addr)) {
- region_kind = "meta shadow";
- } else if (IsShadowMem(addr)) {
- region_kind = "shadow";
- } else {
- bool is_stack = false;
- MBlock *b = 0;
- Allocator *a = allocator();
- if (a->PointerIsMine((void *)addr)) {
- void *block_begin = a->GetBlockBegin((void *)addr);
- if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
- }
-
- if (b != 0) {
- region_address = (uptr)allocator()->GetBlockBegin((void *)addr);
- region_size = b->siz;
- region_kind = "heap";
- } else {
- // TODO(kuba.brecka): We should not lock. This is supposed to be called
- // from within the debugger when other threads are stopped.
- ctx->thread_registry->Lock();
- ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack);
- ctx->thread_registry->Unlock();
- if (tctx) {
- region_kind = is_stack ? "stack" : "tls";
- } else {
- region_kind = "global";
- DataInfo info;
- if (Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) {
- internal_strncpy(name, info.name, name_size);
- region_address = info.start;
- region_size = info.size;
- }
- }
- }
- }
-
- CHECK(region_kind);
- if (region_address_ptr) *region_address_ptr = region_address;
- if (region_size_ptr) *region_size_ptr = region_size;
- return region_kind;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id,
- tid_t *os_id) {
- MBlock *b = 0;
- Allocator *a = allocator();
- if (a->PointerIsMine((void *)addr)) {
- void *block_begin = a->GetBlockBegin((void *)addr);
- if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
- }
- if (b == 0) return 0;
-
- *thread_id = b->tid;
- // No locking. This is supposed to be called from within the debugger when
- // other threads are stopped.
- ThreadContextBase *tctx = ctx->thread_registry->GetThreadLocked(b->tid);
- *os_id = tctx->os_id;
-
- StackTrace stack = StackDepotGet(b->stk);
- size = Min(size, (uptr)stack.size);
- for (uptr i = 0; i < size; i++) trace[i] = stack.trace[stack.size - i - 1];
- return size;
-}
--- /dev/null
+//===-- tsan_debugging.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// TSan debugging API implementation.
+//===----------------------------------------------------------------------===//
+#include "tsan_interface.h"
+#include "tsan_report.h"
+#include "tsan_rtl.h"
+
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+using namespace __tsan;
+
+static const char *ReportTypeDescription(ReportType typ) {
+ switch (typ) {
+ case ReportTypeRace: return "data-race";
+ case ReportTypeVptrRace: return "data-race-vptr";
+ case ReportTypeUseAfterFree: return "heap-use-after-free";
+ case ReportTypeVptrUseAfterFree: return "heap-use-after-free-vptr";
+ case ReportTypeExternalRace: return "external-race";
+ case ReportTypeThreadLeak: return "thread-leak";
+ case ReportTypeMutexDestroyLocked: return "locked-mutex-destroy";
+ case ReportTypeMutexDoubleLock: return "mutex-double-lock";
+ case ReportTypeMutexInvalidAccess: return "mutex-invalid-access";
+ case ReportTypeMutexBadUnlock: return "mutex-bad-unlock";
+ case ReportTypeMutexBadReadLock: return "mutex-bad-read-lock";
+ case ReportTypeMutexBadReadUnlock: return "mutex-bad-read-unlock";
+ case ReportTypeSignalUnsafe: return "signal-unsafe-call";
+ case ReportTypeErrnoInSignal: return "errno-in-signal-handler";
+ case ReportTypeDeadlock: return "lock-order-inversion";
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
+}
+
+static const char *ReportLocationTypeDescription(ReportLocationType typ) {
+ switch (typ) {
+ case ReportLocationGlobal: return "global";
+ case ReportLocationHeap: return "heap";
+ case ReportLocationStack: return "stack";
+ case ReportLocationTLS: return "tls";
+ case ReportLocationFD: return "fd";
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
+}
+
+static void CopyTrace(SymbolizedStack *first_frame, void **trace,
+ uptr trace_size) {
+ uptr i = 0;
+ for (SymbolizedStack *frame = first_frame; frame != nullptr;
+ frame = frame->next) {
+ trace[i++] = (void *)frame->info.address;
+ if (i >= trace_size) break;
+ }
+}
+
+// Meant to be called by the debugger.
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_get_current_report() {
+ return const_cast<ReportDesc*>(cur_thread()->current_report);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_data(void *report, const char **description, int *count,
+ int *stack_count, int *mop_count, int *loc_count,
+ int *mutex_count, int *thread_count,
+ int *unique_tid_count, void **sleep_trace,
+ uptr trace_size) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ *description = ReportTypeDescription(rep->typ);
+ *count = rep->count;
+ *stack_count = rep->stacks.Size();
+ *mop_count = rep->mops.Size();
+ *loc_count = rep->locs.Size();
+ *mutex_count = rep->mutexes.Size();
+ *thread_count = rep->threads.Size();
+ *unique_tid_count = rep->unique_tids.Size();
+ if (rep->sleep) CopyTrace(rep->sleep->frames, sleep_trace, trace_size);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_tag(void *report, uptr *tag) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ *tag = rep->tag;
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_stack(void *report, uptr idx, void **trace,
+ uptr trace_size) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->stacks.Size());
+ ReportStack *stack = rep->stacks[idx];
+ if (stack) CopyTrace(stack->frames, trace, trace_size);
+ return stack ? 1 : 0;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_mop(void *report, uptr idx, int *tid, void **addr,
+ int *size, int *write, int *atomic, void **trace,
+ uptr trace_size) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->mops.Size());
+ ReportMop *mop = rep->mops[idx];
+ *tid = mop->tid;
+ *addr = (void *)mop->addr;
+ *size = mop->size;
+ *write = mop->write ? 1 : 0;
+ *atomic = mop->atomic ? 1 : 0;
+ if (mop->stack) CopyTrace(mop->stack->frames, trace, trace_size);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_loc(void *report, uptr idx, const char **type,
+ void **addr, uptr *start, uptr *size, int *tid,
+ int *fd, int *suppressable, void **trace,
+ uptr trace_size) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->locs.Size());
+ ReportLocation *loc = rep->locs[idx];
+ *type = ReportLocationTypeDescription(loc->type);
+ *addr = (void *)loc->global.start;
+ *start = loc->heap_chunk_start;
+ *size = loc->heap_chunk_size;
+ *tid = loc->tid;
+ *fd = loc->fd;
+ *suppressable = loc->suppressable;
+ if (loc->stack) CopyTrace(loc->stack->frames, trace, trace_size);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_loc_object_type(void *report, uptr idx,
+ const char **object_type) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->locs.Size());
+ ReportLocation *loc = rep->locs[idx];
+ *object_type = GetObjectTypeFromTag(loc->external_tag);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_mutex(void *report, uptr idx, uptr *mutex_id, void **addr,
+ int *destroyed, void **trace, uptr trace_size) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->mutexes.Size());
+ ReportMutex *mutex = rep->mutexes[idx];
+ *mutex_id = mutex->id;
+ *addr = (void *)mutex->addr;
+ *destroyed = mutex->destroyed;
+ if (mutex->stack) CopyTrace(mutex->stack->frames, trace, trace_size);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_thread(void *report, uptr idx, int *tid, tid_t *os_id,
+ int *running, const char **name, int *parent_tid,
+ void **trace, uptr trace_size) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->threads.Size());
+ ReportThread *thread = rep->threads[idx];
+ *tid = thread->id;
+ *os_id = thread->os_id;
+ *running = thread->running;
+ *name = thread->name;
+ *parent_tid = thread->parent_tid;
+ if (thread->stack) CopyTrace(thread->stack->frames, trace, trace_size);
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_report_unique_tid(void *report, uptr idx, int *tid) {
+ const ReportDesc *rep = (ReportDesc *)report;
+ CHECK_LT(idx, rep->unique_tids.Size());
+ *tid = rep->unique_tids[idx];
+ return 1;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+const char *__tsan_locate_address(uptr addr, char *name, uptr name_size,
+ uptr *region_address_ptr,
+ uptr *region_size_ptr) {
+ uptr region_address = 0;
+ uptr region_size = 0;
+ const char *region_kind = nullptr;
+ if (name && name_size > 0) name[0] = 0;
+
+ if (IsMetaMem(addr)) {
+ region_kind = "meta shadow";
+ } else if (IsShadowMem(addr)) {
+ region_kind = "shadow";
+ } else {
+ bool is_stack = false;
+ MBlock *b = 0;
+ Allocator *a = allocator();
+ if (a->PointerIsMine((void *)addr)) {
+ void *block_begin = a->GetBlockBegin((void *)addr);
+ if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
+ }
+
+ if (b != 0) {
+ region_address = (uptr)allocator()->GetBlockBegin((void *)addr);
+ region_size = b->siz;
+ region_kind = "heap";
+ } else {
+ // TODO(kuba.brecka): We should not lock. This is supposed to be called
+ // from within the debugger when other threads are stopped.
+ ctx->thread_registry->Lock();
+ ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack);
+ ctx->thread_registry->Unlock();
+ if (tctx) {
+ region_kind = is_stack ? "stack" : "tls";
+ } else {
+ region_kind = "global";
+ DataInfo info;
+ if (Symbolizer::GetOrInit()->SymbolizeData(addr, &info)) {
+ internal_strncpy(name, info.name, name_size);
+ region_address = info.start;
+ region_size = info.size;
+ }
+ }
+ }
+ }
+
+ CHECK(region_kind);
+ if (region_address_ptr) *region_address_ptr = region_address;
+ if (region_size_ptr) *region_size_ptr = region_size;
+ return region_kind;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_get_alloc_stack(uptr addr, uptr *trace, uptr size, int *thread_id,
+ tid_t *os_id) {
+ MBlock *b = 0;
+ Allocator *a = allocator();
+ if (a->PointerIsMine((void *)addr)) {
+ void *block_begin = a->GetBlockBegin((void *)addr);
+ if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
+ }
+ if (b == 0) return 0;
+
+ *thread_id = b->tid;
+ // No locking. This is supposed to be called from within the debugger when
+ // other threads are stopped.
+ ThreadContextBase *tctx = ctx->thread_registry->GetThreadLocked(b->tid);
+ *os_id = tctx->os_id;
+
+ StackTrace stack = StackDepotGet(b->stk);
+ size = Min(size, (uptr)stack.size);
+ for (uptr i = 0; i < size; i++) trace[i] = stack.trace[stack.size - i - 1];
+ return size;
+}
//===-- tsan_defs.h ---------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- tsan_dense_alloc.h --------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
--- /dev/null
+//===-- tsan_dispatch_defs.h ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TSAN_DISPATCH_DEFS_H
+#define TSAN_DISPATCH_DEFS_H
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+typedef struct dispatch_object_s {} *dispatch_object_t;
+
+#define DISPATCH_DECL(name) \
+ typedef struct name##_s : public dispatch_object_s {} *name##_t
+
+DISPATCH_DECL(dispatch_queue);
+DISPATCH_DECL(dispatch_source);
+DISPATCH_DECL(dispatch_group);
+DISPATCH_DECL(dispatch_data);
+DISPATCH_DECL(dispatch_semaphore);
+DISPATCH_DECL(dispatch_io);
+
+typedef void (*dispatch_function_t)(void *arg);
+typedef void (^dispatch_block_t)(void);
+typedef void (^dispatch_io_handler_t)(bool done, dispatch_data_t data,
+ int error);
+
+typedef long dispatch_once_t; // NOLINT
+typedef __sanitizer::u64 dispatch_time_t;
+typedef int dispatch_fd_t; // NOLINT
+typedef unsigned long dispatch_io_type_t; // NOLINT
+typedef unsigned long dispatch_io_close_flags_t; // NOLINT
+
+extern "C" {
+void *dispatch_get_context(dispatch_object_t object);
+void dispatch_retain(dispatch_object_t object);
+void dispatch_release(dispatch_object_t object);
+
+extern const dispatch_block_t _dispatch_data_destructor_free;
+extern const dispatch_block_t _dispatch_data_destructor_munmap;
+} // extern "C"
+
+#define DISPATCH_DATA_DESTRUCTOR_DEFAULT nullptr
+#define DISPATCH_DATA_DESTRUCTOR_FREE _dispatch_data_destructor_free
+#define DISPATCH_DATA_DESTRUCTOR_MUNMAP _dispatch_data_destructor_munmap
+
+#if __has_attribute(noescape)
+ #define DISPATCH_NOESCAPE __attribute__((__noescape__))
+#else
+ #define DISPATCH_NOESCAPE
+#endif
+
+// Data types used in dispatch APIs
+typedef unsigned long size_t; // NOLINT
+typedef unsigned long uintptr_t; // NOLINT
+typedef __sanitizer::s64 off_t;
+typedef __sanitizer::u16 mode_t;
+typedef long long_t; // NOLINT
+
+#endif // TSAN_DISPATCH_DEFS_H
+++ /dev/null
-//===-- tsan_external.cc --------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_rtl.h"
-#include "tsan_interceptors.h"
-
-namespace __tsan {
-
-#define CALLERPC ((uptr)__builtin_return_address(0))
-
-struct TagData {
- const char *object_type;
- const char *header;
-};
-
-static TagData registered_tags[kExternalTagMax] = {
- {},
- {"Swift variable", "Swift access race"},
-};
-static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable}; // NOLINT.
-static TagData *GetTagData(uptr tag) {
- // Invalid/corrupted tag? Better return NULL and let the caller deal with it.
- if (tag >= atomic_load(&used_tags, memory_order_relaxed)) return nullptr;
- return ®istered_tags[tag];
-}
-
-const char *GetObjectTypeFromTag(uptr tag) {
- TagData *tag_data = GetTagData(tag);
- return tag_data ? tag_data->object_type : nullptr;
-}
-
-const char *GetReportHeaderFromTag(uptr tag) {
- TagData *tag_data = GetTagData(tag);
- return tag_data ? tag_data->header : nullptr;
-}
-
-void InsertShadowStackFrameForTag(ThreadState *thr, uptr tag) {
- FuncEntry(thr, (uptr)®istered_tags[tag]);
-}
-
-uptr TagFromShadowStackFrame(uptr pc) {
- uptr tag_count = atomic_load(&used_tags, memory_order_relaxed);
- void *pc_ptr = (void *)pc;
- if (pc_ptr < GetTagData(0) || pc_ptr > GetTagData(tag_count - 1))
- return 0;
- return (TagData *)pc_ptr - GetTagData(0);
-}
-
-#if !SANITIZER_GO
-
-typedef void(*AccessFunc)(ThreadState *, uptr, uptr, int);
-void ExternalAccess(void *addr, void *caller_pc, void *tag, AccessFunc access) {
- CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
- ThreadState *thr = cur_thread();
- if (caller_pc) FuncEntry(thr, (uptr)caller_pc);
- InsertShadowStackFrameForTag(thr, (uptr)tag);
- bool in_ignored_lib;
- if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) {
- access(thr, CALLERPC, (uptr)addr, kSizeLog1);
- }
- FuncExit(thr);
- if (caller_pc) FuncExit(thr);
-}
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void *__tsan_external_register_tag(const char *object_type) {
- uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
- CHECK_LT(new_tag, kExternalTagMax);
- GetTagData(new_tag)->object_type = internal_strdup(object_type);
- char header[127] = {0};
- internal_snprintf(header, sizeof(header), "race on %s", object_type);
- GetTagData(new_tag)->header = internal_strdup(header);
- return (void *)new_tag;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_external_register_header(void *tag, const char *header) {
- CHECK_GE((uptr)tag, kExternalTagFirstUserAvailable);
- CHECK_LT((uptr)tag, kExternalTagMax);
- atomic_uintptr_t *header_ptr =
- (atomic_uintptr_t *)&GetTagData((uptr)tag)->header;
- header = internal_strdup(header);
- char *old_header =
- (char *)atomic_exchange(header_ptr, (uptr)header, memory_order_seq_cst);
- if (old_header) internal_free(old_header);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_external_assign_tag(void *addr, void *tag) {
- CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
- Allocator *a = allocator();
- MBlock *b = nullptr;
- if (a->PointerIsMine((void *)addr)) {
- void *block_begin = a->GetBlockBegin((void *)addr);
- if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
- }
- if (b) {
- b->tag = (uptr)tag;
- }
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
- ExternalAccess(addr, caller_pc, tag, MemoryRead);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
- ExternalAccess(addr, caller_pc, tag, MemoryWrite);
-}
-} // extern "C"
-
-#endif // !SANITIZER_GO
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_external.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_rtl.h"
+#include "tsan_interceptors.h"
+
+namespace __tsan {
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+struct TagData {
+ const char *object_type;
+ const char *header;
+};
+
+static TagData registered_tags[kExternalTagMax] = {
+ {},
+ {"Swift variable", "Swift access race"},
+};
+static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable}; // NOLINT.
+static TagData *GetTagData(uptr tag) {
+ // Invalid/corrupted tag? Better return NULL and let the caller deal with it.
+ if (tag >= atomic_load(&used_tags, memory_order_relaxed)) return nullptr;
+ return ®istered_tags[tag];
+}
+
+const char *GetObjectTypeFromTag(uptr tag) {
+ TagData *tag_data = GetTagData(tag);
+ return tag_data ? tag_data->object_type : nullptr;
+}
+
+const char *GetReportHeaderFromTag(uptr tag) {
+ TagData *tag_data = GetTagData(tag);
+ return tag_data ? tag_data->header : nullptr;
+}
+
+void InsertShadowStackFrameForTag(ThreadState *thr, uptr tag) {
+ FuncEntry(thr, (uptr)®istered_tags[tag]);
+}
+
+uptr TagFromShadowStackFrame(uptr pc) {
+ uptr tag_count = atomic_load(&used_tags, memory_order_relaxed);
+ void *pc_ptr = (void *)pc;
+ if (pc_ptr < GetTagData(0) || pc_ptr > GetTagData(tag_count - 1))
+ return 0;
+ return (TagData *)pc_ptr - GetTagData(0);
+}
+
+#if !SANITIZER_GO
+
+typedef void(*AccessFunc)(ThreadState *, uptr, uptr, int);
+void ExternalAccess(void *addr, void *caller_pc, void *tag, AccessFunc access) {
+ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+ ThreadState *thr = cur_thread();
+ if (caller_pc) FuncEntry(thr, (uptr)caller_pc);
+ InsertShadowStackFrameForTag(thr, (uptr)tag);
+ bool in_ignored_lib;
+ if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) {
+ access(thr, CALLERPC, (uptr)addr, kSizeLog1);
+ }
+ FuncExit(thr);
+ if (caller_pc) FuncExit(thr);
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_external_register_tag(const char *object_type) {
+ uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed);
+ CHECK_LT(new_tag, kExternalTagMax);
+ GetTagData(new_tag)->object_type = internal_strdup(object_type);
+ char header[127] = {0};
+ internal_snprintf(header, sizeof(header), "race on %s", object_type);
+ GetTagData(new_tag)->header = internal_strdup(header);
+ return (void *)new_tag;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_register_header(void *tag, const char *header) {
+ CHECK_GE((uptr)tag, kExternalTagFirstUserAvailable);
+ CHECK_LT((uptr)tag, kExternalTagMax);
+ atomic_uintptr_t *header_ptr =
+ (atomic_uintptr_t *)&GetTagData((uptr)tag)->header;
+ header = internal_strdup(header);
+ char *old_header =
+ (char *)atomic_exchange(header_ptr, (uptr)header, memory_order_seq_cst);
+ if (old_header) internal_free(old_header);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_assign_tag(void *addr, void *tag) {
+ CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
+ Allocator *a = allocator();
+ MBlock *b = nullptr;
+ if (a->PointerIsMine((void *)addr)) {
+ void *block_begin = a->GetBlockBegin((void *)addr);
+ if (block_begin) b = ctx->metamap.GetBlock((uptr)block_begin);
+ }
+ if (b) {
+ b->tag = (uptr)tag;
+ }
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
+ ExternalAccess(addr, caller_pc, tag, MemoryRead);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
+ ExternalAccess(addr, caller_pc, tag, MemoryWrite);
+}
+} // extern "C"
+
+#endif // !SANITIZER_GO
+
+} // namespace __tsan
+++ /dev/null
-//===-- tsan_fd.cc --------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "tsan_fd.h"
-#include "tsan_rtl.h"
-#include <sanitizer_common/sanitizer_atomic.h>
-
-namespace __tsan {
-
-const int kTableSizeL1 = 1024;
-const int kTableSizeL2 = 1024;
-const int kTableSize = kTableSizeL1 * kTableSizeL2;
-
-struct FdSync {
- atomic_uint64_t rc;
-};
-
-struct FdDesc {
- FdSync *sync;
- int creation_tid;
- u32 creation_stack;
-};
-
-struct FdContext {
- atomic_uintptr_t tab[kTableSizeL1];
- // Addresses used for synchronization.
- FdSync globsync;
- FdSync filesync;
- FdSync socksync;
- u64 connectsync;
-};
-
-static FdContext fdctx;
-
-static bool bogusfd(int fd) {
- // Apparently a bogus fd value.
- return fd < 0 || fd >= kTableSize;
-}
-
-static FdSync *allocsync(ThreadState *thr, uptr pc) {
- FdSync *s = (FdSync*)user_alloc_internal(thr, pc, sizeof(FdSync),
- kDefaultAlignment, false);
- atomic_store(&s->rc, 1, memory_order_relaxed);
- return s;
-}
-
-static FdSync *ref(FdSync *s) {
- if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)
- atomic_fetch_add(&s->rc, 1, memory_order_relaxed);
- return s;
-}
-
-static void unref(ThreadState *thr, uptr pc, FdSync *s) {
- if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {
- if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {
- CHECK_NE(s, &fdctx.globsync);
- CHECK_NE(s, &fdctx.filesync);
- CHECK_NE(s, &fdctx.socksync);
- user_free(thr, pc, s, false);
- }
- }
-}
-
-static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
- CHECK_GE(fd, 0);
- CHECK_LT(fd, kTableSize);
- atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
- uptr l1 = atomic_load(pl1, memory_order_consume);
- if (l1 == 0) {
- uptr size = kTableSizeL2 * sizeof(FdDesc);
- // We need this to reside in user memory to properly catch races on it.
- void *p = user_alloc_internal(thr, pc, size, kDefaultAlignment, false);
- internal_memset(p, 0, size);
- MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
- if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
- l1 = (uptr)p;
- else
- user_free(thr, pc, p, false);
- }
- return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT
-}
-
-// pd must be already ref'ed.
-static void init(ThreadState *thr, uptr pc, int fd, FdSync *s,
- bool write = true) {
- FdDesc *d = fddesc(thr, pc, fd);
- // As a matter of fact, we don't intercept all close calls.
- // See e.g. libc __res_iclose().
- if (d->sync) {
- unref(thr, pc, d->sync);
- d->sync = 0;
- }
- if (flags()->io_sync == 0) {
- unref(thr, pc, s);
- } else if (flags()->io_sync == 1) {
- d->sync = s;
- } else if (flags()->io_sync == 2) {
- unref(thr, pc, s);
- d->sync = &fdctx.globsync;
- }
- d->creation_tid = thr->tid;
- d->creation_stack = CurrentStackId(thr, pc);
- if (write) {
- // To catch races between fd usage and open.
- MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
- } else {
- // See the dup-related comment in FdClose.
- MemoryRead(thr, pc, (uptr)d, kSizeLog8);
- }
-}
-
-void FdInit() {
- atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
- atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
- atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
-}
-
-void FdOnFork(ThreadState *thr, uptr pc) {
- // On fork() we need to reset all fd's, because the child is going
- // close all them, and that will cause races between previous read/write
- // and the close.
- for (int l1 = 0; l1 < kTableSizeL1; l1++) {
- FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
- if (tab == 0)
- break;
- for (int l2 = 0; l2 < kTableSizeL2; l2++) {
- FdDesc *d = &tab[l2];
- MemoryResetRange(thr, pc, (uptr)d, 8);
- }
- }
-}
-
-bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
- for (int l1 = 0; l1 < kTableSizeL1; l1++) {
- FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
- if (tab == 0)
- break;
- if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
- int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
- FdDesc *d = &tab[l2];
- *fd = l1 * kTableSizeL1 + l2;
- *tid = d->creation_tid;
- *stack = d->creation_stack;
- return true;
- }
- }
- return false;
-}
-
-void FdAcquire(ThreadState *thr, uptr pc, int fd) {
- if (bogusfd(fd))
- return;
- FdDesc *d = fddesc(thr, pc, fd);
- FdSync *s = d->sync;
- DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
- MemoryRead(thr, pc, (uptr)d, kSizeLog8);
- if (s)
- Acquire(thr, pc, (uptr)s);
-}
-
-void FdRelease(ThreadState *thr, uptr pc, int fd) {
- if (bogusfd(fd))
- return;
- FdDesc *d = fddesc(thr, pc, fd);
- FdSync *s = d->sync;
- DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
- MemoryRead(thr, pc, (uptr)d, kSizeLog8);
- if (s)
- Release(thr, pc, (uptr)s);
-}
-
-void FdAccess(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- FdDesc *d = fddesc(thr, pc, fd);
- MemoryRead(thr, pc, (uptr)d, kSizeLog8);
-}
-
-void FdClose(ThreadState *thr, uptr pc, int fd, bool write) {
- DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- FdDesc *d = fddesc(thr, pc, fd);
- if (write) {
- // To catch races between fd usage and close.
- MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
- } else {
- // This path is used only by dup2/dup3 calls.
- // We do read instead of write because there is a number of legitimate
- // cases where write would lead to false positives:
- // 1. Some software dups a closed pipe in place of a socket before closing
- // the socket (to prevent races actually).
- // 2. Some daemons dup /dev/null in place of stdin/stdout.
- // On the other hand we have not seen cases when write here catches real
- // bugs.
- MemoryRead(thr, pc, (uptr)d, kSizeLog8);
- }
- // We need to clear it, because if we do not intercept any call out there
- // that creates fd, we will hit false postives.
- MemoryResetRange(thr, pc, (uptr)d, 8);
- unref(thr, pc, d->sync);
- d->sync = 0;
- d->creation_tid = 0;
- d->creation_stack = 0;
-}
-
-void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- init(thr, pc, fd, &fdctx.filesync);
-}
-
-void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) {
- DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
- if (bogusfd(oldfd) || bogusfd(newfd))
- return;
- // Ignore the case when user dups not yet connected socket.
- FdDesc *od = fddesc(thr, pc, oldfd);
- MemoryRead(thr, pc, (uptr)od, kSizeLog8);
- FdClose(thr, pc, newfd, write);
- init(thr, pc, newfd, ref(od->sync), write);
-}
-
-void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
- DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
- FdSync *s = allocsync(thr, pc);
- init(thr, pc, rfd, ref(s));
- init(thr, pc, wfd, ref(s));
- unref(thr, pc, s);
-}
-
-void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- init(thr, pc, fd, allocsync(thr, pc));
-}
-
-void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- init(thr, pc, fd, 0);
-}
-
-void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- init(thr, pc, fd, 0);
-}
-
-void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- init(thr, pc, fd, allocsync(thr, pc));
-}
-
-void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- // It can be a UDP socket.
- init(thr, pc, fd, &fdctx.socksync);
-}
-
-void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
- DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
- if (bogusfd(fd))
- return;
- // Synchronize connect->accept.
- Acquire(thr, pc, (uptr)&fdctx.connectsync);
- init(thr, pc, newfd, &fdctx.socksync);
-}
-
-void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- // Synchronize connect->accept.
- Release(thr, pc, (uptr)&fdctx.connectsync);
-}
-
-void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
- DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
- if (bogusfd(fd))
- return;
- init(thr, pc, fd, &fdctx.socksync);
-}
-
-uptr File2addr(const char *path) {
- (void)path;
- static u64 addr;
- return (uptr)&addr;
-}
-
-uptr Dir2addr(const char *path) {
- (void)path;
- static u64 addr;
- return (uptr)&addr;
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_fd.cpp -------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_fd.h"
+#include "tsan_rtl.h"
+#include <sanitizer_common/sanitizer_atomic.h>
+
+namespace __tsan {
+
+const int kTableSizeL1 = 1024;
+const int kTableSizeL2 = 1024;
+const int kTableSize = kTableSizeL1 * kTableSizeL2;
+
+struct FdSync {
+ atomic_uint64_t rc;
+};
+
+struct FdDesc {
+ FdSync *sync;
+ int creation_tid;
+ u32 creation_stack;
+};
+
+struct FdContext {
+ atomic_uintptr_t tab[kTableSizeL1];
+ // Addresses used for synchronization.
+ FdSync globsync;
+ FdSync filesync;
+ FdSync socksync;
+ u64 connectsync;
+};
+
+static FdContext fdctx;
+
+static bool bogusfd(int fd) {
+ // Apparently a bogus fd value.
+ return fd < 0 || fd >= kTableSize;
+}
+
+static FdSync *allocsync(ThreadState *thr, uptr pc) {
+ FdSync *s = (FdSync*)user_alloc_internal(thr, pc, sizeof(FdSync),
+ kDefaultAlignment, false);
+ atomic_store(&s->rc, 1, memory_order_relaxed);
+ return s;
+}
+
+static FdSync *ref(FdSync *s) {
+ if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1)
+ atomic_fetch_add(&s->rc, 1, memory_order_relaxed);
+ return s;
+}
+
+static void unref(ThreadState *thr, uptr pc, FdSync *s) {
+ if (s && atomic_load(&s->rc, memory_order_relaxed) != (u64)-1) {
+ if (atomic_fetch_sub(&s->rc, 1, memory_order_acq_rel) == 1) {
+ CHECK_NE(s, &fdctx.globsync);
+ CHECK_NE(s, &fdctx.filesync);
+ CHECK_NE(s, &fdctx.socksync);
+ user_free(thr, pc, s, false);
+ }
+ }
+}
+
+static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
+ CHECK_GE(fd, 0);
+ CHECK_LT(fd, kTableSize);
+ atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2];
+ uptr l1 = atomic_load(pl1, memory_order_consume);
+ if (l1 == 0) {
+ uptr size = kTableSizeL2 * sizeof(FdDesc);
+ // We need this to reside in user memory to properly catch races on it.
+ void *p = user_alloc_internal(thr, pc, size, kDefaultAlignment, false);
+ internal_memset(p, 0, size);
+ MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
+ if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
+ l1 = (uptr)p;
+ else
+ user_free(thr, pc, p, false);
+ }
+ return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT
+}
+
+// pd must be already ref'ed.
+static void init(ThreadState *thr, uptr pc, int fd, FdSync *s,
+ bool write = true) {
+ FdDesc *d = fddesc(thr, pc, fd);
+ // As a matter of fact, we don't intercept all close calls.
+ // See e.g. libc __res_iclose().
+ if (d->sync) {
+ unref(thr, pc, d->sync);
+ d->sync = 0;
+ }
+ if (flags()->io_sync == 0) {
+ unref(thr, pc, s);
+ } else if (flags()->io_sync == 1) {
+ d->sync = s;
+ } else if (flags()->io_sync == 2) {
+ unref(thr, pc, s);
+ d->sync = &fdctx.globsync;
+ }
+ d->creation_tid = thr->tid;
+ d->creation_stack = CurrentStackId(thr, pc);
+ if (write) {
+ // To catch races between fd usage and open.
+ MemoryRangeImitateWrite(thr, pc, (uptr)d, 8);
+ } else {
+ // See the dup-related comment in FdClose.
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ }
+}
+
+void FdInit() {
+ atomic_store(&fdctx.globsync.rc, (u64)-1, memory_order_relaxed);
+ atomic_store(&fdctx.filesync.rc, (u64)-1, memory_order_relaxed);
+ atomic_store(&fdctx.socksync.rc, (u64)-1, memory_order_relaxed);
+}
+
+void FdOnFork(ThreadState *thr, uptr pc) {
+ // On fork() we need to reset all fd's, because the child is going
+ // close all them, and that will cause races between previous read/write
+ // and the close.
+ for (int l1 = 0; l1 < kTableSizeL1; l1++) {
+ FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
+ if (tab == 0)
+ break;
+ for (int l2 = 0; l2 < kTableSizeL2; l2++) {
+ FdDesc *d = &tab[l2];
+ MemoryResetRange(thr, pc, (uptr)d, 8);
+ }
+ }
+}
+
+bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) {
+ for (int l1 = 0; l1 < kTableSizeL1; l1++) {
+ FdDesc *tab = (FdDesc*)atomic_load(&fdctx.tab[l1], memory_order_relaxed);
+ if (tab == 0)
+ break;
+ if (addr >= (uptr)tab && addr < (uptr)(tab + kTableSizeL2)) {
+ int l2 = (addr - (uptr)tab) / sizeof(FdDesc);
+ FdDesc *d = &tab[l2];
+ *fd = l1 * kTableSizeL1 + l2;
+ *tid = d->creation_tid;
+ *stack = d->creation_stack;
+ return true;
+ }
+ }
+ return false;
+}
+
+void FdAcquire(ThreadState *thr, uptr pc, int fd) {
+ if (bogusfd(fd))
+ return;
+ FdDesc *d = fddesc(thr, pc, fd);
+ FdSync *s = d->sync;
+ DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s);
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ if (s)
+ Acquire(thr, pc, (uptr)s);
+}
+
+void FdRelease(ThreadState *thr, uptr pc, int fd) {
+ if (bogusfd(fd))
+ return;
+ FdDesc *d = fddesc(thr, pc, fd);
+ FdSync *s = d->sync;
+ DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s);
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ if (s)
+ Release(thr, pc, (uptr)s);
+}
+
+void FdAccess(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ FdDesc *d = fddesc(thr, pc, fd);
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+}
+
+void FdClose(ThreadState *thr, uptr pc, int fd, bool write) {
+ DPrintf("#%d: FdClose(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ FdDesc *d = fddesc(thr, pc, fd);
+ if (write) {
+ // To catch races between fd usage and close.
+ MemoryWrite(thr, pc, (uptr)d, kSizeLog8);
+ } else {
+ // This path is used only by dup2/dup3 calls.
+ // We do read instead of write because there is a number of legitimate
+ // cases where write would lead to false positives:
+ // 1. Some software dups a closed pipe in place of a socket before closing
+ // the socket (to prevent races actually).
+ // 2. Some daemons dup /dev/null in place of stdin/stdout.
+ // On the other hand we have not seen cases when write here catches real
+ // bugs.
+ MemoryRead(thr, pc, (uptr)d, kSizeLog8);
+ }
+ // We need to clear it, because if we do not intercept any call out there
+ // that creates fd, we will hit false postives.
+ MemoryResetRange(thr, pc, (uptr)d, 8);
+ unref(thr, pc, d->sync);
+ d->sync = 0;
+ d->creation_tid = 0;
+ d->creation_stack = 0;
+}
+
+void FdFileCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ init(thr, pc, fd, &fdctx.filesync);
+}
+
+void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) {
+ DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd);
+ if (bogusfd(oldfd) || bogusfd(newfd))
+ return;
+ // Ignore the case when user dups not yet connected socket.
+ FdDesc *od = fddesc(thr, pc, oldfd);
+ MemoryRead(thr, pc, (uptr)od, kSizeLog8);
+ FdClose(thr, pc, newfd, write);
+ init(thr, pc, newfd, ref(od->sync), write);
+}
+
+void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) {
+ DPrintf("#%d: FdCreatePipe(%d, %d)\n", thr->tid, rfd, wfd);
+ FdSync *s = allocsync(thr, pc);
+ init(thr, pc, rfd, ref(s));
+ init(thr, pc, wfd, ref(s));
+ unref(thr, pc, s);
+}
+
+void FdEventCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ init(thr, pc, fd, allocsync(thr, pc));
+}
+
+void FdSignalCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ init(thr, pc, fd, 0);
+}
+
+void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ init(thr, pc, fd, 0);
+}
+
+void FdPollCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ init(thr, pc, fd, allocsync(thr, pc));
+}
+
+void FdSocketCreate(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ // It can be a UDP socket.
+ init(thr, pc, fd, &fdctx.socksync);
+}
+
+void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) {
+ DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd);
+ if (bogusfd(fd))
+ return;
+ // Synchronize connect->accept.
+ Acquire(thr, pc, (uptr)&fdctx.connectsync);
+ init(thr, pc, newfd, &fdctx.socksync);
+}
+
+void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ // Synchronize connect->accept.
+ Release(thr, pc, (uptr)&fdctx.connectsync);
+}
+
+void FdSocketConnect(ThreadState *thr, uptr pc, int fd) {
+ DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd);
+ if (bogusfd(fd))
+ return;
+ init(thr, pc, fd, &fdctx.socksync);
+}
+
+uptr File2addr(const char *path) {
+ (void)path;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+uptr Dir2addr(const char *path) {
+ (void)path;
+ static u64 addr;
+ return (uptr)&addr;
+}
+
+} // namespace __tsan
//===-- tsan_fd.h -----------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_flags.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_flag_parser.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "tsan_flags.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-#include "ubsan/ubsan_flags.h"
-
-namespace __tsan {
-
-// Can be overriden in frontend.
-#ifdef TSAN_EXTERNAL_HOOKS
-extern "C" const char* __tsan_default_options();
-#else
-SANITIZER_WEAK_DEFAULT_IMPL
-const char *__tsan_default_options() {
- return "";
-}
-#endif
-
-void Flags::SetDefaults() {
-#define TSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
-#include "tsan_flags.inc"
-#undef TSAN_FLAG
- // DDFlags
- second_deadlock_stack = false;
-}
-
-void RegisterTsanFlags(FlagParser *parser, Flags *f) {
-#define TSAN_FLAG(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &f->Name);
-#include "tsan_flags.inc"
-#undef TSAN_FLAG
- // DDFlags
- RegisterFlag(parser, "second_deadlock_stack",
- "Report where each mutex is locked in deadlock reports",
- &f->second_deadlock_stack);
-}
-
-void InitializeFlags(Flags *f, const char *env) {
- SetCommonFlagsDefaults();
- {
- // Override some common flags defaults.
- CommonFlags cf;
- cf.CopyFrom(*common_flags());
- cf.allow_addr2line = true;
- if (SANITIZER_GO) {
- // Does not work as expected for Go: runtime handles SIGABRT and crashes.
- cf.abort_on_error = false;
- // Go does not have mutexes.
- } else {
- cf.detect_deadlocks = true;
- }
- cf.print_suppressions = false;
- cf.stack_trace_format = " #%n %f %S %M";
- cf.exitcode = 66;
- cf.intercept_tls_get_addr = true;
- OverrideCommonFlags(cf);
- }
-
- f->SetDefaults();
-
- FlagParser parser;
- RegisterTsanFlags(&parser, f);
- RegisterCommonFlags(&parser);
-
-#if TSAN_CONTAINS_UBSAN
- __ubsan::Flags *uf = __ubsan::flags();
- uf->SetDefaults();
-
- FlagParser ubsan_parser;
- __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
- RegisterCommonFlags(&ubsan_parser);
-#endif
-
- // Let a frontend override.
- parser.ParseString(__tsan_default_options());
-#if TSAN_CONTAINS_UBSAN
- const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
- ubsan_parser.ParseString(ubsan_default_options);
-#endif
- // Override from command line.
- parser.ParseString(env);
-#if TSAN_CONTAINS_UBSAN
- ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
-#endif
-
- // Sanity check.
- if (!f->report_bugs) {
- f->report_thread_leaks = false;
- f->report_destroy_locked = false;
- f->report_signal_unsafe = false;
- }
-
- InitializeCommonFlags();
-
- if (Verbosity()) ReportUnrecognizedFlags();
-
- if (common_flags()->help) parser.PrintFlagDescriptions();
-
- if (f->history_size < 0 || f->history_size > 7) {
- Printf("ThreadSanitizer: incorrect value for history_size"
- " (must be [0..7])\n");
- Die();
- }
-
- if (f->io_sync < 0 || f->io_sync > 2) {
- Printf("ThreadSanitizer: incorrect value for io_sync"
- " (must be [0..2])\n");
- Die();
- }
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_flags.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_flags.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "ubsan/ubsan_flags.h"
+
+namespace __tsan {
+
+// Can be overriden in frontend.
+#ifdef TSAN_EXTERNAL_HOOKS
+extern "C" const char* __tsan_default_options();
+#else
+SANITIZER_WEAK_DEFAULT_IMPL
+const char *__tsan_default_options() {
+ return "";
+}
+#endif
+
+void Flags::SetDefaults() {
+#define TSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "tsan_flags.inc"
+#undef TSAN_FLAG
+ // DDFlags
+ second_deadlock_stack = false;
+}
+
+void RegisterTsanFlags(FlagParser *parser, Flags *f) {
+#define TSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "tsan_flags.inc"
+#undef TSAN_FLAG
+ // DDFlags
+ RegisterFlag(parser, "second_deadlock_stack",
+ "Report where each mutex is locked in deadlock reports",
+ &f->second_deadlock_stack);
+}
+
+void InitializeFlags(Flags *f, const char *env, const char *env_option_name) {
+ SetCommonFlagsDefaults();
+ {
+ // Override some common flags defaults.
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.allow_addr2line = true;
+ if (SANITIZER_GO) {
+ // Does not work as expected for Go: runtime handles SIGABRT and crashes.
+ cf.abort_on_error = false;
+ // Go does not have mutexes.
+ cf.detect_deadlocks = false;
+ }
+ cf.print_suppressions = false;
+ cf.stack_trace_format = " #%n %f %S %M";
+ cf.exitcode = 66;
+ cf.intercept_tls_get_addr = true;
+ OverrideCommonFlags(cf);
+ }
+
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterTsanFlags(&parser, f);
+ RegisterCommonFlags(&parser);
+
+#if TSAN_CONTAINS_UBSAN
+ __ubsan::Flags *uf = __ubsan::flags();
+ uf->SetDefaults();
+
+ FlagParser ubsan_parser;
+ __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
+ RegisterCommonFlags(&ubsan_parser);
+#endif
+
+ // Let a frontend override.
+ parser.ParseString(__tsan_default_options());
+#if TSAN_CONTAINS_UBSAN
+ const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
+ ubsan_parser.ParseString(ubsan_default_options);
+#endif
+ // Override from command line.
+ parser.ParseString(env, env_option_name);
+#if TSAN_CONTAINS_UBSAN
+ ubsan_parser.ParseStringFromEnv("UBSAN_OPTIONS");
+#endif
+
+ // Sanity check.
+ if (!f->report_bugs) {
+ f->report_thread_leaks = false;
+ f->report_destroy_locked = false;
+ f->report_signal_unsafe = false;
+ }
+
+ InitializeCommonFlags();
+
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+
+ if (f->history_size < 0 || f->history_size > 7) {
+ Printf("ThreadSanitizer: incorrect value for history_size"
+ " (must be [0..7])\n");
+ Die();
+ }
+
+ if (f->io_sync < 0 || f->io_sync > 2) {
+ Printf("ThreadSanitizer: incorrect value for io_sync"
+ " (must be [0..2])\n");
+ Die();
+ }
+}
+
+} // namespace __tsan
//===-- tsan_flags.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void ParseFromString(const char *str);
};
-void InitializeFlags(Flags *flags, const char *env);
+void InitializeFlags(Flags *flags, const char *env,
+ const char *env_option_name = nullptr);
} // namespace __tsan
#endif // TSAN_FLAGS_H
//===-- tsan_flags.inc ------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
TSAN_FLAG(bool, die_after_fork, true,
"Die after multi-threaded fork if the child creates new threads.")
TSAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
-TSAN_FLAG(bool, ignore_interceptors_accesses, false,
- "Ignore reads and writes from all interceptors.")
TSAN_FLAG(bool, ignore_noninstrumented_modules, SANITIZER_MAC ? true : false,
"Interceptors should only detect races when called from instrumented "
"modules.")
+++ /dev/null
-//===-- tsan_ignoreset.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_ignoreset.h"
-
-namespace __tsan {
-
-const uptr IgnoreSet::kMaxSize;
-
-IgnoreSet::IgnoreSet()
- : size_() {
-}
-
-void IgnoreSet::Add(u32 stack_id) {
- if (size_ == kMaxSize)
- return;
- for (uptr i = 0; i < size_; i++) {
- if (stacks_[i] == stack_id)
- return;
- }
- stacks_[size_++] = stack_id;
-}
-
-void IgnoreSet::Reset() {
- size_ = 0;
-}
-
-uptr IgnoreSet::Size() const {
- return size_;
-}
-
-u32 IgnoreSet::At(uptr i) const {
- CHECK_LT(i, size_);
- CHECK_LE(size_, kMaxSize);
- return stacks_[i];
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_ignoreset.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_ignoreset.h"
+
+namespace __tsan {
+
+const uptr IgnoreSet::kMaxSize;
+
+IgnoreSet::IgnoreSet()
+ : size_() {
+}
+
+void IgnoreSet::Add(u32 stack_id) {
+ if (size_ == kMaxSize)
+ return;
+ for (uptr i = 0; i < size_; i++) {
+ if (stacks_[i] == stack_id)
+ return;
+ }
+ stacks_[size_++] = stack_id;
+}
+
+void IgnoreSet::Reset() {
+ size_ = 0;
+}
+
+uptr IgnoreSet::Size() const {
+ return size_;
+}
+
+u32 IgnoreSet::At(uptr i) const {
+ CHECK_LT(i, size_);
+ CHECK_LE(size_, kMaxSize);
+ return stacks_[i];
+}
+
+} // namespace __tsan
//===-- tsan_ignoreset.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_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 ThreadSanitizer (TSan), a race detector.
-//
-// FIXME: move as many interceptors as possible into
-// sanitizer_common/sanitizer_common_interceptors.inc
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_linux.h"
-#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_common/sanitizer_platform_limits_posix.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_posix.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_tls_get_addr.h"
-#include "interception/interception.h"
-#include "tsan_interceptors.h"
-#include "tsan_interface.h"
-#include "tsan_platform.h"
-#include "tsan_suppressions.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-#include "tsan_fd.h"
-
-
-using namespace __tsan; // NOLINT
-
-#if SANITIZER_FREEBSD || SANITIZER_MAC
-#define stdout __stdoutp
-#define stderr __stderrp
-#endif
-
-#if SANITIZER_NETBSD
-#define dirfd(dirp) (*(int *)(dirp))
-#define fileno_unlocked fileno
-
-#if _LP64
-#define __sF_size 152
-#else
-#define __sF_size 88
-#endif
-
-#define stdout ((char*)&__sF + (__sF_size * 1))
-#define stderr ((char*)&__sF + (__sF_size * 2))
-
-#endif
-
-#if SANITIZER_ANDROID
-#define mallopt(a, b)
-#endif
-
-#ifdef __mips__
-const int kSigCount = 129;
-#else
-const int kSigCount = 65;
-#endif
-
-#ifdef __mips__
-struct ucontext_t {
- u64 opaque[768 / sizeof(u64) + 1];
-};
-#else
-struct ucontext_t {
- // The size is determined by looking at sizeof of real ucontext_t on linux.
- u64 opaque[936 / sizeof(u64) + 1];
-};
-#endif
-
-#if defined(__x86_64__) || defined(__mips__) || SANITIZER_PPC64V1
-#define PTHREAD_ABI_BASE "GLIBC_2.3.2"
-#elif defined(__aarch64__) || SANITIZER_PPC64V2
-#define PTHREAD_ABI_BASE "GLIBC_2.17"
-#endif
-
-extern "C" int pthread_attr_init(void *attr);
-extern "C" int pthread_attr_destroy(void *attr);
-DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *)
-extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
-extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
-extern "C" int pthread_setspecific(unsigned key, const void *v);
-DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *)
-DECLARE_REAL(int, fflush, __sanitizer_FILE *fp)
-DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-extern "C" void *pthread_self();
-extern "C" void _exit(int status);
-extern "C" int fileno_unlocked(void *stream);
-#if !SANITIZER_NETBSD
-extern "C" int dirfd(void *dirp);
-#endif
-#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_NETBSD
-extern "C" int mallopt(int param, int value);
-#endif
-#if SANITIZER_NETBSD
-extern __sanitizer_FILE __sF[];
-#else
-extern __sanitizer_FILE *stdout, *stderr;
-#endif
-#if !SANITIZER_FREEBSD && !SANITIZER_MAC && !SANITIZER_NETBSD
-const int PTHREAD_MUTEX_RECURSIVE = 1;
-const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
-#else
-const int PTHREAD_MUTEX_RECURSIVE = 2;
-const int PTHREAD_MUTEX_RECURSIVE_NP = 2;
-#endif
-#if !SANITIZER_FREEBSD && !SANITIZER_MAC && !SANITIZER_NETBSD
-const int EPOLL_CTL_ADD = 1;
-#endif
-const int SIGILL = 4;
-const int SIGABRT = 6;
-const int SIGFPE = 8;
-const int SIGSEGV = 11;
-const int SIGPIPE = 13;
-const int SIGTERM = 15;
-#if defined(__mips__) || SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD
-const int SIGBUS = 10;
-const int SIGSYS = 12;
-#else
-const int SIGBUS = 7;
-const int SIGSYS = 31;
-#endif
-void *const MAP_FAILED = (void*)-1;
-#if SANITIZER_NETBSD
-const int PTHREAD_BARRIER_SERIAL_THREAD = 1234567;
-#elif !SANITIZER_MAC
-const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
-#endif
-const int MAP_FIXED = 0x10;
-typedef long long_t; // NOLINT
-
-// From /usr/include/unistd.h
-# define F_ULOCK 0 /* Unlock a previously locked region. */
-# define F_LOCK 1 /* Lock a region for exclusive use. */
-# define F_TLOCK 2 /* Test and lock a region for exclusive use. */
-# define F_TEST 3 /* Test a region for other processes locks. */
-
-#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD
-const int SA_SIGINFO = 0x40;
-const int SIG_SETMASK = 3;
-#elif defined(__mips__)
-const int SA_SIGINFO = 8;
-const int SIG_SETMASK = 3;
-#else
-const int SA_SIGINFO = 4;
-const int SIG_SETMASK = 2;
-#endif
-
-#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED \
- (!cur_thread()->is_inited)
-
-namespace __tsan {
-struct SignalDesc {
- bool armed;
- bool sigaction;
- __sanitizer_siginfo siginfo;
- ucontext_t ctx;
-};
-
-struct ThreadSignalContext {
- int int_signal_send;
- atomic_uintptr_t in_blocking_func;
- atomic_uintptr_t have_pending_signals;
- SignalDesc pending_signals[kSigCount];
- // emptyset and oldset are too big for stack.
- __sanitizer_sigset_t emptyset;
- __sanitizer_sigset_t oldset;
-};
-
-// The sole reason tsan wraps atexit callbacks is to establish synchronization
-// between callback setup and callback execution.
-struct AtExitCtx {
- void (*f)();
- void *arg;
-};
-
-// InterceptorContext holds all global data required for interceptors.
-// It's explicitly constructed in InitializeInterceptors with placement new
-// and is never destroyed. This allows usage of members with non-trivial
-// constructors and destructors.
-struct InterceptorContext {
- // The object is 64-byte aligned, because we want hot data to be located
- // in a single cache line if possible (it's accessed in every interceptor).
- ALIGNED(64) LibIgnore libignore;
- __sanitizer_sigaction sigactions[kSigCount];
-#if !SANITIZER_MAC && !SANITIZER_NETBSD
- unsigned finalize_key;
-#endif
-
- BlockingMutex atexit_mu;
- Vector<struct AtExitCtx *> AtExitStack;
-
- InterceptorContext()
- : libignore(LINKER_INITIALIZED), AtExitStack() {
- }
-};
-
-static ALIGNED(64) char interceptor_placeholder[sizeof(InterceptorContext)];
-InterceptorContext *interceptor_ctx() {
- return reinterpret_cast<InterceptorContext*>(&interceptor_placeholder[0]);
-}
-
-LibIgnore *libignore() {
- return &interceptor_ctx()->libignore;
-}
-
-void InitializeLibIgnore() {
- const SuppressionContext &supp = *Suppressions();
- const uptr n = supp.SuppressionCount();
- for (uptr i = 0; i < n; i++) {
- const Suppression *s = supp.SuppressionAt(i);
- if (0 == internal_strcmp(s->type, kSuppressionLib))
- libignore()->AddIgnoredLibrary(s->templ);
- }
- if (flags()->ignore_noninstrumented_modules)
- libignore()->IgnoreNoninstrumentedModules(true);
- libignore()->OnLibraryLoaded(0);
-}
-
-} // namespace __tsan
-
-static ThreadSignalContext *SigCtx(ThreadState *thr) {
- ThreadSignalContext *ctx = (ThreadSignalContext*)thr->signal_ctx;
- if (ctx == 0 && !thr->is_dead) {
- ctx = (ThreadSignalContext*)MmapOrDie(sizeof(*ctx), "ThreadSignalContext");
- MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
- thr->signal_ctx = ctx;
- }
- return ctx;
-}
-
-ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
- uptr pc)
- : thr_(thr), pc_(pc), in_ignored_lib_(false), ignoring_(false) {
- Initialize(thr);
- if (!thr_->is_inited) return;
- if (!thr_->ignore_interceptors) FuncEntry(thr, pc);
- DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
- ignoring_ =
- !thr_->in_ignored_lib && (flags()->ignore_interceptors_accesses ||
- libignore()->IsIgnored(pc, &in_ignored_lib_));
- EnableIgnores();
-}
-
-ScopedInterceptor::~ScopedInterceptor() {
- if (!thr_->is_inited) return;
- DisableIgnores();
- if (!thr_->ignore_interceptors) {
- ProcessPendingSignals(thr_);
- FuncExit(thr_);
- CheckNoLocks(thr_);
- }
-}
-
-void ScopedInterceptor::EnableIgnores() {
- if (ignoring_) {
- ThreadIgnoreBegin(thr_, pc_, /*save_stack=*/false);
- if (flags()->ignore_noninstrumented_modules) thr_->suppress_reports++;
- if (in_ignored_lib_) {
- DCHECK(!thr_->in_ignored_lib);
- thr_->in_ignored_lib = true;
- }
- }
-}
-
-void ScopedInterceptor::DisableIgnores() {
- if (ignoring_) {
- ThreadIgnoreEnd(thr_, pc_);
- if (flags()->ignore_noninstrumented_modules) thr_->suppress_reports--;
- if (in_ignored_lib_) {
- DCHECK(thr_->in_ignored_lib);
- thr_->in_ignored_lib = false;
- }
- }
-}
-
-#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
-#if SANITIZER_FREEBSD
-# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
-# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(func)
-# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(func)
-#elif SANITIZER_NETBSD
-# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
-# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(func) \
- INTERCEPT_FUNCTION(__libc_##func)
-# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(func) \
- INTERCEPT_FUNCTION(__libc_thr_##func)
-#else
-# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
-# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(func)
-# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(func)
-#endif
-
-#define READ_STRING_OF_LEN(thr, pc, s, len, n) \
- MemoryAccessRange((thr), (pc), (uptr)(s), \
- common_flags()->strict_string_checks ? (len) + 1 : (n), false)
-
-#define READ_STRING(thr, pc, s, n) \
- READ_STRING_OF_LEN((thr), (pc), (s), internal_strlen(s), (n))
-
-#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
-
-struct BlockingCall {
- explicit BlockingCall(ThreadState *thr)
- : thr(thr)
- , ctx(SigCtx(thr)) {
- for (;;) {
- atomic_store(&ctx->in_blocking_func, 1, memory_order_relaxed);
- if (atomic_load(&ctx->have_pending_signals, memory_order_relaxed) == 0)
- break;
- atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
- ProcessPendingSignals(thr);
- }
- // When we are in a "blocking call", we process signals asynchronously
- // (right when they arrive). In this context we do not expect to be
- // executing any user/runtime code. The known interceptor sequence when
- // this is not true is: pthread_join -> munmap(stack). It's fine
- // to ignore munmap in this case -- we handle stack shadow separately.
- thr->ignore_interceptors++;
- }
-
- ~BlockingCall() {
- thr->ignore_interceptors--;
- atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
- }
-
- ThreadState *thr;
- ThreadSignalContext *ctx;
-};
-
-TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
- SCOPED_TSAN_INTERCEPTOR(sleep, sec);
- unsigned res = BLOCK_REAL(sleep)(sec);
- AfterSleep(thr, pc);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, usleep, long_t usec) {
- SCOPED_TSAN_INTERCEPTOR(usleep, usec);
- int res = BLOCK_REAL(usleep)(usec);
- AfterSleep(thr, pc);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) {
- SCOPED_TSAN_INTERCEPTOR(nanosleep, req, rem);
- int res = BLOCK_REAL(nanosleep)(req, rem);
- AfterSleep(thr, pc);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pause, int fake) {
- SCOPED_TSAN_INTERCEPTOR(pause, fake);
- return BLOCK_REAL(pause)(fake);
-}
-
-static void at_exit_wrapper() {
- AtExitCtx *ctx;
- {
- // Ensure thread-safety.
- BlockingMutexLock l(&interceptor_ctx()->atexit_mu);
-
- // Pop AtExitCtx from the top of the stack of callback functions
- uptr element = interceptor_ctx()->AtExitStack.Size() - 1;
- ctx = interceptor_ctx()->AtExitStack[element];
- interceptor_ctx()->AtExitStack.PopBack();
- }
-
- Acquire(cur_thread(), (uptr)0, (uptr)ctx);
- ((void(*)())ctx->f)();
- InternalFree(ctx);
-}
-
-static void cxa_at_exit_wrapper(void *arg) {
- Acquire(cur_thread(), 0, (uptr)arg);
- AtExitCtx *ctx = (AtExitCtx*)arg;
- ((void(*)(void *arg))ctx->f)(ctx->arg);
- InternalFree(ctx);
-}
-
-static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
- void *arg, void *dso);
-
-#if !SANITIZER_ANDROID
-TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return 0;
- // We want to setup the atexit callback even if we are in ignored lib
- // or after fork.
- SCOPED_INTERCEPTOR_RAW(atexit, f);
- return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0);
-}
-#endif
-
-TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return 0;
- SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso);
- return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso);
-}
-
-static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
- void *arg, void *dso) {
- AtExitCtx *ctx = (AtExitCtx*)InternalAlloc(sizeof(AtExitCtx));
- ctx->f = f;
- ctx->arg = arg;
- Release(thr, pc, (uptr)ctx);
- // Memory allocation in __cxa_atexit will race with free during exit,
- // because we do not see synchronization around atexit callback list.
- ThreadIgnoreBegin(thr, pc);
- int res;
- if (!dso) {
- // NetBSD does not preserve the 2nd argument if dso is equal to 0
- // Store ctx in a local stack-like structure
-
- // Ensure thread-safety.
- BlockingMutexLock l(&interceptor_ctx()->atexit_mu);
-
- res = REAL(__cxa_atexit)((void (*)(void *a))at_exit_wrapper, 0, 0);
- // Push AtExitCtx on the top of the stack of callback functions
- if (!res) {
- interceptor_ctx()->AtExitStack.PushBack(ctx);
- }
- } else {
- res = REAL(__cxa_atexit)(cxa_at_exit_wrapper, ctx, dso);
- }
- ThreadIgnoreEnd(thr, pc);
- return res;
-}
-
-#if !SANITIZER_MAC && !SANITIZER_NETBSD
-static void on_exit_wrapper(int status, void *arg) {
- ThreadState *thr = cur_thread();
- uptr pc = 0;
- Acquire(thr, pc, (uptr)arg);
- AtExitCtx *ctx = (AtExitCtx*)arg;
- ((void(*)(int status, void *arg))ctx->f)(status, ctx->arg);
- InternalFree(ctx);
-}
-
-TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return 0;
- SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
- AtExitCtx *ctx = (AtExitCtx*)InternalAlloc(sizeof(AtExitCtx));
- ctx->f = (void(*)())f;
- ctx->arg = arg;
- Release(thr, pc, (uptr)ctx);
- // Memory allocation in __cxa_atexit will race with free during exit,
- // because we do not see synchronization around atexit callback list.
- ThreadIgnoreBegin(thr, pc);
- int res = REAL(on_exit)(on_exit_wrapper, ctx);
- ThreadIgnoreEnd(thr, pc);
- return res;
-}
-#define TSAN_MAYBE_INTERCEPT_ON_EXIT TSAN_INTERCEPT(on_exit)
-#else
-#define TSAN_MAYBE_INTERCEPT_ON_EXIT
-#endif
-
-// Cleanup old bufs.
-static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
- for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
- JmpBuf *buf = &thr->jmp_bufs[i];
- if (buf->sp <= sp) {
- uptr sz = thr->jmp_bufs.Size();
- internal_memcpy(buf, &thr->jmp_bufs[sz - 1], sizeof(*buf));
- thr->jmp_bufs.PopBack();
- i--;
- }
- }
-}
-
-static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
- if (!thr->is_inited) // called from libc guts during bootstrap
- return;
- // Cleanup old bufs.
- JmpBufGarbageCollect(thr, sp);
- // Remember the buf.
- JmpBuf *buf = thr->jmp_bufs.PushBack();
- buf->sp = sp;
- buf->mangled_sp = mangled_sp;
- buf->shadow_stack_pos = thr->shadow_stack_pos;
- ThreadSignalContext *sctx = SigCtx(thr);
- buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
- buf->in_blocking_func = sctx ?
- atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
- false;
- buf->in_signal_handler = atomic_load(&thr->in_signal_handler,
- memory_order_relaxed);
-}
-
-static void LongJmp(ThreadState *thr, uptr *env) {
-#ifdef __powerpc__
- uptr mangled_sp = env[0];
-#elif SANITIZER_FREEBSD
- uptr mangled_sp = env[2];
-#elif SANITIZER_NETBSD
- uptr mangled_sp = env[6];
-#elif SANITIZER_MAC
-# ifdef __aarch64__
- uptr mangled_sp =
- (GetMacosVersion() >= MACOS_VERSION_MOJAVE) ? env[12] : env[13];
-# else
- uptr mangled_sp = env[2];
-# endif
-#elif SANITIZER_LINUX
-# ifdef __aarch64__
- uptr mangled_sp = env[13];
-# elif defined(__mips64)
- uptr mangled_sp = env[1];
-# else
- uptr mangled_sp = env[6];
-# endif
-#endif
- // Find the saved buf by mangled_sp.
- for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
- JmpBuf *buf = &thr->jmp_bufs[i];
- if (buf->mangled_sp == mangled_sp) {
- CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos);
- // Unwind the stack.
- while (thr->shadow_stack_pos > buf->shadow_stack_pos)
- FuncExit(thr);
- ThreadSignalContext *sctx = SigCtx(thr);
- if (sctx) {
- sctx->int_signal_send = buf->int_signal_send;
- atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
- memory_order_relaxed);
- }
- atomic_store(&thr->in_signal_handler, buf->in_signal_handler,
- memory_order_relaxed);
- JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp
- return;
- }
- }
- Printf("ThreadSanitizer: can't find longjmp buf\n");
- CHECK(0);
-}
-
-// FIXME: put everything below into a common extern "C" block?
-extern "C" void __tsan_setjmp(uptr sp, uptr mangled_sp) {
- SetJmp(cur_thread(), sp, mangled_sp);
-}
-
-#if SANITIZER_MAC
-TSAN_INTERCEPTOR(int, setjmp, void *env);
-TSAN_INTERCEPTOR(int, _setjmp, void *env);
-TSAN_INTERCEPTOR(int, sigsetjmp, void *env);
-#else // SANITIZER_MAC
-
-#if SANITIZER_NETBSD
-#define setjmp_symname __setjmp14
-#define sigsetjmp_symname __sigsetjmp14
-#else
-#define setjmp_symname setjmp
-#define sigsetjmp_symname sigsetjmp
-#endif
-
-#define TSAN_INTERCEPTOR_SETJMP_(x) __interceptor_ ## x
-#define TSAN_INTERCEPTOR_SETJMP__(x) TSAN_INTERCEPTOR_SETJMP_(x)
-#define TSAN_INTERCEPTOR_SETJMP TSAN_INTERCEPTOR_SETJMP__(setjmp_symname)
-#define TSAN_INTERCEPTOR_SIGSETJMP TSAN_INTERCEPTOR_SETJMP__(sigsetjmp_symname)
-
-#define TSAN_STRING_SETJMP SANITIZER_STRINGIFY(setjmp_symname)
-#define TSAN_STRING_SIGSETJMP SANITIZER_STRINGIFY(sigsetjmp_symname)
-
-// Not called. Merely to satisfy TSAN_INTERCEPT().
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-int TSAN_INTERCEPTOR_SETJMP(void *env);
-extern "C" int TSAN_INTERCEPTOR_SETJMP(void *env) {
- CHECK(0);
- return 0;
-}
-
-// FIXME: any reason to have a separate declaration?
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-int __interceptor__setjmp(void *env);
-extern "C" int __interceptor__setjmp(void *env) {
- CHECK(0);
- return 0;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-int TSAN_INTERCEPTOR_SIGSETJMP(void *env);
-extern "C" int TSAN_INTERCEPTOR_SIGSETJMP(void *env) {
- CHECK(0);
- return 0;
-}
-
-#if !SANITIZER_NETBSD
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-int __interceptor___sigsetjmp(void *env);
-extern "C" int __interceptor___sigsetjmp(void *env) {
- CHECK(0);
- return 0;
-}
-#endif
-
-extern "C" int setjmp_symname(void *env);
-extern "C" int _setjmp(void *env);
-extern "C" int sigsetjmp_symname(void *env);
-#if !SANITIZER_NETBSD
-extern "C" int __sigsetjmp(void *env);
-#endif
-DEFINE_REAL(int, setjmp_symname, void *env)
-DEFINE_REAL(int, _setjmp, void *env)
-DEFINE_REAL(int, sigsetjmp_symname, void *env)
-#if !SANITIZER_NETBSD
-DEFINE_REAL(int, __sigsetjmp, void *env)
-#endif
-#endif // SANITIZER_MAC
-
-#if SANITIZER_NETBSD
-#define longjmp_symname __longjmp14
-#define siglongjmp_symname __siglongjmp14
-#else
-#define longjmp_symname longjmp
-#define siglongjmp_symname siglongjmp
-#endif
-
-TSAN_INTERCEPTOR(void, longjmp_symname, uptr *env, int val) {
- // Note: if we call REAL(longjmp) in the context of ScopedInterceptor,
- // bad things will happen. We will jump over ScopedInterceptor dtor and can
- // leave thr->in_ignored_lib set.
- {
- SCOPED_INTERCEPTOR_RAW(longjmp_symname, env, val);
- }
- LongJmp(cur_thread(), env);
- REAL(longjmp_symname)(env, val);
-}
-
-TSAN_INTERCEPTOR(void, siglongjmp_symname, uptr *env, int val) {
- {
- SCOPED_INTERCEPTOR_RAW(siglongjmp_symname, env, val);
- }
- LongJmp(cur_thread(), env);
- REAL(siglongjmp_symname)(env, val);
-}
-
-#if SANITIZER_NETBSD
-TSAN_INTERCEPTOR(void, _longjmp, uptr *env, int val) {
- {
- SCOPED_INTERCEPTOR_RAW(_longjmp, env, val);
- }
- LongJmp(cur_thread(), env);
- REAL(_longjmp)(env, val);
-}
-#endif
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(void*, malloc, uptr size) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalAlloc(size);
- void *p = 0;
- {
- SCOPED_INTERCEPTOR_RAW(malloc, size);
- p = user_alloc(thr, pc, size);
- }
- invoke_malloc_hook(p, size);
- return p;
-}
-
-TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
- SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz);
- return user_memalign(thr, pc, align, sz);
-}
-
-TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalCalloc(size, n);
- void *p = 0;
- {
- SCOPED_INTERCEPTOR_RAW(calloc, size, n);
- p = user_calloc(thr, pc, size, n);
- }
- invoke_malloc_hook(p, n * size);
- return p;
-}
-
-TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalRealloc(p, size);
- if (p)
- invoke_free_hook(p);
- {
- SCOPED_INTERCEPTOR_RAW(realloc, p, size);
- p = user_realloc(thr, pc, p, size);
- }
- invoke_malloc_hook(p, size);
- return p;
-}
-
-TSAN_INTERCEPTOR(void, free, void *p) {
- if (p == 0)
- return;
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalFree(p);
- invoke_free_hook(p);
- SCOPED_INTERCEPTOR_RAW(free, p);
- user_free(thr, pc, p);
-}
-
-TSAN_INTERCEPTOR(void, cfree, void *p) {
- if (p == 0)
- return;
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalFree(p);
- invoke_free_hook(p);
- SCOPED_INTERCEPTOR_RAW(cfree, p);
- user_free(thr, pc, p);
-}
-
-TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
- SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
- return user_alloc_usable_size(p);
-}
-#endif
-
-TSAN_INTERCEPTOR(char*, strcpy, char *dst, const char *src) { // NOLINT
- SCOPED_TSAN_INTERCEPTOR(strcpy, dst, src); // NOLINT
- uptr srclen = internal_strlen(src);
- MemoryAccessRange(thr, pc, (uptr)dst, srclen + 1, true);
- MemoryAccessRange(thr, pc, (uptr)src, srclen + 1, false);
- return REAL(strcpy)(dst, src); // NOLINT
-}
-
-TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) {
- SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n);
- uptr srclen = internal_strnlen(src, n);
- MemoryAccessRange(thr, pc, (uptr)dst, n, true);
- MemoryAccessRange(thr, pc, (uptr)src, min(srclen + 1, n), false);
- return REAL(strncpy)(dst, src, n);
-}
-
-TSAN_INTERCEPTOR(char*, strdup, const char *str) {
- SCOPED_TSAN_INTERCEPTOR(strdup, str);
- // strdup will call malloc, so no instrumentation is required here.
- return REAL(strdup)(str);
-}
-
-static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
- if (*addr) {
- if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) {
- if (flags & MAP_FIXED) {
- errno = errno_EINVAL;
- return false;
- } else {
- *addr = 0;
- }
- }
- }
- return true;
-}
-
-template <class Mmap>
-static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap,
- void *addr, SIZE_T sz, int prot, int flags,
- int fd, OFF64_T off) {
- if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED;
- void *res = real_mmap(addr, sz, prot, flags, fd, off);
- if (res != MAP_FAILED) {
- if (fd > 0) FdAccess(thr, pc, fd);
- if (thr->ignore_reads_and_writes == 0)
- MemoryRangeImitateWrite(thr, pc, (uptr)res, sz);
- else
- MemoryResetRange(thr, pc, (uptr)res, sz);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
- SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
- if (sz != 0) {
- // If sz == 0, munmap will return EINVAL and don't unmap any memory.
- DontNeedShadowFor((uptr)addr, sz);
- ScopedGlobalProcessor sgp;
- ctx->metamap.ResetRange(thr->proc(), (uptr)addr, (uptr)sz);
- }
- int res = REAL(munmap)(addr, sz);
- return res;
-}
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
- SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
- return user_memalign(thr, pc, align, sz);
-}
-#define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign)
-#else
-#define TSAN_MAYBE_INTERCEPT_MEMALIGN
-#endif
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalAlloc(sz, nullptr, align);
- SCOPED_INTERCEPTOR_RAW(aligned_alloc, align, sz);
- return user_aligned_alloc(thr, pc, align, sz);
-}
-
-TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return InternalAlloc(sz, nullptr, GetPageSizeCached());
- SCOPED_INTERCEPTOR_RAW(valloc, sz);
- return user_valloc(thr, pc, sz);
-}
-#endif
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
- if (UNLIKELY(cur_thread()->in_symbolizer)) {
- uptr PageSize = GetPageSizeCached();
- sz = sz ? RoundUpTo(sz, PageSize) : PageSize;
- return InternalAlloc(sz, nullptr, PageSize);
- }
- SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
- return user_pvalloc(thr, pc, sz);
-}
-#define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc)
-#else
-#define TSAN_MAYBE_INTERCEPT_PVALLOC
-#endif
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
- if (UNLIKELY(cur_thread()->in_symbolizer)) {
- void *p = InternalAlloc(sz, nullptr, align);
- if (!p)
- return errno_ENOMEM;
- *memptr = p;
- return 0;
- }
- SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
- return user_posix_memalign(thr, pc, memptr, align, sz);
-}
-#endif
-
-// __cxa_guard_acquire and friends need to be intercepted in a special way -
-// regular interceptors will break statically-linked libstdc++. Linux
-// interceptors are especially defined as weak functions (so that they don't
-// cause link errors when user defines them as well). So they silently
-// auto-disable themselves when such symbol is already present in the binary. If
-// we link libstdc++ statically, it will bring own __cxa_guard_acquire which
-// will silently replace our interceptor. That's why on Linux we simply export
-// these interceptors with INTERFACE_ATTRIBUTE.
-// On OS X, we don't support statically linking, so we just use a regular
-// interceptor.
-#if SANITIZER_MAC
-#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
-#else
-#define STDCXX_INTERCEPTOR(rettype, name, ...) \
- extern "C" rettype INTERFACE_ATTRIBUTE name(__VA_ARGS__)
-#endif
-
-// Used in thread-safe function static initialization.
-STDCXX_INTERCEPTOR(int, __cxa_guard_acquire, atomic_uint32_t *g) {
- SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g);
- for (;;) {
- u32 cmp = atomic_load(g, memory_order_acquire);
- if (cmp == 0) {
- if (atomic_compare_exchange_strong(g, &cmp, 1<<16, memory_order_relaxed))
- return 1;
- } else if (cmp == 1) {
- Acquire(thr, pc, (uptr)g);
- return 0;
- } else {
- internal_sched_yield();
- }
- }
-}
-
-STDCXX_INTERCEPTOR(void, __cxa_guard_release, atomic_uint32_t *g) {
- SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g);
- Release(thr, pc, (uptr)g);
- atomic_store(g, 1, memory_order_release);
-}
-
-STDCXX_INTERCEPTOR(void, __cxa_guard_abort, atomic_uint32_t *g) {
- SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g);
- atomic_store(g, 0, memory_order_relaxed);
-}
-
-namespace __tsan {
-void DestroyThreadState() {
- ThreadState *thr = cur_thread();
- Processor *proc = thr->proc();
- ThreadFinish(thr);
- ProcUnwire(proc, thr);
- ProcDestroy(proc);
- ThreadSignalContext *sctx = thr->signal_ctx;
- if (sctx) {
- thr->signal_ctx = 0;
- UnmapOrDie(sctx, sizeof(*sctx));
- }
- DTLS_Destroy();
- cur_thread_finalize();
-}
-} // namespace __tsan
-
-#if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD
-static void thread_finalize(void *v) {
- uptr iter = (uptr)v;
- if (iter > 1) {
- if (pthread_setspecific(interceptor_ctx()->finalize_key,
- (void*)(iter - 1))) {
- Printf("ThreadSanitizer: failed to set thread key\n");
- Die();
- }
- return;
- }
- DestroyThreadState();
-}
-#endif
-
-
-struct ThreadParam {
- void* (*callback)(void *arg);
- void *param;
- atomic_uintptr_t tid;
-};
-
-extern "C" void *__tsan_thread_start_func(void *arg) {
- ThreadParam *p = (ThreadParam*)arg;
- void* (*callback)(void *arg) = p->callback;
- void *param = p->param;
- int tid = 0;
- {
- ThreadState *thr = cur_thread();
- // Thread-local state is not initialized yet.
- ScopedIgnoreInterceptors ignore;
-#if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD
- ThreadIgnoreBegin(thr, 0);
- if (pthread_setspecific(interceptor_ctx()->finalize_key,
- (void *)GetPthreadDestructorIterations())) {
- Printf("ThreadSanitizer: failed to set thread key\n");
- Die();
- }
- ThreadIgnoreEnd(thr, 0);
-#endif
- while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
- internal_sched_yield();
- Processor *proc = ProcCreate();
- ProcWire(proc, thr);
- ThreadStart(thr, tid, GetTid(), /*workerthread*/ false);
- atomic_store(&p->tid, 0, memory_order_release);
- }
- void *res = callback(param);
- // Prevent the callback from being tail called,
- // it mixes up stack traces.
- volatile int foo = 42;
- foo++;
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_create,
- void *th, void *attr, void *(*callback)(void*), void * param) {
- SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param);
-
- MaybeSpawnBackgroundThread();
-
- if (ctx->after_multithreaded_fork) {
- if (flags()->die_after_fork) {
- Report("ThreadSanitizer: starting new threads after multi-threaded "
- "fork is not supported. Dying (set die_after_fork=0 to override)\n");
- Die();
- } else {
- VPrintf(1, "ThreadSanitizer: starting new threads after multi-threaded "
- "fork is not supported (pid %d). Continuing because of "
- "die_after_fork=0, but you are on your own\n", internal_getpid());
- }
- }
- __sanitizer_pthread_attr_t myattr;
- if (attr == 0) {
- pthread_attr_init(&myattr);
- attr = &myattr;
- }
- int detached = 0;
- REAL(pthread_attr_getdetachstate)(attr, &detached);
- AdjustStackSize(attr);
-
- ThreadParam p;
- p.callback = callback;
- p.param = param;
- atomic_store(&p.tid, 0, memory_order_relaxed);
- int res = -1;
- {
- // Otherwise we see false positives in pthread stack manipulation.
- ScopedIgnoreInterceptors ignore;
- ThreadIgnoreBegin(thr, pc);
- res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
- ThreadIgnoreEnd(thr, pc);
- }
- if (res == 0) {
- int tid = ThreadCreate(thr, pc, *(uptr*)th, IsStateDetached(detached));
- CHECK_NE(tid, 0);
- // Synchronization on p.tid serves two purposes:
- // 1. ThreadCreate must finish before the new thread starts.
- // Otherwise the new thread can call pthread_detach, but the pthread_t
- // identifier is not yet registered in ThreadRegistry by ThreadCreate.
- // 2. ThreadStart must finish before this thread continues.
- // Otherwise, this thread can call pthread_detach and reset thr->sync
- // before the new thread got a chance to acquire from it in ThreadStart.
- atomic_store(&p.tid, tid, memory_order_release);
- while (atomic_load(&p.tid, memory_order_acquire) != 0)
- internal_sched_yield();
- }
- if (attr == &myattr)
- pthread_attr_destroy(&myattr);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
- SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
- int tid = ThreadTid(thr, pc, (uptr)th);
- ThreadIgnoreBegin(thr, pc);
- int res = BLOCK_REAL(pthread_join)(th, ret);
- ThreadIgnoreEnd(thr, pc);
- if (res == 0) {
- ThreadJoin(thr, pc, tid);
- }
- return res;
-}
-
-DEFINE_REAL_PTHREAD_FUNCTIONS
-
-TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
- SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
- int tid = ThreadTid(thr, pc, (uptr)th);
- int res = REAL(pthread_detach)(th);
- if (res == 0) {
- ThreadDetach(thr, pc, tid);
- }
- return res;
-}
-
-// Problem:
-// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
-// pthread_cond_t has different size in the different versions.
-// If call new REAL functions for old pthread_cond_t, they will corrupt memory
-// after pthread_cond_t (old cond is smaller).
-// If we call old REAL functions for new pthread_cond_t, we will lose some
-// functionality (e.g. old functions do not support waiting against
-// CLOCK_REALTIME).
-// Proper handling would require to have 2 versions of interceptors as well.
-// But this is messy, in particular requires linker scripts when sanitizer
-// runtime is linked into a shared library.
-// Instead we assume we don't have dynamic libraries built against old
-// pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag
-// that allows to work with old libraries (but this mode does not support
-// some features, e.g. pthread_condattr_getpshared).
-static void *init_cond(void *c, bool force = false) {
- // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions.
- // So we allocate additional memory on the side large enough to hold
- // any pthread_cond_t object. Always call new REAL functions, but pass
- // the aux object to them.
- // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes
- // first word of pthread_cond_t to zero.
- // It's all relevant only for linux.
- if (!common_flags()->legacy_pthread_cond)
- return c;
- atomic_uintptr_t *p = (atomic_uintptr_t*)c;
- uptr cond = atomic_load(p, memory_order_acquire);
- if (!force && cond != 0)
- return (void*)cond;
- void *newcond = WRAP(malloc)(pthread_cond_t_sz);
- internal_memset(newcond, 0, pthread_cond_t_sz);
- if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
- memory_order_acq_rel))
- return newcond;
- WRAP(free)(newcond);
- return (void*)cond;
-}
-
-struct CondMutexUnlockCtx {
- ScopedInterceptor *si;
- ThreadState *thr;
- uptr pc;
- void *m;
-};
-
-static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
- // pthread_cond_wait interceptor has enabled async signal delivery
- // (see BlockingCall below). Disable async signals since we are running
- // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run
- // since the thread is cancelled, so we have to manually execute them
- // (the thread still can run some user code due to pthread_cleanup_push).
- ThreadSignalContext *ctx = SigCtx(arg->thr);
- CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1);
- atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
- MutexPostLock(arg->thr, arg->pc, (uptr)arg->m, MutexFlagDoPreLockOnPostLock);
- // Undo BlockingCall ctor effects.
- arg->thr->ignore_interceptors--;
- arg->si->~ScopedInterceptor();
-}
-
-INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
- void *cond = init_cond(c, true);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, cond, a);
- MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
- return REAL(pthread_cond_init)(cond, a);
-}
-
-static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si,
- int (*fn)(void *c, void *m, void *abstime), void *c,
- void *m, void *t) {
- MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
- MutexUnlock(thr, pc, (uptr)m);
- CondMutexUnlockCtx arg = {si, thr, pc, m};
- int res = 0;
- // This ensures that we handle mutex lock even in case of pthread_cancel.
- // See test/tsan/cond_cancel.cc.
- {
- // Enable signal delivery while the thread is blocked.
- BlockingCall bc(thr);
- res = call_pthread_cancel_with_cleanup(
- fn, c, m, t, (void (*)(void *arg))cond_mutex_unlock, &arg);
- }
- if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m);
- MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock);
- return res;
-}
-
-INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
- void *cond = init_cond(c);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m);
- return cond_wait(thr, pc, &si, (int (*)(void *c, void *m, void *abstime))REAL(
- pthread_cond_wait),
- cond, m, 0);
-}
-
-INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
- void *cond = init_cond(c);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime);
- return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait), cond, m,
- abstime);
-}
-
-#if SANITIZER_MAC
-INTERCEPTOR(int, pthread_cond_timedwait_relative_np, void *c, void *m,
- void *reltime) {
- void *cond = init_cond(c);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait_relative_np, cond, m, reltime);
- return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait_relative_np), cond,
- m, reltime);
-}
-#endif
-
-INTERCEPTOR(int, pthread_cond_signal, void *c) {
- void *cond = init_cond(c);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, cond);
- MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
- return REAL(pthread_cond_signal)(cond);
-}
-
-INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
- void *cond = init_cond(c);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, cond);
- MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
- return REAL(pthread_cond_broadcast)(cond);
-}
-
-INTERCEPTOR(int, pthread_cond_destroy, void *c) {
- void *cond = init_cond(c);
- SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, cond);
- MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
- int res = REAL(pthread_cond_destroy)(cond);
- if (common_flags()->legacy_pthread_cond) {
- // Free our aux cond and zero the pointer to not leave dangling pointers.
- WRAP(free)(cond);
- atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
- SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a);
- int res = REAL(pthread_mutex_init)(m, a);
- if (res == 0) {
- u32 flagz = 0;
- if (a) {
- int type = 0;
- if (REAL(pthread_mutexattr_gettype)(a, &type) == 0)
- if (type == PTHREAD_MUTEX_RECURSIVE ||
- type == PTHREAD_MUTEX_RECURSIVE_NP)
- flagz |= MutexFlagWriteReentrant;
- }
- MutexCreate(thr, pc, (uptr)m, flagz);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m);
- int res = REAL(pthread_mutex_destroy)(m);
- if (res == 0 || res == errno_EBUSY) {
- MutexDestroy(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
- int res = REAL(pthread_mutex_trylock)(m);
- if (res == errno_EOWNERDEAD)
- MutexRepair(thr, pc, (uptr)m);
- if (res == 0 || res == errno_EOWNERDEAD)
- MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
- return res;
-}
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
- SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
- int res = REAL(pthread_mutex_timedlock)(m, abstime);
- if (res == 0) {
- MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
- }
- return res;
-}
-#endif
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
- SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
- int res = REAL(pthread_spin_init)(m, pshared);
- if (res == 0) {
- MutexCreate(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m);
- int res = REAL(pthread_spin_destroy)(m);
- if (res == 0) {
- MutexDestroy(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
- MutexPreLock(thr, pc, (uptr)m);
- int res = REAL(pthread_spin_lock)(m);
- if (res == 0) {
- MutexPostLock(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
- int res = REAL(pthread_spin_trylock)(m);
- if (res == 0) {
- MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m);
- MutexUnlock(thr, pc, (uptr)m);
- int res = REAL(pthread_spin_unlock)(m);
- return res;
-}
-#endif
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
- int res = REAL(pthread_rwlock_init)(m, a);
- if (res == 0) {
- MutexCreate(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m);
- int res = REAL(pthread_rwlock_destroy)(m);
- if (res == 0) {
- MutexDestroy(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m);
- MutexPreReadLock(thr, pc, (uptr)m);
- int res = REAL(pthread_rwlock_rdlock)(m);
- if (res == 0) {
- MutexPostReadLock(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
- int res = REAL(pthread_rwlock_tryrdlock)(m);
- if (res == 0) {
- MutexPostReadLock(thr, pc, (uptr)m, MutexFlagTryLock);
- }
- return res;
-}
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
- int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
- if (res == 0) {
- MutexPostReadLock(thr, pc, (uptr)m);
- }
- return res;
-}
-#endif
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
- MutexPreLock(thr, pc, (uptr)m);
- int res = REAL(pthread_rwlock_wrlock)(m);
- if (res == 0) {
- MutexPostLock(thr, pc, (uptr)m);
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
- int res = REAL(pthread_rwlock_trywrlock)(m);
- if (res == 0) {
- MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
- }
- return res;
-}
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
- int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
- if (res == 0) {
- MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
- }
- return res;
-}
-#endif
-
-TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
- SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
- MutexReadOrWriteUnlock(thr, pc, (uptr)m);
- int res = REAL(pthread_rwlock_unlock)(m);
- return res;
-}
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
- SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
- MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
- int res = REAL(pthread_barrier_init)(b, a, count);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) {
- SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b);
- MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
- int res = REAL(pthread_barrier_destroy)(b);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
- SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b);
- Release(thr, pc, (uptr)b);
- MemoryRead(thr, pc, (uptr)b, kSizeLog1);
- int res = REAL(pthread_barrier_wait)(b);
- MemoryRead(thr, pc, (uptr)b, kSizeLog1);
- if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) {
- Acquire(thr, pc, (uptr)b);
- }
- return res;
-}
-#endif
-
-TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
- SCOPED_INTERCEPTOR_RAW(pthread_once, o, f);
- if (o == 0 || f == 0)
- return errno_EINVAL;
- atomic_uint32_t *a;
-
- if (SANITIZER_MAC)
- a = static_cast<atomic_uint32_t*>((void *)((char *)o + sizeof(long_t)));
- else if (SANITIZER_NETBSD)
- a = static_cast<atomic_uint32_t*>
- ((void *)((char *)o + __sanitizer::pthread_mutex_t_sz));
- else
- a = static_cast<atomic_uint32_t*>(o);
-
- u32 v = atomic_load(a, memory_order_acquire);
- if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
- memory_order_relaxed)) {
- (*f)();
- if (!thr->in_ignored_lib)
- Release(thr, pc, (uptr)o);
- atomic_store(a, 2, memory_order_release);
- } else {
- while (v != 2) {
- internal_sched_yield();
- v = atomic_load(a, memory_order_acquire);
- }
- if (!thr->in_ignored_lib)
- Acquire(thr, pc, (uptr)o);
- }
- return 0;
-}
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
- SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(__fxstat)(version, fd, buf);
-}
-#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat)
-#else
-#define TSAN_MAYBE_INTERCEPT___FXSTAT
-#endif
-
-TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
-#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID || SANITIZER_NETBSD
- SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(fstat)(fd, buf);
-#else
- SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(__fxstat)(0, fd, buf);
-#endif
-}
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
- SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(__fxstat64)(version, fd, buf);
-}
-#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64)
-#else
-#define TSAN_MAYBE_INTERCEPT___FXSTAT64
-#endif
-
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
- SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
- if (fd > 0)
- FdAccess(thr, pc, fd);
- return REAL(__fxstat64)(0, fd, buf);
-}
-#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
-#else
-#define TSAN_MAYBE_INTERCEPT_FSTAT64
-#endif
-
-TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
- SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
- READ_STRING(thr, pc, name, 0);
- int fd = REAL(open)(name, flags, mode);
- if (fd >= 0)
- FdFileCreate(thr, pc, fd);
- return fd;
-}
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
- SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
- READ_STRING(thr, pc, name, 0);
- int fd = REAL(open64)(name, flags, mode);
- if (fd >= 0)
- FdFileCreate(thr, pc, fd);
- return fd;
-}
-#define TSAN_MAYBE_INTERCEPT_OPEN64 TSAN_INTERCEPT(open64)
-#else
-#define TSAN_MAYBE_INTERCEPT_OPEN64
-#endif
-
-TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
- SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
- READ_STRING(thr, pc, name, 0);
- int fd = REAL(creat)(name, mode);
- if (fd >= 0)
- FdFileCreate(thr, pc, fd);
- return fd;
-}
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
- SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
- READ_STRING(thr, pc, name, 0);
- int fd = REAL(creat64)(name, mode);
- if (fd >= 0)
- FdFileCreate(thr, pc, fd);
- return fd;
-}
-#define TSAN_MAYBE_INTERCEPT_CREAT64 TSAN_INTERCEPT(creat64)
-#else
-#define TSAN_MAYBE_INTERCEPT_CREAT64
-#endif
-
-TSAN_INTERCEPTOR(int, dup, int oldfd) {
- SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
- int newfd = REAL(dup)(oldfd);
- if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
- FdDup(thr, pc, oldfd, newfd, true);
- return newfd;
-}
-
-TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
- SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
- int newfd2 = REAL(dup2)(oldfd, newfd);
- if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
- FdDup(thr, pc, oldfd, newfd2, false);
- return newfd2;
-}
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
- SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
- int newfd2 = REAL(dup3)(oldfd, newfd, flags);
- if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
- FdDup(thr, pc, oldfd, newfd2, false);
- return newfd2;
-}
-#endif
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
- SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
- int fd = REAL(eventfd)(initval, flags);
- if (fd >= 0)
- FdEventCreate(thr, pc, fd);
- return fd;
-}
-#define TSAN_MAYBE_INTERCEPT_EVENTFD TSAN_INTERCEPT(eventfd)
-#else
-#define TSAN_MAYBE_INTERCEPT_EVENTFD
-#endif
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
- SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
- if (fd >= 0)
- FdClose(thr, pc, fd);
- fd = REAL(signalfd)(fd, mask, flags);
- if (fd >= 0)
- FdSignalCreate(thr, pc, fd);
- return fd;
-}
-#define TSAN_MAYBE_INTERCEPT_SIGNALFD TSAN_INTERCEPT(signalfd)
-#else
-#define TSAN_MAYBE_INTERCEPT_SIGNALFD
-#endif
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, inotify_init, int fake) {
- SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
- int fd = REAL(inotify_init)(fake);
- if (fd >= 0)
- FdInotifyCreate(thr, pc, fd);
- return fd;
-}
-#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT TSAN_INTERCEPT(inotify_init)
-#else
-#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
-#endif
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
- SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
- int fd = REAL(inotify_init1)(flags);
- if (fd >= 0)
- FdInotifyCreate(thr, pc, fd);
- return fd;
-}
-#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 TSAN_INTERCEPT(inotify_init1)
-#else
-#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1
-#endif
-
-TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
- SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
- int fd = REAL(socket)(domain, type, protocol);
- if (fd >= 0)
- FdSocketCreate(thr, pc, fd);
- return fd;
-}
-
-TSAN_INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int *fd) {
- SCOPED_TSAN_INTERCEPTOR(socketpair, domain, type, protocol, fd);
- int res = REAL(socketpair)(domain, type, protocol, fd);
- if (res == 0 && fd[0] >= 0 && fd[1] >= 0)
- FdPipeCreate(thr, pc, fd[0], fd[1]);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) {
- SCOPED_TSAN_INTERCEPTOR(connect, fd, addr, addrlen);
- FdSocketConnecting(thr, pc, fd);
- int res = REAL(connect)(fd, addr, addrlen);
- if (res == 0 && fd >= 0)
- FdSocketConnect(thr, pc, fd);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, bind, int fd, void *addr, unsigned addrlen) {
- SCOPED_TSAN_INTERCEPTOR(bind, fd, addr, addrlen);
- int res = REAL(bind)(fd, addr, addrlen);
- if (fd > 0 && res == 0)
- FdAccess(thr, pc, fd);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, listen, int fd, int backlog) {
- SCOPED_TSAN_INTERCEPTOR(listen, fd, backlog);
- int res = REAL(listen)(fd, backlog);
- if (fd > 0 && res == 0)
- FdAccess(thr, pc, fd);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, close, int fd) {
- SCOPED_TSAN_INTERCEPTOR(close, fd);
- if (fd >= 0)
- FdClose(thr, pc, fd);
- return REAL(close)(fd);
-}
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, __close, int fd) {
- SCOPED_TSAN_INTERCEPTOR(__close, fd);
- if (fd >= 0)
- FdClose(thr, pc, fd);
- return REAL(__close)(fd);
-}
-#define TSAN_MAYBE_INTERCEPT___CLOSE TSAN_INTERCEPT(__close)
-#else
-#define TSAN_MAYBE_INTERCEPT___CLOSE
-#endif
-
-// glibc guts
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
-TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
- SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
- int fds[64];
- int cnt = ExtractResolvFDs(state, fds, ARRAY_SIZE(fds));
- for (int i = 0; i < cnt; i++) {
- if (fds[i] > 0)
- FdClose(thr, pc, fds[i]);
- }
- REAL(__res_iclose)(state, free_addr);
-}
-#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE TSAN_INTERCEPT(__res_iclose)
-#else
-#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE
-#endif
-
-TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
- SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
- int res = REAL(pipe)(pipefd);
- if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
- FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
- return res;
-}
-
-#if !SANITIZER_MAC
-TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
- SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
- int res = REAL(pipe2)(pipefd, flags);
- if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
- FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
- return res;
-}
-#endif
-
-TSAN_INTERCEPTOR(int, unlink, char *path) {
- SCOPED_TSAN_INTERCEPTOR(unlink, path);
- Release(thr, pc, File2addr(path));
- int res = REAL(unlink)(path);
- return res;
-}
-
-TSAN_INTERCEPTOR(void*, tmpfile, int fake) {
- SCOPED_TSAN_INTERCEPTOR(tmpfile, fake);
- void *res = REAL(tmpfile)(fake);
- if (res) {
- int fd = fileno_unlocked(res);
- if (fd >= 0)
- FdFileCreate(thr, pc, fd);
- }
- return res;
-}
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
- SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
- void *res = REAL(tmpfile64)(fake);
- if (res) {
- int fd = fileno_unlocked(res);
- if (fd >= 0)
- FdFileCreate(thr, pc, fd);
- }
- return res;
-}
-#define TSAN_MAYBE_INTERCEPT_TMPFILE64 TSAN_INTERCEPT(tmpfile64)
-#else
-#define TSAN_MAYBE_INTERCEPT_TMPFILE64
-#endif
-
-static void FlushStreams() {
- // Flushing all the streams here may freeze the process if a child thread is
- // performing file stream operations at the same time.
- REAL(fflush)(stdout);
- REAL(fflush)(stderr);
-}
-
-TSAN_INTERCEPTOR(void, abort, int fake) {
- SCOPED_TSAN_INTERCEPTOR(abort, fake);
- FlushStreams();
- REAL(abort)(fake);
-}
-
-TSAN_INTERCEPTOR(int, rmdir, char *path) {
- SCOPED_TSAN_INTERCEPTOR(rmdir, path);
- Release(thr, pc, Dir2addr(path));
- int res = REAL(rmdir)(path);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, closedir, void *dirp) {
- SCOPED_TSAN_INTERCEPTOR(closedir, dirp);
- if (dirp) {
- int fd = dirfd(dirp);
- FdClose(thr, pc, fd);
- }
- return REAL(closedir)(dirp);
-}
-
-#if SANITIZER_LINUX
-TSAN_INTERCEPTOR(int, epoll_create, int size) {
- SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
- int fd = REAL(epoll_create)(size);
- if (fd >= 0)
- FdPollCreate(thr, pc, fd);
- return fd;
-}
-
-TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
- SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
- int fd = REAL(epoll_create1)(flags);
- if (fd >= 0)
- FdPollCreate(thr, pc, fd);
- return fd;
-}
-
-TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
- SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
- if (epfd >= 0)
- FdAccess(thr, pc, epfd);
- if (epfd >= 0 && fd >= 0)
- FdAccess(thr, pc, fd);
- if (op == EPOLL_CTL_ADD && epfd >= 0)
- FdRelease(thr, pc, epfd);
- int res = REAL(epoll_ctl)(epfd, op, fd, ev);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
- SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
- if (epfd >= 0)
- FdAccess(thr, pc, epfd);
- int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout);
- if (res > 0 && epfd >= 0)
- FdAcquire(thr, pc, epfd);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, epoll_pwait, int epfd, void *ev, int cnt, int timeout,
- void *sigmask) {
- SCOPED_TSAN_INTERCEPTOR(epoll_pwait, epfd, ev, cnt, timeout, sigmask);
- if (epfd >= 0)
- FdAccess(thr, pc, epfd);
- int res = BLOCK_REAL(epoll_pwait)(epfd, ev, cnt, timeout, sigmask);
- if (res > 0 && epfd >= 0)
- FdAcquire(thr, pc, epfd);
- return res;
-}
-
-#define TSAN_MAYBE_INTERCEPT_EPOLL \
- TSAN_INTERCEPT(epoll_create); \
- TSAN_INTERCEPT(epoll_create1); \
- TSAN_INTERCEPT(epoll_ctl); \
- TSAN_INTERCEPT(epoll_wait); \
- TSAN_INTERCEPT(epoll_pwait)
-#else
-#define TSAN_MAYBE_INTERCEPT_EPOLL
-#endif
-
-// The following functions are intercepted merely to process pending signals.
-// If program blocks signal X, we must deliver the signal before the function
-// returns. Similarly, if program unblocks a signal (or returns from sigsuspend)
-// it's better to deliver the signal straight away.
-TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) {
- SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask);
- return REAL(sigsuspend)(mask);
-}
-
-TSAN_INTERCEPTOR(int, sigblock, int mask) {
- SCOPED_TSAN_INTERCEPTOR(sigblock, mask);
- return REAL(sigblock)(mask);
-}
-
-TSAN_INTERCEPTOR(int, sigsetmask, int mask) {
- SCOPED_TSAN_INTERCEPTOR(sigsetmask, mask);
- return REAL(sigsetmask)(mask);
-}
-
-TSAN_INTERCEPTOR(int, pthread_sigmask, int how, const __sanitizer_sigset_t *set,
- __sanitizer_sigset_t *oldset) {
- SCOPED_TSAN_INTERCEPTOR(pthread_sigmask, how, set, oldset);
- return REAL(pthread_sigmask)(how, set, oldset);
-}
-
-namespace __tsan {
-
-static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
- bool sigact, int sig,
- __sanitizer_siginfo *info, void *uctx) {
- __sanitizer_sigaction *sigactions = interceptor_ctx()->sigactions;
- if (acquire)
- Acquire(thr, 0, (uptr)&sigactions[sig]);
- // Signals are generally asynchronous, so if we receive a signals when
- // ignores are enabled we should disable ignores. This is critical for sync
- // and interceptors, because otherwise we can miss syncronization and report
- // false races.
- int ignore_reads_and_writes = thr->ignore_reads_and_writes;
- int ignore_interceptors = thr->ignore_interceptors;
- int ignore_sync = thr->ignore_sync;
- if (!ctx->after_multithreaded_fork) {
- thr->ignore_reads_and_writes = 0;
- thr->fast_state.ClearIgnoreBit();
- thr->ignore_interceptors = 0;
- thr->ignore_sync = 0;
- }
- // Ensure that the handler does not spoil errno.
- const int saved_errno = errno;
- errno = 99;
- // This code races with sigaction. Be careful to not read sa_sigaction twice.
- // Also need to remember pc for reporting before the call,
- // because the handler can reset it.
- volatile uptr pc =
- sigact ? (uptr)sigactions[sig].sigaction : (uptr)sigactions[sig].handler;
- if (pc != sig_dfl && pc != sig_ign) {
- if (sigact)
- ((__sanitizer_sigactionhandler_ptr)pc)(sig, info, uctx);
- else
- ((__sanitizer_sighandler_ptr)pc)(sig);
- }
- if (!ctx->after_multithreaded_fork) {
- thr->ignore_reads_and_writes = ignore_reads_and_writes;
- if (ignore_reads_and_writes)
- thr->fast_state.SetIgnoreBit();
- thr->ignore_interceptors = ignore_interceptors;
- thr->ignore_sync = ignore_sync;
- }
- // We do not detect errno spoiling for SIGTERM,
- // because some SIGTERM handlers do spoil errno but reraise SIGTERM,
- // tsan reports false positive in such case.
- // It's difficult to properly detect this situation (reraise),
- // because in async signal processing case (when handler is called directly
- // from rtl_generic_sighandler) we have not yet received the reraised
- // signal; and it looks too fragile to intercept all ways to reraise a signal.
- if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) {
- VarSizeStackTrace stack;
- // StackTrace::GetNestInstructionPc(pc) is used because return address is
- // expected, OutputReport() will undo this.
- ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
- ThreadRegistryLock l(ctx->thread_registry);
- ScopedReport rep(ReportTypeErrnoInSignal);
- if (!IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack)) {
- rep.AddStack(stack, true);
- OutputReport(thr, rep);
- }
- }
- errno = saved_errno;
-}
-
-void ProcessPendingSignals(ThreadState *thr) {
- ThreadSignalContext *sctx = SigCtx(thr);
- if (sctx == 0 ||
- atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
- return;
- atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed);
- atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
- internal_sigfillset(&sctx->emptyset);
- int res = REAL(pthread_sigmask)(SIG_SETMASK, &sctx->emptyset, &sctx->oldset);
- CHECK_EQ(res, 0);
- for (int sig = 0; sig < kSigCount; sig++) {
- SignalDesc *signal = &sctx->pending_signals[sig];
- if (signal->armed) {
- signal->armed = false;
- CallUserSignalHandler(thr, false, true, signal->sigaction, sig,
- &signal->siginfo, &signal->ctx);
- }
- }
- res = REAL(pthread_sigmask)(SIG_SETMASK, &sctx->oldset, 0);
- CHECK_EQ(res, 0);
- atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
-}
-
-} // namespace __tsan
-
-static bool is_sync_signal(ThreadSignalContext *sctx, int sig) {
- return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
- sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
- // If we are sending signal to ourselves, we must process it now.
- (sctx && sig == sctx->int_signal_send);
-}
-
-void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
- __sanitizer_siginfo *info,
- void *ctx) {
- ThreadState *thr = cur_thread();
- ThreadSignalContext *sctx = SigCtx(thr);
- if (sig < 0 || sig >= kSigCount) {
- VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig);
- return;
- }
- // Don't mess with synchronous signals.
- const bool sync = is_sync_signal(sctx, sig);
- if (sync ||
- // If we are in blocking function, we can safely process it now
- // (but check if we are in a recursive interceptor,
- // i.e. pthread_join()->munmap()).
- (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed))) {
- atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
- if (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed)) {
- atomic_store(&sctx->in_blocking_func, 0, memory_order_relaxed);
- CallUserSignalHandler(thr, sync, true, sigact, sig, info, ctx);
- atomic_store(&sctx->in_blocking_func, 1, memory_order_relaxed);
- } else {
- // Be very conservative with when we do acquire in this case.
- // It's unsafe to do acquire in async handlers, because ThreadState
- // can be in inconsistent state.
- // SIGSYS looks relatively safe -- it's synchronous and can actually
- // need some global state.
- bool acq = (sig == SIGSYS);
- CallUserSignalHandler(thr, sync, acq, sigact, sig, info, ctx);
- }
- atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
- return;
- }
-
- if (sctx == 0)
- return;
- SignalDesc *signal = &sctx->pending_signals[sig];
- if (signal->armed == false) {
- signal->armed = true;
- signal->sigaction = sigact;
- if (info)
- internal_memcpy(&signal->siginfo, info, sizeof(*info));
- if (ctx)
- internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
- atomic_store(&sctx->have_pending_signals, 1, memory_order_relaxed);
- }
-}
-
-static void rtl_sighandler(int sig) {
- rtl_generic_sighandler(false, sig, 0, 0);
-}
-
-static void rtl_sigaction(int sig, __sanitizer_siginfo *info, void *ctx) {
- rtl_generic_sighandler(true, sig, info, ctx);
-}
-
-TSAN_INTERCEPTOR(int, raise, int sig) {
- SCOPED_TSAN_INTERCEPTOR(raise, sig);
- ThreadSignalContext *sctx = SigCtx(thr);
- CHECK_NE(sctx, 0);
- int prev = sctx->int_signal_send;
- sctx->int_signal_send = sig;
- int res = REAL(raise)(sig);
- CHECK_EQ(sctx->int_signal_send, sig);
- sctx->int_signal_send = prev;
- return res;
-}
-
-TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
- SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
- ThreadSignalContext *sctx = SigCtx(thr);
- CHECK_NE(sctx, 0);
- int prev = sctx->int_signal_send;
- if (pid == (int)internal_getpid()) {
- sctx->int_signal_send = sig;
- }
- int res = REAL(kill)(pid, sig);
- if (pid == (int)internal_getpid()) {
- CHECK_EQ(sctx->int_signal_send, sig);
- sctx->int_signal_send = prev;
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
- SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
- ThreadSignalContext *sctx = SigCtx(thr);
- CHECK_NE(sctx, 0);
- int prev = sctx->int_signal_send;
- if (tid == pthread_self()) {
- sctx->int_signal_send = sig;
- }
- int res = REAL(pthread_kill)(tid, sig);
- if (tid == pthread_self()) {
- CHECK_EQ(sctx->int_signal_send, sig);
- sctx->int_signal_send = prev;
- }
- return res;
-}
-
-TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) {
- SCOPED_TSAN_INTERCEPTOR(gettimeofday, tv, tz);
- // It's intercepted merely to process pending signals.
- return REAL(gettimeofday)(tv, tz);
-}
-
-TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service,
- void *hints, void *rv) {
- SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv);
- // We miss atomic synchronization in getaddrinfo,
- // and can report false race between malloc and free
- // inside of getaddrinfo. So ignore memory accesses.
- ThreadIgnoreBegin(thr, pc);
- int res = REAL(getaddrinfo)(node, service, hints, rv);
- ThreadIgnoreEnd(thr, pc);
- return res;
-}
-
-TSAN_INTERCEPTOR(int, fork, int fake) {
- if (UNLIKELY(cur_thread()->in_symbolizer))
- return REAL(fork)(fake);
- SCOPED_INTERCEPTOR_RAW(fork, fake);
- ForkBefore(thr, pc);
- int pid;
- {
- // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and
- // we'll assert in CheckNoLocks() unless we ignore interceptors.
- ScopedIgnoreInterceptors ignore;
- pid = REAL(fork)(fake);
- }
- if (pid == 0) {
- // child
- ForkChildAfter(thr, pc);
- FdOnFork(thr, pc);
- } else if (pid > 0) {
- // parent
- ForkParentAfter(thr, pc);
- } else {
- // error
- ForkParentAfter(thr, pc);
- }
- return pid;
-}
-
-TSAN_INTERCEPTOR(int, vfork, int fake) {
- // Some programs (e.g. openjdk) call close for all file descriptors
- // in the child process. Under tsan it leads to false positives, because
- // address space is shared, so the parent process also thinks that
- // the descriptors are closed (while they are actually not).
- // This leads to false positives due to missed synchronization.
- // Strictly saying this is undefined behavior, because vfork child is not
- // allowed to call any functions other than exec/exit. But this is what
- // openjdk does, so we want to handle it.
- // We could disable interceptors in the child process. But it's not possible
- // to simply intercept and wrap vfork, because vfork child is not allowed
- // to return from the function that calls vfork, and that's exactly what
- // we would do. So this would require some assembly trickery as well.
- // Instead we simply turn vfork into fork.
- return WRAP(fork)(fake);
-}
-
-#if !SANITIZER_MAC && !SANITIZER_ANDROID
-typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
- void *data);
-struct dl_iterate_phdr_data {
- ThreadState *thr;
- uptr pc;
- dl_iterate_phdr_cb_t cb;
- void *data;
-};
-
-static bool IsAppNotRodata(uptr addr) {
- return IsAppMem(addr) && *(u64*)MemToShadow(addr) != kShadowRodata;
-}
-
-static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size,
- void *data) {
- dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data;
- // dlopen/dlclose allocate/free dynamic-linker-internal memory, which is later
- // accessible in dl_iterate_phdr callback. But we don't see synchronization
- // inside of dynamic linker, so we "unpoison" it here in order to not
- // produce false reports. Ignoring malloc/free in dlopen/dlclose is not enough
- // because some libc functions call __libc_dlopen.
- if (info && IsAppNotRodata((uptr)info->dlpi_name))
- MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
- internal_strlen(info->dlpi_name));
- int res = cbdata->cb(info, size, cbdata->data);
- // Perform the check one more time in case info->dlpi_name was overwritten
- // by user callback.
- if (info && IsAppNotRodata((uptr)info->dlpi_name))
- MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
- internal_strlen(info->dlpi_name));
- return res;
-}
-
-TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) {
- SCOPED_TSAN_INTERCEPTOR(dl_iterate_phdr, cb, data);
- dl_iterate_phdr_data cbdata;
- cbdata.thr = thr;
- cbdata.pc = pc;
- cbdata.cb = cb;
- cbdata.data = data;
- int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata);
- return res;
-}
-#endif
-
-static int OnExit(ThreadState *thr) {
- int status = Finalize(thr);
- FlushStreams();
- return status;
-}
-
-struct TsanInterceptorContext {
- ThreadState *thr;
- const uptr caller_pc;
- const uptr pc;
-};
-
-#if !SANITIZER_MAC
-static void HandleRecvmsg(ThreadState *thr, uptr pc,
- __sanitizer_msghdr *msg) {
- int fds[64];
- int cnt = ExtractRecvmsgFDs(msg, fds, ARRAY_SIZE(fds));
- for (int i = 0; i < cnt; i++)
- FdEventCreate(thr, pc, fds[i]);
-}
-#endif
-
-#include "sanitizer_common/sanitizer_platform_interceptors.h"
-// Causes interceptor recursion (getaddrinfo() and fopen())
-#undef SANITIZER_INTERCEPT_GETADDRINFO
-// There interceptors do not seem to be strictly necessary for tsan.
-// But we see cases where the interceptors consume 70% of execution time.
-// Memory blocks passed to fgetgrent_r are "written to" by tsan several times.
-// First, there is some recursion (getgrnam_r calls fgetgrent_r), and each
-// function "writes to" the buffer. Then, the same memory is "written to"
-// twice, first as buf and then as pwbufp (both of them refer to the same
-// addresses).
-#undef SANITIZER_INTERCEPT_GETPWENT
-#undef SANITIZER_INTERCEPT_GETPWENT_R
-#undef SANITIZER_INTERCEPT_FGETPWENT
-#undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS
-#undef SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS
-// We define our own.
-#if SANITIZER_INTERCEPT_TLS_GET_ADDR
-#define NEED_TLS_GET_ADDR
-#endif
-#undef SANITIZER_INTERCEPT_TLS_GET_ADDR
-
-#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
-#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
- INTERCEPT_FUNCTION_VER(name, ver)
-
-#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
- MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \
- true)
-
-#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
- MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \
- ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \
- false)
-
-#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
- SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \
- TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
- ctx = (void *)&_ctx; \
- (void) ctx;
-
-#define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \
- SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
- TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
- ctx = (void *)&_ctx; \
- (void) ctx;
-
-#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \
- Acquire(thr, pc, File2addr(path)); \
- if (file) { \
- int fd = fileno_unlocked(file); \
- if (fd >= 0) FdFileCreate(thr, pc, fd); \
- }
-
-#define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \
- if (file) { \
- int fd = fileno_unlocked(file); \
- if (fd >= 0) FdClose(thr, pc, fd); \
- }
-
-#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
- libignore()->OnLibraryLoaded(filename)
-
-#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \
- libignore()->OnLibraryUnloaded()
-
-#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \
- Acquire(((TsanInterceptorContext *) ctx)->thr, pc, u)
-
-#define COMMON_INTERCEPTOR_RELEASE(ctx, u) \
- Release(((TsanInterceptorContext *) ctx)->thr, pc, u)
-
-#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
- Acquire(((TsanInterceptorContext *) ctx)->thr, pc, Dir2addr(path))
-
-#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
- FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd)
-
-#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
- FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd)
-
-#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \
- FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd)
-
-#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
- FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd)
-
-#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
- ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name)
-
-#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
- __tsan::ctx->thread_registry->SetThreadNameByUserId(thread, name)
-
-#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name)
-
-#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \
- OnExit(((TsanInterceptorContext *) ctx)->thr)
-
-#define COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m) \
- MutexPreLock(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
-
-#define COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m) \
- MutexPostLock(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
-
-#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \
- MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
-
-#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \
- MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
-
-#define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) \
- MutexInvalidAccess(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
-
-#define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, sz, prot, flags, fd, \
- off) \
- do { \
- return mmap_interceptor(thr, pc, REAL(mmap), addr, sz, prot, flags, fd, \
- off); \
- } while (false)
-
-#if !SANITIZER_MAC
-#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
- HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
- ((TsanInterceptorContext *)ctx)->pc, msg)
-#endif
-
-#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
- if (TsanThread *t = GetCurrentThread()) { \
- *begin = t->tls_begin(); \
- *end = t->tls_end(); \
- } else { \
- *begin = *end = 0; \
- }
-
-#define COMMON_INTERCEPTOR_USER_CALLBACK_START() \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START()
-
-#define COMMON_INTERCEPTOR_USER_CALLBACK_END() \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END()
-
-#include "sanitizer_common/sanitizer_common_interceptors.inc"
-
-static int sigaction_impl(int sig, const __sanitizer_sigaction *act,
- __sanitizer_sigaction *old);
-static __sanitizer_sighandler_ptr signal_impl(int sig,
- __sanitizer_sighandler_ptr h);
-
-#define SIGNAL_INTERCEPTOR_SIGACTION_IMPL(signo, act, oldact) \
- { return sigaction_impl(signo, act, oldact); }
-
-#define SIGNAL_INTERCEPTOR_SIGNAL_IMPL(func, signo, handler) \
- { return (uptr)signal_impl(signo, (__sanitizer_sighandler_ptr)handler); }
-
-#include "sanitizer_common/sanitizer_signal_interceptors.inc"
-
-int sigaction_impl(int sig, const __sanitizer_sigaction *act,
- __sanitizer_sigaction *old) {
- // Note: if we call REAL(sigaction) directly for any reason without proxying
- // the signal handler through rtl_sigaction, very bad things will happen.
- // The handler will run synchronously and corrupt tsan per-thread state.
- SCOPED_INTERCEPTOR_RAW(sigaction, sig, act, old);
- __sanitizer_sigaction *sigactions = interceptor_ctx()->sigactions;
- __sanitizer_sigaction old_stored;
- if (old) internal_memcpy(&old_stored, &sigactions[sig], sizeof(old_stored));
- __sanitizer_sigaction newact;
- if (act) {
- // Copy act into sigactions[sig].
- // Can't use struct copy, because compiler can emit call to memcpy.
- // Can't use internal_memcpy, because it copies byte-by-byte,
- // and signal handler reads the handler concurrently. It it can read
- // some bytes from old value and some bytes from new value.
- // Use volatile to prevent insertion of memcpy.
- sigactions[sig].handler =
- *(volatile __sanitizer_sighandler_ptr const *)&act->handler;
- sigactions[sig].sa_flags = *(volatile int const *)&act->sa_flags;
- internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask,
- sizeof(sigactions[sig].sa_mask));
-#if !SANITIZER_FREEBSD && !SANITIZER_MAC && !SANITIZER_NETBSD
- sigactions[sig].sa_restorer = act->sa_restorer;
-#endif
- internal_memcpy(&newact, act, sizeof(newact));
- internal_sigfillset(&newact.sa_mask);
- if ((uptr)act->handler != sig_ign && (uptr)act->handler != sig_dfl) {
- if (newact.sa_flags & SA_SIGINFO)
- newact.sigaction = rtl_sigaction;
- else
- newact.handler = rtl_sighandler;
- }
- ReleaseStore(thr, pc, (uptr)&sigactions[sig]);
- act = &newact;
- }
- int res = REAL(sigaction)(sig, act, old);
- if (res == 0 && old) {
- uptr cb = (uptr)old->sigaction;
- if (cb == (uptr)rtl_sigaction || cb == (uptr)rtl_sighandler) {
- internal_memcpy(old, &old_stored, sizeof(*old));
- }
- }
- return res;
-}
-
-static __sanitizer_sighandler_ptr signal_impl(int sig,
- __sanitizer_sighandler_ptr h) {
- __sanitizer_sigaction act;
- act.handler = h;
- internal_memset(&act.sa_mask, -1, sizeof(act.sa_mask));
- act.sa_flags = 0;
- __sanitizer_sigaction old;
- int res = sigaction_symname(sig, &act, &old);
- if (res) return (__sanitizer_sighandler_ptr)sig_err;
- return old.handler;
-}
-
-#define TSAN_SYSCALL() \
- ThreadState *thr = cur_thread(); \
- if (thr->ignore_interceptors) \
- return; \
- ScopedSyscall scoped_syscall(thr) \
-/**/
-
-struct ScopedSyscall {
- ThreadState *thr;
-
- explicit ScopedSyscall(ThreadState *thr)
- : thr(thr) {
- Initialize(thr);
- }
-
- ~ScopedSyscall() {
- ProcessPendingSignals(thr);
- }
-};
-
-#if !SANITIZER_FREEBSD && !SANITIZER_MAC
-static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
- TSAN_SYSCALL();
- MemoryAccessRange(thr, pc, p, s, write);
-}
-
-static void syscall_acquire(uptr pc, uptr addr) {
- TSAN_SYSCALL();
- Acquire(thr, pc, addr);
- DPrintf("syscall_acquire(%p)\n", addr);
-}
-
-static void syscall_release(uptr pc, uptr addr) {
- TSAN_SYSCALL();
- DPrintf("syscall_release(%p)\n", addr);
- Release(thr, pc, addr);
-}
-
-static void syscall_fd_close(uptr pc, int fd) {
- TSAN_SYSCALL();
- FdClose(thr, pc, fd);
-}
-
-static USED void syscall_fd_acquire(uptr pc, int fd) {
- TSAN_SYSCALL();
- FdAcquire(thr, pc, fd);
- DPrintf("syscall_fd_acquire(%p)\n", fd);
-}
-
-static USED void syscall_fd_release(uptr pc, int fd) {
- TSAN_SYSCALL();
- DPrintf("syscall_fd_release(%p)\n", fd);
- FdRelease(thr, pc, fd);
-}
-
-static void syscall_pre_fork(uptr pc) {
- TSAN_SYSCALL();
- ForkBefore(thr, pc);
-}
-
-static void syscall_post_fork(uptr pc, int pid) {
- TSAN_SYSCALL();
- if (pid == 0) {
- // child
- ForkChildAfter(thr, pc);
- FdOnFork(thr, pc);
- } else if (pid > 0) {
- // parent
- ForkParentAfter(thr, pc);
- } else {
- // error
- ForkParentAfter(thr, pc);
- }
-}
-#endif
-
-#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
- syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
-
-#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
- syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true)
-
-#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-
-#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
- do { \
- (void)(p); \
- (void)(s); \
- } while (false)
-
-#define COMMON_SYSCALL_ACQUIRE(addr) \
- syscall_acquire(GET_CALLER_PC(), (uptr)(addr))
-
-#define COMMON_SYSCALL_RELEASE(addr) \
- syscall_release(GET_CALLER_PC(), (uptr)(addr))
-
-#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd)
-
-#define COMMON_SYSCALL_FD_ACQUIRE(fd) syscall_fd_acquire(GET_CALLER_PC(), fd)
-
-#define COMMON_SYSCALL_FD_RELEASE(fd) syscall_fd_release(GET_CALLER_PC(), fd)
-
-#define COMMON_SYSCALL_PRE_FORK() \
- syscall_pre_fork(GET_CALLER_PC())
-
-#define COMMON_SYSCALL_POST_FORK(res) \
- syscall_post_fork(GET_CALLER_PC(), res)
-
-#include "sanitizer_common/sanitizer_common_syscalls.inc"
-#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
-
-#ifdef NEED_TLS_GET_ADDR
-// Define own interceptor instead of sanitizer_common's for three reasons:
-// 1. It must not process pending signals.
-// Signal handlers may contain MOVDQA instruction (see below).
-// 2. It must be as simple as possible to not contain MOVDQA.
-// 3. Sanitizer_common version uses COMMON_INTERCEPTOR_INITIALIZE_RANGE which
-// is empty for tsan (meant only for msan).
-// Note: __tls_get_addr can be called with mis-aligned stack due to:
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
-// So the interceptor must work with mis-aligned stack, in particular, does not
-// execute MOVDQA with stack addresses.
-TSAN_INTERCEPTOR(void *, __tls_get_addr, void *arg) {
- void *res = REAL(__tls_get_addr)(arg);
- ThreadState *thr = cur_thread();
- if (!thr)
- return res;
- DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, thr->tls_addr,
- thr->tls_addr + thr->tls_size);
- if (!dtv)
- return res;
- // New DTLS block has been allocated.
- MemoryResetRange(thr, 0, dtv->beg, dtv->size);
- return res;
-}
-#endif
-
-#if SANITIZER_NETBSD
-TSAN_INTERCEPTOR(void, _lwp_exit) {
- SCOPED_TSAN_INTERCEPTOR(_lwp_exit);
- DestroyThreadState();
- REAL(_lwp_exit)();
-}
-#define TSAN_MAYBE_INTERCEPT__LWP_EXIT TSAN_INTERCEPT(_lwp_exit)
-#else
-#define TSAN_MAYBE_INTERCEPT__LWP_EXIT
-#endif
-
-#if SANITIZER_FREEBSD
-TSAN_INTERCEPTOR(void, thr_exit, tid_t *state) {
- SCOPED_TSAN_INTERCEPTOR(thr_exit, state);
- DestroyThreadState();
- REAL(thr_exit(state));
-}
-#define TSAN_MAYBE_INTERCEPT_THR_EXIT TSAN_INTERCEPT(thr_exit)
-#else
-#define TSAN_MAYBE_INTERCEPT_THR_EXIT
-#endif
-
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_init, void *c, void *a)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_signal, void *c)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_broadcast, void *c)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_wait, void *c, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_destroy, void *c)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, mutex_init, void *m, void *a)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, mutex_destroy, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, mutex_trylock, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_init, void *m, void *a)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_destroy, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_rdlock, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_tryrdlock, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_wrlock, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_trywrlock, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_unlock, void *m)
-TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(int, once, void *o, void (*f)())
-
-namespace __tsan {
-
-static void finalize(void *arg) {
- ThreadState *thr = cur_thread();
- int status = Finalize(thr);
- // Make sure the output is not lost.
- FlushStreams();
- if (status)
- Die();
-}
-
-#if !SANITIZER_MAC && !SANITIZER_ANDROID
-static void unreachable() {
- Report("FATAL: ThreadSanitizer: unreachable called\n");
- Die();
-}
-#endif
-
-void InitializeInterceptors() {
-#if !SANITIZER_MAC
- // We need to setup it early, because functions like dlsym() can call it.
- REAL(memset) = internal_memset;
- REAL(memcpy) = internal_memcpy;
-#endif
-
- // Instruct libc malloc to consume less memory.
-#if SANITIZER_LINUX
- mallopt(1, 0); // M_MXFAST
- mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
-#endif
-
- new(interceptor_ctx()) InterceptorContext();
-
- InitializeCommonInterceptors();
- InitializeSignalInterceptors();
-
-#if !SANITIZER_MAC
- // We can not use TSAN_INTERCEPT to get setjmp addr,
- // because it does &setjmp and setjmp is not present in some versions of libc.
- using __interception::GetRealFunctionAddress;
- GetRealFunctionAddress(TSAN_STRING_SETJMP,
- (uptr*)&REAL(setjmp_symname), 0, 0);
- GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0);
- GetRealFunctionAddress(TSAN_STRING_SIGSETJMP,
- (uptr*)&REAL(sigsetjmp_symname), 0, 0);
-#if !SANITIZER_NETBSD
- GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0);
-#endif
-#endif
-
- TSAN_INTERCEPT(longjmp_symname);
- TSAN_INTERCEPT(siglongjmp_symname);
-#if SANITIZER_NETBSD
- TSAN_INTERCEPT(_longjmp);
-#endif
-
- TSAN_INTERCEPT(malloc);
- TSAN_INTERCEPT(__libc_memalign);
- TSAN_INTERCEPT(calloc);
- TSAN_INTERCEPT(realloc);
- TSAN_INTERCEPT(free);
- TSAN_INTERCEPT(cfree);
- TSAN_INTERCEPT(munmap);
- TSAN_MAYBE_INTERCEPT_MEMALIGN;
- TSAN_INTERCEPT(valloc);
- TSAN_MAYBE_INTERCEPT_PVALLOC;
- TSAN_INTERCEPT(posix_memalign);
-
- TSAN_INTERCEPT(strcpy); // NOLINT
- TSAN_INTERCEPT(strncpy);
- TSAN_INTERCEPT(strdup);
-
- TSAN_INTERCEPT(pthread_create);
- TSAN_INTERCEPT(pthread_join);
- TSAN_INTERCEPT(pthread_detach);
-
- TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE);
- TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE);
- TSAN_INTERCEPT_VER(pthread_cond_broadcast, PTHREAD_ABI_BASE);
- TSAN_INTERCEPT_VER(pthread_cond_wait, PTHREAD_ABI_BASE);
- TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE);
- TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE);
-
- TSAN_INTERCEPT(pthread_mutex_init);
- TSAN_INTERCEPT(pthread_mutex_destroy);
- TSAN_INTERCEPT(pthread_mutex_trylock);
- TSAN_INTERCEPT(pthread_mutex_timedlock);
-
- TSAN_INTERCEPT(pthread_spin_init);
- TSAN_INTERCEPT(pthread_spin_destroy);
- TSAN_INTERCEPT(pthread_spin_lock);
- TSAN_INTERCEPT(pthread_spin_trylock);
- TSAN_INTERCEPT(pthread_spin_unlock);
-
- TSAN_INTERCEPT(pthread_rwlock_init);
- TSAN_INTERCEPT(pthread_rwlock_destroy);
- TSAN_INTERCEPT(pthread_rwlock_rdlock);
- TSAN_INTERCEPT(pthread_rwlock_tryrdlock);
- TSAN_INTERCEPT(pthread_rwlock_timedrdlock);
- TSAN_INTERCEPT(pthread_rwlock_wrlock);
- TSAN_INTERCEPT(pthread_rwlock_trywrlock);
- TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
- TSAN_INTERCEPT(pthread_rwlock_unlock);
-
- TSAN_INTERCEPT(pthread_barrier_init);
- TSAN_INTERCEPT(pthread_barrier_destroy);
- TSAN_INTERCEPT(pthread_barrier_wait);
-
- TSAN_INTERCEPT(pthread_once);
-
- TSAN_INTERCEPT(fstat);
- TSAN_MAYBE_INTERCEPT___FXSTAT;
- TSAN_MAYBE_INTERCEPT_FSTAT64;
- TSAN_MAYBE_INTERCEPT___FXSTAT64;
- TSAN_INTERCEPT(open);
- TSAN_MAYBE_INTERCEPT_OPEN64;
- TSAN_INTERCEPT(creat);
- TSAN_MAYBE_INTERCEPT_CREAT64;
- TSAN_INTERCEPT(dup);
- TSAN_INTERCEPT(dup2);
- TSAN_INTERCEPT(dup3);
- TSAN_MAYBE_INTERCEPT_EVENTFD;
- TSAN_MAYBE_INTERCEPT_SIGNALFD;
- TSAN_MAYBE_INTERCEPT_INOTIFY_INIT;
- TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1;
- TSAN_INTERCEPT(socket);
- TSAN_INTERCEPT(socketpair);
- TSAN_INTERCEPT(connect);
- TSAN_INTERCEPT(bind);
- TSAN_INTERCEPT(listen);
- TSAN_MAYBE_INTERCEPT_EPOLL;
- TSAN_INTERCEPT(close);
- TSAN_MAYBE_INTERCEPT___CLOSE;
- TSAN_MAYBE_INTERCEPT___RES_ICLOSE;
- TSAN_INTERCEPT(pipe);
- TSAN_INTERCEPT(pipe2);
-
- TSAN_INTERCEPT(unlink);
- TSAN_INTERCEPT(tmpfile);
- TSAN_MAYBE_INTERCEPT_TMPFILE64;
- TSAN_INTERCEPT(abort);
- TSAN_INTERCEPT(rmdir);
- TSAN_INTERCEPT(closedir);
-
- TSAN_INTERCEPT(sigsuspend);
- TSAN_INTERCEPT(sigblock);
- TSAN_INTERCEPT(sigsetmask);
- TSAN_INTERCEPT(pthread_sigmask);
- TSAN_INTERCEPT(raise);
- TSAN_INTERCEPT(kill);
- TSAN_INTERCEPT(pthread_kill);
- TSAN_INTERCEPT(sleep);
- TSAN_INTERCEPT(usleep);
- TSAN_INTERCEPT(nanosleep);
- TSAN_INTERCEPT(pause);
- TSAN_INTERCEPT(gettimeofday);
- TSAN_INTERCEPT(getaddrinfo);
-
- TSAN_INTERCEPT(fork);
- TSAN_INTERCEPT(vfork);
-#if !SANITIZER_ANDROID
- TSAN_INTERCEPT(dl_iterate_phdr);
-#endif
- TSAN_MAYBE_INTERCEPT_ON_EXIT;
- TSAN_INTERCEPT(__cxa_atexit);
- TSAN_INTERCEPT(_exit);
-
-#ifdef NEED_TLS_GET_ADDR
- TSAN_INTERCEPT(__tls_get_addr);
-#endif
-
- TSAN_MAYBE_INTERCEPT__LWP_EXIT;
- TSAN_MAYBE_INTERCEPT_THR_EXIT;
-
-#if !SANITIZER_MAC && !SANITIZER_ANDROID
- // Need to setup it, because interceptors check that the function is resolved.
- // But atexit is emitted directly into the module, so can't be resolved.
- REAL(atexit) = (int(*)(void(*)()))unreachable;
-#endif
-
- if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
- Printf("ThreadSanitizer: failed to setup atexit callback\n");
- Die();
- }
-
-#if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD
- if (pthread_key_create(&interceptor_ctx()->finalize_key, &thread_finalize)) {
- Printf("ThreadSanitizer: failed to create thread key\n");
- Die();
- }
-#endif
-
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_init);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_signal);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_broadcast);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_wait);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_destroy);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(mutex_init);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(mutex_destroy);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(mutex_trylock);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_init);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_destroy);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_rdlock);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_tryrdlock);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_wrlock);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_trywrlock);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_unlock);
- TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(once);
-
- FdInit();
-}
-
-} // namespace __tsan
-
-// Invisible barrier for tests.
-// There were several unsuccessful iterations for this functionality:
-// 1. Initially it was implemented in user code using
-// REAL(pthread_barrier_wait). But pthread_barrier_wait is not supported on
-// MacOS. Futexes are linux-specific for this matter.
-// 2. Then we switched to atomics+usleep(10). But usleep produced parasitic
-// "as-if synchronized via sleep" messages in reports which failed some
-// output tests.
-// 3. Then we switched to atomics+sched_yield. But this produced tons of tsan-
-// visible events, which lead to "failed to restore stack trace" failures.
-// Note that no_sanitize_thread attribute does not turn off atomic interception
-// so attaching it to the function defined in user code does not help.
-// That's why we now have what we have.
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_testonly_barrier_init(u64 *barrier, u32 count) {
- if (count >= (1 << 8)) {
- Printf("barrier_init: count is too large (%d)\n", count);
- Die();
- }
- // 8 lsb is thread count, the remaining are count of entered threads.
- *barrier = count;
-}
-
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_testonly_barrier_wait(u64 *barrier) {
- unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED);
- unsigned old_epoch = (old >> 8) / (old & 0xff);
- for (;;) {
- unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
- unsigned cur_epoch = (cur >> 8) / (cur & 0xff);
- if (cur_epoch != old_epoch)
- return;
- internal_sched_yield();
- }
-}
--- /dev/null
+//===-- tsan_interceptors.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// FIXME: move as many interceptors as possible into
+// sanitizer_common/sanitizer_common_interceptors.inc
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_posix.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_tls_get_addr.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_interface.h"
+#include "tsan_platform.h"
+#include "tsan_suppressions.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_fd.h"
+
+
+using namespace __tsan; // NOLINT
+
+#if SANITIZER_FREEBSD || SANITIZER_MAC
+#define stdout __stdoutp
+#define stderr __stderrp
+#endif
+
+#if SANITIZER_NETBSD
+#define dirfd(dirp) (*(int *)(dirp))
+#define fileno_unlocked(fp) \
+ (((__sanitizer_FILE*)fp)->_file == -1 ? -1 : \
+ (int)(unsigned short)(((__sanitizer_FILE*)fp)->_file)) // NOLINT
+
+#define stdout ((__sanitizer_FILE*)&__sF[1])
+#define stderr ((__sanitizer_FILE*)&__sF[2])
+
+#define nanosleep __nanosleep50
+#define vfork __vfork14
+#endif
+
+#if SANITIZER_ANDROID
+#define mallopt(a, b)
+#endif
+
+#ifdef __mips__
+const int kSigCount = 129;
+#else
+const int kSigCount = 65;
+#endif
+
+#ifdef __mips__
+struct ucontext_t {
+ u64 opaque[768 / sizeof(u64) + 1];
+};
+#else
+struct ucontext_t {
+ // The size is determined by looking at sizeof of real ucontext_t on linux.
+ u64 opaque[936 / sizeof(u64) + 1];
+};
+#endif
+
+#if defined(__x86_64__) || defined(__mips__) || SANITIZER_PPC64V1
+#define PTHREAD_ABI_BASE "GLIBC_2.3.2"
+#elif defined(__aarch64__) || SANITIZER_PPC64V2
+#define PTHREAD_ABI_BASE "GLIBC_2.17"
+#endif
+
+extern "C" int pthread_attr_init(void *attr);
+extern "C" int pthread_attr_destroy(void *attr);
+DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *)
+extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize);
+extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v));
+extern "C" int pthread_setspecific(unsigned key, const void *v);
+DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *)
+DECLARE_REAL(int, fflush, __sanitizer_FILE *fp)
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr size)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
+extern "C" void *pthread_self();
+extern "C" void _exit(int status);
+#if !SANITIZER_NETBSD
+extern "C" int fileno_unlocked(void *stream);
+extern "C" int dirfd(void *dirp);
+#endif
+#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_NETBSD
+extern "C" int mallopt(int param, int value);
+#endif
+#if SANITIZER_NETBSD
+extern __sanitizer_FILE __sF[];
+#else
+extern __sanitizer_FILE *stdout, *stderr;
+#endif
+#if !SANITIZER_FREEBSD && !SANITIZER_MAC && !SANITIZER_NETBSD
+const int PTHREAD_MUTEX_RECURSIVE = 1;
+const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
+#else
+const int PTHREAD_MUTEX_RECURSIVE = 2;
+const int PTHREAD_MUTEX_RECURSIVE_NP = 2;
+#endif
+#if !SANITIZER_FREEBSD && !SANITIZER_MAC && !SANITIZER_NETBSD
+const int EPOLL_CTL_ADD = 1;
+#endif
+const int SIGILL = 4;
+const int SIGABRT = 6;
+const int SIGFPE = 8;
+const int SIGSEGV = 11;
+const int SIGPIPE = 13;
+const int SIGTERM = 15;
+#if defined(__mips__) || SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD
+const int SIGBUS = 10;
+const int SIGSYS = 12;
+#else
+const int SIGBUS = 7;
+const int SIGSYS = 31;
+#endif
+void *const MAP_FAILED = (void*)-1;
+#if SANITIZER_NETBSD
+const int PTHREAD_BARRIER_SERIAL_THREAD = 1234567;
+#elif !SANITIZER_MAC
+const int PTHREAD_BARRIER_SERIAL_THREAD = -1;
+#endif
+const int MAP_FIXED = 0x10;
+typedef long long_t; // NOLINT
+
+// From /usr/include/unistd.h
+# define F_ULOCK 0 /* Unlock a previously locked region. */
+# define F_LOCK 1 /* Lock a region for exclusive use. */
+# define F_TLOCK 2 /* Test and lock a region for exclusive use. */
+# define F_TEST 3 /* Test a region for other processes locks. */
+
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_NETBSD
+const int SA_SIGINFO = 0x40;
+const int SIG_SETMASK = 3;
+#elif defined(__mips__)
+const int SA_SIGINFO = 8;
+const int SIG_SETMASK = 3;
+#else
+const int SA_SIGINFO = 4;
+const int SIG_SETMASK = 2;
+#endif
+
+#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED \
+ (cur_thread_init(), !cur_thread()->is_inited)
+
+namespace __tsan {
+struct SignalDesc {
+ bool armed;
+ bool sigaction;
+ __sanitizer_siginfo siginfo;
+ ucontext_t ctx;
+};
+
+struct ThreadSignalContext {
+ int int_signal_send;
+ atomic_uintptr_t in_blocking_func;
+ atomic_uintptr_t have_pending_signals;
+ SignalDesc pending_signals[kSigCount];
+ // emptyset and oldset are too big for stack.
+ __sanitizer_sigset_t emptyset;
+ __sanitizer_sigset_t oldset;
+};
+
+// The sole reason tsan wraps atexit callbacks is to establish synchronization
+// between callback setup and callback execution.
+struct AtExitCtx {
+ void (*f)();
+ void *arg;
+};
+
+// InterceptorContext holds all global data required for interceptors.
+// It's explicitly constructed in InitializeInterceptors with placement new
+// and is never destroyed. This allows usage of members with non-trivial
+// constructors and destructors.
+struct InterceptorContext {
+ // The object is 64-byte aligned, because we want hot data to be located
+ // in a single cache line if possible (it's accessed in every interceptor).
+ ALIGNED(64) LibIgnore libignore;
+ __sanitizer_sigaction sigactions[kSigCount];
+#if !SANITIZER_MAC && !SANITIZER_NETBSD
+ unsigned finalize_key;
+#endif
+
+ BlockingMutex atexit_mu;
+ Vector<struct AtExitCtx *> AtExitStack;
+
+ InterceptorContext()
+ : libignore(LINKER_INITIALIZED), AtExitStack() {
+ }
+};
+
+static ALIGNED(64) char interceptor_placeholder[sizeof(InterceptorContext)];
+InterceptorContext *interceptor_ctx() {
+ return reinterpret_cast<InterceptorContext*>(&interceptor_placeholder[0]);
+}
+
+LibIgnore *libignore() {
+ return &interceptor_ctx()->libignore;
+}
+
+void InitializeLibIgnore() {
+ const SuppressionContext &supp = *Suppressions();
+ const uptr n = supp.SuppressionCount();
+ for (uptr i = 0; i < n; i++) {
+ const Suppression *s = supp.SuppressionAt(i);
+ if (0 == internal_strcmp(s->type, kSuppressionLib))
+ libignore()->AddIgnoredLibrary(s->templ);
+ }
+ if (flags()->ignore_noninstrumented_modules)
+ libignore()->IgnoreNoninstrumentedModules(true);
+ libignore()->OnLibraryLoaded(0);
+}
+
+// The following two hooks can be used by for cooperative scheduling when
+// locking.
+#ifdef TSAN_EXTERNAL_HOOKS
+void OnPotentiallyBlockingRegionBegin();
+void OnPotentiallyBlockingRegionEnd();
+#else
+SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionBegin() {}
+SANITIZER_WEAK_CXX_DEFAULT_IMPL void OnPotentiallyBlockingRegionEnd() {}
+#endif
+
+} // namespace __tsan
+
+static ThreadSignalContext *SigCtx(ThreadState *thr) {
+ ThreadSignalContext *ctx = (ThreadSignalContext*)thr->signal_ctx;
+ if (ctx == 0 && !thr->is_dead) {
+ ctx = (ThreadSignalContext*)MmapOrDie(sizeof(*ctx), "ThreadSignalContext");
+ MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx));
+ thr->signal_ctx = ctx;
+ }
+ return ctx;
+}
+
+ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname,
+ uptr pc)
+ : thr_(thr), pc_(pc), in_ignored_lib_(false), ignoring_(false) {
+ Initialize(thr);
+ if (!thr_->is_inited) return;
+ if (!thr_->ignore_interceptors) FuncEntry(thr, pc);
+ DPrintf("#%d: intercept %s()\n", thr_->tid, fname);
+ ignoring_ =
+ !thr_->in_ignored_lib && libignore()->IsIgnored(pc, &in_ignored_lib_);
+ EnableIgnores();
+}
+
+ScopedInterceptor::~ScopedInterceptor() {
+ if (!thr_->is_inited) return;
+ DisableIgnores();
+ if (!thr_->ignore_interceptors) {
+ ProcessPendingSignals(thr_);
+ FuncExit(thr_);
+ CheckNoLocks(thr_);
+ }
+}
+
+void ScopedInterceptor::EnableIgnores() {
+ if (ignoring_) {
+ ThreadIgnoreBegin(thr_, pc_, /*save_stack=*/false);
+ if (flags()->ignore_noninstrumented_modules) thr_->suppress_reports++;
+ if (in_ignored_lib_) {
+ DCHECK(!thr_->in_ignored_lib);
+ thr_->in_ignored_lib = true;
+ }
+ }
+}
+
+void ScopedInterceptor::DisableIgnores() {
+ if (ignoring_) {
+ ThreadIgnoreEnd(thr_, pc_);
+ if (flags()->ignore_noninstrumented_modules) thr_->suppress_reports--;
+ if (in_ignored_lib_) {
+ DCHECK(thr_->in_ignored_lib);
+ thr_->in_ignored_lib = false;
+ }
+ }
+}
+
+#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
+#if SANITIZER_FREEBSD
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
+# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(func)
+# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(func)
+#elif SANITIZER_NETBSD
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
+# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(func) \
+ INTERCEPT_FUNCTION(__libc_##func)
+# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(func) \
+ INTERCEPT_FUNCTION(__libc_thr_##func)
+#else
+# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
+# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(func)
+# define TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(func)
+#endif
+
+#define READ_STRING_OF_LEN(thr, pc, s, len, n) \
+ MemoryAccessRange((thr), (pc), (uptr)(s), \
+ common_flags()->strict_string_checks ? (len) + 1 : (n), false)
+
+#define READ_STRING(thr, pc, s, n) \
+ READ_STRING_OF_LEN((thr), (pc), (s), internal_strlen(s), (n))
+
+#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
+
+struct BlockingCall {
+ explicit BlockingCall(ThreadState *thr)
+ : thr(thr)
+ , ctx(SigCtx(thr)) {
+ for (;;) {
+ atomic_store(&ctx->in_blocking_func, 1, memory_order_relaxed);
+ if (atomic_load(&ctx->have_pending_signals, memory_order_relaxed) == 0)
+ break;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ ProcessPendingSignals(thr);
+ }
+ // When we are in a "blocking call", we process signals asynchronously
+ // (right when they arrive). In this context we do not expect to be
+ // executing any user/runtime code. The known interceptor sequence when
+ // this is not true is: pthread_join -> munmap(stack). It's fine
+ // to ignore munmap in this case -- we handle stack shadow separately.
+ thr->ignore_interceptors++;
+ }
+
+ ~BlockingCall() {
+ thr->ignore_interceptors--;
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ }
+
+ ThreadState *thr;
+ ThreadSignalContext *ctx;
+};
+
+TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) {
+ SCOPED_TSAN_INTERCEPTOR(sleep, sec);
+ unsigned res = BLOCK_REAL(sleep)(sec);
+ AfterSleep(thr, pc);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, usleep, long_t usec) {
+ SCOPED_TSAN_INTERCEPTOR(usleep, usec);
+ int res = BLOCK_REAL(usleep)(usec);
+ AfterSleep(thr, pc);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) {
+ SCOPED_TSAN_INTERCEPTOR(nanosleep, req, rem);
+ int res = BLOCK_REAL(nanosleep)(req, rem);
+ AfterSleep(thr, pc);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pause, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(pause, fake);
+ return BLOCK_REAL(pause)(fake);
+}
+
+static void at_exit_wrapper() {
+ AtExitCtx *ctx;
+ {
+ // Ensure thread-safety.
+ BlockingMutexLock l(&interceptor_ctx()->atexit_mu);
+
+ // Pop AtExitCtx from the top of the stack of callback functions
+ uptr element = interceptor_ctx()->AtExitStack.Size() - 1;
+ ctx = interceptor_ctx()->AtExitStack[element];
+ interceptor_ctx()->AtExitStack.PopBack();
+ }
+
+ Acquire(cur_thread(), (uptr)0, (uptr)ctx);
+ ((void(*)())ctx->f)();
+ InternalFree(ctx);
+}
+
+static void cxa_at_exit_wrapper(void *arg) {
+ Acquire(cur_thread(), 0, (uptr)arg);
+ AtExitCtx *ctx = (AtExitCtx*)arg;
+ ((void(*)(void *arg))ctx->f)(ctx->arg);
+ InternalFree(ctx);
+}
+
+static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
+ void *arg, void *dso);
+
+#if !SANITIZER_ANDROID
+TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
+ if (in_symbolizer())
+ return 0;
+ // We want to setup the atexit callback even if we are in ignored lib
+ // or after fork.
+ SCOPED_INTERCEPTOR_RAW(atexit, f);
+ return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0);
+}
+#endif
+
+TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
+ if (in_symbolizer())
+ return 0;
+ SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso);
+ return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso);
+}
+
+static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
+ void *arg, void *dso) {
+ AtExitCtx *ctx = (AtExitCtx*)InternalAlloc(sizeof(AtExitCtx));
+ ctx->f = f;
+ ctx->arg = arg;
+ Release(thr, pc, (uptr)ctx);
+ // Memory allocation in __cxa_atexit will race with free during exit,
+ // because we do not see synchronization around atexit callback list.
+ ThreadIgnoreBegin(thr, pc);
+ int res;
+ if (!dso) {
+ // NetBSD does not preserve the 2nd argument if dso is equal to 0
+ // Store ctx in a local stack-like structure
+
+ // Ensure thread-safety.
+ BlockingMutexLock l(&interceptor_ctx()->atexit_mu);
+
+ res = REAL(__cxa_atexit)((void (*)(void *a))at_exit_wrapper, 0, 0);
+ // Push AtExitCtx on the top of the stack of callback functions
+ if (!res) {
+ interceptor_ctx()->AtExitStack.PushBack(ctx);
+ }
+ } else {
+ res = REAL(__cxa_atexit)(cxa_at_exit_wrapper, ctx, dso);
+ }
+ ThreadIgnoreEnd(thr, pc);
+ return res;
+}
+
+#if !SANITIZER_MAC && !SANITIZER_NETBSD
+static void on_exit_wrapper(int status, void *arg) {
+ ThreadState *thr = cur_thread();
+ uptr pc = 0;
+ Acquire(thr, pc, (uptr)arg);
+ AtExitCtx *ctx = (AtExitCtx*)arg;
+ ((void(*)(int status, void *arg))ctx->f)(status, ctx->arg);
+ InternalFree(ctx);
+}
+
+TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
+ if (in_symbolizer())
+ return 0;
+ SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
+ AtExitCtx *ctx = (AtExitCtx*)InternalAlloc(sizeof(AtExitCtx));
+ ctx->f = (void(*)())f;
+ ctx->arg = arg;
+ Release(thr, pc, (uptr)ctx);
+ // Memory allocation in __cxa_atexit will race with free during exit,
+ // because we do not see synchronization around atexit callback list.
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(on_exit)(on_exit_wrapper, ctx);
+ ThreadIgnoreEnd(thr, pc);
+ return res;
+}
+#define TSAN_MAYBE_INTERCEPT_ON_EXIT TSAN_INTERCEPT(on_exit)
+#else
+#define TSAN_MAYBE_INTERCEPT_ON_EXIT
+#endif
+
+// Cleanup old bufs.
+static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) {
+ for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
+ JmpBuf *buf = &thr->jmp_bufs[i];
+ if (buf->sp <= sp) {
+ uptr sz = thr->jmp_bufs.Size();
+ internal_memcpy(buf, &thr->jmp_bufs[sz - 1], sizeof(*buf));
+ thr->jmp_bufs.PopBack();
+ i--;
+ }
+ }
+}
+
+static void SetJmp(ThreadState *thr, uptr sp) {
+ if (!thr->is_inited) // called from libc guts during bootstrap
+ return;
+ // Cleanup old bufs.
+ JmpBufGarbageCollect(thr, sp);
+ // Remember the buf.
+ JmpBuf *buf = thr->jmp_bufs.PushBack();
+ buf->sp = sp;
+ buf->shadow_stack_pos = thr->shadow_stack_pos;
+ ThreadSignalContext *sctx = SigCtx(thr);
+ buf->int_signal_send = sctx ? sctx->int_signal_send : 0;
+ buf->in_blocking_func = sctx ?
+ atomic_load(&sctx->in_blocking_func, memory_order_relaxed) :
+ false;
+ buf->in_signal_handler = atomic_load(&thr->in_signal_handler,
+ memory_order_relaxed);
+}
+
+static void LongJmp(ThreadState *thr, uptr *env) {
+ uptr sp = ExtractLongJmpSp(env);
+ // Find the saved buf with matching sp.
+ for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
+ JmpBuf *buf = &thr->jmp_bufs[i];
+ if (buf->sp == sp) {
+ CHECK_GE(thr->shadow_stack_pos, buf->shadow_stack_pos);
+ // Unwind the stack.
+ while (thr->shadow_stack_pos > buf->shadow_stack_pos)
+ FuncExit(thr);
+ ThreadSignalContext *sctx = SigCtx(thr);
+ if (sctx) {
+ sctx->int_signal_send = buf->int_signal_send;
+ atomic_store(&sctx->in_blocking_func, buf->in_blocking_func,
+ memory_order_relaxed);
+ }
+ atomic_store(&thr->in_signal_handler, buf->in_signal_handler,
+ memory_order_relaxed);
+ JmpBufGarbageCollect(thr, buf->sp - 1); // do not collect buf->sp
+ return;
+ }
+ }
+ Printf("ThreadSanitizer: can't find longjmp buf\n");
+ CHECK(0);
+}
+
+// FIXME: put everything below into a common extern "C" block?
+extern "C" void __tsan_setjmp(uptr sp) {
+ cur_thread_init();
+ SetJmp(cur_thread(), sp);
+}
+
+#if SANITIZER_MAC
+TSAN_INTERCEPTOR(int, setjmp, void *env);
+TSAN_INTERCEPTOR(int, _setjmp, void *env);
+TSAN_INTERCEPTOR(int, sigsetjmp, void *env);
+#else // SANITIZER_MAC
+
+#if SANITIZER_NETBSD
+#define setjmp_symname __setjmp14
+#define sigsetjmp_symname __sigsetjmp14
+#else
+#define setjmp_symname setjmp
+#define sigsetjmp_symname sigsetjmp
+#endif
+
+#define TSAN_INTERCEPTOR_SETJMP_(x) __interceptor_ ## x
+#define TSAN_INTERCEPTOR_SETJMP__(x) TSAN_INTERCEPTOR_SETJMP_(x)
+#define TSAN_INTERCEPTOR_SETJMP TSAN_INTERCEPTOR_SETJMP__(setjmp_symname)
+#define TSAN_INTERCEPTOR_SIGSETJMP TSAN_INTERCEPTOR_SETJMP__(sigsetjmp_symname)
+
+#define TSAN_STRING_SETJMP SANITIZER_STRINGIFY(setjmp_symname)
+#define TSAN_STRING_SIGSETJMP SANITIZER_STRINGIFY(sigsetjmp_symname)
+
+// Not called. Merely to satisfy TSAN_INTERCEPT().
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int TSAN_INTERCEPTOR_SETJMP(void *env);
+extern "C" int TSAN_INTERCEPTOR_SETJMP(void *env) {
+ CHECK(0);
+ return 0;
+}
+
+// FIXME: any reason to have a separate declaration?
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int __interceptor__setjmp(void *env);
+extern "C" int __interceptor__setjmp(void *env) {
+ CHECK(0);
+ return 0;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int TSAN_INTERCEPTOR_SIGSETJMP(void *env);
+extern "C" int TSAN_INTERCEPTOR_SIGSETJMP(void *env) {
+ CHECK(0);
+ return 0;
+}
+
+#if !SANITIZER_NETBSD
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int __interceptor___sigsetjmp(void *env);
+extern "C" int __interceptor___sigsetjmp(void *env) {
+ CHECK(0);
+ return 0;
+}
+#endif
+
+extern "C" int setjmp_symname(void *env);
+extern "C" int _setjmp(void *env);
+extern "C" int sigsetjmp_symname(void *env);
+#if !SANITIZER_NETBSD
+extern "C" int __sigsetjmp(void *env);
+#endif
+DEFINE_REAL(int, setjmp_symname, void *env)
+DEFINE_REAL(int, _setjmp, void *env)
+DEFINE_REAL(int, sigsetjmp_symname, void *env)
+#if !SANITIZER_NETBSD
+DEFINE_REAL(int, __sigsetjmp, void *env)
+#endif
+#endif // SANITIZER_MAC
+
+#if SANITIZER_NETBSD
+#define longjmp_symname __longjmp14
+#define siglongjmp_symname __siglongjmp14
+#else
+#define longjmp_symname longjmp
+#define siglongjmp_symname siglongjmp
+#endif
+
+TSAN_INTERCEPTOR(void, longjmp_symname, uptr *env, int val) {
+ // Note: if we call REAL(longjmp) in the context of ScopedInterceptor,
+ // bad things will happen. We will jump over ScopedInterceptor dtor and can
+ // leave thr->in_ignored_lib set.
+ {
+ SCOPED_INTERCEPTOR_RAW(longjmp_symname, env, val);
+ }
+ LongJmp(cur_thread(), env);
+ REAL(longjmp_symname)(env, val);
+}
+
+TSAN_INTERCEPTOR(void, siglongjmp_symname, uptr *env, int val) {
+ {
+ SCOPED_INTERCEPTOR_RAW(siglongjmp_symname, env, val);
+ }
+ LongJmp(cur_thread(), env);
+ REAL(siglongjmp_symname)(env, val);
+}
+
+#if SANITIZER_NETBSD
+TSAN_INTERCEPTOR(void, _longjmp, uptr *env, int val) {
+ {
+ SCOPED_INTERCEPTOR_RAW(_longjmp, env, val);
+ }
+ LongJmp(cur_thread(), env);
+ REAL(_longjmp)(env, val);
+}
+#endif
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(void*, malloc, uptr size) {
+ if (in_symbolizer())
+ return InternalAlloc(size);
+ void *p = 0;
+ {
+ SCOPED_INTERCEPTOR_RAW(malloc, size);
+ p = user_alloc(thr, pc, size);
+ }
+ invoke_malloc_hook(p, size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) {
+ SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz);
+ return user_memalign(thr, pc, align, sz);
+}
+
+TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) {
+ if (in_symbolizer())
+ return InternalCalloc(size, n);
+ void *p = 0;
+ {
+ SCOPED_INTERCEPTOR_RAW(calloc, size, n);
+ p = user_calloc(thr, pc, size, n);
+ }
+ invoke_malloc_hook(p, n * size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) {
+ if (in_symbolizer())
+ return InternalRealloc(p, size);
+ if (p)
+ invoke_free_hook(p);
+ {
+ SCOPED_INTERCEPTOR_RAW(realloc, p, size);
+ p = user_realloc(thr, pc, p, size);
+ }
+ invoke_malloc_hook(p, size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void*, reallocarray, void *p, uptr size, uptr n) {
+ if (in_symbolizer())
+ return InternalReallocArray(p, size, n);
+ if (p)
+ invoke_free_hook(p);
+ {
+ SCOPED_INTERCEPTOR_RAW(reallocarray, p, size, n);
+ p = user_reallocarray(thr, pc, p, size, n);
+ }
+ invoke_malloc_hook(p, size);
+ return p;
+}
+
+TSAN_INTERCEPTOR(void, free, void *p) {
+ if (p == 0)
+ return;
+ if (in_symbolizer())
+ return InternalFree(p);
+ invoke_free_hook(p);
+ SCOPED_INTERCEPTOR_RAW(free, p);
+ user_free(thr, pc, p);
+}
+
+TSAN_INTERCEPTOR(void, cfree, void *p) {
+ if (p == 0)
+ return;
+ if (in_symbolizer())
+ return InternalFree(p);
+ invoke_free_hook(p);
+ SCOPED_INTERCEPTOR_RAW(cfree, p);
+ user_free(thr, pc, p);
+}
+
+TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) {
+ SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p);
+ return user_alloc_usable_size(p);
+}
+#endif
+
+TSAN_INTERCEPTOR(char*, strcpy, char *dst, const char *src) { // NOLINT
+ SCOPED_TSAN_INTERCEPTOR(strcpy, dst, src); // NOLINT
+ uptr srclen = internal_strlen(src);
+ MemoryAccessRange(thr, pc, (uptr)dst, srclen + 1, true);
+ MemoryAccessRange(thr, pc, (uptr)src, srclen + 1, false);
+ return REAL(strcpy)(dst, src); // NOLINT
+}
+
+TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) {
+ SCOPED_TSAN_INTERCEPTOR(strncpy, dst, src, n);
+ uptr srclen = internal_strnlen(src, n);
+ MemoryAccessRange(thr, pc, (uptr)dst, n, true);
+ MemoryAccessRange(thr, pc, (uptr)src, min(srclen + 1, n), false);
+ return REAL(strncpy)(dst, src, n);
+}
+
+TSAN_INTERCEPTOR(char*, strdup, const char *str) {
+ SCOPED_TSAN_INTERCEPTOR(strdup, str);
+ // strdup will call malloc, so no instrumentation is required here.
+ return REAL(strdup)(str);
+}
+
+static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
+ if (*addr) {
+ if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) {
+ if (flags & MAP_FIXED) {
+ errno = errno_EINVAL;
+ return false;
+ } else {
+ *addr = 0;
+ }
+ }
+ }
+ return true;
+}
+
+template <class Mmap>
+static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap,
+ void *addr, SIZE_T sz, int prot, int flags,
+ int fd, OFF64_T off) {
+ if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED;
+ void *res = real_mmap(addr, sz, prot, flags, fd, off);
+ if (res != MAP_FAILED) {
+ if (fd > 0) FdAccess(thr, pc, fd);
+ if (thr->ignore_reads_and_writes == 0)
+ MemoryRangeImitateWrite(thr, pc, (uptr)res, sz);
+ else
+ MemoryResetRange(thr, pc, (uptr)res, sz);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
+ SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
+ if (sz != 0) {
+ // If sz == 0, munmap will return EINVAL and don't unmap any memory.
+ DontNeedShadowFor((uptr)addr, sz);
+ ScopedGlobalProcessor sgp;
+ ctx->metamap.ResetRange(thr->proc(), (uptr)addr, (uptr)sz);
+ }
+ int res = REAL(munmap)(addr, sz);
+ return res;
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
+ SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
+ return user_memalign(thr, pc, align, sz);
+}
+#define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign)
+#else
+#define TSAN_MAYBE_INTERCEPT_MEMALIGN
+#endif
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
+ if (in_symbolizer())
+ return InternalAlloc(sz, nullptr, align);
+ SCOPED_INTERCEPTOR_RAW(aligned_alloc, align, sz);
+ return user_aligned_alloc(thr, pc, align, sz);
+}
+
+TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
+ if (in_symbolizer())
+ return InternalAlloc(sz, nullptr, GetPageSizeCached());
+ SCOPED_INTERCEPTOR_RAW(valloc, sz);
+ return user_valloc(thr, pc, sz);
+}
+#endif
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
+ if (in_symbolizer()) {
+ uptr PageSize = GetPageSizeCached();
+ sz = sz ? RoundUpTo(sz, PageSize) : PageSize;
+ return InternalAlloc(sz, nullptr, PageSize);
+ }
+ SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
+ return user_pvalloc(thr, pc, sz);
+}
+#define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc)
+#else
+#define TSAN_MAYBE_INTERCEPT_PVALLOC
+#endif
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
+ if (in_symbolizer()) {
+ void *p = InternalAlloc(sz, nullptr, align);
+ if (!p)
+ return errno_ENOMEM;
+ *memptr = p;
+ return 0;
+ }
+ SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
+ return user_posix_memalign(thr, pc, memptr, align, sz);
+}
+#endif
+
+// __cxa_guard_acquire and friends need to be intercepted in a special way -
+// regular interceptors will break statically-linked libstdc++. Linux
+// interceptors are especially defined as weak functions (so that they don't
+// cause link errors when user defines them as well). So they silently
+// auto-disable themselves when such symbol is already present in the binary. If
+// we link libstdc++ statically, it will bring own __cxa_guard_acquire which
+// will silently replace our interceptor. That's why on Linux we simply export
+// these interceptors with INTERFACE_ATTRIBUTE.
+// On OS X, we don't support statically linking, so we just use a regular
+// interceptor.
+#if SANITIZER_MAC
+#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
+#else
+#define STDCXX_INTERCEPTOR(rettype, name, ...) \
+ extern "C" rettype INTERFACE_ATTRIBUTE name(__VA_ARGS__)
+#endif
+
+// Used in thread-safe function static initialization.
+STDCXX_INTERCEPTOR(int, __cxa_guard_acquire, atomic_uint32_t *g) {
+ SCOPED_INTERCEPTOR_RAW(__cxa_guard_acquire, g);
+ OnPotentiallyBlockingRegionBegin();
+ auto on_exit = at_scope_exit(&OnPotentiallyBlockingRegionEnd);
+ for (;;) {
+ u32 cmp = atomic_load(g, memory_order_acquire);
+ if (cmp == 0) {
+ if (atomic_compare_exchange_strong(g, &cmp, 1<<16, memory_order_relaxed))
+ return 1;
+ } else if (cmp == 1) {
+ Acquire(thr, pc, (uptr)g);
+ return 0;
+ } else {
+ internal_sched_yield();
+ }
+ }
+}
+
+STDCXX_INTERCEPTOR(void, __cxa_guard_release, atomic_uint32_t *g) {
+ SCOPED_INTERCEPTOR_RAW(__cxa_guard_release, g);
+ Release(thr, pc, (uptr)g);
+ atomic_store(g, 1, memory_order_release);
+}
+
+STDCXX_INTERCEPTOR(void, __cxa_guard_abort, atomic_uint32_t *g) {
+ SCOPED_INTERCEPTOR_RAW(__cxa_guard_abort, g);
+ atomic_store(g, 0, memory_order_relaxed);
+}
+
+namespace __tsan {
+void DestroyThreadState() {
+ ThreadState *thr = cur_thread();
+ Processor *proc = thr->proc();
+ ThreadFinish(thr);
+ ProcUnwire(proc, thr);
+ ProcDestroy(proc);
+ ThreadSignalContext *sctx = thr->signal_ctx;
+ if (sctx) {
+ thr->signal_ctx = 0;
+ UnmapOrDie(sctx, sizeof(*sctx));
+ }
+ DTLS_Destroy();
+ cur_thread_finalize();
+}
+} // namespace __tsan
+
+#if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD
+static void thread_finalize(void *v) {
+ uptr iter = (uptr)v;
+ if (iter > 1) {
+ if (pthread_setspecific(interceptor_ctx()->finalize_key,
+ (void*)(iter - 1))) {
+ Printf("ThreadSanitizer: failed to set thread key\n");
+ Die();
+ }
+ return;
+ }
+ DestroyThreadState();
+}
+#endif
+
+
+struct ThreadParam {
+ void* (*callback)(void *arg);
+ void *param;
+ atomic_uintptr_t tid;
+};
+
+extern "C" void *__tsan_thread_start_func(void *arg) {
+ ThreadParam *p = (ThreadParam*)arg;
+ void* (*callback)(void *arg) = p->callback;
+ void *param = p->param;
+ int tid = 0;
+ {
+ cur_thread_init();
+ ThreadState *thr = cur_thread();
+ // Thread-local state is not initialized yet.
+ ScopedIgnoreInterceptors ignore;
+#if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD
+ ThreadIgnoreBegin(thr, 0);
+ if (pthread_setspecific(interceptor_ctx()->finalize_key,
+ (void *)GetPthreadDestructorIterations())) {
+ Printf("ThreadSanitizer: failed to set thread key\n");
+ Die();
+ }
+ ThreadIgnoreEnd(thr, 0);
+#endif
+ while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
+ internal_sched_yield();
+ Processor *proc = ProcCreate();
+ ProcWire(proc, thr);
+ ThreadStart(thr, tid, GetTid(), ThreadType::Regular);
+ atomic_store(&p->tid, 0, memory_order_release);
+ }
+ void *res = callback(param);
+ // Prevent the callback from being tail called,
+ // it mixes up stack traces.
+ volatile int foo = 42;
+ foo++;
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_create,
+ void *th, void *attr, void *(*callback)(void*), void * param) {
+ SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param);
+
+ MaybeSpawnBackgroundThread();
+
+ if (ctx->after_multithreaded_fork) {
+ if (flags()->die_after_fork) {
+ Report("ThreadSanitizer: starting new threads after multi-threaded "
+ "fork is not supported. Dying (set die_after_fork=0 to override)\n");
+ Die();
+ } else {
+ VPrintf(1, "ThreadSanitizer: starting new threads after multi-threaded "
+ "fork is not supported (pid %d). Continuing because of "
+ "die_after_fork=0, but you are on your own\n", internal_getpid());
+ }
+ }
+ __sanitizer_pthread_attr_t myattr;
+ if (attr == 0) {
+ pthread_attr_init(&myattr);
+ attr = &myattr;
+ }
+ int detached = 0;
+ REAL(pthread_attr_getdetachstate)(attr, &detached);
+ AdjustStackSize(attr);
+
+ ThreadParam p;
+ p.callback = callback;
+ p.param = param;
+ atomic_store(&p.tid, 0, memory_order_relaxed);
+ int res = -1;
+ {
+ // Otherwise we see false positives in pthread stack manipulation.
+ ScopedIgnoreInterceptors ignore;
+ ThreadIgnoreBegin(thr, pc);
+ res = REAL(pthread_create)(th, attr, __tsan_thread_start_func, &p);
+ ThreadIgnoreEnd(thr, pc);
+ }
+ if (res == 0) {
+ int tid = ThreadCreate(thr, pc, *(uptr*)th, IsStateDetached(detached));
+ CHECK_NE(tid, 0);
+ // Synchronization on p.tid serves two purposes:
+ // 1. ThreadCreate must finish before the new thread starts.
+ // Otherwise the new thread can call pthread_detach, but the pthread_t
+ // identifier is not yet registered in ThreadRegistry by ThreadCreate.
+ // 2. ThreadStart must finish before this thread continues.
+ // Otherwise, this thread can call pthread_detach and reset thr->sync
+ // before the new thread got a chance to acquire from it in ThreadStart.
+ atomic_store(&p.tid, tid, memory_order_release);
+ while (atomic_load(&p.tid, memory_order_acquire) != 0)
+ internal_sched_yield();
+ }
+ if (attr == &myattr)
+ pthread_attr_destroy(&myattr);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
+ SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ ThreadIgnoreBegin(thr, pc);
+ int res = BLOCK_REAL(pthread_join)(th, ret);
+ ThreadIgnoreEnd(thr, pc);
+ if (res == 0) {
+ ThreadJoin(thr, pc, tid);
+ }
+ return res;
+}
+
+DEFINE_REAL_PTHREAD_FUNCTIONS
+
+TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_detach, th);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ int res = REAL(pthread_detach)(th);
+ if (res == 0) {
+ ThreadDetach(thr, pc, tid);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(void, pthread_exit, void *retval) {
+ {
+ SCOPED_INTERCEPTOR_RAW(pthread_exit, retval);
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
+ CHECK_EQ(thr, &cur_thread_placeholder);
+#endif
+ }
+ REAL(pthread_exit)(retval);
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_tryjoin_np, th, ret);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(pthread_tryjoin_np)(th, ret);
+ ThreadIgnoreEnd(thr, pc);
+ if (res == 0)
+ ThreadJoin(thr, pc, tid);
+ else
+ ThreadNotJoined(thr, pc, tid, (uptr)th);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
+ const struct timespec *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_timedjoin_np, th, ret, abstime);
+ int tid = ThreadTid(thr, pc, (uptr)th);
+ ThreadIgnoreBegin(thr, pc);
+ int res = BLOCK_REAL(pthread_timedjoin_np)(th, ret, abstime);
+ ThreadIgnoreEnd(thr, pc);
+ if (res == 0)
+ ThreadJoin(thr, pc, tid);
+ else
+ ThreadNotJoined(thr, pc, tid, (uptr)th);
+ return res;
+}
+#endif
+
+// Problem:
+// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2).
+// pthread_cond_t has different size in the different versions.
+// If call new REAL functions for old pthread_cond_t, they will corrupt memory
+// after pthread_cond_t (old cond is smaller).
+// If we call old REAL functions for new pthread_cond_t, we will lose some
+// functionality (e.g. old functions do not support waiting against
+// CLOCK_REALTIME).
+// Proper handling would require to have 2 versions of interceptors as well.
+// But this is messy, in particular requires linker scripts when sanitizer
+// runtime is linked into a shared library.
+// Instead we assume we don't have dynamic libraries built against old
+// pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag
+// that allows to work with old libraries (but this mode does not support
+// some features, e.g. pthread_condattr_getpshared).
+static void *init_cond(void *c, bool force = false) {
+ // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions.
+ // So we allocate additional memory on the side large enough to hold
+ // any pthread_cond_t object. Always call new REAL functions, but pass
+ // the aux object to them.
+ // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes
+ // first word of pthread_cond_t to zero.
+ // It's all relevant only for linux.
+ if (!common_flags()->legacy_pthread_cond)
+ return c;
+ atomic_uintptr_t *p = (atomic_uintptr_t*)c;
+ uptr cond = atomic_load(p, memory_order_acquire);
+ if (!force && cond != 0)
+ return (void*)cond;
+ void *newcond = WRAP(malloc)(pthread_cond_t_sz);
+ internal_memset(newcond, 0, pthread_cond_t_sz);
+ if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond,
+ memory_order_acq_rel))
+ return newcond;
+ WRAP(free)(newcond);
+ return (void*)cond;
+}
+
+struct CondMutexUnlockCtx {
+ ScopedInterceptor *si;
+ ThreadState *thr;
+ uptr pc;
+ void *m;
+};
+
+static void cond_mutex_unlock(CondMutexUnlockCtx *arg) {
+ // pthread_cond_wait interceptor has enabled async signal delivery
+ // (see BlockingCall below). Disable async signals since we are running
+ // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run
+ // since the thread is cancelled, so we have to manually execute them
+ // (the thread still can run some user code due to pthread_cleanup_push).
+ ThreadSignalContext *ctx = SigCtx(arg->thr);
+ CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1);
+ atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed);
+ MutexPostLock(arg->thr, arg->pc, (uptr)arg->m, MutexFlagDoPreLockOnPostLock);
+ // Undo BlockingCall ctor effects.
+ arg->thr->ignore_interceptors--;
+ arg->si->~ScopedInterceptor();
+}
+
+INTERCEPTOR(int, pthread_cond_init, void *c, void *a) {
+ void *cond = init_cond(c, true);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, cond, a);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
+ return REAL(pthread_cond_init)(cond, a);
+}
+
+static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si,
+ int (*fn)(void *c, void *m, void *abstime), void *c,
+ void *m, void *t) {
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ MutexUnlock(thr, pc, (uptr)m);
+ CondMutexUnlockCtx arg = {si, thr, pc, m};
+ int res = 0;
+ // This ensures that we handle mutex lock even in case of pthread_cancel.
+ // See test/tsan/cond_cancel.cpp.
+ {
+ // Enable signal delivery while the thread is blocked.
+ BlockingCall bc(thr);
+ res = call_pthread_cancel_with_cleanup(
+ fn, c, m, t, (void (*)(void *arg))cond_mutex_unlock, &arg);
+ }
+ if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m);
+ MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock);
+ return res;
+}
+
+INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m);
+ return cond_wait(thr, pc, &si, (int (*)(void *c, void *m, void *abstime))REAL(
+ pthread_cond_wait),
+ cond, m, 0);
+}
+
+INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime);
+ return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait), cond, m,
+ abstime);
+}
+
+#if SANITIZER_MAC
+INTERCEPTOR(int, pthread_cond_timedwait_relative_np, void *c, void *m,
+ void *reltime) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait_relative_np, cond, m, reltime);
+ return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait_relative_np), cond,
+ m, reltime);
+}
+#endif
+
+INTERCEPTOR(int, pthread_cond_signal, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ return REAL(pthread_cond_signal)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_broadcast, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false);
+ return REAL(pthread_cond_broadcast)(cond);
+}
+
+INTERCEPTOR(int, pthread_cond_destroy, void *c) {
+ void *cond = init_cond(c);
+ SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, cond);
+ MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), true);
+ int res = REAL(pthread_cond_destroy)(cond);
+ if (common_flags()->legacy_pthread_cond) {
+ // Free our aux cond and zero the pointer to not leave dangling pointers.
+ WRAP(free)(cond);
+ atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_init, void *m, void *a) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_init, m, a);
+ int res = REAL(pthread_mutex_init)(m, a);
+ if (res == 0) {
+ u32 flagz = 0;
+ if (a) {
+ int type = 0;
+ if (REAL(pthread_mutexattr_gettype)(a, &type) == 0)
+ if (type == PTHREAD_MUTEX_RECURSIVE ||
+ type == PTHREAD_MUTEX_RECURSIVE_NP)
+ flagz |= MutexFlagWriteReentrant;
+ }
+ MutexCreate(thr, pc, (uptr)m, flagz);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_destroy, m);
+ int res = REAL(pthread_mutex_destroy)(m);
+ if (res == 0 || res == errno_EBUSY) {
+ MutexDestroy(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m);
+ int res = REAL(pthread_mutex_trylock)(m);
+ if (res == errno_EOWNERDEAD)
+ MutexRepair(thr, pc, (uptr)m);
+ if (res == 0 || res == errno_EOWNERDEAD)
+ MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
+ return res;
+}
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_mutex_timedlock, m, abstime);
+ int res = REAL(pthread_mutex_timedlock)(m, abstime);
+ if (res == 0) {
+ MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
+ }
+ return res;
+}
+#endif
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared);
+ int res = REAL(pthread_spin_init)(m, pshared);
+ if (res == 0) {
+ MutexCreate(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_destroy, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_destroy, m);
+ int res = REAL(pthread_spin_destroy)(m);
+ if (res == 0) {
+ MutexDestroy(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_lock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_lock, m);
+ MutexPreLock(thr, pc, (uptr)m);
+ int res = REAL(pthread_spin_lock)(m);
+ if (res == 0) {
+ MutexPostLock(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_trylock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_trylock, m);
+ int res = REAL(pthread_spin_trylock)(m);
+ if (res == 0) {
+ MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_spin_unlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_spin_unlock, m);
+ MutexUnlock(thr, pc, (uptr)m);
+ int res = REAL(pthread_spin_unlock)(m);
+ return res;
+}
+#endif
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_init, void *m, void *a) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_init, m, a);
+ int res = REAL(pthread_rwlock_init)(m, a);
+ if (res == 0) {
+ MutexCreate(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_destroy, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_destroy, m);
+ int res = REAL(pthread_rwlock_destroy)(m);
+ if (res == 0) {
+ MutexDestroy(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_rdlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_rdlock, m);
+ MutexPreReadLock(thr, pc, (uptr)m);
+ int res = REAL(pthread_rwlock_rdlock)(m);
+ if (res == 0) {
+ MutexPostReadLock(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_tryrdlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_tryrdlock, m);
+ int res = REAL(pthread_rwlock_tryrdlock)(m);
+ if (res == 0) {
+ MutexPostReadLock(thr, pc, (uptr)m, MutexFlagTryLock);
+ }
+ return res;
+}
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, pthread_rwlock_timedrdlock, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedrdlock, m, abstime);
+ int res = REAL(pthread_rwlock_timedrdlock)(m, abstime);
+ if (res == 0) {
+ MutexPostReadLock(thr, pc, (uptr)m);
+ }
+ return res;
+}
+#endif
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_wrlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_wrlock, m);
+ MutexPreLock(thr, pc, (uptr)m);
+ int res = REAL(pthread_rwlock_wrlock)(m);
+ if (res == 0) {
+ MutexPostLock(thr, pc, (uptr)m);
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_trywrlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_trywrlock, m);
+ int res = REAL(pthread_rwlock_trywrlock)(m);
+ if (res == 0) {
+ MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
+ }
+ return res;
+}
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, pthread_rwlock_timedwrlock, void *m, void *abstime) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_timedwrlock, m, abstime);
+ int res = REAL(pthread_rwlock_timedwrlock)(m, abstime);
+ if (res == 0) {
+ MutexPostLock(thr, pc, (uptr)m, MutexFlagTryLock);
+ }
+ return res;
+}
+#endif
+
+TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_rwlock_unlock, m);
+ MutexReadOrWriteUnlock(thr, pc, (uptr)m);
+ int res = REAL(pthread_rwlock_unlock)(m);
+ return res;
+}
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, pthread_barrier_init, void *b, void *a, unsigned count) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_barrier_init, b, a, count);
+ MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
+ int res = REAL(pthread_barrier_init)(b, a, count);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_destroy, void *b) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_barrier_destroy, b);
+ MemoryWrite(thr, pc, (uptr)b, kSizeLog1);
+ int res = REAL(pthread_barrier_destroy)(b);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_barrier_wait, b);
+ Release(thr, pc, (uptr)b);
+ MemoryRead(thr, pc, (uptr)b, kSizeLog1);
+ int res = REAL(pthread_barrier_wait)(b);
+ MemoryRead(thr, pc, (uptr)b, kSizeLog1);
+ if (res == 0 || res == PTHREAD_BARRIER_SERIAL_THREAD) {
+ Acquire(thr, pc, (uptr)b);
+ }
+ return res;
+}
+#endif
+
+TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) {
+ SCOPED_INTERCEPTOR_RAW(pthread_once, o, f);
+ if (o == 0 || f == 0)
+ return errno_EINVAL;
+ atomic_uint32_t *a;
+
+ if (SANITIZER_MAC)
+ a = static_cast<atomic_uint32_t*>((void *)((char *)o + sizeof(long_t)));
+ else if (SANITIZER_NETBSD)
+ a = static_cast<atomic_uint32_t*>
+ ((void *)((char *)o + __sanitizer::pthread_mutex_t_sz));
+ else
+ a = static_cast<atomic_uint32_t*>(o);
+
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 && atomic_compare_exchange_strong(a, &v, 1,
+ memory_order_relaxed)) {
+ (*f)();
+ if (!thr->in_ignored_lib)
+ Release(thr, pc, (uptr)o);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ internal_sched_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ if (!thr->in_ignored_lib)
+ Acquire(thr, pc, (uptr)o);
+ }
+ return 0;
+}
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
+ SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(__fxstat)(version, fd, buf);
+}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat)
+#else
+#define TSAN_MAYBE_INTERCEPT___FXSTAT
+#endif
+
+TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
+#if SANITIZER_FREEBSD || SANITIZER_MAC || SANITIZER_ANDROID || SANITIZER_NETBSD
+ SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(fstat)(fd, buf);
+#else
+ SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(__fxstat)(0, fd, buf);
+#endif
+}
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
+ SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(__fxstat64)(version, fd, buf);
+}
+#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT___FXSTAT64
+#endif
+
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
+ SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
+ if (fd > 0)
+ FdAccess(thr, pc, fd);
+ return REAL(__fxstat64)(0, fd, buf);
+}
+#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_FSTAT64
+#endif
+
+TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
+ READ_STRING(thr, pc, name, 0);
+ int fd = REAL(open)(name, flags, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
+ READ_STRING(thr, pc, name, 0);
+ int fd = REAL(open64)(name, flags, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+#define TSAN_MAYBE_INTERCEPT_OPEN64 TSAN_INTERCEPT(open64)
+#else
+#define TSAN_MAYBE_INTERCEPT_OPEN64
+#endif
+
+TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
+ READ_STRING(thr, pc, name, 0);
+ int fd = REAL(creat)(name, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
+ SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
+ READ_STRING(thr, pc, name, 0);
+ int fd = REAL(creat64)(name, mode);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ return fd;
+}
+#define TSAN_MAYBE_INTERCEPT_CREAT64 TSAN_INTERCEPT(creat64)
+#else
+#define TSAN_MAYBE_INTERCEPT_CREAT64
+#endif
+
+TSAN_INTERCEPTOR(int, dup, int oldfd) {
+ SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
+ int newfd = REAL(dup)(oldfd);
+ if (oldfd >= 0 && newfd >= 0 && newfd != oldfd)
+ FdDup(thr, pc, oldfd, newfd, true);
+ return newfd;
+}
+
+TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) {
+ SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd);
+ int newfd2 = REAL(dup2)(oldfd, newfd);
+ if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
+ FdDup(thr, pc, oldfd, newfd2, false);
+ return newfd2;
+}
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags);
+ int newfd2 = REAL(dup3)(oldfd, newfd, flags);
+ if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd)
+ FdDup(thr, pc, oldfd, newfd2, false);
+ return newfd2;
+}
+#endif
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
+ int fd = REAL(eventfd)(initval, flags);
+ if (fd >= 0)
+ FdEventCreate(thr, pc, fd);
+ return fd;
+}
+#define TSAN_MAYBE_INTERCEPT_EVENTFD TSAN_INTERCEPT(eventfd)
+#else
+#define TSAN_MAYBE_INTERCEPT_EVENTFD
+#endif
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ fd = REAL(signalfd)(fd, mask, flags);
+ if (fd >= 0)
+ FdSignalCreate(thr, pc, fd);
+ return fd;
+}
+#define TSAN_MAYBE_INTERCEPT_SIGNALFD TSAN_INTERCEPT(signalfd)
+#else
+#define TSAN_MAYBE_INTERCEPT_SIGNALFD
+#endif
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, inotify_init, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
+ int fd = REAL(inotify_init)(fake);
+ if (fd >= 0)
+ FdInotifyCreate(thr, pc, fd);
+ return fd;
+}
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT TSAN_INTERCEPT(inotify_init)
+#else
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
+#endif
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
+ int fd = REAL(inotify_init1)(flags);
+ if (fd >= 0)
+ FdInotifyCreate(thr, pc, fd);
+ return fd;
+}
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 TSAN_INTERCEPT(inotify_init1)
+#else
+#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1
+#endif
+
+TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
+ SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
+ int fd = REAL(socket)(domain, type, protocol);
+ if (fd >= 0)
+ FdSocketCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int *fd) {
+ SCOPED_TSAN_INTERCEPTOR(socketpair, domain, type, protocol, fd);
+ int res = REAL(socketpair)(domain, type, protocol, fd);
+ if (res == 0 && fd[0] >= 0 && fd[1] >= 0)
+ FdPipeCreate(thr, pc, fd[0], fd[1]);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, connect, int fd, void *addr, unsigned addrlen) {
+ SCOPED_TSAN_INTERCEPTOR(connect, fd, addr, addrlen);
+ FdSocketConnecting(thr, pc, fd);
+ int res = REAL(connect)(fd, addr, addrlen);
+ if (res == 0 && fd >= 0)
+ FdSocketConnect(thr, pc, fd);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, bind, int fd, void *addr, unsigned addrlen) {
+ SCOPED_TSAN_INTERCEPTOR(bind, fd, addr, addrlen);
+ int res = REAL(bind)(fd, addr, addrlen);
+ if (fd > 0 && res == 0)
+ FdAccess(thr, pc, fd);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, listen, int fd, int backlog) {
+ SCOPED_TSAN_INTERCEPTOR(listen, fd, backlog);
+ int res = REAL(listen)(fd, backlog);
+ if (fd > 0 && res == 0)
+ FdAccess(thr, pc, fd);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, close, int fd) {
+ SCOPED_TSAN_INTERCEPTOR(close, fd);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ return REAL(close)(fd);
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, __close, int fd) {
+ SCOPED_TSAN_INTERCEPTOR(__close, fd);
+ if (fd >= 0)
+ FdClose(thr, pc, fd);
+ return REAL(__close)(fd);
+}
+#define TSAN_MAYBE_INTERCEPT___CLOSE TSAN_INTERCEPT(__close)
+#else
+#define TSAN_MAYBE_INTERCEPT___CLOSE
+#endif
+
+// glibc guts
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
+ SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
+ int fds[64];
+ int cnt = ExtractResolvFDs(state, fds, ARRAY_SIZE(fds));
+ for (int i = 0; i < cnt; i++) {
+ if (fds[i] > 0)
+ FdClose(thr, pc, fds[i]);
+ }
+ REAL(__res_iclose)(state, free_addr);
+}
+#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE TSAN_INTERCEPT(__res_iclose)
+#else
+#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE
+#endif
+
+TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
+ SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
+ int res = REAL(pipe)(pipefd);
+ if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
+ FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
+ return res;
+}
+
+#if !SANITIZER_MAC
+TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(pipe2, pipefd, flags);
+ int res = REAL(pipe2)(pipefd, flags);
+ if (res == 0 && pipefd[0] >= 0 && pipefd[1] >= 0)
+ FdPipeCreate(thr, pc, pipefd[0], pipefd[1]);
+ return res;
+}
+#endif
+
+TSAN_INTERCEPTOR(int, unlink, char *path) {
+ SCOPED_TSAN_INTERCEPTOR(unlink, path);
+ Release(thr, pc, File2addr(path));
+ int res = REAL(unlink)(path);
+ return res;
+}
+
+TSAN_INTERCEPTOR(void*, tmpfile, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(tmpfile, fake);
+ void *res = REAL(tmpfile)(fake);
+ if (res) {
+ int fd = fileno_unlocked(res);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ }
+ return res;
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
+ void *res = REAL(tmpfile64)(fake);
+ if (res) {
+ int fd = fileno_unlocked(res);
+ if (fd >= 0)
+ FdFileCreate(thr, pc, fd);
+ }
+ return res;
+}
+#define TSAN_MAYBE_INTERCEPT_TMPFILE64 TSAN_INTERCEPT(tmpfile64)
+#else
+#define TSAN_MAYBE_INTERCEPT_TMPFILE64
+#endif
+
+static void FlushStreams() {
+ // Flushing all the streams here may freeze the process if a child thread is
+ // performing file stream operations at the same time.
+ REAL(fflush)(stdout);
+ REAL(fflush)(stderr);
+}
+
+TSAN_INTERCEPTOR(void, abort, int fake) {
+ SCOPED_TSAN_INTERCEPTOR(abort, fake);
+ FlushStreams();
+ REAL(abort)(fake);
+}
+
+TSAN_INTERCEPTOR(int, rmdir, char *path) {
+ SCOPED_TSAN_INTERCEPTOR(rmdir, path);
+ Release(thr, pc, Dir2addr(path));
+ int res = REAL(rmdir)(path);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, closedir, void *dirp) {
+ SCOPED_TSAN_INTERCEPTOR(closedir, dirp);
+ if (dirp) {
+ int fd = dirfd(dirp);
+ FdClose(thr, pc, fd);
+ }
+ return REAL(closedir)(dirp);
+}
+
+#if SANITIZER_LINUX
+TSAN_INTERCEPTOR(int, epoll_create, int size) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
+ int fd = REAL(epoll_create)(size);
+ if (fd >= 0)
+ FdPollCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
+ int fd = REAL(epoll_create1)(flags);
+ if (fd >= 0)
+ FdPollCreate(thr, pc, fd);
+ return fd;
+}
+
+TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
+ if (epfd >= 0)
+ FdAccess(thr, pc, epfd);
+ if (epfd >= 0 && fd >= 0)
+ FdAccess(thr, pc, fd);
+ if (op == EPOLL_CTL_ADD && epfd >= 0)
+ FdRelease(thr, pc, epfd);
+ int res = REAL(epoll_ctl)(epfd, op, fd, ev);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
+ if (epfd >= 0)
+ FdAccess(thr, pc, epfd);
+ int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout);
+ if (res > 0 && epfd >= 0)
+ FdAcquire(thr, pc, epfd);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, epoll_pwait, int epfd, void *ev, int cnt, int timeout,
+ void *sigmask) {
+ SCOPED_TSAN_INTERCEPTOR(epoll_pwait, epfd, ev, cnt, timeout, sigmask);
+ if (epfd >= 0)
+ FdAccess(thr, pc, epfd);
+ int res = BLOCK_REAL(epoll_pwait)(epfd, ev, cnt, timeout, sigmask);
+ if (res > 0 && epfd >= 0)
+ FdAcquire(thr, pc, epfd);
+ return res;
+}
+
+#define TSAN_MAYBE_INTERCEPT_EPOLL \
+ TSAN_INTERCEPT(epoll_create); \
+ TSAN_INTERCEPT(epoll_create1); \
+ TSAN_INTERCEPT(epoll_ctl); \
+ TSAN_INTERCEPT(epoll_wait); \
+ TSAN_INTERCEPT(epoll_pwait)
+#else
+#define TSAN_MAYBE_INTERCEPT_EPOLL
+#endif
+
+// The following functions are intercepted merely to process pending signals.
+// If program blocks signal X, we must deliver the signal before the function
+// returns. Similarly, if program unblocks a signal (or returns from sigsuspend)
+// it's better to deliver the signal straight away.
+TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) {
+ SCOPED_TSAN_INTERCEPTOR(sigsuspend, mask);
+ return REAL(sigsuspend)(mask);
+}
+
+TSAN_INTERCEPTOR(int, sigblock, int mask) {
+ SCOPED_TSAN_INTERCEPTOR(sigblock, mask);
+ return REAL(sigblock)(mask);
+}
+
+TSAN_INTERCEPTOR(int, sigsetmask, int mask) {
+ SCOPED_TSAN_INTERCEPTOR(sigsetmask, mask);
+ return REAL(sigsetmask)(mask);
+}
+
+TSAN_INTERCEPTOR(int, pthread_sigmask, int how, const __sanitizer_sigset_t *set,
+ __sanitizer_sigset_t *oldset) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_sigmask, how, set, oldset);
+ return REAL(pthread_sigmask)(how, set, oldset);
+}
+
+namespace __tsan {
+
+static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
+ bool sigact, int sig,
+ __sanitizer_siginfo *info, void *uctx) {
+ __sanitizer_sigaction *sigactions = interceptor_ctx()->sigactions;
+ if (acquire)
+ Acquire(thr, 0, (uptr)&sigactions[sig]);
+ // Signals are generally asynchronous, so if we receive a signals when
+ // ignores are enabled we should disable ignores. This is critical for sync
+ // and interceptors, because otherwise we can miss syncronization and report
+ // false races.
+ int ignore_reads_and_writes = thr->ignore_reads_and_writes;
+ int ignore_interceptors = thr->ignore_interceptors;
+ int ignore_sync = thr->ignore_sync;
+ if (!ctx->after_multithreaded_fork) {
+ thr->ignore_reads_and_writes = 0;
+ thr->fast_state.ClearIgnoreBit();
+ thr->ignore_interceptors = 0;
+ thr->ignore_sync = 0;
+ }
+ // Ensure that the handler does not spoil errno.
+ const int saved_errno = errno;
+ errno = 99;
+ // This code races with sigaction. Be careful to not read sa_sigaction twice.
+ // Also need to remember pc for reporting before the call,
+ // because the handler can reset it.
+ volatile uptr pc =
+ sigact ? (uptr)sigactions[sig].sigaction : (uptr)sigactions[sig].handler;
+ if (pc != sig_dfl && pc != sig_ign) {
+ if (sigact)
+ ((__sanitizer_sigactionhandler_ptr)pc)(sig, info, uctx);
+ else
+ ((__sanitizer_sighandler_ptr)pc)(sig);
+ }
+ if (!ctx->after_multithreaded_fork) {
+ thr->ignore_reads_and_writes = ignore_reads_and_writes;
+ if (ignore_reads_and_writes)
+ thr->fast_state.SetIgnoreBit();
+ thr->ignore_interceptors = ignore_interceptors;
+ thr->ignore_sync = ignore_sync;
+ }
+ // We do not detect errno spoiling for SIGTERM,
+ // because some SIGTERM handlers do spoil errno but reraise SIGTERM,
+ // tsan reports false positive in such case.
+ // It's difficult to properly detect this situation (reraise),
+ // because in async signal processing case (when handler is called directly
+ // from rtl_generic_sighandler) we have not yet received the reraised
+ // signal; and it looks too fragile to intercept all ways to reraise a signal.
+ if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) {
+ VarSizeStackTrace stack;
+ // StackTrace::GetNestInstructionPc(pc) is used because return address is
+ // expected, OutputReport() will undo this.
+ ObtainCurrentStack(thr, StackTrace::GetNextInstructionPc(pc), &stack);
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(ReportTypeErrnoInSignal);
+ if (!IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack)) {
+ rep.AddStack(stack, true);
+ OutputReport(thr, rep);
+ }
+ }
+ errno = saved_errno;
+}
+
+void ProcessPendingSignals(ThreadState *thr) {
+ ThreadSignalContext *sctx = SigCtx(thr);
+ if (sctx == 0 ||
+ atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0)
+ return;
+ atomic_store(&sctx->have_pending_signals, 0, memory_order_relaxed);
+ atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
+ internal_sigfillset(&sctx->emptyset);
+ int res = REAL(pthread_sigmask)(SIG_SETMASK, &sctx->emptyset, &sctx->oldset);
+ CHECK_EQ(res, 0);
+ for (int sig = 0; sig < kSigCount; sig++) {
+ SignalDesc *signal = &sctx->pending_signals[sig];
+ if (signal->armed) {
+ signal->armed = false;
+ CallUserSignalHandler(thr, false, true, signal->sigaction, sig,
+ &signal->siginfo, &signal->ctx);
+ }
+ }
+ res = REAL(pthread_sigmask)(SIG_SETMASK, &sctx->oldset, 0);
+ CHECK_EQ(res, 0);
+ atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
+}
+
+} // namespace __tsan
+
+static bool is_sync_signal(ThreadSignalContext *sctx, int sig) {
+ return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
+ sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS ||
+ // If we are sending signal to ourselves, we must process it now.
+ (sctx && sig == sctx->int_signal_send);
+}
+
+void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig,
+ __sanitizer_siginfo *info,
+ void *ctx) {
+ cur_thread_init();
+ ThreadState *thr = cur_thread();
+ ThreadSignalContext *sctx = SigCtx(thr);
+ if (sig < 0 || sig >= kSigCount) {
+ VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig);
+ return;
+ }
+ // Don't mess with synchronous signals.
+ const bool sync = is_sync_signal(sctx, sig);
+ if (sync ||
+ // If we are in blocking function, we can safely process it now
+ // (but check if we are in a recursive interceptor,
+ // i.e. pthread_join()->munmap()).
+ (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed))) {
+ atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed);
+ if (sctx && atomic_load(&sctx->in_blocking_func, memory_order_relaxed)) {
+ atomic_store(&sctx->in_blocking_func, 0, memory_order_relaxed);
+ CallUserSignalHandler(thr, sync, true, sigact, sig, info, ctx);
+ atomic_store(&sctx->in_blocking_func, 1, memory_order_relaxed);
+ } else {
+ // Be very conservative with when we do acquire in this case.
+ // It's unsafe to do acquire in async handlers, because ThreadState
+ // can be in inconsistent state.
+ // SIGSYS looks relatively safe -- it's synchronous and can actually
+ // need some global state.
+ bool acq = (sig == SIGSYS);
+ CallUserSignalHandler(thr, sync, acq, sigact, sig, info, ctx);
+ }
+ atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed);
+ return;
+ }
+
+ if (sctx == 0)
+ return;
+ SignalDesc *signal = &sctx->pending_signals[sig];
+ if (signal->armed == false) {
+ signal->armed = true;
+ signal->sigaction = sigact;
+ if (info)
+ internal_memcpy(&signal->siginfo, info, sizeof(*info));
+ if (ctx)
+ internal_memcpy(&signal->ctx, ctx, sizeof(signal->ctx));
+ atomic_store(&sctx->have_pending_signals, 1, memory_order_relaxed);
+ }
+}
+
+static void rtl_sighandler(int sig) {
+ rtl_generic_sighandler(false, sig, 0, 0);
+}
+
+static void rtl_sigaction(int sig, __sanitizer_siginfo *info, void *ctx) {
+ rtl_generic_sighandler(true, sig, info, ctx);
+}
+
+TSAN_INTERCEPTOR(int, raise, int sig) {
+ SCOPED_TSAN_INTERCEPTOR(raise, sig);
+ ThreadSignalContext *sctx = SigCtx(thr);
+ CHECK_NE(sctx, 0);
+ int prev = sctx->int_signal_send;
+ sctx->int_signal_send = sig;
+ int res = REAL(raise)(sig);
+ CHECK_EQ(sctx->int_signal_send, sig);
+ sctx->int_signal_send = prev;
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, kill, int pid, int sig) {
+ SCOPED_TSAN_INTERCEPTOR(kill, pid, sig);
+ ThreadSignalContext *sctx = SigCtx(thr);
+ CHECK_NE(sctx, 0);
+ int prev = sctx->int_signal_send;
+ if (pid == (int)internal_getpid()) {
+ sctx->int_signal_send = sig;
+ }
+ int res = REAL(kill)(pid, sig);
+ if (pid == (int)internal_getpid()) {
+ CHECK_EQ(sctx->int_signal_send, sig);
+ sctx->int_signal_send = prev;
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) {
+ SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig);
+ ThreadSignalContext *sctx = SigCtx(thr);
+ CHECK_NE(sctx, 0);
+ int prev = sctx->int_signal_send;
+ if (tid == pthread_self()) {
+ sctx->int_signal_send = sig;
+ }
+ int res = REAL(pthread_kill)(tid, sig);
+ if (tid == pthread_self()) {
+ CHECK_EQ(sctx->int_signal_send, sig);
+ sctx->int_signal_send = prev;
+ }
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, gettimeofday, void *tv, void *tz) {
+ SCOPED_TSAN_INTERCEPTOR(gettimeofday, tv, tz);
+ // It's intercepted merely to process pending signals.
+ return REAL(gettimeofday)(tv, tz);
+}
+
+TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service,
+ void *hints, void *rv) {
+ SCOPED_TSAN_INTERCEPTOR(getaddrinfo, node, service, hints, rv);
+ // We miss atomic synchronization in getaddrinfo,
+ // and can report false race between malloc and free
+ // inside of getaddrinfo. So ignore memory accesses.
+ ThreadIgnoreBegin(thr, pc);
+ int res = REAL(getaddrinfo)(node, service, hints, rv);
+ ThreadIgnoreEnd(thr, pc);
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, fork, int fake) {
+ if (in_symbolizer())
+ return REAL(fork)(fake);
+ SCOPED_INTERCEPTOR_RAW(fork, fake);
+ ForkBefore(thr, pc);
+ int pid;
+ {
+ // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and
+ // we'll assert in CheckNoLocks() unless we ignore interceptors.
+ ScopedIgnoreInterceptors ignore;
+ pid = REAL(fork)(fake);
+ }
+ if (pid == 0) {
+ // child
+ ForkChildAfter(thr, pc);
+ FdOnFork(thr, pc);
+ } else if (pid > 0) {
+ // parent
+ ForkParentAfter(thr, pc);
+ } else {
+ // error
+ ForkParentAfter(thr, pc);
+ }
+ return pid;
+}
+
+TSAN_INTERCEPTOR(int, vfork, int fake) {
+ // Some programs (e.g. openjdk) call close for all file descriptors
+ // in the child process. Under tsan it leads to false positives, because
+ // address space is shared, so the parent process also thinks that
+ // the descriptors are closed (while they are actually not).
+ // This leads to false positives due to missed synchronization.
+ // Strictly saying this is undefined behavior, because vfork child is not
+ // allowed to call any functions other than exec/exit. But this is what
+ // openjdk does, so we want to handle it.
+ // We could disable interceptors in the child process. But it's not possible
+ // to simply intercept and wrap vfork, because vfork child is not allowed
+ // to return from the function that calls vfork, and that's exactly what
+ // we would do. So this would require some assembly trickery as well.
+ // Instead we simply turn vfork into fork.
+ return WRAP(fork)(fake);
+}
+
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
+typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size,
+ void *data);
+struct dl_iterate_phdr_data {
+ ThreadState *thr;
+ uptr pc;
+ dl_iterate_phdr_cb_t cb;
+ void *data;
+};
+
+static bool IsAppNotRodata(uptr addr) {
+ return IsAppMem(addr) && *(u64*)MemToShadow(addr) != kShadowRodata;
+}
+
+static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size,
+ void *data) {
+ dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data;
+ // dlopen/dlclose allocate/free dynamic-linker-internal memory, which is later
+ // accessible in dl_iterate_phdr callback. But we don't see synchronization
+ // inside of dynamic linker, so we "unpoison" it here in order to not
+ // produce false reports. Ignoring malloc/free in dlopen/dlclose is not enough
+ // because some libc functions call __libc_dlopen.
+ if (info && IsAppNotRodata((uptr)info->dlpi_name))
+ MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
+ internal_strlen(info->dlpi_name));
+ int res = cbdata->cb(info, size, cbdata->data);
+ // Perform the check one more time in case info->dlpi_name was overwritten
+ // by user callback.
+ if (info && IsAppNotRodata((uptr)info->dlpi_name))
+ MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name,
+ internal_strlen(info->dlpi_name));
+ return res;
+}
+
+TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) {
+ SCOPED_TSAN_INTERCEPTOR(dl_iterate_phdr, cb, data);
+ dl_iterate_phdr_data cbdata;
+ cbdata.thr = thr;
+ cbdata.pc = pc;
+ cbdata.cb = cb;
+ cbdata.data = data;
+ int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata);
+ return res;
+}
+#endif
+
+static int OnExit(ThreadState *thr) {
+ int status = Finalize(thr);
+ FlushStreams();
+ return status;
+}
+
+struct TsanInterceptorContext {
+ ThreadState *thr;
+ const uptr caller_pc;
+ const uptr pc;
+};
+
+#if !SANITIZER_MAC
+static void HandleRecvmsg(ThreadState *thr, uptr pc,
+ __sanitizer_msghdr *msg) {
+ int fds[64];
+ int cnt = ExtractRecvmsgFDs(msg, fds, ARRAY_SIZE(fds));
+ for (int i = 0; i < cnt; i++)
+ FdEventCreate(thr, pc, fds[i]);
+}
+#endif
+
+#include "sanitizer_common/sanitizer_platform_interceptors.h"
+// Causes interceptor recursion (getaddrinfo() and fopen())
+#undef SANITIZER_INTERCEPT_GETADDRINFO
+// We define our own.
+#if SANITIZER_INTERCEPT_TLS_GET_ADDR
+#define NEED_TLS_GET_ADDR
+#endif
+#undef SANITIZER_INTERCEPT_TLS_GET_ADDR
+#undef SANITIZER_INTERCEPT_PTHREAD_SIGMASK
+
+#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
+#define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \
+ INTERCEPT_FUNCTION_VER(name, ver)
+
+#define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
+ MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \
+ true)
+
+#define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
+ MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \
+ ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \
+ false)
+
+#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
+ SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \
+ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
+ ctx = (void *)&_ctx; \
+ (void) ctx;
+
+#define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, func, ...) \
+ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \
+ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \
+ ctx = (void *)&_ctx; \
+ (void) ctx;
+
+#define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) \
+ if (path) \
+ Acquire(thr, pc, File2addr(path)); \
+ if (file) { \
+ int fd = fileno_unlocked(file); \
+ if (fd >= 0) FdFileCreate(thr, pc, fd); \
+ }
+
+#define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) \
+ if (file) { \
+ int fd = fileno_unlocked(file); \
+ if (fd >= 0) FdClose(thr, pc, fd); \
+ }
+
+#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \
+ libignore()->OnLibraryLoaded(filename)
+
+#define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() \
+ libignore()->OnLibraryUnloaded()
+
+#define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) \
+ Acquire(((TsanInterceptorContext *) ctx)->thr, pc, u)
+
+#define COMMON_INTERCEPTOR_RELEASE(ctx, u) \
+ Release(((TsanInterceptorContext *) ctx)->thr, pc, u)
+
+#define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
+ Acquire(((TsanInterceptorContext *) ctx)->thr, pc, Dir2addr(path))
+
+#define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
+ FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd)
+
+#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
+ FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd)
+
+#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \
+ FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd)
+
+#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
+ FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd)
+
+#define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
+ ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name)
+
+#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
+ __tsan::ctx->thread_registry->SetThreadNameByUserId(thread, name)
+
+#define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name)
+
+#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \
+ OnExit(((TsanInterceptorContext *) ctx)->thr)
+
+#define COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m) \
+ MutexPreLock(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m) \
+ MutexPostLock(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \
+ MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \
+ MutexRepair(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) \
+ MutexInvalidAccess(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, (uptr)m)
+
+#define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, sz, prot, flags, fd, \
+ off) \
+ do { \
+ return mmap_interceptor(thr, pc, REAL(mmap), addr, sz, prot, flags, fd, \
+ off); \
+ } while (false)
+
+#if !SANITIZER_MAC
+#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \
+ HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \
+ ((TsanInterceptorContext *)ctx)->pc, msg)
+#endif
+
+#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \
+ if (TsanThread *t = GetCurrentThread()) { \
+ *begin = t->tls_begin(); \
+ *end = t->tls_end(); \
+ } else { \
+ *begin = *end = 0; \
+ }
+
+#define COMMON_INTERCEPTOR_USER_CALLBACK_START() \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START()
+
+#define COMMON_INTERCEPTOR_USER_CALLBACK_END() \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END()
+
+#include "sanitizer_common/sanitizer_common_interceptors.inc"
+
+static int sigaction_impl(int sig, const __sanitizer_sigaction *act,
+ __sanitizer_sigaction *old);
+static __sanitizer_sighandler_ptr signal_impl(int sig,
+ __sanitizer_sighandler_ptr h);
+
+#define SIGNAL_INTERCEPTOR_SIGACTION_IMPL(signo, act, oldact) \
+ { return sigaction_impl(signo, act, oldact); }
+
+#define SIGNAL_INTERCEPTOR_SIGNAL_IMPL(func, signo, handler) \
+ { return (uptr)signal_impl(signo, (__sanitizer_sighandler_ptr)handler); }
+
+#include "sanitizer_common/sanitizer_signal_interceptors.inc"
+
+int sigaction_impl(int sig, const __sanitizer_sigaction *act,
+ __sanitizer_sigaction *old) {
+ // Note: if we call REAL(sigaction) directly for any reason without proxying
+ // the signal handler through rtl_sigaction, very bad things will happen.
+ // The handler will run synchronously and corrupt tsan per-thread state.
+ SCOPED_INTERCEPTOR_RAW(sigaction, sig, act, old);
+ __sanitizer_sigaction *sigactions = interceptor_ctx()->sigactions;
+ __sanitizer_sigaction old_stored;
+ if (old) internal_memcpy(&old_stored, &sigactions[sig], sizeof(old_stored));
+ __sanitizer_sigaction newact;
+ if (act) {
+ // Copy act into sigactions[sig].
+ // Can't use struct copy, because compiler can emit call to memcpy.
+ // Can't use internal_memcpy, because it copies byte-by-byte,
+ // and signal handler reads the handler concurrently. It it can read
+ // some bytes from old value and some bytes from new value.
+ // Use volatile to prevent insertion of memcpy.
+ sigactions[sig].handler =
+ *(volatile __sanitizer_sighandler_ptr const *)&act->handler;
+ sigactions[sig].sa_flags = *(volatile int const *)&act->sa_flags;
+ internal_memcpy(&sigactions[sig].sa_mask, &act->sa_mask,
+ sizeof(sigactions[sig].sa_mask));
+#if !SANITIZER_FREEBSD && !SANITIZER_MAC && !SANITIZER_NETBSD
+ sigactions[sig].sa_restorer = act->sa_restorer;
+#endif
+ internal_memcpy(&newact, act, sizeof(newact));
+ internal_sigfillset(&newact.sa_mask);
+ if ((uptr)act->handler != sig_ign && (uptr)act->handler != sig_dfl) {
+ if (newact.sa_flags & SA_SIGINFO)
+ newact.sigaction = rtl_sigaction;
+ else
+ newact.handler = rtl_sighandler;
+ }
+ ReleaseStore(thr, pc, (uptr)&sigactions[sig]);
+ act = &newact;
+ }
+ int res = REAL(sigaction)(sig, act, old);
+ if (res == 0 && old) {
+ uptr cb = (uptr)old->sigaction;
+ if (cb == (uptr)rtl_sigaction || cb == (uptr)rtl_sighandler) {
+ internal_memcpy(old, &old_stored, sizeof(*old));
+ }
+ }
+ return res;
+}
+
+static __sanitizer_sighandler_ptr signal_impl(int sig,
+ __sanitizer_sighandler_ptr h) {
+ __sanitizer_sigaction act;
+ act.handler = h;
+ internal_memset(&act.sa_mask, -1, sizeof(act.sa_mask));
+ act.sa_flags = 0;
+ __sanitizer_sigaction old;
+ int res = sigaction_symname(sig, &act, &old);
+ if (res) return (__sanitizer_sighandler_ptr)sig_err;
+ return old.handler;
+}
+
+#define TSAN_SYSCALL() \
+ ThreadState *thr = cur_thread(); \
+ if (thr->ignore_interceptors) \
+ return; \
+ ScopedSyscall scoped_syscall(thr) \
+/**/
+
+struct ScopedSyscall {
+ ThreadState *thr;
+
+ explicit ScopedSyscall(ThreadState *thr)
+ : thr(thr) {
+ Initialize(thr);
+ }
+
+ ~ScopedSyscall() {
+ ProcessPendingSignals(thr);
+ }
+};
+
+#if !SANITIZER_FREEBSD && !SANITIZER_MAC
+static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) {
+ TSAN_SYSCALL();
+ MemoryAccessRange(thr, pc, p, s, write);
+}
+
+static void syscall_acquire(uptr pc, uptr addr) {
+ TSAN_SYSCALL();
+ Acquire(thr, pc, addr);
+ DPrintf("syscall_acquire(%p)\n", addr);
+}
+
+static void syscall_release(uptr pc, uptr addr) {
+ TSAN_SYSCALL();
+ DPrintf("syscall_release(%p)\n", addr);
+ Release(thr, pc, addr);
+}
+
+static void syscall_fd_close(uptr pc, int fd) {
+ TSAN_SYSCALL();
+ FdClose(thr, pc, fd);
+}
+
+static USED void syscall_fd_acquire(uptr pc, int fd) {
+ TSAN_SYSCALL();
+ FdAcquire(thr, pc, fd);
+ DPrintf("syscall_fd_acquire(%p)\n", fd);
+}
+
+static USED void syscall_fd_release(uptr pc, int fd) {
+ TSAN_SYSCALL();
+ DPrintf("syscall_fd_release(%p)\n", fd);
+ FdRelease(thr, pc, fd);
+}
+
+static void syscall_pre_fork(uptr pc) {
+ TSAN_SYSCALL();
+ ForkBefore(thr, pc);
+}
+
+static void syscall_post_fork(uptr pc, int pid) {
+ TSAN_SYSCALL();
+ if (pid == 0) {
+ // child
+ ForkChildAfter(thr, pc);
+ FdOnFork(thr, pc);
+ } else if (pid > 0) {
+ // parent
+ ForkParentAfter(thr, pc);
+ } else {
+ // error
+ ForkParentAfter(thr, pc);
+ }
+}
+#endif
+
+#define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \
+ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false)
+
+#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
+ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true)
+
+#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
+ do { \
+ (void)(p); \
+ (void)(s); \
+ } while (false)
+
+#define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
+ do { \
+ (void)(p); \
+ (void)(s); \
+ } while (false)
+
+#define COMMON_SYSCALL_ACQUIRE(addr) \
+ syscall_acquire(GET_CALLER_PC(), (uptr)(addr))
+
+#define COMMON_SYSCALL_RELEASE(addr) \
+ syscall_release(GET_CALLER_PC(), (uptr)(addr))
+
+#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd)
+
+#define COMMON_SYSCALL_FD_ACQUIRE(fd) syscall_fd_acquire(GET_CALLER_PC(), fd)
+
+#define COMMON_SYSCALL_FD_RELEASE(fd) syscall_fd_release(GET_CALLER_PC(), fd)
+
+#define COMMON_SYSCALL_PRE_FORK() \
+ syscall_pre_fork(GET_CALLER_PC())
+
+#define COMMON_SYSCALL_POST_FORK(res) \
+ syscall_post_fork(GET_CALLER_PC(), res)
+
+#include "sanitizer_common/sanitizer_common_syscalls.inc"
+#include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
+
+#ifdef NEED_TLS_GET_ADDR
+// Define own interceptor instead of sanitizer_common's for three reasons:
+// 1. It must not process pending signals.
+// Signal handlers may contain MOVDQA instruction (see below).
+// 2. It must be as simple as possible to not contain MOVDQA.
+// 3. Sanitizer_common version uses COMMON_INTERCEPTOR_INITIALIZE_RANGE which
+// is empty for tsan (meant only for msan).
+// Note: __tls_get_addr can be called with mis-aligned stack due to:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066
+// So the interceptor must work with mis-aligned stack, in particular, does not
+// execute MOVDQA with stack addresses.
+TSAN_INTERCEPTOR(void *, __tls_get_addr, void *arg) {
+ void *res = REAL(__tls_get_addr)(arg);
+ ThreadState *thr = cur_thread();
+ if (!thr)
+ return res;
+ DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, thr->tls_addr,
+ thr->tls_addr + thr->tls_size);
+ if (!dtv)
+ return res;
+ // New DTLS block has been allocated.
+ MemoryResetRange(thr, 0, dtv->beg, dtv->size);
+ return res;
+}
+#endif
+
+#if SANITIZER_NETBSD
+TSAN_INTERCEPTOR(void, _lwp_exit) {
+ SCOPED_TSAN_INTERCEPTOR(_lwp_exit);
+ DestroyThreadState();
+ REAL(_lwp_exit)();
+}
+#define TSAN_MAYBE_INTERCEPT__LWP_EXIT TSAN_INTERCEPT(_lwp_exit)
+#else
+#define TSAN_MAYBE_INTERCEPT__LWP_EXIT
+#endif
+
+#if SANITIZER_FREEBSD
+TSAN_INTERCEPTOR(void, thr_exit, tid_t *state) {
+ SCOPED_TSAN_INTERCEPTOR(thr_exit, state);
+ DestroyThreadState();
+ REAL(thr_exit(state));
+}
+#define TSAN_MAYBE_INTERCEPT_THR_EXIT TSAN_INTERCEPT(thr_exit)
+#else
+#define TSAN_MAYBE_INTERCEPT_THR_EXIT
+#endif
+
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_init, void *c, void *a)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_signal, void *c)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_broadcast, void *c)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_wait, void *c, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, cond_destroy, void *c)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, mutex_init, void *m, void *a)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, mutex_destroy, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, mutex_trylock, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_init, void *m, void *a)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_destroy, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_rdlock, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_tryrdlock, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_wrlock, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_trywrlock, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS(int, rwlock_unlock, void *m)
+TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(int, once, void *o, void (*f)())
+TSAN_INTERCEPTOR_NETBSD_ALIAS_THR2(int, sigsetmask, sigmask, int a, void *b,
+ void *c)
+
+namespace __tsan {
+
+static void finalize(void *arg) {
+ ThreadState *thr = cur_thread();
+ int status = Finalize(thr);
+ // Make sure the output is not lost.
+ FlushStreams();
+ if (status)
+ Die();
+}
+
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
+static void unreachable() {
+ Report("FATAL: ThreadSanitizer: unreachable called\n");
+ Die();
+}
+#endif
+
+// Define default implementation since interception of libdispatch is optional.
+SANITIZER_WEAK_ATTRIBUTE void InitializeLibdispatchInterceptors() {}
+
+void InitializeInterceptors() {
+#if !SANITIZER_MAC
+ // We need to setup it early, because functions like dlsym() can call it.
+ REAL(memset) = internal_memset;
+ REAL(memcpy) = internal_memcpy;
+#endif
+
+ // Instruct libc malloc to consume less memory.
+#if SANITIZER_LINUX
+ mallopt(1, 0); // M_MXFAST
+ mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
+#endif
+
+ new(interceptor_ctx()) InterceptorContext();
+
+ InitializeCommonInterceptors();
+ InitializeSignalInterceptors();
+ InitializeLibdispatchInterceptors();
+
+#if !SANITIZER_MAC
+ // We can not use TSAN_INTERCEPT to get setjmp addr,
+ // because it does &setjmp and setjmp is not present in some versions of libc.
+ using __interception::InterceptFunction;
+ InterceptFunction(TSAN_STRING_SETJMP, (uptr*)&REAL(setjmp_symname), 0, 0);
+ InterceptFunction("_setjmp", (uptr*)&REAL(_setjmp), 0, 0);
+ InterceptFunction(TSAN_STRING_SIGSETJMP, (uptr*)&REAL(sigsetjmp_symname), 0,
+ 0);
+#if !SANITIZER_NETBSD
+ InterceptFunction("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0);
+#endif
+#endif
+
+ TSAN_INTERCEPT(longjmp_symname);
+ TSAN_INTERCEPT(siglongjmp_symname);
+#if SANITIZER_NETBSD
+ TSAN_INTERCEPT(_longjmp);
+#endif
+
+ TSAN_INTERCEPT(malloc);
+ TSAN_INTERCEPT(__libc_memalign);
+ TSAN_INTERCEPT(calloc);
+ TSAN_INTERCEPT(realloc);
+ TSAN_INTERCEPT(reallocarray);
+ TSAN_INTERCEPT(free);
+ TSAN_INTERCEPT(cfree);
+ TSAN_INTERCEPT(munmap);
+ TSAN_MAYBE_INTERCEPT_MEMALIGN;
+ TSAN_INTERCEPT(valloc);
+ TSAN_MAYBE_INTERCEPT_PVALLOC;
+ TSAN_INTERCEPT(posix_memalign);
+
+ TSAN_INTERCEPT(strcpy); // NOLINT
+ TSAN_INTERCEPT(strncpy);
+ TSAN_INTERCEPT(strdup);
+
+ TSAN_INTERCEPT(pthread_create);
+ TSAN_INTERCEPT(pthread_join);
+ TSAN_INTERCEPT(pthread_detach);
+ TSAN_INTERCEPT(pthread_exit);
+ #if SANITIZER_LINUX
+ TSAN_INTERCEPT(pthread_tryjoin_np);
+ TSAN_INTERCEPT(pthread_timedjoin_np);
+ #endif
+
+ TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_broadcast, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_wait, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE);
+ TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE);
+
+ TSAN_INTERCEPT(pthread_mutex_init);
+ TSAN_INTERCEPT(pthread_mutex_destroy);
+ TSAN_INTERCEPT(pthread_mutex_trylock);
+ TSAN_INTERCEPT(pthread_mutex_timedlock);
+
+ TSAN_INTERCEPT(pthread_spin_init);
+ TSAN_INTERCEPT(pthread_spin_destroy);
+ TSAN_INTERCEPT(pthread_spin_lock);
+ TSAN_INTERCEPT(pthread_spin_trylock);
+ TSAN_INTERCEPT(pthread_spin_unlock);
+
+ TSAN_INTERCEPT(pthread_rwlock_init);
+ TSAN_INTERCEPT(pthread_rwlock_destroy);
+ TSAN_INTERCEPT(pthread_rwlock_rdlock);
+ TSAN_INTERCEPT(pthread_rwlock_tryrdlock);
+ TSAN_INTERCEPT(pthread_rwlock_timedrdlock);
+ TSAN_INTERCEPT(pthread_rwlock_wrlock);
+ TSAN_INTERCEPT(pthread_rwlock_trywrlock);
+ TSAN_INTERCEPT(pthread_rwlock_timedwrlock);
+ TSAN_INTERCEPT(pthread_rwlock_unlock);
+
+ TSAN_INTERCEPT(pthread_barrier_init);
+ TSAN_INTERCEPT(pthread_barrier_destroy);
+ TSAN_INTERCEPT(pthread_barrier_wait);
+
+ TSAN_INTERCEPT(pthread_once);
+
+ TSAN_INTERCEPT(fstat);
+ TSAN_MAYBE_INTERCEPT___FXSTAT;
+ TSAN_MAYBE_INTERCEPT_FSTAT64;
+ TSAN_MAYBE_INTERCEPT___FXSTAT64;
+ TSAN_INTERCEPT(open);
+ TSAN_MAYBE_INTERCEPT_OPEN64;
+ TSAN_INTERCEPT(creat);
+ TSAN_MAYBE_INTERCEPT_CREAT64;
+ TSAN_INTERCEPT(dup);
+ TSAN_INTERCEPT(dup2);
+ TSAN_INTERCEPT(dup3);
+ TSAN_MAYBE_INTERCEPT_EVENTFD;
+ TSAN_MAYBE_INTERCEPT_SIGNALFD;
+ TSAN_MAYBE_INTERCEPT_INOTIFY_INIT;
+ TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1;
+ TSAN_INTERCEPT(socket);
+ TSAN_INTERCEPT(socketpair);
+ TSAN_INTERCEPT(connect);
+ TSAN_INTERCEPT(bind);
+ TSAN_INTERCEPT(listen);
+ TSAN_MAYBE_INTERCEPT_EPOLL;
+ TSAN_INTERCEPT(close);
+ TSAN_MAYBE_INTERCEPT___CLOSE;
+ TSAN_MAYBE_INTERCEPT___RES_ICLOSE;
+ TSAN_INTERCEPT(pipe);
+ TSAN_INTERCEPT(pipe2);
+
+ TSAN_INTERCEPT(unlink);
+ TSAN_INTERCEPT(tmpfile);
+ TSAN_MAYBE_INTERCEPT_TMPFILE64;
+ TSAN_INTERCEPT(abort);
+ TSAN_INTERCEPT(rmdir);
+ TSAN_INTERCEPT(closedir);
+
+ TSAN_INTERCEPT(sigsuspend);
+ TSAN_INTERCEPT(sigblock);
+ TSAN_INTERCEPT(sigsetmask);
+ TSAN_INTERCEPT(pthread_sigmask);
+ TSAN_INTERCEPT(raise);
+ TSAN_INTERCEPT(kill);
+ TSAN_INTERCEPT(pthread_kill);
+ TSAN_INTERCEPT(sleep);
+ TSAN_INTERCEPT(usleep);
+ TSAN_INTERCEPT(nanosleep);
+ TSAN_INTERCEPT(pause);
+ TSAN_INTERCEPT(gettimeofday);
+ TSAN_INTERCEPT(getaddrinfo);
+
+ TSAN_INTERCEPT(fork);
+ TSAN_INTERCEPT(vfork);
+#if !SANITIZER_ANDROID
+ TSAN_INTERCEPT(dl_iterate_phdr);
+#endif
+ TSAN_MAYBE_INTERCEPT_ON_EXIT;
+ TSAN_INTERCEPT(__cxa_atexit);
+ TSAN_INTERCEPT(_exit);
+
+#ifdef NEED_TLS_GET_ADDR
+ TSAN_INTERCEPT(__tls_get_addr);
+#endif
+
+ TSAN_MAYBE_INTERCEPT__LWP_EXIT;
+ TSAN_MAYBE_INTERCEPT_THR_EXIT;
+
+#if !SANITIZER_MAC && !SANITIZER_ANDROID
+ // Need to setup it, because interceptors check that the function is resolved.
+ // But atexit is emitted directly into the module, so can't be resolved.
+ REAL(atexit) = (int(*)(void(*)()))unreachable;
+#endif
+
+ if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
+ Printf("ThreadSanitizer: failed to setup atexit callback\n");
+ Die();
+ }
+
+#if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD
+ if (pthread_key_create(&interceptor_ctx()->finalize_key, &thread_finalize)) {
+ Printf("ThreadSanitizer: failed to create thread key\n");
+ Die();
+ }
+#endif
+
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_init);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_signal);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_broadcast);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_wait);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(cond_destroy);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(mutex_init);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(mutex_destroy);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(mutex_trylock);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_init);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_destroy);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_rdlock);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_tryrdlock);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_wrlock);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_trywrlock);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS(rwlock_unlock);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(once);
+ TSAN_MAYBE_INTERCEPT_NETBSD_ALIAS_THR(sigsetmask);
+
+ FdInit();
+}
+
+} // namespace __tsan
+
+// Invisible barrier for tests.
+// There were several unsuccessful iterations for this functionality:
+// 1. Initially it was implemented in user code using
+// REAL(pthread_barrier_wait). But pthread_barrier_wait is not supported on
+// MacOS. Futexes are linux-specific for this matter.
+// 2. Then we switched to atomics+usleep(10). But usleep produced parasitic
+// "as-if synchronized via sleep" messages in reports which failed some
+// output tests.
+// 3. Then we switched to atomics+sched_yield. But this produced tons of tsan-
+// visible events, which lead to "failed to restore stack trace" failures.
+// Note that no_sanitize_thread attribute does not turn off atomic interception
+// so attaching it to the function defined in user code does not help.
+// That's why we now have what we have.
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_testonly_barrier_init(u64 *barrier, u32 count) {
+ if (count >= (1 << 8)) {
+ Printf("barrier_init: count is too large (%d)\n", count);
+ Die();
+ }
+ // 8 lsb is thread count, the remaining are count of entered threads.
+ *barrier = count;
+}
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_testonly_barrier_wait(u64 *barrier) {
+ unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED);
+ unsigned old_epoch = (old >> 8) / (old & 0xff);
+ for (;;) {
+ unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
+ unsigned cur_epoch = (cur >> 8) / (cur & 0xff);
+ if (cur_epoch != old_epoch)
+ return;
+ internal_sched_yield();
+ }
+}
LibIgnore *libignore();
+#if !SANITIZER_GO
+INLINE bool in_symbolizer() {
+ cur_thread_init();
+ return UNLIKELY(cur_thread()->in_symbolizer);
+}
+#endif
+
} // namespace __tsan
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
+ cur_thread_init(); \
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
ScopedInterceptor si(thr, #func, caller_pc); \
# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(ret, func, ...) \
TSAN_INTERCEPTOR(ret, __libc_thr_##func, __VA_ARGS__) \
ALIAS(WRAPPER_NAME(pthread_##func));
+# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR2(ret, func, func2, ...) \
+ TSAN_INTERCEPTOR(ret, __libc_thr_##func, __VA_ARGS__) \
+ ALIAS(WRAPPER_NAME(pthread_##func2));
#else
# define TSAN_INTERCEPTOR_NETBSD_ALIAS(ret, func, ...)
# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR(ret, func, ...)
+# define TSAN_INTERCEPTOR_NETBSD_ALIAS_THR2(ret, func, func2, ...)
#endif
#endif // TSAN_INTERCEPTORS_H
+++ /dev/null
-//===-- tsan_interceptors_mac.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Mac-specific interceptors.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "interception/interception.h"
-#include "tsan_interceptors.h"
-#include "tsan_interface.h"
-#include "tsan_interface_ann.h"
-
-#include <libkern/OSAtomic.h>
-
-#if defined(__has_include) && __has_include(<xpc/xpc.h>)
-#include <xpc/xpc.h>
-#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
-
-typedef long long_t; // NOLINT
-
-namespace __tsan {
-
-// The non-barrier versions of OSAtomic* functions are semantically mo_relaxed,
-// but the two variants (e.g. OSAtomicAdd32 and OSAtomicAdd32Barrier) are
-// actually aliases of each other, and we cannot have different interceptors for
-// them, because they're actually the same function. Thus, we have to stay
-// conservative and treat the non-barrier versions as mo_acq_rel.
-static const morder kMacOrderBarrier = mo_acq_rel;
-static const morder kMacOrderNonBarrier = mo_acq_rel;
-
-#define OSATOMIC_INTERCEPTOR(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
- TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
- return tsan_atomic_f((volatile tsan_t *)ptr, x, mo); \
- }
-
-#define OSATOMIC_INTERCEPTOR_PLUS_X(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
- TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
- return tsan_atomic_f((volatile tsan_t *)ptr, x, mo) + x; \
- }
-
-#define OSATOMIC_INTERCEPTOR_PLUS_1(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
- TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f, ptr); \
- return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) + 1; \
- }
-
-#define OSATOMIC_INTERCEPTOR_MINUS_1(return_t, t, tsan_t, f, tsan_atomic_f, \
- mo) \
- TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f, ptr); \
- return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) - 1; \
- }
-
-#define OSATOMIC_INTERCEPTORS_ARITHMETIC(f, tsan_atomic_f, m) \
- m(int32_t, int32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
- kMacOrderNonBarrier) \
- m(int32_t, int32_t, a32, f##32##Barrier, __tsan_atomic32_##tsan_atomic_f, \
- kMacOrderBarrier) \
- m(int64_t, int64_t, a64, f##64, __tsan_atomic64_##tsan_atomic_f, \
- kMacOrderNonBarrier) \
- m(int64_t, int64_t, a64, f##64##Barrier, __tsan_atomic64_##tsan_atomic_f, \
- kMacOrderBarrier)
-
-#define OSATOMIC_INTERCEPTORS_BITWISE(f, tsan_atomic_f, m, m_orig) \
- m(int32_t, uint32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
- kMacOrderNonBarrier) \
- m(int32_t, uint32_t, a32, f##32##Barrier, __tsan_atomic32_##tsan_atomic_f, \
- kMacOrderBarrier) \
- m_orig(int32_t, uint32_t, a32, f##32##Orig, __tsan_atomic32_##tsan_atomic_f, \
- kMacOrderNonBarrier) \
- m_orig(int32_t, uint32_t, a32, f##32##OrigBarrier, \
- __tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier)
-
-OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicAdd, fetch_add,
- OSATOMIC_INTERCEPTOR_PLUS_X)
-OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicIncrement, fetch_add,
- OSATOMIC_INTERCEPTOR_PLUS_1)
-OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicDecrement, fetch_sub,
- OSATOMIC_INTERCEPTOR_MINUS_1)
-OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicOr, fetch_or, OSATOMIC_INTERCEPTOR_PLUS_X,
- OSATOMIC_INTERCEPTOR)
-OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicAnd, fetch_and,
- OSATOMIC_INTERCEPTOR_PLUS_X, OSATOMIC_INTERCEPTOR)
-OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicXor, fetch_xor,
- OSATOMIC_INTERCEPTOR_PLUS_X, OSATOMIC_INTERCEPTOR)
-
-#define OSATOMIC_INTERCEPTORS_CAS(f, tsan_atomic_f, tsan_t, t) \
- TSAN_INTERCEPTOR(bool, f, t old_value, t new_value, t volatile *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f, old_value, new_value, ptr); \
- return tsan_atomic_f##_compare_exchange_strong( \
- (volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
- kMacOrderNonBarrier, kMacOrderNonBarrier); \
- } \
- \
- TSAN_INTERCEPTOR(bool, f##Barrier, t old_value, t new_value, \
- t volatile *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f##Barrier, old_value, new_value, ptr); \
- return tsan_atomic_f##_compare_exchange_strong( \
- (volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
- kMacOrderBarrier, kMacOrderNonBarrier); \
- }
-
-OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapInt, __tsan_atomic32, a32, int)
-OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapLong, __tsan_atomic64, a64,
- long_t)
-OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapPtr, __tsan_atomic64, a64,
- void *)
-OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap32, __tsan_atomic32, a32,
- int32_t)
-OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap64, __tsan_atomic64, a64,
- int64_t)
-
-#define OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, mo) \
- TSAN_INTERCEPTOR(bool, f, uint32_t n, volatile void *ptr) { \
- SCOPED_TSAN_INTERCEPTOR(f, n, ptr); \
- volatile char *byte_ptr = ((volatile char *)ptr) + (n >> 3); \
- char bit = 0x80u >> (n & 7); \
- char mask = clear ? ~bit : bit; \
- char orig_byte = op((volatile a8 *)byte_ptr, mask, mo); \
- return orig_byte & bit; \
- }
-
-#define OSATOMIC_INTERCEPTORS_BITOP(f, op, clear) \
- OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, kMacOrderNonBarrier) \
- OSATOMIC_INTERCEPTOR_BITOP(f##Barrier, op, clear, kMacOrderBarrier)
-
-OSATOMIC_INTERCEPTORS_BITOP(OSAtomicTestAndSet, __tsan_atomic8_fetch_or, false)
-OSATOMIC_INTERCEPTORS_BITOP(OSAtomicTestAndClear, __tsan_atomic8_fetch_and,
- true)
-
-TSAN_INTERCEPTOR(void, OSAtomicEnqueue, OSQueueHead *list, void *item,
- size_t offset) {
- SCOPED_TSAN_INTERCEPTOR(OSAtomicEnqueue, list, item, offset);
- __tsan_release(item);
- REAL(OSAtomicEnqueue)(list, item, offset);
-}
-
-TSAN_INTERCEPTOR(void *, OSAtomicDequeue, OSQueueHead *list, size_t offset) {
- SCOPED_TSAN_INTERCEPTOR(OSAtomicDequeue, list, offset);
- void *item = REAL(OSAtomicDequeue)(list, offset);
- if (item) __tsan_acquire(item);
- return item;
-}
-
-// OSAtomicFifoEnqueue and OSAtomicFifoDequeue are only on OS X.
-#if !SANITIZER_IOS
-
-TSAN_INTERCEPTOR(void, OSAtomicFifoEnqueue, OSFifoQueueHead *list, void *item,
- size_t offset) {
- SCOPED_TSAN_INTERCEPTOR(OSAtomicFifoEnqueue, list, item, offset);
- __tsan_release(item);
- REAL(OSAtomicFifoEnqueue)(list, item, offset);
-}
-
-TSAN_INTERCEPTOR(void *, OSAtomicFifoDequeue, OSFifoQueueHead *list,
- size_t offset) {
- SCOPED_TSAN_INTERCEPTOR(OSAtomicFifoDequeue, list, offset);
- void *item = REAL(OSAtomicFifoDequeue)(list, offset);
- if (item) __tsan_acquire(item);
- return item;
-}
-
-#endif
-
-TSAN_INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) {
- CHECK(!cur_thread()->is_dead);
- if (!cur_thread()->is_inited) {
- return REAL(OSSpinLockLock)(lock);
- }
- SCOPED_TSAN_INTERCEPTOR(OSSpinLockLock, lock);
- REAL(OSSpinLockLock)(lock);
- Acquire(thr, pc, (uptr)lock);
-}
-
-TSAN_INTERCEPTOR(bool, OSSpinLockTry, volatile OSSpinLock *lock) {
- CHECK(!cur_thread()->is_dead);
- if (!cur_thread()->is_inited) {
- return REAL(OSSpinLockTry)(lock);
- }
- SCOPED_TSAN_INTERCEPTOR(OSSpinLockTry, lock);
- bool result = REAL(OSSpinLockTry)(lock);
- if (result)
- Acquire(thr, pc, (uptr)lock);
- return result;
-}
-
-TSAN_INTERCEPTOR(void, OSSpinLockUnlock, volatile OSSpinLock *lock) {
- CHECK(!cur_thread()->is_dead);
- if (!cur_thread()->is_inited) {
- return REAL(OSSpinLockUnlock)(lock);
- }
- SCOPED_TSAN_INTERCEPTOR(OSSpinLockUnlock, lock);
- Release(thr, pc, (uptr)lock);
- REAL(OSSpinLockUnlock)(lock);
-}
-
-TSAN_INTERCEPTOR(void, os_lock_lock, void *lock) {
- CHECK(!cur_thread()->is_dead);
- if (!cur_thread()->is_inited) {
- return REAL(os_lock_lock)(lock);
- }
- SCOPED_TSAN_INTERCEPTOR(os_lock_lock, lock);
- REAL(os_lock_lock)(lock);
- Acquire(thr, pc, (uptr)lock);
-}
-
-TSAN_INTERCEPTOR(bool, os_lock_trylock, void *lock) {
- CHECK(!cur_thread()->is_dead);
- if (!cur_thread()->is_inited) {
- return REAL(os_lock_trylock)(lock);
- }
- SCOPED_TSAN_INTERCEPTOR(os_lock_trylock, lock);
- bool result = REAL(os_lock_trylock)(lock);
- if (result)
- Acquire(thr, pc, (uptr)lock);
- return result;
-}
-
-TSAN_INTERCEPTOR(void, os_lock_unlock, void *lock) {
- CHECK(!cur_thread()->is_dead);
- if (!cur_thread()->is_inited) {
- return REAL(os_lock_unlock)(lock);
- }
- SCOPED_TSAN_INTERCEPTOR(os_lock_unlock, lock);
- Release(thr, pc, (uptr)lock);
- REAL(os_lock_unlock)(lock);
-}
-
-#if defined(__has_include) && __has_include(<xpc/xpc.h>)
-
-TSAN_INTERCEPTOR(void, xpc_connection_set_event_handler,
- xpc_connection_t connection, xpc_handler_t handler) {
- SCOPED_TSAN_INTERCEPTOR(xpc_connection_set_event_handler, connection,
- handler);
- Release(thr, pc, (uptr)connection);
- xpc_handler_t new_handler = ^(xpc_object_t object) {
- {
- SCOPED_INTERCEPTOR_RAW(xpc_connection_set_event_handler);
- Acquire(thr, pc, (uptr)connection);
- }
- handler(object);
- };
- REAL(xpc_connection_set_event_handler)(connection, new_handler);
-}
-
-TSAN_INTERCEPTOR(void, xpc_connection_send_barrier, xpc_connection_t connection,
- dispatch_block_t barrier) {
- SCOPED_TSAN_INTERCEPTOR(xpc_connection_send_barrier, connection, barrier);
- Release(thr, pc, (uptr)connection);
- dispatch_block_t new_barrier = ^() {
- {
- SCOPED_INTERCEPTOR_RAW(xpc_connection_send_barrier);
- Acquire(thr, pc, (uptr)connection);
- }
- barrier();
- };
- REAL(xpc_connection_send_barrier)(connection, new_barrier);
-}
-
-TSAN_INTERCEPTOR(void, xpc_connection_send_message_with_reply,
- xpc_connection_t connection, xpc_object_t message,
- dispatch_queue_t replyq, xpc_handler_t handler) {
- SCOPED_TSAN_INTERCEPTOR(xpc_connection_send_message_with_reply, connection,
- message, replyq, handler);
- Release(thr, pc, (uptr)connection);
- xpc_handler_t new_handler = ^(xpc_object_t object) {
- {
- SCOPED_INTERCEPTOR_RAW(xpc_connection_send_message_with_reply);
- Acquire(thr, pc, (uptr)connection);
- }
- handler(object);
- };
- REAL(xpc_connection_send_message_with_reply)
- (connection, message, replyq, new_handler);
-}
-
-TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
- SCOPED_TSAN_INTERCEPTOR(xpc_connection_cancel, connection);
- Release(thr, pc, (uptr)connection);
- REAL(xpc_connection_cancel)(connection);
-}
-
-#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
-
-// Is the Obj-C object a tagged pointer (i.e. isn't really a valid pointer and
-// contains data in the pointers bits instead)?
-static bool IsTaggedObjCPointer(void *obj) {
- const uptr kPossibleTaggedBits = 0x8000000000000001ull;
- return ((uptr)obj & kPossibleTaggedBits) != 0;
-}
-
-// Return an address on which we can synchronize (Acquire and Release) for a
-// Obj-C tagged pointer (which is not a valid pointer). Ideally should be a
-// derived address from 'obj', but for now just return the same global address.
-// TODO(kubamracek): Return different address for different pointers.
-static uptr SyncAddressForTaggedPointer(void *obj) {
- (void)obj;
- static u64 addr;
- return (uptr)&addr;
-}
-
-// Address on which we can synchronize for an Objective-C object. Supports
-// tagged pointers.
-static uptr SyncAddressForObjCObject(void *obj) {
- if (IsTaggedObjCPointer(obj)) return SyncAddressForTaggedPointer(obj);
- return (uptr)obj;
-}
-
-TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) {
- SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
- int result = REAL(objc_sync_enter)(obj);
- if (obj) Acquire(thr, pc, SyncAddressForObjCObject(obj));
- return result;
-}
-
-TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) {
- SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
- if (obj) Release(thr, pc, SyncAddressForObjCObject(obj));
- return REAL(objc_sync_exit)(obj);
-}
-
-// On macOS, libc++ is always linked dynamically, so intercepting works the
-// usual way.
-#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
-
-namespace {
-struct fake_shared_weak_count {
- volatile a64 shared_owners;
- volatile a64 shared_weak_owners;
- virtual void _unused_0x0() = 0;
- virtual void _unused_0x8() = 0;
- virtual void on_zero_shared() = 0;
- virtual void _unused_0x18() = 0;
- virtual void on_zero_shared_weak() = 0;
-};
-} // namespace
-
-// The following code adds libc++ interceptors for:
-// void __shared_weak_count::__release_shared() _NOEXCEPT;
-// bool __shared_count::__release_shared() _NOEXCEPT;
-// Shared and weak pointers in C++ maintain reference counts via atomics in
-// libc++.dylib, which are TSan-invisible, and this leads to false positives in
-// destructor code. These interceptors re-implements the whole functions so that
-// the mo_acq_rel semantics of the atomic decrement are visible.
-//
-// Unfortunately, the interceptors cannot simply Acquire/Release some sync
-// object and call the original function, because it would have a race between
-// the sync and the destruction of the object. Calling both under a lock will
-// not work because the destructor can invoke this interceptor again (and even
-// in a different thread, so recursive locks don't help).
-
-STDCXX_INTERCEPTOR(void, _ZNSt3__119__shared_weak_count16__release_sharedEv,
- fake_shared_weak_count *o) {
- if (!flags()->shared_ptr_interceptor)
- return REAL(_ZNSt3__119__shared_weak_count16__release_sharedEv)(o);
-
- SCOPED_TSAN_INTERCEPTOR(_ZNSt3__119__shared_weak_count16__release_sharedEv,
- o);
- if (__tsan_atomic64_fetch_add(&o->shared_owners, -1, mo_release) == 0) {
- Acquire(thr, pc, (uptr)&o->shared_owners);
- o->on_zero_shared();
- if (__tsan_atomic64_fetch_add(&o->shared_weak_owners, -1, mo_release) ==
- 0) {
- Acquire(thr, pc, (uptr)&o->shared_weak_owners);
- o->on_zero_shared_weak();
- }
- }
-}
-
-STDCXX_INTERCEPTOR(bool, _ZNSt3__114__shared_count16__release_sharedEv,
- fake_shared_weak_count *o) {
- if (!flags()->shared_ptr_interceptor)
- return REAL(_ZNSt3__114__shared_count16__release_sharedEv)(o);
-
- SCOPED_TSAN_INTERCEPTOR(_ZNSt3__114__shared_count16__release_sharedEv, o);
- if (__tsan_atomic64_fetch_add(&o->shared_owners, -1, mo_release) == 0) {
- Acquire(thr, pc, (uptr)&o->shared_owners);
- o->on_zero_shared();
- return true;
- }
- return false;
-}
-
-namespace {
-struct call_once_callback_args {
- void (*orig_func)(void *arg);
- void *orig_arg;
- void *flag;
-};
-
-void call_once_callback_wrapper(void *arg) {
- call_once_callback_args *new_args = (call_once_callback_args *)arg;
- new_args->orig_func(new_args->orig_arg);
- __tsan_release(new_args->flag);
-}
-} // namespace
-
-// This adds a libc++ interceptor for:
-// void __call_once(volatile unsigned long&, void*, void(*)(void*));
-// C++11 call_once is implemented via an internal function __call_once which is
-// inside libc++.dylib, and the atomic release store inside it is thus
-// TSan-invisible. To avoid false positives, this interceptor wraps the callback
-// function and performs an explicit Release after the user code has run.
-STDCXX_INTERCEPTOR(void, _ZNSt3__111__call_onceERVmPvPFvS2_E, void *flag,
- void *arg, void (*func)(void *arg)) {
- call_once_callback_args new_args = {func, arg, flag};
- REAL(_ZNSt3__111__call_onceERVmPvPFvS2_E)(flag, &new_args,
- call_once_callback_wrapper);
-}
-
-} // namespace __tsan
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- tsan_interceptors_mac.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific interceptors.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_interface.h"
+#include "tsan_interface_ann.h"
+#include "sanitizer_common/sanitizer_addrhashmap.h"
+
+#include <errno.h>
+#include <libkern/OSAtomic.h>
+#include <objc/objc-sync.h>
+#include <sys/ucontext.h>
+
+#if defined(__has_include) && __has_include(<xpc/xpc.h>)
+#include <xpc/xpc.h>
+#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
+
+typedef long long_t; // NOLINT
+
+extern "C" {
+int getcontext(ucontext_t *ucp) __attribute__((returns_twice));
+int setcontext(const ucontext_t *ucp);
+}
+
+namespace __tsan {
+
+// The non-barrier versions of OSAtomic* functions are semantically mo_relaxed,
+// but the two variants (e.g. OSAtomicAdd32 and OSAtomicAdd32Barrier) are
+// actually aliases of each other, and we cannot have different interceptors for
+// them, because they're actually the same function. Thus, we have to stay
+// conservative and treat the non-barrier versions as mo_acq_rel.
+static const morder kMacOrderBarrier = mo_acq_rel;
+static const morder kMacOrderNonBarrier = mo_acq_rel;
+
+#define OSATOMIC_INTERCEPTOR(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
+ TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
+ return tsan_atomic_f((volatile tsan_t *)ptr, x, mo); \
+ }
+
+#define OSATOMIC_INTERCEPTOR_PLUS_X(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
+ TSAN_INTERCEPTOR(return_t, f, t x, volatile t *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f, x, ptr); \
+ return tsan_atomic_f((volatile tsan_t *)ptr, x, mo) + x; \
+ }
+
+#define OSATOMIC_INTERCEPTOR_PLUS_1(return_t, t, tsan_t, f, tsan_atomic_f, mo) \
+ TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f, ptr); \
+ return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) + 1; \
+ }
+
+#define OSATOMIC_INTERCEPTOR_MINUS_1(return_t, t, tsan_t, f, tsan_atomic_f, \
+ mo) \
+ TSAN_INTERCEPTOR(return_t, f, volatile t *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f, ptr); \
+ return tsan_atomic_f((volatile tsan_t *)ptr, 1, mo) - 1; \
+ }
+
+#define OSATOMIC_INTERCEPTORS_ARITHMETIC(f, tsan_atomic_f, m) \
+ m(int32_t, int32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
+ kMacOrderNonBarrier) \
+ m(int32_t, int32_t, a32, f##32##Barrier, __tsan_atomic32_##tsan_atomic_f, \
+ kMacOrderBarrier) \
+ m(int64_t, int64_t, a64, f##64, __tsan_atomic64_##tsan_atomic_f, \
+ kMacOrderNonBarrier) \
+ m(int64_t, int64_t, a64, f##64##Barrier, __tsan_atomic64_##tsan_atomic_f, \
+ kMacOrderBarrier)
+
+#define OSATOMIC_INTERCEPTORS_BITWISE(f, tsan_atomic_f, m, m_orig) \
+ m(int32_t, uint32_t, a32, f##32, __tsan_atomic32_##tsan_atomic_f, \
+ kMacOrderNonBarrier) \
+ m(int32_t, uint32_t, a32, f##32##Barrier, __tsan_atomic32_##tsan_atomic_f, \
+ kMacOrderBarrier) \
+ m_orig(int32_t, uint32_t, a32, f##32##Orig, __tsan_atomic32_##tsan_atomic_f, \
+ kMacOrderNonBarrier) \
+ m_orig(int32_t, uint32_t, a32, f##32##OrigBarrier, \
+ __tsan_atomic32_##tsan_atomic_f, kMacOrderBarrier)
+
+OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicAdd, fetch_add,
+ OSATOMIC_INTERCEPTOR_PLUS_X)
+OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicIncrement, fetch_add,
+ OSATOMIC_INTERCEPTOR_PLUS_1)
+OSATOMIC_INTERCEPTORS_ARITHMETIC(OSAtomicDecrement, fetch_sub,
+ OSATOMIC_INTERCEPTOR_MINUS_1)
+OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicOr, fetch_or, OSATOMIC_INTERCEPTOR_PLUS_X,
+ OSATOMIC_INTERCEPTOR)
+OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicAnd, fetch_and,
+ OSATOMIC_INTERCEPTOR_PLUS_X, OSATOMIC_INTERCEPTOR)
+OSATOMIC_INTERCEPTORS_BITWISE(OSAtomicXor, fetch_xor,
+ OSATOMIC_INTERCEPTOR_PLUS_X, OSATOMIC_INTERCEPTOR)
+
+#define OSATOMIC_INTERCEPTORS_CAS(f, tsan_atomic_f, tsan_t, t) \
+ TSAN_INTERCEPTOR(bool, f, t old_value, t new_value, t volatile *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f, old_value, new_value, ptr); \
+ return tsan_atomic_f##_compare_exchange_strong( \
+ (volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
+ kMacOrderNonBarrier, kMacOrderNonBarrier); \
+ } \
+ \
+ TSAN_INTERCEPTOR(bool, f##Barrier, t old_value, t new_value, \
+ t volatile *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f##Barrier, old_value, new_value, ptr); \
+ return tsan_atomic_f##_compare_exchange_strong( \
+ (volatile tsan_t *)ptr, (tsan_t *)&old_value, (tsan_t)new_value, \
+ kMacOrderBarrier, kMacOrderNonBarrier); \
+ }
+
+OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapInt, __tsan_atomic32, a32, int)
+OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapLong, __tsan_atomic64, a64,
+ long_t)
+OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwapPtr, __tsan_atomic64, a64,
+ void *)
+OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap32, __tsan_atomic32, a32,
+ int32_t)
+OSATOMIC_INTERCEPTORS_CAS(OSAtomicCompareAndSwap64, __tsan_atomic64, a64,
+ int64_t)
+
+#define OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, mo) \
+ TSAN_INTERCEPTOR(bool, f, uint32_t n, volatile void *ptr) { \
+ SCOPED_TSAN_INTERCEPTOR(f, n, ptr); \
+ volatile char *byte_ptr = ((volatile char *)ptr) + (n >> 3); \
+ char bit = 0x80u >> (n & 7); \
+ char mask = clear ? ~bit : bit; \
+ char orig_byte = op((volatile a8 *)byte_ptr, mask, mo); \
+ return orig_byte & bit; \
+ }
+
+#define OSATOMIC_INTERCEPTORS_BITOP(f, op, clear) \
+ OSATOMIC_INTERCEPTOR_BITOP(f, op, clear, kMacOrderNonBarrier) \
+ OSATOMIC_INTERCEPTOR_BITOP(f##Barrier, op, clear, kMacOrderBarrier)
+
+OSATOMIC_INTERCEPTORS_BITOP(OSAtomicTestAndSet, __tsan_atomic8_fetch_or, false)
+OSATOMIC_INTERCEPTORS_BITOP(OSAtomicTestAndClear, __tsan_atomic8_fetch_and,
+ true)
+
+TSAN_INTERCEPTOR(void, OSAtomicEnqueue, OSQueueHead *list, void *item,
+ size_t offset) {
+ SCOPED_TSAN_INTERCEPTOR(OSAtomicEnqueue, list, item, offset);
+ __tsan_release(item);
+ REAL(OSAtomicEnqueue)(list, item, offset);
+}
+
+TSAN_INTERCEPTOR(void *, OSAtomicDequeue, OSQueueHead *list, size_t offset) {
+ SCOPED_TSAN_INTERCEPTOR(OSAtomicDequeue, list, offset);
+ void *item = REAL(OSAtomicDequeue)(list, offset);
+ if (item) __tsan_acquire(item);
+ return item;
+}
+
+// OSAtomicFifoEnqueue and OSAtomicFifoDequeue are only on OS X.
+#if !SANITIZER_IOS
+
+TSAN_INTERCEPTOR(void, OSAtomicFifoEnqueue, OSFifoQueueHead *list, void *item,
+ size_t offset) {
+ SCOPED_TSAN_INTERCEPTOR(OSAtomicFifoEnqueue, list, item, offset);
+ __tsan_release(item);
+ REAL(OSAtomicFifoEnqueue)(list, item, offset);
+}
+
+TSAN_INTERCEPTOR(void *, OSAtomicFifoDequeue, OSFifoQueueHead *list,
+ size_t offset) {
+ SCOPED_TSAN_INTERCEPTOR(OSAtomicFifoDequeue, list, offset);
+ void *item = REAL(OSAtomicFifoDequeue)(list, offset);
+ if (item) __tsan_acquire(item);
+ return item;
+}
+
+#endif
+
+TSAN_INTERCEPTOR(void, OSSpinLockLock, volatile OSSpinLock *lock) {
+ CHECK(!cur_thread()->is_dead);
+ if (!cur_thread()->is_inited) {
+ return REAL(OSSpinLockLock)(lock);
+ }
+ SCOPED_TSAN_INTERCEPTOR(OSSpinLockLock, lock);
+ REAL(OSSpinLockLock)(lock);
+ Acquire(thr, pc, (uptr)lock);
+}
+
+TSAN_INTERCEPTOR(bool, OSSpinLockTry, volatile OSSpinLock *lock) {
+ CHECK(!cur_thread()->is_dead);
+ if (!cur_thread()->is_inited) {
+ return REAL(OSSpinLockTry)(lock);
+ }
+ SCOPED_TSAN_INTERCEPTOR(OSSpinLockTry, lock);
+ bool result = REAL(OSSpinLockTry)(lock);
+ if (result)
+ Acquire(thr, pc, (uptr)lock);
+ return result;
+}
+
+TSAN_INTERCEPTOR(void, OSSpinLockUnlock, volatile OSSpinLock *lock) {
+ CHECK(!cur_thread()->is_dead);
+ if (!cur_thread()->is_inited) {
+ return REAL(OSSpinLockUnlock)(lock);
+ }
+ SCOPED_TSAN_INTERCEPTOR(OSSpinLockUnlock, lock);
+ Release(thr, pc, (uptr)lock);
+ REAL(OSSpinLockUnlock)(lock);
+}
+
+TSAN_INTERCEPTOR(void, os_lock_lock, void *lock) {
+ CHECK(!cur_thread()->is_dead);
+ if (!cur_thread()->is_inited) {
+ return REAL(os_lock_lock)(lock);
+ }
+ SCOPED_TSAN_INTERCEPTOR(os_lock_lock, lock);
+ REAL(os_lock_lock)(lock);
+ Acquire(thr, pc, (uptr)lock);
+}
+
+TSAN_INTERCEPTOR(bool, os_lock_trylock, void *lock) {
+ CHECK(!cur_thread()->is_dead);
+ if (!cur_thread()->is_inited) {
+ return REAL(os_lock_trylock)(lock);
+ }
+ SCOPED_TSAN_INTERCEPTOR(os_lock_trylock, lock);
+ bool result = REAL(os_lock_trylock)(lock);
+ if (result)
+ Acquire(thr, pc, (uptr)lock);
+ return result;
+}
+
+TSAN_INTERCEPTOR(void, os_lock_unlock, void *lock) {
+ CHECK(!cur_thread()->is_dead);
+ if (!cur_thread()->is_inited) {
+ return REAL(os_lock_unlock)(lock);
+ }
+ SCOPED_TSAN_INTERCEPTOR(os_lock_unlock, lock);
+ Release(thr, pc, (uptr)lock);
+ REAL(os_lock_unlock)(lock);
+}
+
+#if defined(__has_include) && __has_include(<xpc/xpc.h>)
+
+TSAN_INTERCEPTOR(void, xpc_connection_set_event_handler,
+ xpc_connection_t connection, xpc_handler_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(xpc_connection_set_event_handler, connection,
+ handler);
+ Release(thr, pc, (uptr)connection);
+ xpc_handler_t new_handler = ^(xpc_object_t object) {
+ {
+ SCOPED_INTERCEPTOR_RAW(xpc_connection_set_event_handler);
+ Acquire(thr, pc, (uptr)connection);
+ }
+ handler(object);
+ };
+ REAL(xpc_connection_set_event_handler)(connection, new_handler);
+}
+
+TSAN_INTERCEPTOR(void, xpc_connection_send_barrier, xpc_connection_t connection,
+ dispatch_block_t barrier) {
+ SCOPED_TSAN_INTERCEPTOR(xpc_connection_send_barrier, connection, barrier);
+ Release(thr, pc, (uptr)connection);
+ dispatch_block_t new_barrier = ^() {
+ {
+ SCOPED_INTERCEPTOR_RAW(xpc_connection_send_barrier);
+ Acquire(thr, pc, (uptr)connection);
+ }
+ barrier();
+ };
+ REAL(xpc_connection_send_barrier)(connection, new_barrier);
+}
+
+TSAN_INTERCEPTOR(void, xpc_connection_send_message_with_reply,
+ xpc_connection_t connection, xpc_object_t message,
+ dispatch_queue_t replyq, xpc_handler_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(xpc_connection_send_message_with_reply, connection,
+ message, replyq, handler);
+ Release(thr, pc, (uptr)connection);
+ xpc_handler_t new_handler = ^(xpc_object_t object) {
+ {
+ SCOPED_INTERCEPTOR_RAW(xpc_connection_send_message_with_reply);
+ Acquire(thr, pc, (uptr)connection);
+ }
+ handler(object);
+ };
+ REAL(xpc_connection_send_message_with_reply)
+ (connection, message, replyq, new_handler);
+}
+
+TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
+ SCOPED_TSAN_INTERCEPTOR(xpc_connection_cancel, connection);
+ Release(thr, pc, (uptr)connection);
+ REAL(xpc_connection_cancel)(connection);
+}
+
+#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
+
+// Determines whether the Obj-C object pointer is a tagged pointer. Tagged
+// pointers encode the object data directly in their pointer bits and do not
+// have an associated memory allocation. The Obj-C runtime uses tagged pointers
+// to transparently optimize small objects.
+static bool IsTaggedObjCPointer(id obj) {
+ const uptr kPossibleTaggedBits = 0x8000000000000001ull;
+ return ((uptr)obj & kPossibleTaggedBits) != 0;
+}
+
+// Returns an address which can be used to inform TSan about synchronization
+// points (MutexLock/Unlock). The TSan infrastructure expects this to be a valid
+// address in the process space. We do a small allocation here to obtain a
+// stable address (the array backing the hash map can change). The memory is
+// never free'd (leaked) and allocation and locking are slow, but this code only
+// runs for @synchronized with tagged pointers, which is very rare.
+static uptr GetOrCreateSyncAddress(uptr addr, ThreadState *thr, uptr pc) {
+ typedef AddrHashMap<uptr, 5> Map;
+ static Map Addresses;
+ Map::Handle h(&Addresses, addr);
+ if (h.created()) {
+ ThreadIgnoreBegin(thr, pc);
+ *h = (uptr) user_alloc(thr, pc, /*size=*/1);
+ ThreadIgnoreEnd(thr, pc);
+ }
+ return *h;
+}
+
+// Returns an address on which we can synchronize given an Obj-C object pointer.
+// For normal object pointers, this is just the address of the object in memory.
+// Tagged pointers are not backed by an actual memory allocation, so we need to
+// synthesize a valid address.
+static uptr SyncAddressForObjCObject(id obj, ThreadState *thr, uptr pc) {
+ if (IsTaggedObjCPointer(obj))
+ return GetOrCreateSyncAddress((uptr)obj, thr, pc);
+ return (uptr)obj;
+}
+
+TSAN_INTERCEPTOR(int, objc_sync_enter, id obj) {
+ SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
+ if (!obj) return REAL(objc_sync_enter)(obj);
+ uptr addr = SyncAddressForObjCObject(obj, thr, pc);
+ MutexPreLock(thr, pc, addr, MutexFlagWriteReentrant);
+ int result = REAL(objc_sync_enter)(obj);
+ CHECK_EQ(result, OBJC_SYNC_SUCCESS);
+ MutexPostLock(thr, pc, addr, MutexFlagWriteReentrant);
+ return result;
+}
+
+TSAN_INTERCEPTOR(int, objc_sync_exit, id obj) {
+ SCOPED_TSAN_INTERCEPTOR(objc_sync_exit, obj);
+ if (!obj) return REAL(objc_sync_exit)(obj);
+ uptr addr = SyncAddressForObjCObject(obj, thr, pc);
+ MutexUnlock(thr, pc, addr);
+ int result = REAL(objc_sync_exit)(obj);
+ if (result != OBJC_SYNC_SUCCESS) MutexInvalidAccess(thr, pc, addr);
+ return result;
+}
+
+TSAN_INTERCEPTOR(int, swapcontext, ucontext_t *oucp, const ucontext_t *ucp) {
+ {
+ SCOPED_INTERCEPTOR_RAW(swapcontext, oucp, ucp);
+ }
+ // Bacause of swapcontext() semantics we have no option but to copy its
+ // impementation here
+ if (!oucp || !ucp) {
+ errno = EINVAL;
+ return -1;
+ }
+ ThreadState *thr = cur_thread();
+ const int UCF_SWAPPED = 0x80000000;
+ oucp->uc_onstack &= ~UCF_SWAPPED;
+ thr->ignore_interceptors++;
+ int ret = getcontext(oucp);
+ if (!(oucp->uc_onstack & UCF_SWAPPED)) {
+ thr->ignore_interceptors--;
+ if (!ret) {
+ oucp->uc_onstack |= UCF_SWAPPED;
+ ret = setcontext(ucp);
+ }
+ }
+ return ret;
+}
+
+// On macOS, libc++ is always linked dynamically, so intercepting works the
+// usual way.
+#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR
+
+namespace {
+struct fake_shared_weak_count {
+ volatile a64 shared_owners;
+ volatile a64 shared_weak_owners;
+ virtual void _unused_0x0() = 0;
+ virtual void _unused_0x8() = 0;
+ virtual void on_zero_shared() = 0;
+ virtual void _unused_0x18() = 0;
+ virtual void on_zero_shared_weak() = 0;
+};
+} // namespace
+
+// The following code adds libc++ interceptors for:
+// void __shared_weak_count::__release_shared() _NOEXCEPT;
+// bool __shared_count::__release_shared() _NOEXCEPT;
+// Shared and weak pointers in C++ maintain reference counts via atomics in
+// libc++.dylib, which are TSan-invisible, and this leads to false positives in
+// destructor code. These interceptors re-implements the whole functions so that
+// the mo_acq_rel semantics of the atomic decrement are visible.
+//
+// Unfortunately, the interceptors cannot simply Acquire/Release some sync
+// object and call the original function, because it would have a race between
+// the sync and the destruction of the object. Calling both under a lock will
+// not work because the destructor can invoke this interceptor again (and even
+// in a different thread, so recursive locks don't help).
+
+STDCXX_INTERCEPTOR(void, _ZNSt3__119__shared_weak_count16__release_sharedEv,
+ fake_shared_weak_count *o) {
+ if (!flags()->shared_ptr_interceptor)
+ return REAL(_ZNSt3__119__shared_weak_count16__release_sharedEv)(o);
+
+ SCOPED_TSAN_INTERCEPTOR(_ZNSt3__119__shared_weak_count16__release_sharedEv,
+ o);
+ if (__tsan_atomic64_fetch_add(&o->shared_owners, -1, mo_release) == 0) {
+ Acquire(thr, pc, (uptr)&o->shared_owners);
+ o->on_zero_shared();
+ if (__tsan_atomic64_fetch_add(&o->shared_weak_owners, -1, mo_release) ==
+ 0) {
+ Acquire(thr, pc, (uptr)&o->shared_weak_owners);
+ o->on_zero_shared_weak();
+ }
+ }
+}
+
+STDCXX_INTERCEPTOR(bool, _ZNSt3__114__shared_count16__release_sharedEv,
+ fake_shared_weak_count *o) {
+ if (!flags()->shared_ptr_interceptor)
+ return REAL(_ZNSt3__114__shared_count16__release_sharedEv)(o);
+
+ SCOPED_TSAN_INTERCEPTOR(_ZNSt3__114__shared_count16__release_sharedEv, o);
+ if (__tsan_atomic64_fetch_add(&o->shared_owners, -1, mo_release) == 0) {
+ Acquire(thr, pc, (uptr)&o->shared_owners);
+ o->on_zero_shared();
+ return true;
+ }
+ return false;
+}
+
+namespace {
+struct call_once_callback_args {
+ void (*orig_func)(void *arg);
+ void *orig_arg;
+ void *flag;
+};
+
+void call_once_callback_wrapper(void *arg) {
+ call_once_callback_args *new_args = (call_once_callback_args *)arg;
+ new_args->orig_func(new_args->orig_arg);
+ __tsan_release(new_args->flag);
+}
+} // namespace
+
+// This adds a libc++ interceptor for:
+// void __call_once(volatile unsigned long&, void*, void(*)(void*));
+// C++11 call_once is implemented via an internal function __call_once which is
+// inside libc++.dylib, and the atomic release store inside it is thus
+// TSan-invisible. To avoid false positives, this interceptor wraps the callback
+// function and performs an explicit Release after the user code has run.
+STDCXX_INTERCEPTOR(void, _ZNSt3__111__call_onceERVmPvPFvS2_E, void *flag,
+ void *arg, void (*func)(void *arg)) {
+ call_once_callback_args new_args = {func, arg, flag};
+ REAL(_ZNSt3__111__call_onceERVmPvPFvS2_E)(flag, &new_args,
+ call_once_callback_wrapper);
+}
+
+} // namespace __tsan
+
+#endif // SANITIZER_MAC
+++ /dev/null
-//===-- tsan_interface.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "tsan_interface.h"
-#include "tsan_interface_ann.h"
-#include "tsan_rtl.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-
-#define CALLERPC ((uptr)__builtin_return_address(0))
-
-using namespace __tsan; // NOLINT
-
-typedef u16 uint16_t;
-typedef u32 uint32_t;
-typedef u64 uint64_t;
-
-void __tsan_init() {
- Initialize(cur_thread());
-}
-
-void __tsan_flush_memory() {
- FlushShadowMemory();
-}
-
-void __tsan_read16(void *addr) {
- MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8);
- MemoryRead(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8);
-}
-
-void __tsan_write16(void *addr) {
- MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8);
- MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8);
-}
-
-void __tsan_read16_pc(void *addr, void *pc) {
- MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
- MemoryRead(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
-}
-
-void __tsan_write16_pc(void *addr, void *pc) {
- MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
- MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
-}
-
-// __tsan_unaligned_read/write calls are emitted by compiler.
-
-void __tsan_unaligned_read2(const void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false);
-}
-
-void __tsan_unaligned_read4(const void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false);
-}
-
-void __tsan_unaligned_read8(const void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false);
-}
-
-void __tsan_unaligned_read16(const void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, false, false);
-}
-
-void __tsan_unaligned_write2(void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false);
-}
-
-void __tsan_unaligned_write4(void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false);
-}
-
-void __tsan_unaligned_write8(void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false);
-}
-
-void __tsan_unaligned_write16(void *addr) {
- UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, true, false);
-}
-
-// __sanitizer_unaligned_load/store are for user instrumentation.
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-u16 __sanitizer_unaligned_load16(const uu16 *addr) {
- __tsan_unaligned_read2(addr);
- return *addr;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-u32 __sanitizer_unaligned_load32(const uu32 *addr) {
- __tsan_unaligned_read4(addr);
- return *addr;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-u64 __sanitizer_unaligned_load64(const uu64 *addr) {
- __tsan_unaligned_read8(addr);
- return *addr;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store16(uu16 *addr, u16 v) {
- __tsan_unaligned_write2(addr);
- *addr = v;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store32(uu32 *addr, u32 v) {
- __tsan_unaligned_write4(addr);
- *addr = v;
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_unaligned_store64(uu64 *addr, u64 v) {
- __tsan_unaligned_write8(addr);
- *addr = v;
-}
-} // extern "C"
-
-void __tsan_acquire(void *addr) {
- Acquire(cur_thread(), CALLERPC, (uptr)addr);
-}
-
-void __tsan_release(void *addr) {
- Release(cur_thread(), CALLERPC, (uptr)addr);
-}
--- /dev/null
+//===-- tsan_interface.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_interface.h"
+#include "tsan_interface_ann.h"
+#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+using namespace __tsan; // NOLINT
+
+typedef u16 uint16_t;
+typedef u32 uint32_t;
+typedef u64 uint64_t;
+
+void __tsan_init() {
+ cur_thread_init();
+ Initialize(cur_thread());
+}
+
+void __tsan_flush_memory() {
+ FlushShadowMemory();
+}
+
+void __tsan_read16(void *addr) {
+ MemoryRead(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8);
+ MemoryRead(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8);
+}
+
+void __tsan_write16(void *addr) {
+ MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8);
+ MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8);
+}
+
+void __tsan_read16_pc(void *addr, void *pc) {
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+ MemoryRead(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
+}
+
+void __tsan_write16_pc(void *addr, void *pc) {
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8);
+ MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8);
+}
+
+// __tsan_unaligned_read/write calls are emitted by compiler.
+
+void __tsan_unaligned_read2(const void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, false, false);
+}
+
+void __tsan_unaligned_read4(const void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, false, false);
+}
+
+void __tsan_unaligned_read8(const void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, false, false);
+}
+
+void __tsan_unaligned_read16(const void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, false, false);
+}
+
+void __tsan_unaligned_write2(void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 2, true, false);
+}
+
+void __tsan_unaligned_write4(void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 4, true, false);
+}
+
+void __tsan_unaligned_write8(void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 8, true, false);
+}
+
+void __tsan_unaligned_write16(void *addr) {
+ UnalignedMemoryAccess(cur_thread(), CALLERPC, (uptr)addr, 16, true, false);
+}
+
+// __sanitizer_unaligned_load/store are for user instrumentation.
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+u16 __sanitizer_unaligned_load16(const uu16 *addr) {
+ __tsan_unaligned_read2(addr);
+ return *addr;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u32 __sanitizer_unaligned_load32(const uu32 *addr) {
+ __tsan_unaligned_read4(addr);
+ return *addr;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+u64 __sanitizer_unaligned_load64(const uu64 *addr) {
+ __tsan_unaligned_read8(addr);
+ return *addr;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store16(uu16 *addr, u16 v) {
+ __tsan_unaligned_write2(addr);
+ *addr = v;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store32(uu32 *addr, u32 v) {
+ __tsan_unaligned_write4(addr);
+ *addr = v;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_unaligned_store64(uu64 *addr, u64 v) {
+ __tsan_unaligned_write8(addr);
+ *addr = v;
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_get_current_fiber() {
+ return cur_thread();
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *__tsan_create_fiber(unsigned flags) {
+ return FiberCreate(cur_thread(), CALLERPC, flags);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_destroy_fiber(void *fiber) {
+ FiberDestroy(cur_thread(), CALLERPC, static_cast<ThreadState *>(fiber));
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_switch_to_fiber(void *fiber, unsigned flags) {
+ FiberSwitch(cur_thread(), CALLERPC, static_cast<ThreadState *>(fiber), flags);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_set_fiber_name(void *fiber, const char *name) {
+ ThreadSetName(static_cast<ThreadState *>(fiber), name);
+}
+} // extern "C"
+
+void __tsan_acquire(void *addr) {
+ Acquire(cur_thread(), CALLERPC, (uptr)addr);
+}
+
+void __tsan_release(void *addr) {
+ Release(cur_thread(), CALLERPC, (uptr)addr);
+}
//===-- tsan_interface.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#endif
// Part of ABI, do not change.
-// http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/atomic?view=markup
+// https://github.com/llvm/llvm-project/blob/master/libcxx/include/atomic
typedef enum {
mo_relaxed,
mo_consume,
+++ /dev/null
-//===-- tsan_interface_ann.cc ---------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_vector.h"
-#include "tsan_interface_ann.h"
-#include "tsan_mutex.h"
-#include "tsan_report.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-#include "tsan_flags.h"
-#include "tsan_platform.h"
-
-#define CALLERPC ((uptr)__builtin_return_address(0))
-
-using namespace __tsan; // NOLINT
-
-namespace __tsan {
-
-class ScopedAnnotation {
- public:
- ScopedAnnotation(ThreadState *thr, const char *aname, uptr pc)
- : thr_(thr) {
- FuncEntry(thr_, pc);
- DPrintf("#%d: annotation %s()\n", thr_->tid, aname);
- }
-
- ~ScopedAnnotation() {
- FuncExit(thr_);
- CheckNoLocks(thr_);
- }
- private:
- ThreadState *const thr_;
-};
-
-#define SCOPED_ANNOTATION_RET(typ, ret) \
- if (!flags()->enable_annotations) \
- return ret; \
- ThreadState *thr = cur_thread(); \
- const uptr caller_pc = (uptr)__builtin_return_address(0); \
- StatInc(thr, StatAnnotation); \
- StatInc(thr, Stat##typ); \
- ScopedAnnotation sa(thr, __func__, caller_pc); \
- const uptr pc = StackTrace::GetCurrentPc(); \
- (void)pc; \
-/**/
-
-#define SCOPED_ANNOTATION(typ) SCOPED_ANNOTATION_RET(typ, )
-
-static const int kMaxDescLen = 128;
-
-struct ExpectRace {
- ExpectRace *next;
- ExpectRace *prev;
- atomic_uintptr_t hitcount;
- atomic_uintptr_t addcount;
- uptr addr;
- uptr size;
- char *file;
- int line;
- char desc[kMaxDescLen];
-};
-
-struct DynamicAnnContext {
- Mutex mtx;
- ExpectRace expect;
- ExpectRace benign;
-
- DynamicAnnContext()
- : mtx(MutexTypeAnnotations, StatMtxAnnotations) {
- }
-};
-
-static DynamicAnnContext *dyn_ann_ctx;
-static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64);
-
-static void AddExpectRace(ExpectRace *list,
- char *f, int l, uptr addr, uptr size, char *desc) {
- ExpectRace *race = list->next;
- for (; race != list; race = race->next) {
- if (race->addr == addr && race->size == size) {
- atomic_store_relaxed(&race->addcount,
- atomic_load_relaxed(&race->addcount) + 1);
- return;
- }
- }
- race = (ExpectRace*)internal_alloc(MBlockExpectRace, sizeof(ExpectRace));
- race->addr = addr;
- race->size = size;
- race->file = f;
- race->line = l;
- race->desc[0] = 0;
- atomic_store_relaxed(&race->hitcount, 0);
- atomic_store_relaxed(&race->addcount, 1);
- if (desc) {
- int i = 0;
- for (; i < kMaxDescLen - 1 && desc[i]; i++)
- race->desc[i] = desc[i];
- race->desc[i] = 0;
- }
- race->prev = list;
- race->next = list->next;
- race->next->prev = race;
- list->next = race;
-}
-
-static ExpectRace *FindRace(ExpectRace *list, uptr addr, uptr size) {
- for (ExpectRace *race = list->next; race != list; race = race->next) {
- uptr maxbegin = max(race->addr, addr);
- uptr minend = min(race->addr + race->size, addr + size);
- if (maxbegin < minend)
- return race;
- }
- return 0;
-}
-
-static bool CheckContains(ExpectRace *list, uptr addr, uptr size) {
- ExpectRace *race = FindRace(list, addr, size);
- if (race == 0)
- return false;
- DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n",
- race->desc, race->addr, (int)race->size, race->file, race->line);
- atomic_fetch_add(&race->hitcount, 1, memory_order_relaxed);
- return true;
-}
-
-static void InitList(ExpectRace *list) {
- list->next = list;
- list->prev = list;
-}
-
-void InitializeDynamicAnnotations() {
- dyn_ann_ctx = new(dyn_ann_ctx_placeholder) DynamicAnnContext;
- InitList(&dyn_ann_ctx->expect);
- InitList(&dyn_ann_ctx->benign);
-}
-
-bool IsExpectedReport(uptr addr, uptr size) {
- ReadLock lock(&dyn_ann_ctx->mtx);
- if (CheckContains(&dyn_ann_ctx->expect, addr, size))
- return true;
- if (CheckContains(&dyn_ann_ctx->benign, addr, size))
- return true;
- return false;
-}
-
-static void CollectMatchedBenignRaces(Vector<ExpectRace> *matched,
- int *unique_count, int *hit_count, atomic_uintptr_t ExpectRace::*counter) {
- ExpectRace *list = &dyn_ann_ctx->benign;
- for (ExpectRace *race = list->next; race != list; race = race->next) {
- (*unique_count)++;
- const uptr cnt = atomic_load_relaxed(&(race->*counter));
- if (cnt == 0)
- continue;
- *hit_count += cnt;
- uptr i = 0;
- for (; i < matched->Size(); i++) {
- ExpectRace *race0 = &(*matched)[i];
- if (race->line == race0->line
- && internal_strcmp(race->file, race0->file) == 0
- && internal_strcmp(race->desc, race0->desc) == 0) {
- atomic_fetch_add(&(race0->*counter), cnt, memory_order_relaxed);
- break;
- }
- }
- if (i == matched->Size())
- matched->PushBack(*race);
- }
-}
-
-void PrintMatchedBenignRaces() {
- Lock lock(&dyn_ann_ctx->mtx);
- int unique_count = 0;
- int hit_count = 0;
- int add_count = 0;
- Vector<ExpectRace> hit_matched;
- CollectMatchedBenignRaces(&hit_matched, &unique_count, &hit_count,
- &ExpectRace::hitcount);
- Vector<ExpectRace> add_matched;
- CollectMatchedBenignRaces(&add_matched, &unique_count, &add_count,
- &ExpectRace::addcount);
- if (hit_matched.Size()) {
- Printf("ThreadSanitizer: Matched %d \"benign\" races (pid=%d):\n",
- hit_count, (int)internal_getpid());
- for (uptr i = 0; i < hit_matched.Size(); i++) {
- Printf("%d %s:%d %s\n",
- atomic_load_relaxed(&hit_matched[i].hitcount),
- hit_matched[i].file, hit_matched[i].line, hit_matched[i].desc);
- }
- }
- if (hit_matched.Size()) {
- Printf("ThreadSanitizer: Annotated %d \"benign\" races, %d unique"
- " (pid=%d):\n",
- add_count, unique_count, (int)internal_getpid());
- for (uptr i = 0; i < add_matched.Size(); i++) {
- Printf("%d %s:%d %s\n",
- atomic_load_relaxed(&add_matched[i].addcount),
- add_matched[i].file, add_matched[i].line, add_matched[i].desc);
- }
- }
-}
-
-static void ReportMissedExpectedRace(ExpectRace *race) {
- Printf("==================\n");
- Printf("WARNING: ThreadSanitizer: missed expected data race\n");
- Printf(" %s addr=%zx %s:%d\n",
- race->desc, race->addr, race->file, race->line);
- Printf("==================\n");
-}
-} // namespace __tsan
-
-using namespace __tsan; // NOLINT
-
-extern "C" {
-void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) {
- SCOPED_ANNOTATION(AnnotateHappensBefore);
- Release(thr, pc, addr);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) {
- SCOPED_ANNOTATION(AnnotateHappensAfter);
- Acquire(thr, pc, addr);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) {
- SCOPED_ANNOTATION(AnnotateCondVarSignal);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateCondVarSignalAll(char *f, int l, uptr cv) {
- SCOPED_ANNOTATION(AnnotateCondVarSignalAll);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateMutexIsNotPHB(char *f, int l, uptr mu) {
- SCOPED_ANNOTATION(AnnotateMutexIsNotPHB);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateCondVarWait(char *f, int l, uptr cv,
- uptr lock) {
- SCOPED_ANNOTATION(AnnotateCondVarWait);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateRWLockCreate(char *f, int l, uptr m) {
- SCOPED_ANNOTATION(AnnotateRWLockCreate);
- MutexCreate(thr, pc, m, MutexFlagWriteReentrant);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateRWLockCreateStatic(char *f, int l, uptr m) {
- SCOPED_ANNOTATION(AnnotateRWLockCreateStatic);
- MutexCreate(thr, pc, m, MutexFlagWriteReentrant | MutexFlagLinkerInit);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateRWLockDestroy(char *f, int l, uptr m) {
- SCOPED_ANNOTATION(AnnotateRWLockDestroy);
- MutexDestroy(thr, pc, m);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateRWLockAcquired(char *f, int l, uptr m,
- uptr is_w) {
- SCOPED_ANNOTATION(AnnotateRWLockAcquired);
- if (is_w)
- MutexPostLock(thr, pc, m, MutexFlagDoPreLockOnPostLock);
- else
- MutexPostReadLock(thr, pc, m, MutexFlagDoPreLockOnPostLock);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateRWLockReleased(char *f, int l, uptr m,
- uptr is_w) {
- SCOPED_ANNOTATION(AnnotateRWLockReleased);
- if (is_w)
- MutexUnlock(thr, pc, m);
- else
- MutexReadUnlock(thr, pc, m);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateTraceMemory(char *f, int l, uptr mem) {
- SCOPED_ANNOTATION(AnnotateTraceMemory);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateFlushState(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateFlushState);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateNewMemory(char *f, int l, uptr mem,
- uptr size) {
- SCOPED_ANNOTATION(AnnotateNewMemory);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateNoOp(char *f, int l, uptr mem) {
- SCOPED_ANNOTATION(AnnotateNoOp);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateFlushExpectedRaces(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateFlushExpectedRaces);
- Lock lock(&dyn_ann_ctx->mtx);
- while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) {
- ExpectRace *race = dyn_ann_ctx->expect.next;
- if (atomic_load_relaxed(&race->hitcount) == 0) {
- ctx->nmissed_expected++;
- ReportMissedExpectedRace(race);
- }
- race->prev->next = race->next;
- race->next->prev = race->prev;
- internal_free(race);
- }
-}
-
-void INTERFACE_ATTRIBUTE AnnotateEnableRaceDetection(
- char *f, int l, int enable) {
- SCOPED_ANNOTATION(AnnotateEnableRaceDetection);
- // FIXME: Reconsider this functionality later. It may be irrelevant.
-}
-
-void INTERFACE_ATTRIBUTE AnnotateMutexIsUsedAsCondVar(
- char *f, int l, uptr mu) {
- SCOPED_ANNOTATION(AnnotateMutexIsUsedAsCondVar);
-}
-
-void INTERFACE_ATTRIBUTE AnnotatePCQGet(
- char *f, int l, uptr pcq) {
- SCOPED_ANNOTATION(AnnotatePCQGet);
-}
-
-void INTERFACE_ATTRIBUTE AnnotatePCQPut(
- char *f, int l, uptr pcq) {
- SCOPED_ANNOTATION(AnnotatePCQPut);
-}
-
-void INTERFACE_ATTRIBUTE AnnotatePCQDestroy(
- char *f, int l, uptr pcq) {
- SCOPED_ANNOTATION(AnnotatePCQDestroy);
-}
-
-void INTERFACE_ATTRIBUTE AnnotatePCQCreate(
- char *f, int l, uptr pcq) {
- SCOPED_ANNOTATION(AnnotatePCQCreate);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateExpectRace(
- char *f, int l, uptr mem, char *desc) {
- SCOPED_ANNOTATION(AnnotateExpectRace);
- Lock lock(&dyn_ann_ctx->mtx);
- AddExpectRace(&dyn_ann_ctx->expect,
- f, l, mem, 1, desc);
- DPrintf("Add expected race: %s addr=%zx %s:%d\n", desc, mem, f, l);
-}
-
-static void BenignRaceImpl(
- char *f, int l, uptr mem, uptr size, char *desc) {
- Lock lock(&dyn_ann_ctx->mtx);
- AddExpectRace(&dyn_ann_ctx->benign,
- f, l, mem, size, desc);
- DPrintf("Add benign race: %s addr=%zx %s:%d\n", desc, mem, f, l);
-}
-
-// FIXME: Turn it off later. WTF is benign race?1?? Go talk to Hans Boehm.
-void INTERFACE_ATTRIBUTE AnnotateBenignRaceSized(
- char *f, int l, uptr mem, uptr size, char *desc) {
- SCOPED_ANNOTATION(AnnotateBenignRaceSized);
- BenignRaceImpl(f, l, mem, size, desc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateBenignRace(
- char *f, int l, uptr mem, char *desc) {
- SCOPED_ANNOTATION(AnnotateBenignRace);
- BenignRaceImpl(f, l, mem, 1, desc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin);
- ThreadIgnoreBegin(thr, pc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd);
- ThreadIgnoreEnd(thr, pc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin);
- ThreadIgnoreBegin(thr, pc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd);
- ThreadIgnoreEnd(thr, pc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin);
- ThreadIgnoreSyncBegin(thr, pc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) {
- SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd);
- ThreadIgnoreSyncEnd(thr, pc);
-}
-
-void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange(
- char *f, int l, uptr addr, uptr size) {
- SCOPED_ANNOTATION(AnnotatePublishMemoryRange);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateUnpublishMemoryRange(
- char *f, int l, uptr addr, uptr size) {
- SCOPED_ANNOTATION(AnnotateUnpublishMemoryRange);
-}
-
-void INTERFACE_ATTRIBUTE AnnotateThreadName(
- char *f, int l, char *name) {
- SCOPED_ANNOTATION(AnnotateThreadName);
- ThreadSetName(thr, name);
-}
-
-// We deliberately omit the implementation of WTFAnnotateHappensBefore() and
-// WTFAnnotateHappensAfter(). Those are being used by Webkit to annotate
-// atomic operations, which should be handled by ThreadSanitizer correctly.
-void INTERFACE_ATTRIBUTE WTFAnnotateHappensBefore(char *f, int l, uptr addr) {
- SCOPED_ANNOTATION(AnnotateHappensBefore);
-}
-
-void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
- SCOPED_ANNOTATION(AnnotateHappensAfter);
-}
-
-void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized(
- char *f, int l, uptr mem, uptr sz, char *desc) {
- SCOPED_ANNOTATION(AnnotateBenignRaceSized);
- BenignRaceImpl(f, l, mem, sz, desc);
-}
-
-int INTERFACE_ATTRIBUTE RunningOnValgrind() {
- return flags()->running_on_valgrind;
-}
-
-double __attribute__((weak)) INTERFACE_ATTRIBUTE ValgrindSlowdown(void) {
- return 10.0;
-}
-
-const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) {
- if (internal_strcmp(query, "pure_happens_before") == 0)
- return "1";
- else
- return "0";
-}
-
-void INTERFACE_ATTRIBUTE
-AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {}
-void INTERFACE_ATTRIBUTE
-AnnotateMemoryIsUninitialized(char *f, int l, uptr mem, uptr sz) {}
-
-// Note: the parameter is called flagz, because flags is already taken
-// by the global function that returns flags.
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_create(void *m, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_create);
- MutexCreate(thr, pc, (uptr)m, flagz & MutexCreationFlagMask);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_destroy(void *m, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_destroy);
- MutexDestroy(thr, pc, (uptr)m, flagz);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_pre_lock(void *m, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_pre_lock);
- if (!(flagz & MutexFlagTryLock)) {
- if (flagz & MutexFlagReadLock)
- MutexPreReadLock(thr, pc, (uptr)m);
- else
- MutexPreLock(thr, pc, (uptr)m);
- }
- ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
- ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_post_lock(void *m, unsigned flagz, int rec) {
- SCOPED_ANNOTATION(__tsan_mutex_post_lock);
- ThreadIgnoreSyncEnd(thr, pc);
- ThreadIgnoreEnd(thr, pc);
- if (!(flagz & MutexFlagTryLockFailed)) {
- if (flagz & MutexFlagReadLock)
- MutexPostReadLock(thr, pc, (uptr)m, flagz);
- else
- MutexPostLock(thr, pc, (uptr)m, flagz, rec);
- }
-}
-
-INTERFACE_ATTRIBUTE
-int __tsan_mutex_pre_unlock(void *m, unsigned flagz) {
- SCOPED_ANNOTATION_RET(__tsan_mutex_pre_unlock, 0);
- int ret = 0;
- if (flagz & MutexFlagReadLock) {
- CHECK(!(flagz & MutexFlagRecursiveUnlock));
- MutexReadUnlock(thr, pc, (uptr)m);
- } else {
- ret = MutexUnlock(thr, pc, (uptr)m, flagz);
- }
- ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
- ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
- return ret;
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_post_unlock(void *m, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_post_unlock);
- ThreadIgnoreSyncEnd(thr, pc);
- ThreadIgnoreEnd(thr, pc);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_pre_signal(void *addr, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_pre_signal);
- ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
- ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_post_signal(void *addr, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_post_signal);
- ThreadIgnoreSyncEnd(thr, pc);
- ThreadIgnoreEnd(thr, pc);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_pre_divert(void *addr, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_pre_divert);
- // Exit from ignore region started in __tsan_mutex_pre_lock/unlock/signal.
- ThreadIgnoreSyncEnd(thr, pc);
- ThreadIgnoreEnd(thr, pc);
-}
-
-INTERFACE_ATTRIBUTE
-void __tsan_mutex_post_divert(void *addr, unsigned flagz) {
- SCOPED_ANNOTATION(__tsan_mutex_post_divert);
- ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
- ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
-}
-} // extern "C"
--- /dev/null
+//===-- tsan_interface_ann.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_vector.h"
+#include "tsan_interface_ann.h"
+#include "tsan_mutex.h"
+#include "tsan_report.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+#include "tsan_platform.h"
+
+#define CALLERPC ((uptr)__builtin_return_address(0))
+
+using namespace __tsan; // NOLINT
+
+namespace __tsan {
+
+class ScopedAnnotation {
+ public:
+ ScopedAnnotation(ThreadState *thr, const char *aname, uptr pc)
+ : thr_(thr) {
+ FuncEntry(thr_, pc);
+ DPrintf("#%d: annotation %s()\n", thr_->tid, aname);
+ }
+
+ ~ScopedAnnotation() {
+ FuncExit(thr_);
+ CheckNoLocks(thr_);
+ }
+ private:
+ ThreadState *const thr_;
+};
+
+#define SCOPED_ANNOTATION_RET(typ, ret) \
+ if (!flags()->enable_annotations) \
+ return ret; \
+ ThreadState *thr = cur_thread(); \
+ const uptr caller_pc = (uptr)__builtin_return_address(0); \
+ StatInc(thr, StatAnnotation); \
+ StatInc(thr, Stat##typ); \
+ ScopedAnnotation sa(thr, __func__, caller_pc); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
+ (void)pc; \
+/**/
+
+#define SCOPED_ANNOTATION(typ) SCOPED_ANNOTATION_RET(typ, )
+
+static const int kMaxDescLen = 128;
+
+struct ExpectRace {
+ ExpectRace *next;
+ ExpectRace *prev;
+ atomic_uintptr_t hitcount;
+ atomic_uintptr_t addcount;
+ uptr addr;
+ uptr size;
+ char *file;
+ int line;
+ char desc[kMaxDescLen];
+};
+
+struct DynamicAnnContext {
+ Mutex mtx;
+ ExpectRace expect;
+ ExpectRace benign;
+
+ DynamicAnnContext()
+ : mtx(MutexTypeAnnotations, StatMtxAnnotations) {
+ }
+};
+
+static DynamicAnnContext *dyn_ann_ctx;
+static char dyn_ann_ctx_placeholder[sizeof(DynamicAnnContext)] ALIGNED(64);
+
+static void AddExpectRace(ExpectRace *list,
+ char *f, int l, uptr addr, uptr size, char *desc) {
+ ExpectRace *race = list->next;
+ for (; race != list; race = race->next) {
+ if (race->addr == addr && race->size == size) {
+ atomic_store_relaxed(&race->addcount,
+ atomic_load_relaxed(&race->addcount) + 1);
+ return;
+ }
+ }
+ race = (ExpectRace*)internal_alloc(MBlockExpectRace, sizeof(ExpectRace));
+ race->addr = addr;
+ race->size = size;
+ race->file = f;
+ race->line = l;
+ race->desc[0] = 0;
+ atomic_store_relaxed(&race->hitcount, 0);
+ atomic_store_relaxed(&race->addcount, 1);
+ if (desc) {
+ int i = 0;
+ for (; i < kMaxDescLen - 1 && desc[i]; i++)
+ race->desc[i] = desc[i];
+ race->desc[i] = 0;
+ }
+ race->prev = list;
+ race->next = list->next;
+ race->next->prev = race;
+ list->next = race;
+}
+
+static ExpectRace *FindRace(ExpectRace *list, uptr addr, uptr size) {
+ for (ExpectRace *race = list->next; race != list; race = race->next) {
+ uptr maxbegin = max(race->addr, addr);
+ uptr minend = min(race->addr + race->size, addr + size);
+ if (maxbegin < minend)
+ return race;
+ }
+ return 0;
+}
+
+static bool CheckContains(ExpectRace *list, uptr addr, uptr size) {
+ ExpectRace *race = FindRace(list, addr, size);
+ if (race == 0)
+ return false;
+ DPrintf("Hit expected/benign race: %s addr=%zx:%d %s:%d\n",
+ race->desc, race->addr, (int)race->size, race->file, race->line);
+ atomic_fetch_add(&race->hitcount, 1, memory_order_relaxed);
+ return true;
+}
+
+static void InitList(ExpectRace *list) {
+ list->next = list;
+ list->prev = list;
+}
+
+void InitializeDynamicAnnotations() {
+ dyn_ann_ctx = new(dyn_ann_ctx_placeholder) DynamicAnnContext;
+ InitList(&dyn_ann_ctx->expect);
+ InitList(&dyn_ann_ctx->benign);
+}
+
+bool IsExpectedReport(uptr addr, uptr size) {
+ ReadLock lock(&dyn_ann_ctx->mtx);
+ if (CheckContains(&dyn_ann_ctx->expect, addr, size))
+ return true;
+ if (CheckContains(&dyn_ann_ctx->benign, addr, size))
+ return true;
+ return false;
+}
+
+static void CollectMatchedBenignRaces(Vector<ExpectRace> *matched,
+ int *unique_count, int *hit_count, atomic_uintptr_t ExpectRace::*counter) {
+ ExpectRace *list = &dyn_ann_ctx->benign;
+ for (ExpectRace *race = list->next; race != list; race = race->next) {
+ (*unique_count)++;
+ const uptr cnt = atomic_load_relaxed(&(race->*counter));
+ if (cnt == 0)
+ continue;
+ *hit_count += cnt;
+ uptr i = 0;
+ for (; i < matched->Size(); i++) {
+ ExpectRace *race0 = &(*matched)[i];
+ if (race->line == race0->line
+ && internal_strcmp(race->file, race0->file) == 0
+ && internal_strcmp(race->desc, race0->desc) == 0) {
+ atomic_fetch_add(&(race0->*counter), cnt, memory_order_relaxed);
+ break;
+ }
+ }
+ if (i == matched->Size())
+ matched->PushBack(*race);
+ }
+}
+
+void PrintMatchedBenignRaces() {
+ Lock lock(&dyn_ann_ctx->mtx);
+ int unique_count = 0;
+ int hit_count = 0;
+ int add_count = 0;
+ Vector<ExpectRace> hit_matched;
+ CollectMatchedBenignRaces(&hit_matched, &unique_count, &hit_count,
+ &ExpectRace::hitcount);
+ Vector<ExpectRace> add_matched;
+ CollectMatchedBenignRaces(&add_matched, &unique_count, &add_count,
+ &ExpectRace::addcount);
+ if (hit_matched.Size()) {
+ Printf("ThreadSanitizer: Matched %d \"benign\" races (pid=%d):\n",
+ hit_count, (int)internal_getpid());
+ for (uptr i = 0; i < hit_matched.Size(); i++) {
+ Printf("%d %s:%d %s\n",
+ atomic_load_relaxed(&hit_matched[i].hitcount),
+ hit_matched[i].file, hit_matched[i].line, hit_matched[i].desc);
+ }
+ }
+ if (hit_matched.Size()) {
+ Printf("ThreadSanitizer: Annotated %d \"benign\" races, %d unique"
+ " (pid=%d):\n",
+ add_count, unique_count, (int)internal_getpid());
+ for (uptr i = 0; i < add_matched.Size(); i++) {
+ Printf("%d %s:%d %s\n",
+ atomic_load_relaxed(&add_matched[i].addcount),
+ add_matched[i].file, add_matched[i].line, add_matched[i].desc);
+ }
+ }
+}
+
+static void ReportMissedExpectedRace(ExpectRace *race) {
+ Printf("==================\n");
+ Printf("WARNING: ThreadSanitizer: missed expected data race\n");
+ Printf(" %s addr=%zx %s:%d\n",
+ race->desc, race->addr, race->file, race->line);
+ Printf("==================\n");
+}
+} // namespace __tsan
+
+using namespace __tsan; // NOLINT
+
+extern "C" {
+void INTERFACE_ATTRIBUTE AnnotateHappensBefore(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensBefore);
+ Release(thr, pc, addr);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateHappensAfter(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensAfter);
+ Acquire(thr, pc, addr);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateCondVarSignal(char *f, int l, uptr cv) {
+ SCOPED_ANNOTATION(AnnotateCondVarSignal);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateCondVarSignalAll(char *f, int l, uptr cv) {
+ SCOPED_ANNOTATION(AnnotateCondVarSignalAll);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateMutexIsNotPHB(char *f, int l, uptr mu) {
+ SCOPED_ANNOTATION(AnnotateMutexIsNotPHB);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateCondVarWait(char *f, int l, uptr cv,
+ uptr lock) {
+ SCOPED_ANNOTATION(AnnotateCondVarWait);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateRWLockCreate(char *f, int l, uptr m) {
+ SCOPED_ANNOTATION(AnnotateRWLockCreate);
+ MutexCreate(thr, pc, m, MutexFlagWriteReentrant);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateRWLockCreateStatic(char *f, int l, uptr m) {
+ SCOPED_ANNOTATION(AnnotateRWLockCreateStatic);
+ MutexCreate(thr, pc, m, MutexFlagWriteReentrant | MutexFlagLinkerInit);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateRWLockDestroy(char *f, int l, uptr m) {
+ SCOPED_ANNOTATION(AnnotateRWLockDestroy);
+ MutexDestroy(thr, pc, m);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateRWLockAcquired(char *f, int l, uptr m,
+ uptr is_w) {
+ SCOPED_ANNOTATION(AnnotateRWLockAcquired);
+ if (is_w)
+ MutexPostLock(thr, pc, m, MutexFlagDoPreLockOnPostLock);
+ else
+ MutexPostReadLock(thr, pc, m, MutexFlagDoPreLockOnPostLock);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateRWLockReleased(char *f, int l, uptr m,
+ uptr is_w) {
+ SCOPED_ANNOTATION(AnnotateRWLockReleased);
+ if (is_w)
+ MutexUnlock(thr, pc, m);
+ else
+ MutexReadUnlock(thr, pc, m);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateTraceMemory(char *f, int l, uptr mem) {
+ SCOPED_ANNOTATION(AnnotateTraceMemory);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateFlushState(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateFlushState);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateNewMemory(char *f, int l, uptr mem,
+ uptr size) {
+ SCOPED_ANNOTATION(AnnotateNewMemory);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateNoOp(char *f, int l, uptr mem) {
+ SCOPED_ANNOTATION(AnnotateNoOp);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateFlushExpectedRaces(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateFlushExpectedRaces);
+ Lock lock(&dyn_ann_ctx->mtx);
+ while (dyn_ann_ctx->expect.next != &dyn_ann_ctx->expect) {
+ ExpectRace *race = dyn_ann_ctx->expect.next;
+ if (atomic_load_relaxed(&race->hitcount) == 0) {
+ ctx->nmissed_expected++;
+ ReportMissedExpectedRace(race);
+ }
+ race->prev->next = race->next;
+ race->next->prev = race->prev;
+ internal_free(race);
+ }
+}
+
+void INTERFACE_ATTRIBUTE AnnotateEnableRaceDetection(
+ char *f, int l, int enable) {
+ SCOPED_ANNOTATION(AnnotateEnableRaceDetection);
+ // FIXME: Reconsider this functionality later. It may be irrelevant.
+}
+
+void INTERFACE_ATTRIBUTE AnnotateMutexIsUsedAsCondVar(
+ char *f, int l, uptr mu) {
+ SCOPED_ANNOTATION(AnnotateMutexIsUsedAsCondVar);
+}
+
+void INTERFACE_ATTRIBUTE AnnotatePCQGet(
+ char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQGet);
+}
+
+void INTERFACE_ATTRIBUTE AnnotatePCQPut(
+ char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQPut);
+}
+
+void INTERFACE_ATTRIBUTE AnnotatePCQDestroy(
+ char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQDestroy);
+}
+
+void INTERFACE_ATTRIBUTE AnnotatePCQCreate(
+ char *f, int l, uptr pcq) {
+ SCOPED_ANNOTATION(AnnotatePCQCreate);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateExpectRace(
+ char *f, int l, uptr mem, char *desc) {
+ SCOPED_ANNOTATION(AnnotateExpectRace);
+ Lock lock(&dyn_ann_ctx->mtx);
+ AddExpectRace(&dyn_ann_ctx->expect,
+ f, l, mem, 1, desc);
+ DPrintf("Add expected race: %s addr=%zx %s:%d\n", desc, mem, f, l);
+}
+
+static void BenignRaceImpl(
+ char *f, int l, uptr mem, uptr size, char *desc) {
+ Lock lock(&dyn_ann_ctx->mtx);
+ AddExpectRace(&dyn_ann_ctx->benign,
+ f, l, mem, size, desc);
+ DPrintf("Add benign race: %s addr=%zx %s:%d\n", desc, mem, f, l);
+}
+
+// FIXME: Turn it off later. WTF is benign race?1?? Go talk to Hans Boehm.
+void INTERFACE_ATTRIBUTE AnnotateBenignRaceSized(
+ char *f, int l, uptr mem, uptr size, char *desc) {
+ SCOPED_ANNOTATION(AnnotateBenignRaceSized);
+ BenignRaceImpl(f, l, mem, size, desc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateBenignRace(
+ char *f, int l, uptr mem, char *desc) {
+ SCOPED_ANNOTATION(AnnotateBenignRace);
+ BenignRaceImpl(f, l, mem, 1, desc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin);
+ ThreadIgnoreBegin(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd);
+ ThreadIgnoreEnd(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin);
+ ThreadIgnoreBegin(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd);
+ ThreadIgnoreEnd(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin);
+ ThreadIgnoreSyncBegin(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) {
+ SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd);
+ ThreadIgnoreSyncEnd(thr, pc);
+}
+
+void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange(
+ char *f, int l, uptr addr, uptr size) {
+ SCOPED_ANNOTATION(AnnotatePublishMemoryRange);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateUnpublishMemoryRange(
+ char *f, int l, uptr addr, uptr size) {
+ SCOPED_ANNOTATION(AnnotateUnpublishMemoryRange);
+}
+
+void INTERFACE_ATTRIBUTE AnnotateThreadName(
+ char *f, int l, char *name) {
+ SCOPED_ANNOTATION(AnnotateThreadName);
+ ThreadSetName(thr, name);
+}
+
+// We deliberately omit the implementation of WTFAnnotateHappensBefore() and
+// WTFAnnotateHappensAfter(). Those are being used by Webkit to annotate
+// atomic operations, which should be handled by ThreadSanitizer correctly.
+void INTERFACE_ATTRIBUTE WTFAnnotateHappensBefore(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensBefore);
+}
+
+void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) {
+ SCOPED_ANNOTATION(AnnotateHappensAfter);
+}
+
+void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized(
+ char *f, int l, uptr mem, uptr sz, char *desc) {
+ SCOPED_ANNOTATION(AnnotateBenignRaceSized);
+ BenignRaceImpl(f, l, mem, sz, desc);
+}
+
+int INTERFACE_ATTRIBUTE RunningOnValgrind() {
+ return flags()->running_on_valgrind;
+}
+
+double __attribute__((weak)) INTERFACE_ATTRIBUTE ValgrindSlowdown(void) {
+ return 10.0;
+}
+
+const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) {
+ if (internal_strcmp(query, "pure_happens_before") == 0)
+ return "1";
+ else
+ return "0";
+}
+
+void INTERFACE_ATTRIBUTE
+AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {}
+void INTERFACE_ATTRIBUTE
+AnnotateMemoryIsUninitialized(char *f, int l, uptr mem, uptr sz) {}
+
+// Note: the parameter is called flagz, because flags is already taken
+// by the global function that returns flags.
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_create(void *m, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_create);
+ MutexCreate(thr, pc, (uptr)m, flagz & MutexCreationFlagMask);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_destroy(void *m, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_destroy);
+ MutexDestroy(thr, pc, (uptr)m, flagz);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_pre_lock(void *m, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_pre_lock);
+ if (!(flagz & MutexFlagTryLock)) {
+ if (flagz & MutexFlagReadLock)
+ MutexPreReadLock(thr, pc, (uptr)m);
+ else
+ MutexPreLock(thr, pc, (uptr)m);
+ }
+ ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
+ ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_post_lock(void *m, unsigned flagz, int rec) {
+ SCOPED_ANNOTATION(__tsan_mutex_post_lock);
+ ThreadIgnoreSyncEnd(thr, pc);
+ ThreadIgnoreEnd(thr, pc);
+ if (!(flagz & MutexFlagTryLockFailed)) {
+ if (flagz & MutexFlagReadLock)
+ MutexPostReadLock(thr, pc, (uptr)m, flagz);
+ else
+ MutexPostLock(thr, pc, (uptr)m, flagz, rec);
+ }
+}
+
+INTERFACE_ATTRIBUTE
+int __tsan_mutex_pre_unlock(void *m, unsigned flagz) {
+ SCOPED_ANNOTATION_RET(__tsan_mutex_pre_unlock, 0);
+ int ret = 0;
+ if (flagz & MutexFlagReadLock) {
+ CHECK(!(flagz & MutexFlagRecursiveUnlock));
+ MutexReadUnlock(thr, pc, (uptr)m);
+ } else {
+ ret = MutexUnlock(thr, pc, (uptr)m, flagz);
+ }
+ ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
+ ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
+ return ret;
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_post_unlock(void *m, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_post_unlock);
+ ThreadIgnoreSyncEnd(thr, pc);
+ ThreadIgnoreEnd(thr, pc);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_pre_signal(void *addr, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_pre_signal);
+ ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
+ ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_post_signal(void *addr, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_post_signal);
+ ThreadIgnoreSyncEnd(thr, pc);
+ ThreadIgnoreEnd(thr, pc);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_pre_divert(void *addr, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_pre_divert);
+ // Exit from ignore region started in __tsan_mutex_pre_lock/unlock/signal.
+ ThreadIgnoreSyncEnd(thr, pc);
+ ThreadIgnoreEnd(thr, pc);
+}
+
+INTERFACE_ATTRIBUTE
+void __tsan_mutex_post_divert(void *addr, unsigned flagz) {
+ SCOPED_ANNOTATION(__tsan_mutex_post_divert);
+ ThreadIgnoreBegin(thr, pc, /*save_stack=*/false);
+ ThreadIgnoreSyncBegin(thr, pc, /*save_stack=*/false);
+}
+} // extern "C"
//===-- tsan_interface_ann.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_interface_atomic.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-// ThreadSanitizer atomic operations are based on C++11/C1x standards.
-// For background see C++11 standard. A slightly older, publicly
-// available draft of the standard (not entirely up-to-date, but close enough
-// for casual browsing) is available here:
-// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
-// The following page contains more background information:
-// http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/
-
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "tsan_flags.h"
-#include "tsan_interface.h"
-#include "tsan_rtl.h"
-
-using namespace __tsan; // NOLINT
-
-#if !SANITIZER_GO && __TSAN_HAS_INT128
-// Protects emulation of 128-bit atomic operations.
-static StaticSpinMutex mutex128;
-#endif
-
-static bool IsLoadOrder(morder mo) {
- return mo == mo_relaxed || mo == mo_consume
- || mo == mo_acquire || mo == mo_seq_cst;
-}
-
-static bool IsStoreOrder(morder mo) {
- return mo == mo_relaxed || mo == mo_release || mo == mo_seq_cst;
-}
-
-static bool IsReleaseOrder(morder mo) {
- return mo == mo_release || mo == mo_acq_rel || mo == mo_seq_cst;
-}
-
-static bool IsAcquireOrder(morder mo) {
- return mo == mo_consume || mo == mo_acquire
- || mo == mo_acq_rel || mo == mo_seq_cst;
-}
-
-static bool IsAcqRelOrder(morder mo) {
- return mo == mo_acq_rel || mo == mo_seq_cst;
-}
-
-template<typename T> T func_xchg(volatile T *v, T op) {
- T res = __sync_lock_test_and_set(v, op);
- // __sync_lock_test_and_set does not contain full barrier.
- __sync_synchronize();
- return res;
-}
-
-template<typename T> T func_add(volatile T *v, T op) {
- return __sync_fetch_and_add(v, op);
-}
-
-template<typename T> T func_sub(volatile T *v, T op) {
- return __sync_fetch_and_sub(v, op);
-}
-
-template<typename T> T func_and(volatile T *v, T op) {
- return __sync_fetch_and_and(v, op);
-}
-
-template<typename T> T func_or(volatile T *v, T op) {
- return __sync_fetch_and_or(v, op);
-}
-
-template<typename T> T func_xor(volatile T *v, T op) {
- return __sync_fetch_and_xor(v, op);
-}
-
-template<typename T> T func_nand(volatile T *v, T op) {
- // clang does not support __sync_fetch_and_nand.
- T cmp = *v;
- for (;;) {
- T newv = ~(cmp & op);
- T cur = __sync_val_compare_and_swap(v, cmp, newv);
- if (cmp == cur)
- return cmp;
- cmp = cur;
- }
-}
-
-template<typename T> T func_cas(volatile T *v, T cmp, T xch) {
- return __sync_val_compare_and_swap(v, cmp, xch);
-}
-
-// clang does not support 128-bit atomic ops.
-// Atomic ops are executed under tsan internal mutex,
-// here we assume that the atomic variables are not accessed
-// from non-instrumented code.
-#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !SANITIZER_GO \
- && __TSAN_HAS_INT128
-a128 func_xchg(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = op;
- return cmp;
-}
-
-a128 func_add(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = cmp + op;
- return cmp;
-}
-
-a128 func_sub(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = cmp - op;
- return cmp;
-}
-
-a128 func_and(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = cmp & op;
- return cmp;
-}
-
-a128 func_or(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = cmp | op;
- return cmp;
-}
-
-a128 func_xor(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = cmp ^ op;
- return cmp;
-}
-
-a128 func_nand(volatile a128 *v, a128 op) {
- SpinMutexLock lock(&mutex128);
- a128 cmp = *v;
- *v = ~(cmp & op);
- return cmp;
-}
-
-a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
- SpinMutexLock lock(&mutex128);
- a128 cur = *v;
- if (cur == cmp)
- *v = xch;
- return cur;
-}
-#endif
-
-template<typename T>
-static int SizeLog() {
- if (sizeof(T) <= 1)
- return kSizeLog1;
- else if (sizeof(T) <= 2)
- return kSizeLog2;
- else if (sizeof(T) <= 4)
- return kSizeLog4;
- else
- return kSizeLog8;
- // For 16-byte atomics we also use 8-byte memory access,
- // this leads to false negatives only in very obscure cases.
-}
-
-#if !SANITIZER_GO
-static atomic_uint8_t *to_atomic(const volatile a8 *a) {
- return reinterpret_cast<atomic_uint8_t *>(const_cast<a8 *>(a));
-}
-
-static atomic_uint16_t *to_atomic(const volatile a16 *a) {
- return reinterpret_cast<atomic_uint16_t *>(const_cast<a16 *>(a));
-}
-#endif
-
-static atomic_uint32_t *to_atomic(const volatile a32 *a) {
- return reinterpret_cast<atomic_uint32_t *>(const_cast<a32 *>(a));
-}
-
-static atomic_uint64_t *to_atomic(const volatile a64 *a) {
- return reinterpret_cast<atomic_uint64_t *>(const_cast<a64 *>(a));
-}
-
-static memory_order to_mo(morder mo) {
- switch (mo) {
- case mo_relaxed: return memory_order_relaxed;
- case mo_consume: return memory_order_consume;
- case mo_acquire: return memory_order_acquire;
- case mo_release: return memory_order_release;
- case mo_acq_rel: return memory_order_acq_rel;
- case mo_seq_cst: return memory_order_seq_cst;
- }
- CHECK(0);
- return memory_order_seq_cst;
-}
-
-template<typename T>
-static T NoTsanAtomicLoad(const volatile T *a, morder mo) {
- return atomic_load(to_atomic(a), to_mo(mo));
-}
-
-#if __TSAN_HAS_INT128 && !SANITIZER_GO
-static a128 NoTsanAtomicLoad(const volatile a128 *a, morder mo) {
- SpinMutexLock lock(&mutex128);
- return *a;
-}
-#endif
-
-template<typename T>
-static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, morder mo) {
- CHECK(IsLoadOrder(mo));
- // This fast-path is critical for performance.
- // Assume the access is atomic.
- if (!IsAcquireOrder(mo)) {
- MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
- return NoTsanAtomicLoad(a, mo);
- }
- // Don't create sync object if it does not exist yet. For example, an atomic
- // pointer is initialized to nullptr and then periodically acquire-loaded.
- T v = NoTsanAtomicLoad(a, mo);
- SyncVar *s = ctx->metamap.GetIfExistsAndLock((uptr)a, false);
- if (s) {
- AcquireImpl(thr, pc, &s->clock);
- // Re-read under sync mutex because we need a consistent snapshot
- // of the value and the clock we acquire.
- v = NoTsanAtomicLoad(a, mo);
- s->mtx.ReadUnlock();
- }
- MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
- return v;
-}
-
-template<typename T>
-static void NoTsanAtomicStore(volatile T *a, T v, morder mo) {
- atomic_store(to_atomic(a), v, to_mo(mo));
-}
-
-#if __TSAN_HAS_INT128 && !SANITIZER_GO
-static void NoTsanAtomicStore(volatile a128 *a, a128 v, morder mo) {
- SpinMutexLock lock(&mutex128);
- *a = v;
-}
-#endif
-
-template<typename T>
-static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- CHECK(IsStoreOrder(mo));
- MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
- // This fast-path is critical for performance.
- // Assume the access is atomic.
- // Strictly saying even relaxed store cuts off release sequence,
- // so must reset the clock.
- if (!IsReleaseOrder(mo)) {
- NoTsanAtomicStore(a, v, mo);
- return;
- }
- __sync_synchronize();
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, true);
- thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- ReleaseStoreImpl(thr, pc, &s->clock);
- NoTsanAtomicStore(a, v, mo);
- s->mtx.Unlock();
-}
-
-template<typename T, T (*F)(volatile T *v, T op)>
-static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
- MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
- SyncVar *s = 0;
- if (mo != mo_relaxed) {
- s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, true);
- thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- if (IsAcqRelOrder(mo))
- AcquireReleaseImpl(thr, pc, &s->clock);
- else if (IsReleaseOrder(mo))
- ReleaseImpl(thr, pc, &s->clock);
- else if (IsAcquireOrder(mo))
- AcquireImpl(thr, pc, &s->clock);
- }
- v = F(a, v);
- if (s)
- s->mtx.Unlock();
- return v;
-}
-
-template<typename T>
-static T NoTsanAtomicExchange(volatile T *a, T v, morder mo) {
- return func_xchg(a, v);
-}
-
-template<typename T>
-static T NoTsanAtomicFetchAdd(volatile T *a, T v, morder mo) {
- return func_add(a, v);
-}
-
-template<typename T>
-static T NoTsanAtomicFetchSub(volatile T *a, T v, morder mo) {
- return func_sub(a, v);
-}
-
-template<typename T>
-static T NoTsanAtomicFetchAnd(volatile T *a, T v, morder mo) {
- return func_and(a, v);
-}
-
-template<typename T>
-static T NoTsanAtomicFetchOr(volatile T *a, T v, morder mo) {
- return func_or(a, v);
-}
-
-template<typename T>
-static T NoTsanAtomicFetchXor(volatile T *a, T v, morder mo) {
- return func_xor(a, v);
-}
-
-template<typename T>
-static T NoTsanAtomicFetchNand(volatile T *a, T v, morder mo) {
- return func_nand(a, v);
-}
-
-template<typename T>
-static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_xchg>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static T AtomicFetchAdd(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_add>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static T AtomicFetchSub(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_sub>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static T AtomicFetchAnd(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_and>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static T AtomicFetchOr(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_or>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static T AtomicFetchXor(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_xor>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static T AtomicFetchNand(ThreadState *thr, uptr pc, volatile T *a, T v,
- morder mo) {
- return AtomicRMW<T, func_nand>(thr, pc, a, v, mo);
-}
-
-template<typename T>
-static bool NoTsanAtomicCAS(volatile T *a, T *c, T v, morder mo, morder fmo) {
- return atomic_compare_exchange_strong(to_atomic(a), c, v, to_mo(mo));
-}
-
-#if __TSAN_HAS_INT128
-static bool NoTsanAtomicCAS(volatile a128 *a, a128 *c, a128 v,
- morder mo, morder fmo) {
- a128 old = *c;
- a128 cur = func_cas(a, old, v);
- if (cur == old)
- return true;
- *c = cur;
- return false;
-}
-#endif
-
-template<typename T>
-static T NoTsanAtomicCAS(volatile T *a, T c, T v, morder mo, morder fmo) {
- NoTsanAtomicCAS(a, &c, v, mo, fmo);
- return c;
-}
-
-template<typename T>
-static bool AtomicCAS(ThreadState *thr, uptr pc,
- volatile T *a, T *c, T v, morder mo, morder fmo) {
- (void)fmo; // Unused because llvm does not pass it yet.
- MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
- SyncVar *s = 0;
- bool write_lock = mo != mo_acquire && mo != mo_consume;
- if (mo != mo_relaxed) {
- s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, write_lock);
- thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- if (IsAcqRelOrder(mo))
- AcquireReleaseImpl(thr, pc, &s->clock);
- else if (IsReleaseOrder(mo))
- ReleaseImpl(thr, pc, &s->clock);
- else if (IsAcquireOrder(mo))
- AcquireImpl(thr, pc, &s->clock);
- }
- T cc = *c;
- T pr = func_cas(a, cc, v);
- if (s) {
- if (write_lock)
- s->mtx.Unlock();
- else
- s->mtx.ReadUnlock();
- }
- if (pr == cc)
- return true;
- *c = pr;
- return false;
-}
-
-template<typename T>
-static T AtomicCAS(ThreadState *thr, uptr pc,
- volatile T *a, T c, T v, morder mo, morder fmo) {
- AtomicCAS(thr, pc, a, &c, v, mo, fmo);
- return c;
-}
-
-#if !SANITIZER_GO
-static void NoTsanAtomicFence(morder mo) {
- __sync_synchronize();
-}
-
-static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
- // FIXME(dvyukov): not implemented.
- __sync_synchronize();
-}
-#endif
-
-// Interface functions follow.
-#if !SANITIZER_GO
-
-// C/C++
-
-static morder convert_morder(morder mo) {
- if (flags()->force_seq_cst_atomics)
- return (morder)mo_seq_cst;
-
- // Filter out additional memory order flags:
- // MEMMODEL_SYNC = 1 << 15
- // __ATOMIC_HLE_ACQUIRE = 1 << 16
- // __ATOMIC_HLE_RELEASE = 1 << 17
- //
- // HLE is an optimization, and we pretend that elision always fails.
- // MEMMODEL_SYNC is used when lowering __sync_ atomics,
- // since we use __sync_ atomics for actual atomic operations,
- // we can safely ignore it as well. It also subtly affects semantics,
- // but we don't model the difference.
- return (morder)(mo & 0x7fff);
-}
-
-#define SCOPED_ATOMIC(func, ...) \
- ThreadState *const thr = cur_thread(); \
- if (thr->ignore_sync || thr->ignore_interceptors) { \
- ProcessPendingSignals(thr); \
- return NoTsanAtomic##func(__VA_ARGS__); \
- } \
- const uptr callpc = (uptr)__builtin_return_address(0); \
- uptr pc = StackTrace::GetCurrentPc(); \
- mo = convert_morder(mo); \
- AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
- ScopedAtomic sa(thr, callpc, a, mo, __func__); \
- return Atomic##func(thr, pc, __VA_ARGS__); \
-/**/
-
-class ScopedAtomic {
- public:
- ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
- morder mo, const char *func)
- : thr_(thr) {
- FuncEntry(thr_, pc);
- DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
- }
- ~ScopedAtomic() {
- ProcessPendingSignals(thr_);
- FuncExit(thr_);
- }
- private:
- ThreadState *thr_;
-};
-
-static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
- StatInc(thr, StatAtomic);
- StatInc(thr, t);
- StatInc(thr, size == 1 ? StatAtomic1
- : size == 2 ? StatAtomic2
- : size == 4 ? StatAtomic4
- : size == 8 ? StatAtomic8
- : StatAtomic16);
- StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
- : mo == mo_consume ? StatAtomicConsume
- : mo == mo_acquire ? StatAtomicAcquire
- : mo == mo_release ? StatAtomicRelease
- : mo == mo_acq_rel ? StatAtomicAcq_Rel
- : StatAtomicSeq_Cst);
-}
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_load(const volatile a8 *a, morder mo) {
- SCOPED_ATOMIC(Load, a, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_load(const volatile a16 *a, morder mo) {
- SCOPED_ATOMIC(Load, a, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_load(const volatile a32 *a, morder mo) {
- SCOPED_ATOMIC(Load, a, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) {
- SCOPED_ATOMIC(Load, a, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_load(const volatile a128 *a, morder mo) {
- SCOPED_ATOMIC(Load, a, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(Store, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(Store, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(Store, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(Store, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(Store, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(Exchange, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(Exchange, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(Exchange, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(Exchange, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(Exchange, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(FetchAdd, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(FetchAdd, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(FetchAdd, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(FetchAdd, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(FetchAdd, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(FetchSub, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(FetchSub, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(FetchSub, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(FetchSub, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(FetchSub, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(FetchAnd, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(FetchAnd, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(FetchAnd, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(FetchAnd, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(FetchAnd, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(FetchOr, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(FetchOr, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(FetchOr, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(FetchOr, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(FetchOr, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(FetchXor, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(FetchXor, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(FetchXor, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(FetchXor, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(FetchXor, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo) {
- SCOPED_ATOMIC(FetchNand, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo) {
- SCOPED_ATOMIC(FetchNand, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo) {
- SCOPED_ATOMIC(FetchNand, a, v, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo) {
- SCOPED_ATOMIC(FetchNand, a, v, mo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo) {
- SCOPED_ATOMIC(FetchNand, a, v, mo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-
-#if __TSAN_HAS_INT128
-SANITIZER_INTERFACE_ATTRIBUTE
-a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v,
- morder mo, morder fmo) {
- SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
-}
-#endif
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic_thread_fence(morder mo) {
- char* a = 0;
- SCOPED_ATOMIC(Fence, mo);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_atomic_signal_fence(morder mo) {
-}
-} // extern "C"
-
-#else // #if !SANITIZER_GO
-
-// Go
-
-#define ATOMIC(func, ...) \
- if (thr->ignore_sync) { \
- NoTsanAtomic##func(__VA_ARGS__); \
- } else { \
- FuncEntry(thr, cpc); \
- Atomic##func(thr, pc, __VA_ARGS__); \
- FuncExit(thr); \
- } \
-/**/
-
-#define ATOMIC_RET(func, ret, ...) \
- if (thr->ignore_sync) { \
- (ret) = NoTsanAtomic##func(__VA_ARGS__); \
- } else { \
- FuncEntry(thr, cpc); \
- (ret) = Atomic##func(thr, pc, __VA_ARGS__); \
- FuncExit(thr); \
- } \
-/**/
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC_RET(Load, *(a32*)(a+8), *(a32**)a, mo_acquire);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC_RET(Load, *(a64*)(a+8), *(a64**)a, mo_acquire);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic32_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC(Store, *(a32**)a, *(a32*)(a+8), mo_release);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC(Store, *(a64**)a, *(a64*)(a+8), mo_release);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic32_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC_RET(FetchAdd, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic64_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC_RET(FetchAdd, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic32_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC_RET(Exchange, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic64_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- ATOMIC_RET(Exchange, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic32_compare_exchange(
- ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- a32 cur = 0;
- a32 cmp = *(a32*)(a+8);
- ATOMIC_RET(CAS, cur, *(a32**)a, cmp, *(a32*)(a+12), mo_acq_rel, mo_acquire);
- *(bool*)(a+16) = (cur == cmp);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void __tsan_go_atomic64_compare_exchange(
- ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
- a64 cur = 0;
- a64 cmp = *(a64*)(a+8);
- ATOMIC_RET(CAS, cur, *(a64**)a, cmp, *(a64*)(a+16), mo_acq_rel, mo_acquire);
- *(bool*)(a+24) = (cur == cmp);
-}
-} // extern "C"
-#endif // #if !SANITIZER_GO
--- /dev/null
+//===-- tsan_interface_atomic.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+// ThreadSanitizer atomic operations are based on C++11/C1x standards.
+// For background see C++11 standard. A slightly older, publicly
+// available draft of the standard (not entirely up-to-date, but close enough
+// for casual browsing) is available here:
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
+// The following page contains more background information:
+// http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "tsan_flags.h"
+#include "tsan_interface.h"
+#include "tsan_rtl.h"
+
+using namespace __tsan; // NOLINT
+
+#if !SANITIZER_GO && __TSAN_HAS_INT128
+// Protects emulation of 128-bit atomic operations.
+static StaticSpinMutex mutex128;
+#endif
+
+static bool IsLoadOrder(morder mo) {
+ return mo == mo_relaxed || mo == mo_consume
+ || mo == mo_acquire || mo == mo_seq_cst;
+}
+
+static bool IsStoreOrder(morder mo) {
+ return mo == mo_relaxed || mo == mo_release || mo == mo_seq_cst;
+}
+
+static bool IsReleaseOrder(morder mo) {
+ return mo == mo_release || mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+static bool IsAcquireOrder(morder mo) {
+ return mo == mo_consume || mo == mo_acquire
+ || mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+static bool IsAcqRelOrder(morder mo) {
+ return mo == mo_acq_rel || mo == mo_seq_cst;
+}
+
+template<typename T> T func_xchg(volatile T *v, T op) {
+ T res = __sync_lock_test_and_set(v, op);
+ // __sync_lock_test_and_set does not contain full barrier.
+ __sync_synchronize();
+ return res;
+}
+
+template<typename T> T func_add(volatile T *v, T op) {
+ return __sync_fetch_and_add(v, op);
+}
+
+template<typename T> T func_sub(volatile T *v, T op) {
+ return __sync_fetch_and_sub(v, op);
+}
+
+template<typename T> T func_and(volatile T *v, T op) {
+ return __sync_fetch_and_and(v, op);
+}
+
+template<typename T> T func_or(volatile T *v, T op) {
+ return __sync_fetch_and_or(v, op);
+}
+
+template<typename T> T func_xor(volatile T *v, T op) {
+ return __sync_fetch_and_xor(v, op);
+}
+
+template<typename T> T func_nand(volatile T *v, T op) {
+ // clang does not support __sync_fetch_and_nand.
+ T cmp = *v;
+ for (;;) {
+ T newv = ~(cmp & op);
+ T cur = __sync_val_compare_and_swap(v, cmp, newv);
+ if (cmp == cur)
+ return cmp;
+ cmp = cur;
+ }
+}
+
+template<typename T> T func_cas(volatile T *v, T cmp, T xch) {
+ return __sync_val_compare_and_swap(v, cmp, xch);
+}
+
+// clang does not support 128-bit atomic ops.
+// Atomic ops are executed under tsan internal mutex,
+// here we assume that the atomic variables are not accessed
+// from non-instrumented code.
+#if !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) && !SANITIZER_GO \
+ && __TSAN_HAS_INT128
+a128 func_xchg(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = op;
+ return cmp;
+}
+
+a128 func_add(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = cmp + op;
+ return cmp;
+}
+
+a128 func_sub(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = cmp - op;
+ return cmp;
+}
+
+a128 func_and(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = cmp & op;
+ return cmp;
+}
+
+a128 func_or(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = cmp | op;
+ return cmp;
+}
+
+a128 func_xor(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = cmp ^ op;
+ return cmp;
+}
+
+a128 func_nand(volatile a128 *v, a128 op) {
+ SpinMutexLock lock(&mutex128);
+ a128 cmp = *v;
+ *v = ~(cmp & op);
+ return cmp;
+}
+
+a128 func_cas(volatile a128 *v, a128 cmp, a128 xch) {
+ SpinMutexLock lock(&mutex128);
+ a128 cur = *v;
+ if (cur == cmp)
+ *v = xch;
+ return cur;
+}
+#endif
+
+template<typename T>
+static int SizeLog() {
+ if (sizeof(T) <= 1)
+ return kSizeLog1;
+ else if (sizeof(T) <= 2)
+ return kSizeLog2;
+ else if (sizeof(T) <= 4)
+ return kSizeLog4;
+ else
+ return kSizeLog8;
+ // For 16-byte atomics we also use 8-byte memory access,
+ // this leads to false negatives only in very obscure cases.
+}
+
+#if !SANITIZER_GO
+static atomic_uint8_t *to_atomic(const volatile a8 *a) {
+ return reinterpret_cast<atomic_uint8_t *>(const_cast<a8 *>(a));
+}
+
+static atomic_uint16_t *to_atomic(const volatile a16 *a) {
+ return reinterpret_cast<atomic_uint16_t *>(const_cast<a16 *>(a));
+}
+#endif
+
+static atomic_uint32_t *to_atomic(const volatile a32 *a) {
+ return reinterpret_cast<atomic_uint32_t *>(const_cast<a32 *>(a));
+}
+
+static atomic_uint64_t *to_atomic(const volatile a64 *a) {
+ return reinterpret_cast<atomic_uint64_t *>(const_cast<a64 *>(a));
+}
+
+static memory_order to_mo(morder mo) {
+ switch (mo) {
+ case mo_relaxed: return memory_order_relaxed;
+ case mo_consume: return memory_order_consume;
+ case mo_acquire: return memory_order_acquire;
+ case mo_release: return memory_order_release;
+ case mo_acq_rel: return memory_order_acq_rel;
+ case mo_seq_cst: return memory_order_seq_cst;
+ }
+ CHECK(0);
+ return memory_order_seq_cst;
+}
+
+template<typename T>
+static T NoTsanAtomicLoad(const volatile T *a, morder mo) {
+ return atomic_load(to_atomic(a), to_mo(mo));
+}
+
+#if __TSAN_HAS_INT128 && !SANITIZER_GO
+static a128 NoTsanAtomicLoad(const volatile a128 *a, morder mo) {
+ SpinMutexLock lock(&mutex128);
+ return *a;
+}
+#endif
+
+template<typename T>
+static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, morder mo) {
+ CHECK(IsLoadOrder(mo));
+ // This fast-path is critical for performance.
+ // Assume the access is atomic.
+ if (!IsAcquireOrder(mo)) {
+ MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
+ return NoTsanAtomicLoad(a, mo);
+ }
+ // Don't create sync object if it does not exist yet. For example, an atomic
+ // pointer is initialized to nullptr and then periodically acquire-loaded.
+ T v = NoTsanAtomicLoad(a, mo);
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock((uptr)a, false);
+ if (s) {
+ AcquireImpl(thr, pc, &s->clock);
+ // Re-read under sync mutex because we need a consistent snapshot
+ // of the value and the clock we acquire.
+ v = NoTsanAtomicLoad(a, mo);
+ s->mtx.ReadUnlock();
+ }
+ MemoryReadAtomic(thr, pc, (uptr)a, SizeLog<T>());
+ return v;
+}
+
+template<typename T>
+static void NoTsanAtomicStore(volatile T *a, T v, morder mo) {
+ atomic_store(to_atomic(a), v, to_mo(mo));
+}
+
+#if __TSAN_HAS_INT128 && !SANITIZER_GO
+static void NoTsanAtomicStore(volatile a128 *a, a128 v, morder mo) {
+ SpinMutexLock lock(&mutex128);
+ *a = v;
+}
+#endif
+
+template<typename T>
+static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ CHECK(IsStoreOrder(mo));
+ MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
+ // This fast-path is critical for performance.
+ // Assume the access is atomic.
+ // Strictly saying even relaxed store cuts off release sequence,
+ // so must reset the clock.
+ if (!IsReleaseOrder(mo)) {
+ NoTsanAtomicStore(a, v, mo);
+ return;
+ }
+ __sync_synchronize();
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseStoreImpl(thr, pc, &s->clock);
+ NoTsanAtomicStore(a, v, mo);
+ s->mtx.Unlock();
+}
+
+template<typename T, T (*F)(volatile T *v, T op)>
+static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) {
+ MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
+ SyncVar *s = 0;
+ if (mo != mo_relaxed) {
+ s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, true);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ if (IsAcqRelOrder(mo))
+ AcquireReleaseImpl(thr, pc, &s->clock);
+ else if (IsReleaseOrder(mo))
+ ReleaseImpl(thr, pc, &s->clock);
+ else if (IsAcquireOrder(mo))
+ AcquireImpl(thr, pc, &s->clock);
+ }
+ v = F(a, v);
+ if (s)
+ s->mtx.Unlock();
+ return v;
+}
+
+template<typename T>
+static T NoTsanAtomicExchange(volatile T *a, T v, morder mo) {
+ return func_xchg(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchAdd(volatile T *a, T v, morder mo) {
+ return func_add(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchSub(volatile T *a, T v, morder mo) {
+ return func_sub(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchAnd(volatile T *a, T v, morder mo) {
+ return func_and(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchOr(volatile T *a, T v, morder mo) {
+ return func_or(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchXor(volatile T *a, T v, morder mo) {
+ return func_xor(a, v);
+}
+
+template<typename T>
+static T NoTsanAtomicFetchNand(volatile T *a, T v, morder mo) {
+ return func_nand(a, v);
+}
+
+template<typename T>
+static T AtomicExchange(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_xchg>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchAdd(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_add>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchSub(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_sub>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchAnd(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_and>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchOr(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_or>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchXor(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_xor>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static T AtomicFetchNand(ThreadState *thr, uptr pc, volatile T *a, T v,
+ morder mo) {
+ return AtomicRMW<T, func_nand>(thr, pc, a, v, mo);
+}
+
+template<typename T>
+static bool NoTsanAtomicCAS(volatile T *a, T *c, T v, morder mo, morder fmo) {
+ return atomic_compare_exchange_strong(to_atomic(a), c, v, to_mo(mo));
+}
+
+#if __TSAN_HAS_INT128
+static bool NoTsanAtomicCAS(volatile a128 *a, a128 *c, a128 v,
+ morder mo, morder fmo) {
+ a128 old = *c;
+ a128 cur = func_cas(a, old, v);
+ if (cur == old)
+ return true;
+ *c = cur;
+ return false;
+}
+#endif
+
+template<typename T>
+static T NoTsanAtomicCAS(volatile T *a, T c, T v, morder mo, morder fmo) {
+ NoTsanAtomicCAS(a, &c, v, mo, fmo);
+ return c;
+}
+
+template<typename T>
+static bool AtomicCAS(ThreadState *thr, uptr pc,
+ volatile T *a, T *c, T v, morder mo, morder fmo) {
+ (void)fmo; // Unused because llvm does not pass it yet.
+ MemoryWriteAtomic(thr, pc, (uptr)a, SizeLog<T>());
+ SyncVar *s = 0;
+ bool write_lock = mo != mo_acquire && mo != mo_consume;
+ if (mo != mo_relaxed) {
+ s = ctx->metamap.GetOrCreateAndLock(thr, pc, (uptr)a, write_lock);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ if (IsAcqRelOrder(mo))
+ AcquireReleaseImpl(thr, pc, &s->clock);
+ else if (IsReleaseOrder(mo))
+ ReleaseImpl(thr, pc, &s->clock);
+ else if (IsAcquireOrder(mo))
+ AcquireImpl(thr, pc, &s->clock);
+ }
+ T cc = *c;
+ T pr = func_cas(a, cc, v);
+ if (s) {
+ if (write_lock)
+ s->mtx.Unlock();
+ else
+ s->mtx.ReadUnlock();
+ }
+ if (pr == cc)
+ return true;
+ *c = pr;
+ return false;
+}
+
+template<typename T>
+static T AtomicCAS(ThreadState *thr, uptr pc,
+ volatile T *a, T c, T v, morder mo, morder fmo) {
+ AtomicCAS(thr, pc, a, &c, v, mo, fmo);
+ return c;
+}
+
+#if !SANITIZER_GO
+static void NoTsanAtomicFence(morder mo) {
+ __sync_synchronize();
+}
+
+static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
+ // FIXME(dvyukov): not implemented.
+ __sync_synchronize();
+}
+#endif
+
+// Interface functions follow.
+#if !SANITIZER_GO
+
+// C/C++
+
+static morder convert_morder(morder mo) {
+ if (flags()->force_seq_cst_atomics)
+ return (morder)mo_seq_cst;
+
+ // Filter out additional memory order flags:
+ // MEMMODEL_SYNC = 1 << 15
+ // __ATOMIC_HLE_ACQUIRE = 1 << 16
+ // __ATOMIC_HLE_RELEASE = 1 << 17
+ //
+ // HLE is an optimization, and we pretend that elision always fails.
+ // MEMMODEL_SYNC is used when lowering __sync_ atomics,
+ // since we use __sync_ atomics for actual atomic operations,
+ // we can safely ignore it as well. It also subtly affects semantics,
+ // but we don't model the difference.
+ return (morder)(mo & 0x7fff);
+}
+
+#define SCOPED_ATOMIC(func, ...) \
+ ThreadState *const thr = cur_thread(); \
+ if (UNLIKELY(thr->ignore_sync || thr->ignore_interceptors)) { \
+ ProcessPendingSignals(thr); \
+ return NoTsanAtomic##func(__VA_ARGS__); \
+ } \
+ const uptr callpc = (uptr)__builtin_return_address(0); \
+ uptr pc = StackTrace::GetCurrentPc(); \
+ mo = convert_morder(mo); \
+ AtomicStatInc(thr, sizeof(*a), mo, StatAtomic##func); \
+ ScopedAtomic sa(thr, callpc, a, mo, __func__); \
+ return Atomic##func(thr, pc, __VA_ARGS__); \
+/**/
+
+class ScopedAtomic {
+ public:
+ ScopedAtomic(ThreadState *thr, uptr pc, const volatile void *a,
+ morder mo, const char *func)
+ : thr_(thr) {
+ FuncEntry(thr_, pc);
+ DPrintf("#%d: %s(%p, %d)\n", thr_->tid, func, a, mo);
+ }
+ ~ScopedAtomic() {
+ ProcessPendingSignals(thr_);
+ FuncExit(thr_);
+ }
+ private:
+ ThreadState *thr_;
+};
+
+static void AtomicStatInc(ThreadState *thr, uptr size, morder mo, StatType t) {
+ StatInc(thr, StatAtomic);
+ StatInc(thr, t);
+ StatInc(thr, size == 1 ? StatAtomic1
+ : size == 2 ? StatAtomic2
+ : size == 4 ? StatAtomic4
+ : size == 8 ? StatAtomic8
+ : StatAtomic16);
+ StatInc(thr, mo == mo_relaxed ? StatAtomicRelaxed
+ : mo == mo_consume ? StatAtomicConsume
+ : mo == mo_acquire ? StatAtomicAcquire
+ : mo == mo_release ? StatAtomicRelease
+ : mo == mo_acq_rel ? StatAtomicAcq_Rel
+ : StatAtomicSeq_Cst);
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_load(const volatile a8 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_load(const volatile a16 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_load(const volatile a32 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_load(const volatile a64 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_load(const volatile a128 *a, morder mo) {
+ SCOPED_ATOMIC(Load, a, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic8_store(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic16_store(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic32_store(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic64_store(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic128_store(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(Store, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_exchange(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_exchange(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_exchange(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_exchange(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_exchange(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(Exchange, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_add(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_add(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_add(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_add(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_add(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchAdd, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_sub(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_sub(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_sub(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_sub(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_sub(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchSub, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_and(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_and(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_and(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_and(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_and(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchAnd, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_or(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_or(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_or(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_or(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_or(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchOr, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_xor(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_xor(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_xor(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_xor(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_xor(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchXor, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_fetch_nand(volatile a8 *a, a8 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_fetch_nand(volatile a16 *a, a16 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_fetch_nand(volatile a32 *a, a32 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_fetch_nand(volatile a64 *a, a64 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_fetch_nand(volatile a128 *a, a128 v, morder mo) {
+ SCOPED_ATOMIC(FetchNand, a, v, mo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic8_compare_exchange_strong(volatile a8 *a, a8 *c, a8 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic16_compare_exchange_strong(volatile a16 *a, a16 *c, a16 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic32_compare_exchange_strong(volatile a32 *a, a32 *c, a32 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic64_compare_exchange_strong(volatile a64 *a, a64 *c, a64 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic128_compare_exchange_strong(volatile a128 *a, a128 *c, a128 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic8_compare_exchange_weak(volatile a8 *a, a8 *c, a8 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic16_compare_exchange_weak(volatile a16 *a, a16 *c, a16 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic32_compare_exchange_weak(volatile a32 *a, a32 *c, a32 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic64_compare_exchange_weak(volatile a64 *a, a64 *c, a64 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+int __tsan_atomic128_compare_exchange_weak(volatile a128 *a, a128 *c, a128 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a8 __tsan_atomic8_compare_exchange_val(volatile a8 *a, a8 c, a8 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a16 __tsan_atomic16_compare_exchange_val(volatile a16 *a, a16 c, a16 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a32 __tsan_atomic32_compare_exchange_val(volatile a32 *a, a32 c, a32 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+a64 __tsan_atomic64_compare_exchange_val(volatile a64 *a, a64 c, a64 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+
+#if __TSAN_HAS_INT128
+SANITIZER_INTERFACE_ATTRIBUTE
+a128 __tsan_atomic128_compare_exchange_val(volatile a128 *a, a128 c, a128 v,
+ morder mo, morder fmo) {
+ SCOPED_ATOMIC(CAS, a, c, v, mo, fmo);
+}
+#endif
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic_thread_fence(morder mo) {
+ char* a = 0;
+ SCOPED_ATOMIC(Fence, mo);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_atomic_signal_fence(morder mo) {
+}
+} // extern "C"
+
+#else // #if !SANITIZER_GO
+
+// Go
+
+#define ATOMIC(func, ...) \
+ if (thr->ignore_sync) { \
+ NoTsanAtomic##func(__VA_ARGS__); \
+ } else { \
+ FuncEntry(thr, cpc); \
+ Atomic##func(thr, pc, __VA_ARGS__); \
+ FuncExit(thr); \
+ } \
+/**/
+
+#define ATOMIC_RET(func, ret, ...) \
+ if (thr->ignore_sync) { \
+ (ret) = NoTsanAtomic##func(__VA_ARGS__); \
+ } else { \
+ FuncEntry(thr, cpc); \
+ (ret) = Atomic##func(thr, pc, __VA_ARGS__); \
+ FuncExit(thr); \
+ } \
+/**/
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Load, *(a32*)(a+8), *(a32**)a, mo_acquire);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_load(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Load, *(a64*)(a+8), *(a64**)a, mo_acquire);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC(Store, *(a32**)a, *(a32*)(a+8), mo_release);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_store(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC(Store, *(a64**)a, *(a64*)(a+8), mo_release);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(FetchAdd, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_fetch_add(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(FetchAdd, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Exchange, *(a32*)(a+16), *(a32**)a, *(a32*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ ATOMIC_RET(Exchange, *(a64*)(a+16), *(a64**)a, *(a64*)(a+8), mo_acq_rel);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic32_compare_exchange(
+ ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ a32 cur = 0;
+ a32 cmp = *(a32*)(a+8);
+ ATOMIC_RET(CAS, cur, *(a32**)a, cmp, *(a32*)(a+12), mo_acq_rel, mo_acquire);
+ *(bool*)(a+16) = (cur == cmp);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __tsan_go_atomic64_compare_exchange(
+ ThreadState *thr, uptr cpc, uptr pc, u8 *a) {
+ a64 cur = 0;
+ a64 cmp = *(a64*)(a+8);
+ ATOMIC_RET(CAS, cur, *(a64**)a, cmp, *(a64*)(a+16), mo_acq_rel, mo_acquire);
+ *(bool*)(a+24) = (cur == cmp);
+}
+} // extern "C"
+#endif // #if !SANITIZER_GO
//===-- tsan_interface_inl.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_interface_java.cc --------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "tsan_interface_java.h"
-#include "tsan_rtl.h"
-#include "tsan_mutex.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-
-using namespace __tsan; // NOLINT
-
-const jptr kHeapAlignment = 8;
-
-namespace __tsan {
-
-struct JavaContext {
- const uptr heap_begin;
- const uptr heap_size;
-
- JavaContext(jptr heap_begin, jptr heap_size)
- : heap_begin(heap_begin)
- , heap_size(heap_size) {
- }
-};
-
-class ScopedJavaFunc {
- public:
- ScopedJavaFunc(ThreadState *thr, uptr pc)
- : thr_(thr) {
- Initialize(thr_);
- FuncEntry(thr, pc);
- }
-
- ~ScopedJavaFunc() {
- FuncExit(thr_);
- // FIXME(dvyukov): process pending signals.
- }
-
- private:
- ThreadState *thr_;
-};
-
-static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
-static JavaContext *jctx;
-
-} // namespace __tsan
-
-#define SCOPED_JAVA_FUNC(func) \
- ThreadState *thr = cur_thread(); \
- const uptr caller_pc = GET_CALLER_PC(); \
- const uptr pc = StackTrace::GetCurrentPc(); \
- (void)pc; \
- ScopedJavaFunc scoped(thr, caller_pc); \
-/**/
-
-void __tsan_java_init(jptr heap_begin, jptr heap_size) {
- SCOPED_JAVA_FUNC(__tsan_java_init);
- DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
- CHECK_EQ(jctx, 0);
- CHECK_GT(heap_begin, 0);
- CHECK_GT(heap_size, 0);
- CHECK_EQ(heap_begin % kHeapAlignment, 0);
- CHECK_EQ(heap_size % kHeapAlignment, 0);
- CHECK_LT(heap_begin, heap_begin + heap_size);
- jctx = new(jctx_buf) JavaContext(heap_begin, heap_size);
-}
-
-int __tsan_java_fini() {
- SCOPED_JAVA_FUNC(__tsan_java_fini);
- DPrintf("#%d: java_fini()\n", thr->tid);
- CHECK_NE(jctx, 0);
- // FIXME(dvyukov): this does not call atexit() callbacks.
- int status = Finalize(thr);
- DPrintf("#%d: java_fini() = %d\n", thr->tid, status);
- return status;
-}
-
-void __tsan_java_alloc(jptr ptr, jptr size) {
- SCOPED_JAVA_FUNC(__tsan_java_alloc);
- DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size);
- CHECK_NE(jctx, 0);
- CHECK_NE(size, 0);
- CHECK_EQ(ptr % kHeapAlignment, 0);
- CHECK_EQ(size % kHeapAlignment, 0);
- CHECK_GE(ptr, jctx->heap_begin);
- CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
-
- OnUserAlloc(thr, pc, ptr, size, false);
-}
-
-void __tsan_java_free(jptr ptr, jptr size) {
- SCOPED_JAVA_FUNC(__tsan_java_free);
- DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size);
- CHECK_NE(jctx, 0);
- CHECK_NE(size, 0);
- CHECK_EQ(ptr % kHeapAlignment, 0);
- CHECK_EQ(size % kHeapAlignment, 0);
- CHECK_GE(ptr, jctx->heap_begin);
- CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
-
- ctx->metamap.FreeRange(thr->proc(), ptr, size);
-}
-
-void __tsan_java_move(jptr src, jptr dst, jptr size) {
- SCOPED_JAVA_FUNC(__tsan_java_move);
- DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size);
- CHECK_NE(jctx, 0);
- CHECK_NE(size, 0);
- CHECK_EQ(src % kHeapAlignment, 0);
- CHECK_EQ(dst % kHeapAlignment, 0);
- CHECK_EQ(size % kHeapAlignment, 0);
- CHECK_GE(src, jctx->heap_begin);
- CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size);
- CHECK_GE(dst, jctx->heap_begin);
- CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
- CHECK_NE(dst, src);
- CHECK_NE(size, 0);
-
- // Assuming it's not running concurrently with threads that do
- // memory accesses and mutex operations (stop-the-world phase).
- ctx->metamap.MoveMemory(src, dst, size);
-
- // Move shadow.
- u64 *s = (u64*)MemToShadow(src);
- u64 *d = (u64*)MemToShadow(dst);
- u64 *send = (u64*)MemToShadow(src + size);
- uptr inc = 1;
- if (dst > src) {
- s = (u64*)MemToShadow(src + size) - 1;
- d = (u64*)MemToShadow(dst + size) - 1;
- send = (u64*)MemToShadow(src) - 1;
- inc = -1;
- }
- for (; s != send; s += inc, d += inc) {
- *d = *s;
- *s = 0;
- }
-}
-
-jptr __tsan_java_find(jptr *from_ptr, jptr to) {
- SCOPED_JAVA_FUNC(__tsan_java_find);
- DPrintf("#%d: java_find(&%p, %p)\n", *from_ptr, to);
- CHECK_EQ((*from_ptr) % kHeapAlignment, 0);
- CHECK_EQ(to % kHeapAlignment, 0);
- CHECK_GE(*from_ptr, jctx->heap_begin);
- CHECK_LE(to, jctx->heap_begin + jctx->heap_size);
- for (uptr from = *from_ptr; from < to; from += kHeapAlignment) {
- MBlock *b = ctx->metamap.GetBlock(from);
- if (b) {
- *from_ptr = from;
- return b->siz;
- }
- }
- return 0;
-}
-
-void __tsan_java_finalize() {
- SCOPED_JAVA_FUNC(__tsan_java_finalize);
- DPrintf("#%d: java_mutex_finalize()\n", thr->tid);
- AcquireGlobal(thr, 0);
-}
-
-void __tsan_java_mutex_lock(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_mutex_lock);
- DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- MutexPostLock(thr, pc, addr, MutexFlagLinkerInit | MutexFlagWriteReentrant |
- MutexFlagDoPreLockOnPostLock);
-}
-
-void __tsan_java_mutex_unlock(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock);
- DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- MutexUnlock(thr, pc, addr);
-}
-
-void __tsan_java_mutex_read_lock(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock);
- DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- MutexPostReadLock(thr, pc, addr, MutexFlagLinkerInit |
- MutexFlagWriteReentrant | MutexFlagDoPreLockOnPostLock);
-}
-
-void __tsan_java_mutex_read_unlock(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock);
- DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- MutexReadUnlock(thr, pc, addr);
-}
-
-void __tsan_java_mutex_lock_rec(jptr addr, int rec) {
- SCOPED_JAVA_FUNC(__tsan_java_mutex_lock_rec);
- DPrintf("#%d: java_mutex_lock_rec(%p, %d)\n", thr->tid, addr, rec);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
- CHECK_GT(rec, 0);
-
- MutexPostLock(thr, pc, addr, MutexFlagLinkerInit | MutexFlagWriteReentrant |
- MutexFlagDoPreLockOnPostLock | MutexFlagRecursiveLock, rec);
-}
-
-int __tsan_java_mutex_unlock_rec(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock_rec);
- DPrintf("#%d: java_mutex_unlock_rec(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- return MutexUnlock(thr, pc, addr, MutexFlagRecursiveUnlock);
-}
-
-void __tsan_java_acquire(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_acquire);
- DPrintf("#%d: java_acquire(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- Acquire(thr, caller_pc, addr);
-}
-
-void __tsan_java_release(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_release);
- DPrintf("#%d: java_release(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- Release(thr, caller_pc, addr);
-}
-
-void __tsan_java_release_store(jptr addr) {
- SCOPED_JAVA_FUNC(__tsan_java_release);
- DPrintf("#%d: java_release_store(%p)\n", thr->tid, addr);
- CHECK_NE(jctx, 0);
- CHECK_GE(addr, jctx->heap_begin);
- CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
-
- ReleaseStore(thr, caller_pc, addr);
-}
--- /dev/null
+//===-- tsan_interface_java.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_interface_java.h"
+#include "tsan_rtl.h"
+#include "tsan_mutex.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+
+using namespace __tsan; // NOLINT
+
+const jptr kHeapAlignment = 8;
+
+namespace __tsan {
+
+struct JavaContext {
+ const uptr heap_begin;
+ const uptr heap_size;
+
+ JavaContext(jptr heap_begin, jptr heap_size)
+ : heap_begin(heap_begin)
+ , heap_size(heap_size) {
+ }
+};
+
+class ScopedJavaFunc {
+ public:
+ ScopedJavaFunc(ThreadState *thr, uptr pc)
+ : thr_(thr) {
+ Initialize(thr_);
+ FuncEntry(thr, pc);
+ }
+
+ ~ScopedJavaFunc() {
+ FuncExit(thr_);
+ // FIXME(dvyukov): process pending signals.
+ }
+
+ private:
+ ThreadState *thr_;
+};
+
+static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1];
+static JavaContext *jctx;
+
+} // namespace __tsan
+
+#define SCOPED_JAVA_FUNC(func) \
+ ThreadState *thr = cur_thread(); \
+ const uptr caller_pc = GET_CALLER_PC(); \
+ const uptr pc = StackTrace::GetCurrentPc(); \
+ (void)pc; \
+ ScopedJavaFunc scoped(thr, caller_pc); \
+/**/
+
+void __tsan_java_init(jptr heap_begin, jptr heap_size) {
+ SCOPED_JAVA_FUNC(__tsan_java_init);
+ DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size);
+ CHECK_EQ(jctx, 0);
+ CHECK_GT(heap_begin, 0);
+ CHECK_GT(heap_size, 0);
+ CHECK_EQ(heap_begin % kHeapAlignment, 0);
+ CHECK_EQ(heap_size % kHeapAlignment, 0);
+ CHECK_LT(heap_begin, heap_begin + heap_size);
+ jctx = new(jctx_buf) JavaContext(heap_begin, heap_size);
+}
+
+int __tsan_java_fini() {
+ SCOPED_JAVA_FUNC(__tsan_java_fini);
+ DPrintf("#%d: java_fini()\n", thr->tid);
+ CHECK_NE(jctx, 0);
+ // FIXME(dvyukov): this does not call atexit() callbacks.
+ int status = Finalize(thr);
+ DPrintf("#%d: java_fini() = %d\n", thr->tid, status);
+ return status;
+}
+
+void __tsan_java_alloc(jptr ptr, jptr size) {
+ SCOPED_JAVA_FUNC(__tsan_java_alloc);
+ DPrintf("#%d: java_alloc(%p, %p)\n", thr->tid, ptr, size);
+ CHECK_NE(jctx, 0);
+ CHECK_NE(size, 0);
+ CHECK_EQ(ptr % kHeapAlignment, 0);
+ CHECK_EQ(size % kHeapAlignment, 0);
+ CHECK_GE(ptr, jctx->heap_begin);
+ CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
+
+ OnUserAlloc(thr, pc, ptr, size, false);
+}
+
+void __tsan_java_free(jptr ptr, jptr size) {
+ SCOPED_JAVA_FUNC(__tsan_java_free);
+ DPrintf("#%d: java_free(%p, %p)\n", thr->tid, ptr, size);
+ CHECK_NE(jctx, 0);
+ CHECK_NE(size, 0);
+ CHECK_EQ(ptr % kHeapAlignment, 0);
+ CHECK_EQ(size % kHeapAlignment, 0);
+ CHECK_GE(ptr, jctx->heap_begin);
+ CHECK_LE(ptr + size, jctx->heap_begin + jctx->heap_size);
+
+ ctx->metamap.FreeRange(thr->proc(), ptr, size);
+}
+
+void __tsan_java_move(jptr src, jptr dst, jptr size) {
+ SCOPED_JAVA_FUNC(__tsan_java_move);
+ DPrintf("#%d: java_move(%p, %p, %p)\n", thr->tid, src, dst, size);
+ CHECK_NE(jctx, 0);
+ CHECK_NE(size, 0);
+ CHECK_EQ(src % kHeapAlignment, 0);
+ CHECK_EQ(dst % kHeapAlignment, 0);
+ CHECK_EQ(size % kHeapAlignment, 0);
+ CHECK_GE(src, jctx->heap_begin);
+ CHECK_LE(src + size, jctx->heap_begin + jctx->heap_size);
+ CHECK_GE(dst, jctx->heap_begin);
+ CHECK_LE(dst + size, jctx->heap_begin + jctx->heap_size);
+ CHECK_NE(dst, src);
+ CHECK_NE(size, 0);
+
+ // Assuming it's not running concurrently with threads that do
+ // memory accesses and mutex operations (stop-the-world phase).
+ ctx->metamap.MoveMemory(src, dst, size);
+
+ // Move shadow.
+ u64 *s = (u64*)MemToShadow(src);
+ u64 *d = (u64*)MemToShadow(dst);
+ u64 *send = (u64*)MemToShadow(src + size);
+ uptr inc = 1;
+ if (dst > src) {
+ s = (u64*)MemToShadow(src + size) - 1;
+ d = (u64*)MemToShadow(dst + size) - 1;
+ send = (u64*)MemToShadow(src) - 1;
+ inc = -1;
+ }
+ for (; s != send; s += inc, d += inc) {
+ *d = *s;
+ *s = 0;
+ }
+}
+
+jptr __tsan_java_find(jptr *from_ptr, jptr to) {
+ SCOPED_JAVA_FUNC(__tsan_java_find);
+ DPrintf("#%d: java_find(&%p, %p)\n", *from_ptr, to);
+ CHECK_EQ((*from_ptr) % kHeapAlignment, 0);
+ CHECK_EQ(to % kHeapAlignment, 0);
+ CHECK_GE(*from_ptr, jctx->heap_begin);
+ CHECK_LE(to, jctx->heap_begin + jctx->heap_size);
+ for (uptr from = *from_ptr; from < to; from += kHeapAlignment) {
+ MBlock *b = ctx->metamap.GetBlock(from);
+ if (b) {
+ *from_ptr = from;
+ return b->siz;
+ }
+ }
+ return 0;
+}
+
+void __tsan_java_finalize() {
+ SCOPED_JAVA_FUNC(__tsan_java_finalize);
+ DPrintf("#%d: java_mutex_finalize()\n", thr->tid);
+ AcquireGlobal(thr, 0);
+}
+
+void __tsan_java_mutex_lock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_lock);
+ DPrintf("#%d: java_mutex_lock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexPostLock(thr, pc, addr, MutexFlagLinkerInit | MutexFlagWriteReentrant |
+ MutexFlagDoPreLockOnPostLock);
+}
+
+void __tsan_java_mutex_unlock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock);
+ DPrintf("#%d: java_mutex_unlock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexUnlock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_read_lock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_read_lock);
+ DPrintf("#%d: java_mutex_read_lock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexPostReadLock(thr, pc, addr, MutexFlagLinkerInit |
+ MutexFlagWriteReentrant | MutexFlagDoPreLockOnPostLock);
+}
+
+void __tsan_java_mutex_read_unlock(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_read_unlock);
+ DPrintf("#%d: java_mutex_read_unlock(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ MutexReadUnlock(thr, pc, addr);
+}
+
+void __tsan_java_mutex_lock_rec(jptr addr, int rec) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_lock_rec);
+ DPrintf("#%d: java_mutex_lock_rec(%p, %d)\n", thr->tid, addr, rec);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+ CHECK_GT(rec, 0);
+
+ MutexPostLock(thr, pc, addr, MutexFlagLinkerInit | MutexFlagWriteReentrant |
+ MutexFlagDoPreLockOnPostLock | MutexFlagRecursiveLock, rec);
+}
+
+int __tsan_java_mutex_unlock_rec(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_mutex_unlock_rec);
+ DPrintf("#%d: java_mutex_unlock_rec(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ return MutexUnlock(thr, pc, addr, MutexFlagRecursiveUnlock);
+}
+
+void __tsan_java_acquire(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_acquire);
+ DPrintf("#%d: java_acquire(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ Acquire(thr, caller_pc, addr);
+}
+
+void __tsan_java_release(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_release);
+ DPrintf("#%d: java_release(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ Release(thr, caller_pc, addr);
+}
+
+void __tsan_java_release_store(jptr addr) {
+ SCOPED_JAVA_FUNC(__tsan_java_release);
+ DPrintf("#%d: java_release_store(%p)\n", thr->tid, addr);
+ CHECK_NE(jctx, 0);
+ CHECK_GE(addr, jctx->heap_begin);
+ CHECK_LT(addr, jctx->heap_begin + jctx->heap_size);
+
+ ReleaseStore(thr, caller_pc, addr);
+}
//===-- tsan_interface_java.h -----------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// For volatile memory accesses and atomic operations JVM is intended to use
// standard atomics API: __tsan_atomicN_load/store/etc.
//
-// For usage examples see lit_tests/java_*.cc
+// For usage examples see lit_tests/java_*.cpp
//===----------------------------------------------------------------------===//
#ifndef TSAN_INTERFACE_JAVA_H
#define TSAN_INTERFACE_JAVA_H
--- /dev/null
+//===-- tsan_libdispatch.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Support for intercepting libdispatch (GCD).
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "interception/interception.h"
+#include "tsan_interceptors.h"
+#include "tsan_rtl.h"
+
+#include "tsan_dispatch_defs.h"
+
+namespace __tsan {
+ typedef u16 uint16_t;
+
+typedef struct {
+ dispatch_queue_t queue;
+ void *orig_context;
+ dispatch_function_t orig_work;
+ bool free_context_in_callback;
+ bool submitted_synchronously;
+ bool is_barrier_block;
+ uptr non_queue_sync_object;
+} block_context_t;
+
+// The offsets of different fields of the dispatch_queue_t structure, exported
+// by libdispatch.dylib.
+extern "C" struct dispatch_queue_offsets_s {
+ const uint16_t dqo_version;
+ const uint16_t dqo_label;
+ const uint16_t dqo_label_size;
+ const uint16_t dqo_flags;
+ const uint16_t dqo_flags_size;
+ const uint16_t dqo_serialnum;
+ const uint16_t dqo_serialnum_size;
+ const uint16_t dqo_width;
+ const uint16_t dqo_width_size;
+ const uint16_t dqo_running;
+ const uint16_t dqo_running_size;
+ const uint16_t dqo_suspend_cnt;
+ const uint16_t dqo_suspend_cnt_size;
+ const uint16_t dqo_target_queue;
+ const uint16_t dqo_target_queue_size;
+ const uint16_t dqo_priority;
+ const uint16_t dqo_priority_size;
+} dispatch_queue_offsets;
+
+static bool IsQueueSerial(dispatch_queue_t q) {
+ CHECK_EQ(dispatch_queue_offsets.dqo_width_size, 2);
+ uptr width = *(uint16_t *)(((uptr)q) + dispatch_queue_offsets.dqo_width);
+ CHECK_NE(width, 0);
+ return width == 1;
+}
+
+static dispatch_queue_t GetTargetQueueFromQueue(dispatch_queue_t q) {
+ CHECK_EQ(dispatch_queue_offsets.dqo_target_queue_size, 8);
+ dispatch_queue_t tq = *(
+ dispatch_queue_t *)(((uptr)q) + dispatch_queue_offsets.dqo_target_queue);
+ return tq;
+}
+
+static dispatch_queue_t GetTargetQueueFromSource(dispatch_source_t source) {
+ dispatch_queue_t tq = GetTargetQueueFromQueue((dispatch_queue_t)source);
+ CHECK_NE(tq, 0);
+ return tq;
+}
+
+static block_context_t *AllocContext(ThreadState *thr, uptr pc,
+ dispatch_queue_t queue, void *orig_context,
+ dispatch_function_t orig_work) {
+ block_context_t *new_context =
+ (block_context_t *)user_alloc_internal(thr, pc, sizeof(block_context_t));
+ new_context->queue = queue;
+ new_context->orig_context = orig_context;
+ new_context->orig_work = orig_work;
+ new_context->free_context_in_callback = true;
+ new_context->submitted_synchronously = false;
+ new_context->is_barrier_block = false;
+ new_context->non_queue_sync_object = 0;
+ return new_context;
+}
+
+#define GET_QUEUE_SYNC_VARS(context, q) \
+ bool is_queue_serial = q && IsQueueSerial(q); \
+ uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \
+ uptr serial_sync = (uptr)sync_ptr; \
+ uptr concurrent_sync = sync_ptr ? ((uptr)sync_ptr) + sizeof(uptr) : 0; \
+ bool serial_task = context->is_barrier_block || is_queue_serial
+
+static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc,
+ block_context_t *context) {
+ uptr submit_sync = (uptr)context;
+ Acquire(thr, pc, submit_sync);
+
+ dispatch_queue_t q = context->queue;
+ do {
+ GET_QUEUE_SYNC_VARS(context, q);
+ if (serial_sync) Acquire(thr, pc, serial_sync);
+ if (serial_task && concurrent_sync) Acquire(thr, pc, concurrent_sync);
+
+ if (q) q = GetTargetQueueFromQueue(q);
+ } while (q);
+}
+
+static void dispatch_sync_post_execute(ThreadState *thr, uptr pc,
+ block_context_t *context) {
+ uptr submit_sync = (uptr)context;
+ if (context->submitted_synchronously) Release(thr, pc, submit_sync);
+
+ dispatch_queue_t q = context->queue;
+ do {
+ GET_QUEUE_SYNC_VARS(context, q);
+ if (serial_task && serial_sync) Release(thr, pc, serial_sync);
+ if (!serial_task && concurrent_sync) Release(thr, pc, concurrent_sync);
+
+ if (q) q = GetTargetQueueFromQueue(q);
+ } while (q);
+}
+
+static void dispatch_callback_wrap(void *param) {
+ SCOPED_INTERCEPTOR_RAW(dispatch_callback_wrap);
+ block_context_t *context = (block_context_t *)param;
+
+ dispatch_sync_pre_execute(thr, pc, context);
+
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ context->orig_work(context->orig_context);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+
+ dispatch_sync_post_execute(thr, pc, context);
+
+ if (context->free_context_in_callback) user_free(thr, pc, context);
+}
+
+static void invoke_block(void *param) {
+ dispatch_block_t block = (dispatch_block_t)param;
+ block();
+}
+
+static void invoke_and_release_block(void *param) {
+ dispatch_block_t block = (dispatch_block_t)param;
+ block();
+ Block_release(block);
+}
+
+#define DISPATCH_INTERCEPT_ASYNC_B(name, barrier) \
+ TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
+ SCOPED_TSAN_INTERCEPTOR(name, q, block); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+ dispatch_block_t heap_block = Block_copy(block); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+ block_context_t *new_context = \
+ AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); \
+ new_context->is_barrier_block = barrier; \
+ Release(thr, pc, (uptr)new_context); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+ REAL(name##_f)(q, new_context, dispatch_callback_wrap); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+ }
+
+#define DISPATCH_INTERCEPT_SYNC_B(name, barrier) \
+ TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, \
+ DISPATCH_NOESCAPE dispatch_block_t block) { \
+ SCOPED_TSAN_INTERCEPTOR(name, q, block); \
+ block_context_t new_context = { \
+ q, block, &invoke_block, false, true, barrier, 0}; \
+ Release(thr, pc, (uptr)&new_context); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+ REAL(name##_f)(q, &new_context, dispatch_callback_wrap); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+ Acquire(thr, pc, (uptr)&new_context); \
+ }
+
+#define DISPATCH_INTERCEPT_ASYNC_F(name, barrier) \
+ TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
+ dispatch_function_t work) { \
+ SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
+ block_context_t *new_context = \
+ AllocContext(thr, pc, q, context, work); \
+ new_context->is_barrier_block = barrier; \
+ Release(thr, pc, (uptr)new_context); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+ REAL(name)(q, new_context, dispatch_callback_wrap); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+ }
+
+#define DISPATCH_INTERCEPT_SYNC_F(name, barrier) \
+ TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
+ dispatch_function_t work) { \
+ SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
+ block_context_t new_context = { \
+ q, context, work, false, true, barrier, 0}; \
+ Release(thr, pc, (uptr)&new_context); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
+ REAL(name)(q, &new_context, dispatch_callback_wrap); \
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
+ Acquire(thr, pc, (uptr)&new_context); \
+ }
+
+#define DISPATCH_INTERCEPT(name, barrier) \
+ DISPATCH_INTERCEPT_ASYNC_F(name##_async_f, barrier) \
+ DISPATCH_INTERCEPT_ASYNC_B(name##_async, barrier) \
+ DISPATCH_INTERCEPT_SYNC_F(name##_sync_f, barrier) \
+ DISPATCH_INTERCEPT_SYNC_B(name##_sync, barrier)
+
+// We wrap dispatch_async, dispatch_sync and friends where we allocate a new
+// context, which is used to synchronize (we release the context before
+// submitting, and the callback acquires it before executing the original
+// callback).
+DISPATCH_INTERCEPT(dispatch, false)
+DISPATCH_INTERCEPT(dispatch_barrier, true)
+
+DECLARE_REAL(void, dispatch_after_f, dispatch_time_t when,
+ dispatch_queue_t queue, void *context, dispatch_function_t work)
+
+TSAN_INTERCEPTOR(void, dispatch_after, dispatch_time_t when,
+ dispatch_queue_t queue, dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_after, when, queue, block);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ dispatch_block_t heap_block = Block_copy(block);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ block_context_t *new_context =
+ AllocContext(thr, pc, queue, heap_block, &invoke_and_release_block);
+ Release(thr, pc, (uptr)new_context);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ REAL(dispatch_after_f)(when, queue, new_context, dispatch_callback_wrap);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+}
+
+TSAN_INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
+ dispatch_queue_t queue, void *context,
+ dispatch_function_t work) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_after_f, when, queue, context, work);
+ WRAP(dispatch_after)(when, queue, ^(void) {
+ work(context);
+ });
+}
+
+// GCD's dispatch_once implementation has a fast path that contains a racy read
+// and it's inlined into user's code. Furthermore, this fast path doesn't
+// establish a proper happens-before relations between the initialization and
+// code following the call to dispatch_once. We could deal with this in
+// instrumented code, but there's not much we can do about it in system
+// libraries. Let's disable the fast path (by never storing the value ~0 to
+// predicate), so the interceptor is always called, and let's add proper release
+// and acquire semantics. Since TSan does not see its own atomic stores, the
+// race on predicate won't be reported - the only accesses to it that TSan sees
+// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
+// both a macro and a real function, we want to intercept the function, so we
+// need to undefine the macro.
+#undef dispatch_once
+TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
+ DISPATCH_NOESCAPE dispatch_block_t block) {
+ SCOPED_INTERCEPTOR_RAW(dispatch_once, predicate, block);
+ atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
+ u32 v = atomic_load(a, memory_order_acquire);
+ if (v == 0 &&
+ atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ block();
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ Release(thr, pc, (uptr)a);
+ atomic_store(a, 2, memory_order_release);
+ } else {
+ while (v != 2) {
+ internal_sched_yield();
+ v = atomic_load(a, memory_order_acquire);
+ }
+ Acquire(thr, pc, (uptr)a);
+ }
+}
+
+#undef dispatch_once_f
+TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
+ void *context, dispatch_function_t function) {
+ SCOPED_INTERCEPTOR_RAW(dispatch_once_f, predicate, context, function);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ WRAP(dispatch_once)(predicate, ^(void) {
+ function(context);
+ });
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+}
+
+TSAN_INTERCEPTOR(long_t, dispatch_semaphore_signal,
+ dispatch_semaphore_t dsema) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_signal, dsema);
+ Release(thr, pc, (uptr)dsema);
+ return REAL(dispatch_semaphore_signal)(dsema);
+}
+
+TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema,
+ dispatch_time_t timeout) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_wait, dsema, timeout);
+ long_t result = REAL(dispatch_semaphore_wait)(dsema, timeout);
+ if (result == 0) Acquire(thr, pc, (uptr)dsema);
+ return result;
+}
+
+TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group,
+ dispatch_time_t timeout) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout);
+ long_t result = REAL(dispatch_group_wait)(group, timeout);
+ if (result == 0) Acquire(thr, pc, (uptr)group);
+ return result;
+}
+
+// Used, but not intercepted.
+extern "C" void dispatch_group_enter(dispatch_group_t group);
+
+TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group);
+ // Acquired in the group notification callback in dispatch_group_notify[_f].
+ Release(thr, pc, (uptr)group);
+ REAL(dispatch_group_leave)(group);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group,
+ dispatch_queue_t queue, dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block);
+ dispatch_retain(group);
+ dispatch_group_enter(group);
+ __block dispatch_block_t block_copy = (dispatch_block_t)Block_copy(block);
+ WRAP(dispatch_async)(queue, ^(void) {
+ block_copy();
+ Block_release(block_copy);
+ WRAP(dispatch_group_leave)(group);
+ dispatch_release(group);
+ });
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
+ dispatch_queue_t queue, void *context,
+ dispatch_function_t work) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work);
+ dispatch_retain(group);
+ dispatch_group_enter(group);
+ WRAP(dispatch_async)(queue, ^(void) {
+ work(context);
+ WRAP(dispatch_group_leave)(group);
+ dispatch_release(group);
+ });
+}
+
+DECLARE_REAL(void, dispatch_group_notify_f, dispatch_group_t group,
+ dispatch_queue_t q, void *context, dispatch_function_t work)
+
+TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
+ dispatch_queue_t q, dispatch_block_t block) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, q, block);
+
+ // To make sure the group is still available in the callback (otherwise
+ // it can be already destroyed). Will be released in the callback.
+ dispatch_retain(group);
+
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ dispatch_block_t heap_block = Block_copy(^(void) {
+ {
+ SCOPED_INTERCEPTOR_RAW(dispatch_read_callback);
+ // Released when leaving the group (dispatch_group_leave).
+ Acquire(thr, pc, (uptr)group);
+ }
+ dispatch_release(group);
+ block();
+ });
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ block_context_t *new_context =
+ AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
+ new_context->is_barrier_block = true;
+ Release(thr, pc, (uptr)new_context);
+ REAL(dispatch_group_notify_f)(group, q, new_context, dispatch_callback_wrap);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group,
+ dispatch_queue_t q, void *context, dispatch_function_t work) {
+ WRAP(dispatch_group_notify)(group, q, ^(void) { work(context); });
+}
+
+TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler,
+ dispatch_source_t source, dispatch_block_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler, source, handler);
+ if (handler == nullptr)
+ return REAL(dispatch_source_set_event_handler)(source, nullptr);
+ dispatch_queue_t q = GetTargetQueueFromSource(source);
+ __block block_context_t new_context = {
+ q, handler, &invoke_block, false, false, false, 0 };
+ dispatch_block_t new_handler = Block_copy(^(void) {
+ new_context.orig_context = handler; // To explicitly capture "handler".
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_source_set_event_handler)(source, new_handler);
+ Block_release(new_handler);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler_f,
+ dispatch_source_t source, dispatch_function_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler_f, source, handler);
+ if (handler == nullptr)
+ return REAL(dispatch_source_set_event_handler)(source, nullptr);
+ dispatch_block_t block = ^(void) {
+ handler(dispatch_get_context(source));
+ };
+ WRAP(dispatch_source_set_event_handler)(source, block);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler,
+ dispatch_source_t source, dispatch_block_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler, source, handler);
+ if (handler == nullptr)
+ return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
+ dispatch_queue_t q = GetTargetQueueFromSource(source);
+ __block block_context_t new_context = {
+ q, handler, &invoke_block, false, false, false, 0};
+ dispatch_block_t new_handler = Block_copy(^(void) {
+ new_context.orig_context = handler; // To explicitly capture "handler".
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_source_set_cancel_handler)(source, new_handler);
+ Block_release(new_handler);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler_f,
+ dispatch_source_t source, dispatch_function_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler_f, source,
+ handler);
+ if (handler == nullptr)
+ return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
+ dispatch_block_t block = ^(void) {
+ handler(dispatch_get_context(source));
+ };
+ WRAP(dispatch_source_set_cancel_handler)(source, block);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler,
+ dispatch_source_t source, dispatch_block_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler, source,
+ handler);
+ if (handler == nullptr)
+ return REAL(dispatch_source_set_registration_handler)(source, nullptr);
+ dispatch_queue_t q = GetTargetQueueFromSource(source);
+ __block block_context_t new_context = {
+ q, handler, &invoke_block, false, false, false, 0};
+ dispatch_block_t new_handler = Block_copy(^(void) {
+ new_context.orig_context = handler; // To explicitly capture "handler".
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_source_set_registration_handler)(source, new_handler);
+ Block_release(new_handler);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler_f,
+ dispatch_source_t source, dispatch_function_t handler) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler_f, source,
+ handler);
+ if (handler == nullptr)
+ return REAL(dispatch_source_set_registration_handler)(source, nullptr);
+ dispatch_block_t block = ^(void) {
+ handler(dispatch_get_context(source));
+ };
+ WRAP(dispatch_source_set_registration_handler)(source, block);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_apply, size_t iterations,
+ dispatch_queue_t queue,
+ DISPATCH_NOESCAPE void (^block)(size_t)) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_apply, iterations, queue, block);
+
+ u8 sync1, sync2;
+ uptr parent_to_child_sync = (uptr)&sync1;
+ uptr child_to_parent_sync = (uptr)&sync2;
+
+ Release(thr, pc, parent_to_child_sync);
+ void (^new_block)(size_t) = ^(size_t iteration) {
+ SCOPED_INTERCEPTOR_RAW(dispatch_apply);
+ Acquire(thr, pc, parent_to_child_sync);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ block(iteration);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ Release(thr, pc, child_to_parent_sync);
+ };
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ REAL(dispatch_apply)(iterations, queue, new_block);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ Acquire(thr, pc, child_to_parent_sync);
+}
+
+static void invoke_block_iteration(void *param, size_t iteration) {
+ auto block = (void (^)(size_t)) param;
+ block(iteration);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_apply_f, size_t iterations,
+ dispatch_queue_t queue, void *context,
+ void (*work)(void *, size_t)) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_apply_f, iterations, queue, context, work);
+
+ // Unfortunately, we cannot delegate to dispatch_apply, since libdispatch
+ // implements dispatch_apply in terms of dispatch_apply_f.
+ u8 sync1, sync2;
+ uptr parent_to_child_sync = (uptr)&sync1;
+ uptr child_to_parent_sync = (uptr)&sync2;
+
+ Release(thr, pc, parent_to_child_sync);
+ void (^new_block)(size_t) = ^(size_t iteration) {
+ SCOPED_INTERCEPTOR_RAW(dispatch_apply_f);
+ Acquire(thr, pc, parent_to_child_sync);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ work(context, iteration);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ Release(thr, pc, child_to_parent_sync);
+ };
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ REAL(dispatch_apply_f)(iterations, queue, new_block, invoke_block_iteration);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ Acquire(thr, pc, child_to_parent_sync);
+}
+
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
+DECLARE_REAL_AND_INTERCEPTOR(int, munmap, void *addr, long_t sz)
+
+TSAN_INTERCEPTOR(dispatch_data_t, dispatch_data_create, const void *buffer,
+ size_t size, dispatch_queue_t q, dispatch_block_t destructor) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_data_create, buffer, size, q, destructor);
+ if ((q == nullptr) || (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT))
+ return REAL(dispatch_data_create)(buffer, size, q, destructor);
+
+ if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE)
+ destructor = ^(void) { WRAP(free)((void *)(uintptr_t)buffer); };
+ else if (destructor == DISPATCH_DATA_DESTRUCTOR_MUNMAP)
+ destructor = ^(void) { WRAP(munmap)((void *)(uintptr_t)buffer, size); };
+
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
+ dispatch_block_t heap_block = Block_copy(destructor);
+ SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
+ block_context_t *new_context =
+ AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
+ uptr submit_sync = (uptr)new_context;
+ Release(thr, pc, submit_sync);
+ return REAL(dispatch_data_create)(buffer, size, q, ^(void) {
+ dispatch_callback_wrap(new_context);
+ });
+}
+
+typedef void (^fd_handler_t)(dispatch_data_t data, int error);
+typedef void (^cleanup_handler_t)(int error);
+
+TSAN_INTERCEPTOR(void, dispatch_read, dispatch_fd_t fd, size_t length,
+ dispatch_queue_t q, fd_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_read, fd, length, q, h);
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
+ new_context.orig_context = ^(void) {
+ h(data, error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_read)(fd, length, q, new_h);
+ Block_release(new_h);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_write, dispatch_fd_t fd, dispatch_data_t data,
+ dispatch_queue_t q, fd_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_write, fd, data, q, h);
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
+ new_context.orig_context = ^(void) {
+ h(data, error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_write)(fd, data, q, new_h);
+ Block_release(new_h);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_io_read, dispatch_io_t channel, off_t offset,
+ size_t length, dispatch_queue_t q, dispatch_io_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_read, channel, offset, length, q, h);
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ dispatch_io_handler_t new_h =
+ Block_copy(^(bool done, dispatch_data_t data, int error) {
+ new_context.orig_context = ^(void) {
+ h(done, data, error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_io_read)(channel, offset, length, q, new_h);
+ Block_release(new_h);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_io_write, dispatch_io_t channel, off_t offset,
+ dispatch_data_t data, dispatch_queue_t q,
+ dispatch_io_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_write, channel, offset, data, q, h);
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ dispatch_io_handler_t new_h =
+ Block_copy(^(bool done, dispatch_data_t data, int error) {
+ new_context.orig_context = ^(void) {
+ h(done, data, error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_io_write)(channel, offset, data, q, new_h);
+ Block_release(new_h);
+}
+
+TSAN_INTERCEPTOR(void, dispatch_io_barrier, dispatch_io_t channel,
+ dispatch_block_t barrier) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_barrier, channel, barrier);
+ __block block_context_t new_context = {
+ nullptr, nullptr, &invoke_block, false, false, false, 0};
+ new_context.non_queue_sync_object = (uptr)channel;
+ new_context.is_barrier_block = true;
+ dispatch_block_t new_block = Block_copy(^(void) {
+ new_context.orig_context = ^(void) {
+ barrier();
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ REAL(dispatch_io_barrier)(channel, new_block);
+ Block_release(new_block);
+}
+
+TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create, dispatch_io_type_t type,
+ dispatch_fd_t fd, dispatch_queue_t q, cleanup_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_create, type, fd, q, h);
+ __block dispatch_io_t new_channel = nullptr;
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ cleanup_handler_t new_h = Block_copy(^(int error) {
+ {
+ SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
+ Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
+ }
+ new_context.orig_context = ^(void) {
+ h(error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ new_channel = REAL(dispatch_io_create)(type, fd, q, new_h);
+ Block_release(new_h);
+ return new_channel;
+}
+
+TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_path,
+ dispatch_io_type_t type, const char *path, int oflag,
+ mode_t mode, dispatch_queue_t q, cleanup_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_path, type, path, oflag, mode,
+ q, h);
+ __block dispatch_io_t new_channel = nullptr;
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ cleanup_handler_t new_h = Block_copy(^(int error) {
+ {
+ SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
+ Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
+ }
+ new_context.orig_context = ^(void) {
+ h(error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ new_channel =
+ REAL(dispatch_io_create_with_path)(type, path, oflag, mode, q, new_h);
+ Block_release(new_h);
+ return new_channel;
+}
+
+TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_io,
+ dispatch_io_type_t type, dispatch_io_t io, dispatch_queue_t q,
+ cleanup_handler_t h) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_io, type, io, q, h);
+ __block dispatch_io_t new_channel = nullptr;
+ __block block_context_t new_context = {
+ q, nullptr, &invoke_block, false, false, false, 0};
+ cleanup_handler_t new_h = Block_copy(^(int error) {
+ {
+ SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
+ Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
+ }
+ new_context.orig_context = ^(void) {
+ h(error);
+ };
+ dispatch_callback_wrap(&new_context);
+ });
+ uptr submit_sync = (uptr)&new_context;
+ Release(thr, pc, submit_sync);
+ new_channel = REAL(dispatch_io_create_with_io)(type, io, q, new_h);
+ Block_release(new_h);
+ return new_channel;
+}
+
+TSAN_INTERCEPTOR(void, dispatch_io_close, dispatch_io_t channel,
+ dispatch_io_close_flags_t flags) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_io_close, channel, flags);
+ Release(thr, pc, (uptr)channel); // Acquire() in dispatch_io_create[_*].
+ return REAL(dispatch_io_close)(channel, flags);
+}
+
+// Resuming a suspended queue needs to synchronize with all subsequent
+// executions of blocks in that queue.
+TSAN_INTERCEPTOR(void, dispatch_resume, dispatch_object_t o) {
+ SCOPED_TSAN_INTERCEPTOR(dispatch_resume, o);
+ Release(thr, pc, (uptr)o); // Synchronizes with the Acquire() on serial_sync
+ // in dispatch_sync_pre_execute
+ return REAL(dispatch_resume)(o);
+}
+
+void InitializeLibdispatchInterceptors() {
+ INTERCEPT_FUNCTION(dispatch_async);
+ INTERCEPT_FUNCTION(dispatch_async_f);
+ INTERCEPT_FUNCTION(dispatch_sync);
+ INTERCEPT_FUNCTION(dispatch_sync_f);
+ INTERCEPT_FUNCTION(dispatch_barrier_async);
+ INTERCEPT_FUNCTION(dispatch_barrier_async_f);
+ INTERCEPT_FUNCTION(dispatch_barrier_sync);
+ INTERCEPT_FUNCTION(dispatch_barrier_sync_f);
+ INTERCEPT_FUNCTION(dispatch_after);
+ INTERCEPT_FUNCTION(dispatch_after_f);
+ INTERCEPT_FUNCTION(dispatch_once);
+ INTERCEPT_FUNCTION(dispatch_once_f);
+ INTERCEPT_FUNCTION(dispatch_semaphore_signal);
+ INTERCEPT_FUNCTION(dispatch_semaphore_wait);
+ INTERCEPT_FUNCTION(dispatch_group_wait);
+ INTERCEPT_FUNCTION(dispatch_group_leave);
+ INTERCEPT_FUNCTION(dispatch_group_async);
+ INTERCEPT_FUNCTION(dispatch_group_async_f);
+ INTERCEPT_FUNCTION(dispatch_group_notify);
+ INTERCEPT_FUNCTION(dispatch_group_notify_f);
+ INTERCEPT_FUNCTION(dispatch_source_set_event_handler);
+ INTERCEPT_FUNCTION(dispatch_source_set_event_handler_f);
+ INTERCEPT_FUNCTION(dispatch_source_set_cancel_handler);
+ INTERCEPT_FUNCTION(dispatch_source_set_cancel_handler_f);
+ INTERCEPT_FUNCTION(dispatch_source_set_registration_handler);
+ INTERCEPT_FUNCTION(dispatch_source_set_registration_handler_f);
+ INTERCEPT_FUNCTION(dispatch_apply);
+ INTERCEPT_FUNCTION(dispatch_apply_f);
+ INTERCEPT_FUNCTION(dispatch_data_create);
+ INTERCEPT_FUNCTION(dispatch_read);
+ INTERCEPT_FUNCTION(dispatch_write);
+ INTERCEPT_FUNCTION(dispatch_io_read);
+ INTERCEPT_FUNCTION(dispatch_io_write);
+ INTERCEPT_FUNCTION(dispatch_io_barrier);
+ INTERCEPT_FUNCTION(dispatch_io_create);
+ INTERCEPT_FUNCTION(dispatch_io_create_with_path);
+ INTERCEPT_FUNCTION(dispatch_io_create_with_io);
+ INTERCEPT_FUNCTION(dispatch_io_close);
+ INTERCEPT_FUNCTION(dispatch_resume);
+}
+
+} // namespace __tsan
+++ /dev/null
-//===-- tsan_libdispatch_mac.cc -------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Mac-specific libdispatch (GCD) support.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "interception/interception.h"
-#include "tsan_interceptors.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-
-#include <Block.h>
-#include <dispatch/dispatch.h>
-#include <pthread.h>
-
-// DISPATCH_NOESCAPE is not defined prior to XCode 8.
-#ifndef DISPATCH_NOESCAPE
-#define DISPATCH_NOESCAPE
-#endif
-
-typedef long long_t; // NOLINT
-
-namespace __tsan {
-
-typedef struct {
- dispatch_queue_t queue;
- void *orig_context;
- dispatch_function_t orig_work;
- bool free_context_in_callback;
- bool submitted_synchronously;
- bool is_barrier_block;
- uptr non_queue_sync_object;
-} tsan_block_context_t;
-
-// The offsets of different fields of the dispatch_queue_t structure, exported
-// by libdispatch.dylib.
-extern "C" struct dispatch_queue_offsets_s {
- const uint16_t dqo_version;
- const uint16_t dqo_label;
- const uint16_t dqo_label_size;
- const uint16_t dqo_flags;
- const uint16_t dqo_flags_size;
- const uint16_t dqo_serialnum;
- const uint16_t dqo_serialnum_size;
- const uint16_t dqo_width;
- const uint16_t dqo_width_size;
- const uint16_t dqo_running;
- const uint16_t dqo_running_size;
- const uint16_t dqo_suspend_cnt;
- const uint16_t dqo_suspend_cnt_size;
- const uint16_t dqo_target_queue;
- const uint16_t dqo_target_queue_size;
- const uint16_t dqo_priority;
- const uint16_t dqo_priority_size;
-} dispatch_queue_offsets;
-
-static bool IsQueueSerial(dispatch_queue_t q) {
- CHECK_EQ(dispatch_queue_offsets.dqo_width_size, 2);
- uptr width = *(uint16_t *)(((uptr)q) + dispatch_queue_offsets.dqo_width);
- CHECK_NE(width, 0);
- return width == 1;
-}
-
-static dispatch_queue_t GetTargetQueueFromQueue(dispatch_queue_t q) {
- CHECK_EQ(dispatch_queue_offsets.dqo_target_queue_size, 8);
- dispatch_queue_t tq = *(
- dispatch_queue_t *)(((uptr)q) + dispatch_queue_offsets.dqo_target_queue);
- return tq;
-}
-
-static dispatch_queue_t GetTargetQueueFromSource(dispatch_source_t source) {
- dispatch_queue_t tq = GetTargetQueueFromQueue((dispatch_queue_t)source);
- CHECK_NE(tq, 0);
- return tq;
-}
-
-static tsan_block_context_t *AllocContext(ThreadState *thr, uptr pc,
- dispatch_queue_t queue,
- void *orig_context,
- dispatch_function_t orig_work) {
- tsan_block_context_t *new_context =
- (tsan_block_context_t *)user_alloc_internal(thr, pc,
- sizeof(tsan_block_context_t));
- new_context->queue = queue;
- new_context->orig_context = orig_context;
- new_context->orig_work = orig_work;
- new_context->free_context_in_callback = true;
- new_context->submitted_synchronously = false;
- new_context->is_barrier_block = false;
- new_context->non_queue_sync_object = 0;
- return new_context;
-}
-
-#define GET_QUEUE_SYNC_VARS(context, q) \
- bool is_queue_serial = q && IsQueueSerial(q); \
- uptr sync_ptr = (uptr)q ?: context->non_queue_sync_object; \
- uptr serial_sync = (uptr)sync_ptr; \
- uptr concurrent_sync = sync_ptr ? ((uptr)sync_ptr) + sizeof(uptr) : 0; \
- bool serial_task = context->is_barrier_block || is_queue_serial
-
-static void dispatch_sync_pre_execute(ThreadState *thr, uptr pc,
- tsan_block_context_t *context) {
- uptr submit_sync = (uptr)context;
- Acquire(thr, pc, submit_sync);
-
- dispatch_queue_t q = context->queue;
- do {
- GET_QUEUE_SYNC_VARS(context, q);
- if (serial_sync) Acquire(thr, pc, serial_sync);
- if (serial_task && concurrent_sync) Acquire(thr, pc, concurrent_sync);
-
- if (q) q = GetTargetQueueFromQueue(q);
- } while (q);
-}
-
-static void dispatch_sync_post_execute(ThreadState *thr, uptr pc,
- tsan_block_context_t *context) {
- uptr submit_sync = (uptr)context;
- if (context->submitted_synchronously) Release(thr, pc, submit_sync);
-
- dispatch_queue_t q = context->queue;
- do {
- GET_QUEUE_SYNC_VARS(context, q);
- if (serial_task && serial_sync) Release(thr, pc, serial_sync);
- if (!serial_task && concurrent_sync) Release(thr, pc, concurrent_sync);
-
- if (q) q = GetTargetQueueFromQueue(q);
- } while (q);
-}
-
-static void dispatch_callback_wrap(void *param) {
- SCOPED_INTERCEPTOR_RAW(dispatch_callback_wrap);
- tsan_block_context_t *context = (tsan_block_context_t *)param;
-
- dispatch_sync_pre_execute(thr, pc, context);
-
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- context->orig_work(context->orig_context);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
-
- dispatch_sync_post_execute(thr, pc, context);
-
- if (context->free_context_in_callback) user_free(thr, pc, context);
-}
-
-static void invoke_block(void *param) {
- dispatch_block_t block = (dispatch_block_t)param;
- block();
-}
-
-static void invoke_and_release_block(void *param) {
- dispatch_block_t block = (dispatch_block_t)param;
- block();
- Block_release(block);
-}
-
-#define DISPATCH_INTERCEPT_B(name, barrier) \
- TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, dispatch_block_t block) { \
- SCOPED_TSAN_INTERCEPTOR(name, q, block); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
- dispatch_block_t heap_block = Block_copy(block); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
- tsan_block_context_t *new_context = \
- AllocContext(thr, pc, q, heap_block, &invoke_and_release_block); \
- new_context->is_barrier_block = barrier; \
- Release(thr, pc, (uptr)new_context); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
- REAL(name##_f)(q, new_context, dispatch_callback_wrap); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
- }
-
-#define DISPATCH_INTERCEPT_SYNC_B(name, barrier) \
- TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, \
- DISPATCH_NOESCAPE dispatch_block_t block) { \
- SCOPED_TSAN_INTERCEPTOR(name, q, block); \
- tsan_block_context_t new_context = { \
- q, block, &invoke_block, false, true, barrier, 0}; \
- Release(thr, pc, (uptr)&new_context); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
- REAL(name##_f)(q, &new_context, dispatch_callback_wrap); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
- Acquire(thr, pc, (uptr)&new_context); \
- }
-
-#define DISPATCH_INTERCEPT_F(name, barrier) \
- TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
- dispatch_function_t work) { \
- SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
- tsan_block_context_t *new_context = \
- AllocContext(thr, pc, q, context, work); \
- new_context->is_barrier_block = barrier; \
- Release(thr, pc, (uptr)new_context); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
- REAL(name)(q, new_context, dispatch_callback_wrap); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
- }
-
-#define DISPATCH_INTERCEPT_SYNC_F(name, barrier) \
- TSAN_INTERCEPTOR(void, name, dispatch_queue_t q, void *context, \
- dispatch_function_t work) { \
- SCOPED_TSAN_INTERCEPTOR(name, q, context, work); \
- tsan_block_context_t new_context = { \
- q, context, work, false, true, barrier, 0}; \
- Release(thr, pc, (uptr)&new_context); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START(); \
- REAL(name)(q, &new_context, dispatch_callback_wrap); \
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END(); \
- Acquire(thr, pc, (uptr)&new_context); \
- }
-
-// We wrap dispatch_async, dispatch_sync and friends where we allocate a new
-// context, which is used to synchronize (we release the context before
-// submitting, and the callback acquires it before executing the original
-// callback).
-DISPATCH_INTERCEPT_B(dispatch_async, false)
-DISPATCH_INTERCEPT_B(dispatch_barrier_async, true)
-DISPATCH_INTERCEPT_F(dispatch_async_f, false)
-DISPATCH_INTERCEPT_F(dispatch_barrier_async_f, true)
-DISPATCH_INTERCEPT_SYNC_B(dispatch_sync, false)
-DISPATCH_INTERCEPT_SYNC_B(dispatch_barrier_sync, true)
-DISPATCH_INTERCEPT_SYNC_F(dispatch_sync_f, false)
-DISPATCH_INTERCEPT_SYNC_F(dispatch_barrier_sync_f, true)
-
-TSAN_INTERCEPTOR(void, dispatch_after, dispatch_time_t when,
- dispatch_queue_t queue, dispatch_block_t block) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_after, when, queue, block);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- dispatch_block_t heap_block = Block_copy(block);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- tsan_block_context_t *new_context =
- AllocContext(thr, pc, queue, heap_block, &invoke_and_release_block);
- Release(thr, pc, (uptr)new_context);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- REAL(dispatch_after_f)(when, queue, new_context, dispatch_callback_wrap);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
-}
-
-TSAN_INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when,
- dispatch_queue_t queue, void *context,
- dispatch_function_t work) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_after_f, when, queue, context, work);
- WRAP(dispatch_after)(when, queue, ^(void) {
- work(context);
- });
-}
-
-// GCD's dispatch_once implementation has a fast path that contains a racy read
-// and it's inlined into user's code. Furthermore, this fast path doesn't
-// establish a proper happens-before relations between the initialization and
-// code following the call to dispatch_once. We could deal with this in
-// instrumented code, but there's not much we can do about it in system
-// libraries. Let's disable the fast path (by never storing the value ~0 to
-// predicate), so the interceptor is always called, and let's add proper release
-// and acquire semantics. Since TSan does not see its own atomic stores, the
-// race on predicate won't be reported - the only accesses to it that TSan sees
-// are the loads on the fast path. Loads don't race. Secondly, dispatch_once is
-// both a macro and a real function, we want to intercept the function, so we
-// need to undefine the macro.
-#undef dispatch_once
-TSAN_INTERCEPTOR(void, dispatch_once, dispatch_once_t *predicate,
- DISPATCH_NOESCAPE dispatch_block_t block) {
- SCOPED_INTERCEPTOR_RAW(dispatch_once, predicate, block);
- atomic_uint32_t *a = reinterpret_cast<atomic_uint32_t *>(predicate);
- u32 v = atomic_load(a, memory_order_acquire);
- if (v == 0 &&
- atomic_compare_exchange_strong(a, &v, 1, memory_order_relaxed)) {
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- block();
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- Release(thr, pc, (uptr)a);
- atomic_store(a, 2, memory_order_release);
- } else {
- while (v != 2) {
- internal_sched_yield();
- v = atomic_load(a, memory_order_acquire);
- }
- Acquire(thr, pc, (uptr)a);
- }
-}
-
-#undef dispatch_once_f
-TSAN_INTERCEPTOR(void, dispatch_once_f, dispatch_once_t *predicate,
- void *context, dispatch_function_t function) {
- SCOPED_INTERCEPTOR_RAW(dispatch_once_f, predicate, context, function);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- WRAP(dispatch_once)(predicate, ^(void) {
- function(context);
- });
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
-}
-
-TSAN_INTERCEPTOR(long_t, dispatch_semaphore_signal,
- dispatch_semaphore_t dsema) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_signal, dsema);
- Release(thr, pc, (uptr)dsema);
- return REAL(dispatch_semaphore_signal)(dsema);
-}
-
-TSAN_INTERCEPTOR(long_t, dispatch_semaphore_wait, dispatch_semaphore_t dsema,
- dispatch_time_t timeout) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_semaphore_wait, dsema, timeout);
- long_t result = REAL(dispatch_semaphore_wait)(dsema, timeout);
- if (result == 0) Acquire(thr, pc, (uptr)dsema);
- return result;
-}
-
-TSAN_INTERCEPTOR(long_t, dispatch_group_wait, dispatch_group_t group,
- dispatch_time_t timeout) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_group_wait, group, timeout);
- long_t result = REAL(dispatch_group_wait)(group, timeout);
- if (result == 0) Acquire(thr, pc, (uptr)group);
- return result;
-}
-
-TSAN_INTERCEPTOR(void, dispatch_group_leave, dispatch_group_t group) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_group_leave, group);
- // Acquired in the group noticifaction callback in dispatch_group_notify[_f].
- Release(thr, pc, (uptr)group);
- REAL(dispatch_group_leave)(group);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_group_async, dispatch_group_t group,
- dispatch_queue_t queue, dispatch_block_t block) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_group_async, group, queue, block);
- dispatch_retain(group);
- dispatch_group_enter(group);
- __block dispatch_block_t block_copy = (dispatch_block_t)_Block_copy(block);
- WRAP(dispatch_async)(queue, ^(void) {
- block_copy();
- _Block_release(block_copy);
- WRAP(dispatch_group_leave)(group);
- dispatch_release(group);
- });
-}
-
-TSAN_INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group,
- dispatch_queue_t queue, void *context,
- dispatch_function_t work) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_group_async_f, group, queue, context, work);
- dispatch_retain(group);
- dispatch_group_enter(group);
- WRAP(dispatch_async)(queue, ^(void) {
- work(context);
- WRAP(dispatch_group_leave)(group);
- dispatch_release(group);
- });
-}
-
-TSAN_INTERCEPTOR(void, dispatch_group_notify, dispatch_group_t group,
- dispatch_queue_t q, dispatch_block_t block) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_group_notify, group, q, block);
-
- // To make sure the group is still available in the callback (otherwise
- // it can be already destroyed). Will be released in the callback.
- dispatch_retain(group);
-
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- dispatch_block_t heap_block = Block_copy(^(void) {
- {
- SCOPED_INTERCEPTOR_RAW(dispatch_read_callback);
- // Released when leaving the group (dispatch_group_leave).
- Acquire(thr, pc, (uptr)group);
- }
- dispatch_release(group);
- block();
- });
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- tsan_block_context_t *new_context =
- AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
- new_context->is_barrier_block = true;
- Release(thr, pc, (uptr)new_context);
- REAL(dispatch_group_notify_f)(group, q, new_context, dispatch_callback_wrap);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_group_notify_f, dispatch_group_t group,
- dispatch_queue_t q, void *context, dispatch_function_t work) {
- WRAP(dispatch_group_notify)(group, q, ^(void) { work(context); });
-}
-
-TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler,
- dispatch_source_t source, dispatch_block_t handler) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler, source, handler);
- if (handler == nullptr)
- return REAL(dispatch_source_set_event_handler)(source, nullptr);
- dispatch_queue_t q = GetTargetQueueFromSource(source);
- __block tsan_block_context_t new_context = {
- q, handler, &invoke_block, false, false, false, 0 };
- dispatch_block_t new_handler = Block_copy(^(void) {
- new_context.orig_context = handler; // To explicitly capture "handler".
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_source_set_event_handler)(source, new_handler);
- Block_release(new_handler);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_source_set_event_handler_f,
- dispatch_source_t source, dispatch_function_t handler) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_event_handler_f, source, handler);
- if (handler == nullptr)
- return REAL(dispatch_source_set_event_handler)(source, nullptr);
- dispatch_block_t block = ^(void) {
- handler(dispatch_get_context(source));
- };
- WRAP(dispatch_source_set_event_handler)(source, block);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler,
- dispatch_source_t source, dispatch_block_t handler) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler, source, handler);
- if (handler == nullptr)
- return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
- dispatch_queue_t q = GetTargetQueueFromSource(source);
- __block tsan_block_context_t new_context = {
- q, handler, &invoke_block, false, false, false, 0};
- dispatch_block_t new_handler = Block_copy(^(void) {
- new_context.orig_context = handler; // To explicitly capture "handler".
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_source_set_cancel_handler)(source, new_handler);
- Block_release(new_handler);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_source_set_cancel_handler_f,
- dispatch_source_t source, dispatch_function_t handler) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_cancel_handler_f, source,
- handler);
- if (handler == nullptr)
- return REAL(dispatch_source_set_cancel_handler)(source, nullptr);
- dispatch_block_t block = ^(void) {
- handler(dispatch_get_context(source));
- };
- WRAP(dispatch_source_set_cancel_handler)(source, block);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler,
- dispatch_source_t source, dispatch_block_t handler) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler, source,
- handler);
- if (handler == nullptr)
- return REAL(dispatch_source_set_registration_handler)(source, nullptr);
- dispatch_queue_t q = GetTargetQueueFromSource(source);
- __block tsan_block_context_t new_context = {
- q, handler, &invoke_block, false, false, false, 0};
- dispatch_block_t new_handler = Block_copy(^(void) {
- new_context.orig_context = handler; // To explicitly capture "handler".
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_source_set_registration_handler)(source, new_handler);
- Block_release(new_handler);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_source_set_registration_handler_f,
- dispatch_source_t source, dispatch_function_t handler) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_source_set_registration_handler_f, source,
- handler);
- if (handler == nullptr)
- return REAL(dispatch_source_set_registration_handler)(source, nullptr);
- dispatch_block_t block = ^(void) {
- handler(dispatch_get_context(source));
- };
- WRAP(dispatch_source_set_registration_handler)(source, block);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_apply, size_t iterations,
- dispatch_queue_t queue,
- DISPATCH_NOESCAPE void (^block)(size_t)) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_apply, iterations, queue, block);
-
- void *parent_to_child_sync = nullptr;
- uptr parent_to_child_sync_uptr = (uptr)&parent_to_child_sync;
- void *child_to_parent_sync = nullptr;
- uptr child_to_parent_sync_uptr = (uptr)&child_to_parent_sync;
-
- Release(thr, pc, parent_to_child_sync_uptr);
- void (^new_block)(size_t) = ^(size_t iteration) {
- SCOPED_INTERCEPTOR_RAW(dispatch_apply);
- Acquire(thr, pc, parent_to_child_sync_uptr);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- block(iteration);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- Release(thr, pc, child_to_parent_sync_uptr);
- };
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- REAL(dispatch_apply)(iterations, queue, new_block);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- Acquire(thr, pc, child_to_parent_sync_uptr);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_apply_f, size_t iterations,
- dispatch_queue_t queue, void *context,
- void (*work)(void *, size_t)) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_apply_f, iterations, queue, context, work);
- void (^new_block)(size_t) = ^(size_t iteration) {
- work(context, iteration);
- };
- WRAP(dispatch_apply)(iterations, queue, new_block);
-}
-
-DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
-DECLARE_REAL_AND_INTERCEPTOR(int, munmap, void *addr, long_t sz)
-
-TSAN_INTERCEPTOR(dispatch_data_t, dispatch_data_create, const void *buffer,
- size_t size, dispatch_queue_t q, dispatch_block_t destructor) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_data_create, buffer, size, q, destructor);
- if ((q == nullptr) || (destructor == DISPATCH_DATA_DESTRUCTOR_DEFAULT))
- return REAL(dispatch_data_create)(buffer, size, q, destructor);
-
- if (destructor == DISPATCH_DATA_DESTRUCTOR_FREE)
- destructor = ^(void) { WRAP(free)((void *)(uintptr_t)buffer); };
- else if (destructor == DISPATCH_DATA_DESTRUCTOR_MUNMAP)
- destructor = ^(void) { WRAP(munmap)((void *)(uintptr_t)buffer, size); };
-
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_START();
- dispatch_block_t heap_block = Block_copy(destructor);
- SCOPED_TSAN_INTERCEPTOR_USER_CALLBACK_END();
- tsan_block_context_t *new_context =
- AllocContext(thr, pc, q, heap_block, &invoke_and_release_block);
- uptr submit_sync = (uptr)new_context;
- Release(thr, pc, submit_sync);
- return REAL(dispatch_data_create)(buffer, size, q, ^(void) {
- dispatch_callback_wrap(new_context);
- });
-}
-
-typedef void (^fd_handler_t)(dispatch_data_t data, int error);
-typedef void (^cleanup_handler_t)(int error);
-
-TSAN_INTERCEPTOR(void, dispatch_read, dispatch_fd_t fd, size_t length,
- dispatch_queue_t q, fd_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_read, fd, length, q, h);
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
- new_context.orig_context = ^(void) {
- h(data, error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_read)(fd, length, q, new_h);
- Block_release(new_h);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_write, dispatch_fd_t fd, dispatch_data_t data,
- dispatch_queue_t q, fd_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_write, fd, data, q, h);
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- fd_handler_t new_h = Block_copy(^(dispatch_data_t data, int error) {
- new_context.orig_context = ^(void) {
- h(data, error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_write)(fd, data, q, new_h);
- Block_release(new_h);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_io_read, dispatch_io_t channel, off_t offset,
- size_t length, dispatch_queue_t q, dispatch_io_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_read, channel, offset, length, q, h);
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- dispatch_io_handler_t new_h =
- Block_copy(^(bool done, dispatch_data_t data, int error) {
- new_context.orig_context = ^(void) {
- h(done, data, error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_io_read)(channel, offset, length, q, new_h);
- Block_release(new_h);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_io_write, dispatch_io_t channel, off_t offset,
- dispatch_data_t data, dispatch_queue_t q,
- dispatch_io_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_write, channel, offset, data, q, h);
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- dispatch_io_handler_t new_h =
- Block_copy(^(bool done, dispatch_data_t data, int error) {
- new_context.orig_context = ^(void) {
- h(done, data, error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_io_write)(channel, offset, data, q, new_h);
- Block_release(new_h);
-}
-
-TSAN_INTERCEPTOR(void, dispatch_io_barrier, dispatch_io_t channel,
- dispatch_block_t barrier) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_barrier, channel, barrier);
- __block tsan_block_context_t new_context = {
- nullptr, nullptr, &invoke_block, false, false, false, 0};
- new_context.non_queue_sync_object = (uptr)channel;
- new_context.is_barrier_block = true;
- dispatch_block_t new_block = Block_copy(^(void) {
- new_context.orig_context = ^(void) {
- barrier();
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- REAL(dispatch_io_barrier)(channel, new_block);
- Block_release(new_block);
-}
-
-TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create, dispatch_io_type_t type,
- dispatch_fd_t fd, dispatch_queue_t q, cleanup_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_create, type, fd, q, h);
- __block dispatch_io_t new_channel = nullptr;
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- cleanup_handler_t new_h = Block_copy(^(int error) {
- {
- SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
- Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
- }
- new_context.orig_context = ^(void) {
- h(error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- new_channel = REAL(dispatch_io_create)(type, fd, q, new_h);
- Block_release(new_h);
- return new_channel;
-}
-
-TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_path,
- dispatch_io_type_t type, const char *path, int oflag,
- mode_t mode, dispatch_queue_t q, cleanup_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_path, type, path, oflag, mode,
- q, h);
- __block dispatch_io_t new_channel = nullptr;
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- cleanup_handler_t new_h = Block_copy(^(int error) {
- {
- SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
- Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
- }
- new_context.orig_context = ^(void) {
- h(error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- new_channel =
- REAL(dispatch_io_create_with_path)(type, path, oflag, mode, q, new_h);
- Block_release(new_h);
- return new_channel;
-}
-
-TSAN_INTERCEPTOR(dispatch_io_t, dispatch_io_create_with_io,
- dispatch_io_type_t type, dispatch_io_t io, dispatch_queue_t q,
- cleanup_handler_t h) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_create_with_io, type, io, q, h);
- __block dispatch_io_t new_channel = nullptr;
- __block tsan_block_context_t new_context = {
- q, nullptr, &invoke_block, false, false, false, 0};
- cleanup_handler_t new_h = Block_copy(^(int error) {
- {
- SCOPED_INTERCEPTOR_RAW(dispatch_io_create_callback);
- Acquire(thr, pc, (uptr)new_channel); // Release() in dispatch_io_close.
- }
- new_context.orig_context = ^(void) {
- h(error);
- };
- dispatch_callback_wrap(&new_context);
- });
- uptr submit_sync = (uptr)&new_context;
- Release(thr, pc, submit_sync);
- new_channel = REAL(dispatch_io_create_with_io)(type, io, q, new_h);
- Block_release(new_h);
- return new_channel;
-}
-
-TSAN_INTERCEPTOR(void, dispatch_io_close, dispatch_io_t channel,
- dispatch_io_close_flags_t flags) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_io_close, channel, flags);
- Release(thr, pc, (uptr)channel); // Acquire() in dispatch_io_create[_*].
- return REAL(dispatch_io_close)(channel, flags);
-}
-
-// Resuming a suspended queue needs to synchronize with all subsequent
-// executions of blocks in that queue.
-TSAN_INTERCEPTOR(void, dispatch_resume, dispatch_object_t o) {
- SCOPED_TSAN_INTERCEPTOR(dispatch_resume, o);
- Release(thr, pc, (uptr)o); // Synchronizes with the Acquire() on serial_sync
- // in dispatch_sync_pre_execute
- return REAL(dispatch_resume)(o);
-}
-
-} // namespace __tsan
-
-#endif // SANITIZER_MAC
+++ /dev/null
-//===-- tsan_malloc_mac.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Mac-specific malloc interception.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "sanitizer_common/sanitizer_errno.h"
-#include "tsan_interceptors.h"
-#include "tsan_stack_trace.h"
-
-using namespace __tsan;
-#define COMMON_MALLOC_ZONE_NAME "tsan"
-#define COMMON_MALLOC_ENTER()
-#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited)
-#define COMMON_MALLOC_FORCE_LOCK()
-#define COMMON_MALLOC_FORCE_UNLOCK()
-#define COMMON_MALLOC_MEMALIGN(alignment, size) \
- void *p = \
- user_memalign(cur_thread(), StackTrace::GetCurrentPc(), alignment, size)
-#define COMMON_MALLOC_MALLOC(size) \
- if (cur_thread()->in_symbolizer) return InternalAlloc(size); \
- SCOPED_INTERCEPTOR_RAW(malloc, size); \
- void *p = user_alloc(thr, pc, size)
-#define COMMON_MALLOC_REALLOC(ptr, size) \
- if (cur_thread()->in_symbolizer) return InternalRealloc(ptr, size); \
- SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \
- void *p = user_realloc(thr, pc, ptr, size)
-#define COMMON_MALLOC_CALLOC(count, size) \
- if (cur_thread()->in_symbolizer) return InternalCalloc(count, size); \
- SCOPED_INTERCEPTOR_RAW(calloc, size, count); \
- void *p = user_calloc(thr, pc, size, count)
-#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \
- if (cur_thread()->in_symbolizer) { \
- void *p = InternalAlloc(size, nullptr, alignment); \
- if (!p) return errno_ENOMEM; \
- *memptr = p; \
- return 0; \
- } \
- SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, alignment, size); \
- int res = user_posix_memalign(thr, pc, memptr, alignment, size);
-#define COMMON_MALLOC_VALLOC(size) \
- if (cur_thread()->in_symbolizer) \
- return InternalAlloc(size, nullptr, GetPageSizeCached()); \
- SCOPED_INTERCEPTOR_RAW(valloc, size); \
- void *p = user_valloc(thr, pc, size)
-#define COMMON_MALLOC_FREE(ptr) \
- if (cur_thread()->in_symbolizer) return InternalFree(ptr); \
- SCOPED_INTERCEPTOR_RAW(free, ptr); \
- user_free(thr, pc, ptr)
-#define COMMON_MALLOC_SIZE(ptr) uptr size = user_alloc_usable_size(ptr);
-#define COMMON_MALLOC_FILL_STATS(zone, stats)
-#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
- (void)zone_name; \
- Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
-#define COMMON_MALLOC_NAMESPACE __tsan
-
-#include "sanitizer_common/sanitizer_malloc_mac.inc"
-
-#endif
--- /dev/null
+//===-- tsan_malloc_mac.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific malloc interception.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_errno.h"
+#include "tsan_interceptors.h"
+#include "tsan_stack_trace.h"
+
+using namespace __tsan;
+#define COMMON_MALLOC_ZONE_NAME "tsan"
+#define COMMON_MALLOC_ENTER()
+#define COMMON_MALLOC_SANITIZER_INITIALIZED (cur_thread()->is_inited)
+#define COMMON_MALLOC_FORCE_LOCK()
+#define COMMON_MALLOC_FORCE_UNLOCK()
+#define COMMON_MALLOC_MEMALIGN(alignment, size) \
+ void *p = \
+ user_memalign(cur_thread(), StackTrace::GetCurrentPc(), alignment, size)
+#define COMMON_MALLOC_MALLOC(size) \
+ if (in_symbolizer()) return InternalAlloc(size); \
+ SCOPED_INTERCEPTOR_RAW(malloc, size); \
+ void *p = user_alloc(thr, pc, size)
+#define COMMON_MALLOC_REALLOC(ptr, size) \
+ if (in_symbolizer()) return InternalRealloc(ptr, size); \
+ SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \
+ void *p = user_realloc(thr, pc, ptr, size)
+#define COMMON_MALLOC_CALLOC(count, size) \
+ if (in_symbolizer()) return InternalCalloc(count, size); \
+ SCOPED_INTERCEPTOR_RAW(calloc, size, count); \
+ void *p = user_calloc(thr, pc, size, count)
+#define COMMON_MALLOC_POSIX_MEMALIGN(memptr, alignment, size) \
+ if (in_symbolizer()) { \
+ void *p = InternalAlloc(size, nullptr, alignment); \
+ if (!p) return errno_ENOMEM; \
+ *memptr = p; \
+ return 0; \
+ } \
+ SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, alignment, size); \
+ int res = user_posix_memalign(thr, pc, memptr, alignment, size);
+#define COMMON_MALLOC_VALLOC(size) \
+ if (in_symbolizer()) \
+ return InternalAlloc(size, nullptr, GetPageSizeCached()); \
+ SCOPED_INTERCEPTOR_RAW(valloc, size); \
+ void *p = user_valloc(thr, pc, size)
+#define COMMON_MALLOC_FREE(ptr) \
+ if (in_symbolizer()) return InternalFree(ptr); \
+ SCOPED_INTERCEPTOR_RAW(free, ptr); \
+ user_free(thr, pc, ptr)
+#define COMMON_MALLOC_SIZE(ptr) uptr size = user_alloc_usable_size(ptr);
+#define COMMON_MALLOC_FILL_STATS(zone, stats)
+#define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \
+ (void)zone_name; \
+ Report("mz_realloc(%p) -- attempting to realloc unallocated memory.\n", ptr);
+#define COMMON_MALLOC_NAMESPACE __tsan
+#define COMMON_MALLOC_HAS_ZONE_ENUMERATOR 0
+#define COMMON_MALLOC_HAS_EXTRA_INTROSPECTION_INIT 0
+
+#include "sanitizer_common/sanitizer_malloc_mac.inc"
+
+#endif
+++ /dev/null
-//===-- tsan_md5.cc -------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_defs.h"
-
-namespace __tsan {
-
-#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
-#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | ~(z)))
-
-#define STEP(f, a, b, c, d, x, t, s) \
- (a) += f((b), (c), (d)) + (x) + (t); \
- (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
- (a) += (b);
-
-#define SET(n) \
- (*(const MD5_u32plus *)&ptr[(n) * 4])
-#define GET(n) \
- SET(n)
-
-typedef unsigned int MD5_u32plus;
-typedef unsigned long ulong_t; // NOLINT
-
-typedef struct {
- MD5_u32plus lo, hi;
- MD5_u32plus a, b, c, d;
- unsigned char buffer[64];
- MD5_u32plus block[16];
-} MD5_CTX;
-
-static const void *body(MD5_CTX *ctx, const void *data, ulong_t size) {
- const unsigned char *ptr = (const unsigned char *)data;
- MD5_u32plus a, b, c, d;
- MD5_u32plus saved_a, saved_b, saved_c, saved_d;
-
- a = ctx->a;
- b = ctx->b;
- c = ctx->c;
- d = ctx->d;
-
- do {
- saved_a = a;
- saved_b = b;
- saved_c = c;
- saved_d = d;
-
- STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
- STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
- STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
- STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
- STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
- STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
- STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
- STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
- STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
- STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
- STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
- STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
- STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
- STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
- STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
- STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
-
- STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
- STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
- STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
- STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
- STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
- STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
- STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
- STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
- STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
- STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
- STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
- STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
- STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
- STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
- STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
- STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
-
- STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
- STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
- STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
- STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
- STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
- STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
- STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
- STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
- STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
- STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
- STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
- STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
- STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
- STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
- STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
- STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
-
- STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
- STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
- STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
- STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
- STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
- STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
- STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
- STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
- STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
- STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
- STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
- STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
- STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
- STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
- STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
- STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
-
- a += saved_a;
- b += saved_b;
- c += saved_c;
- d += saved_d;
-
- ptr += 64;
- } while (size -= 64);
-
- ctx->a = a;
- ctx->b = b;
- ctx->c = c;
- ctx->d = d;
-
- return ptr;
-}
-
-void MD5_Init(MD5_CTX *ctx) {
- ctx->a = 0x67452301;
- ctx->b = 0xefcdab89;
- ctx->c = 0x98badcfe;
- ctx->d = 0x10325476;
-
- ctx->lo = 0;
- ctx->hi = 0;
-}
-
-void MD5_Update(MD5_CTX *ctx, const void *data, ulong_t size) {
- MD5_u32plus saved_lo;
- ulong_t used, free;
-
- saved_lo = ctx->lo;
- if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
- ctx->hi++;
- ctx->hi += size >> 29;
-
- used = saved_lo & 0x3f;
-
- if (used) {
- free = 64 - used;
-
- if (size < free) {
- internal_memcpy(&ctx->buffer[used], data, size);
- return;
- }
-
- internal_memcpy(&ctx->buffer[used], data, free);
- data = (const unsigned char *)data + free;
- size -= free;
- body(ctx, ctx->buffer, 64);
- }
-
- if (size >= 64) {
- data = body(ctx, data, size & ~(ulong_t)0x3f);
- size &= 0x3f;
- }
-
- internal_memcpy(ctx->buffer, data, size);
-}
-
-void MD5_Final(unsigned char *result, MD5_CTX *ctx) {
- ulong_t used, free;
-
- used = ctx->lo & 0x3f;
-
- ctx->buffer[used++] = 0x80;
-
- free = 64 - used;
-
- if (free < 8) {
- internal_memset(&ctx->buffer[used], 0, free);
- body(ctx, ctx->buffer, 64);
- used = 0;
- free = 64;
- }
-
- internal_memset(&ctx->buffer[used], 0, free - 8);
-
- ctx->lo <<= 3;
- ctx->buffer[56] = ctx->lo;
- ctx->buffer[57] = ctx->lo >> 8;
- ctx->buffer[58] = ctx->lo >> 16;
- ctx->buffer[59] = ctx->lo >> 24;
- ctx->buffer[60] = ctx->hi;
- ctx->buffer[61] = ctx->hi >> 8;
- ctx->buffer[62] = ctx->hi >> 16;
- ctx->buffer[63] = ctx->hi >> 24;
-
- body(ctx, ctx->buffer, 64);
-
- result[0] = ctx->a;
- result[1] = ctx->a >> 8;
- result[2] = ctx->a >> 16;
- result[3] = ctx->a >> 24;
- result[4] = ctx->b;
- result[5] = ctx->b >> 8;
- result[6] = ctx->b >> 16;
- result[7] = ctx->b >> 24;
- result[8] = ctx->c;
- result[9] = ctx->c >> 8;
- result[10] = ctx->c >> 16;
- result[11] = ctx->c >> 24;
- result[12] = ctx->d;
- result[13] = ctx->d >> 8;
- result[14] = ctx->d >> 16;
- result[15] = ctx->d >> 24;
-
- internal_memset(ctx, 0, sizeof(*ctx));
-}
-
-MD5Hash md5_hash(const void *data, uptr size) {
- MD5Hash res;
- MD5_CTX ctx;
- MD5_Init(&ctx);
- MD5_Update(&ctx, data, size);
- MD5_Final((unsigned char*)&res.hash[0], &ctx);
- return res;
-}
-} // namespace __tsan
--- /dev/null
+//===-- tsan_md5.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_defs.h"
+
+namespace __tsan {
+
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b);
+
+#define SET(n) \
+ (*(const MD5_u32plus *)&ptr[(n) * 4])
+#define GET(n) \
+ SET(n)
+
+typedef unsigned int MD5_u32plus;
+typedef unsigned long ulong_t; // NOLINT
+
+typedef struct {
+ MD5_u32plus lo, hi;
+ MD5_u32plus a, b, c, d;
+ unsigned char buffer[64];
+ MD5_u32plus block[16];
+} MD5_CTX;
+
+static const void *body(MD5_CTX *ctx, const void *data, ulong_t size) {
+ const unsigned char *ptr = (const unsigned char *)data;
+ MD5_u32plus a, b, c, d;
+ MD5_u32plus saved_a, saved_b, saved_c, saved_d;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11)
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23)
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23)
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11)
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23)
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11)
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ ptr += 64;
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return ptr;
+}
+
+#undef F
+#undef G
+#undef H
+#undef I
+#undef STEP
+#undef SET
+#undef GET
+
+void MD5_Init(MD5_CTX *ctx) {
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->lo = 0;
+ ctx->hi = 0;
+}
+
+void MD5_Update(MD5_CTX *ctx, const void *data, ulong_t size) {
+ MD5_u32plus saved_lo;
+ ulong_t used, free;
+
+ saved_lo = ctx->lo;
+ if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+ ctx->hi++;
+ ctx->hi += size >> 29;
+
+ used = saved_lo & 0x3f;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ internal_memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ internal_memcpy(&ctx->buffer[used], data, free);
+ data = (const unsigned char *)data + free;
+ size -= free;
+ body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = body(ctx, data, size & ~(ulong_t)0x3f);
+ size &= 0x3f;
+ }
+
+ internal_memcpy(ctx->buffer, data, size);
+}
+
+void MD5_Final(unsigned char *result, MD5_CTX *ctx) {
+ ulong_t used, free;
+
+ used = ctx->lo & 0x3f;
+
+ ctx->buffer[used++] = 0x80;
+
+ free = 64 - used;
+
+ if (free < 8) {
+ internal_memset(&ctx->buffer[used], 0, free);
+ body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+
+ internal_memset(&ctx->buffer[used], 0, free - 8);
+
+ ctx->lo <<= 3;
+ ctx->buffer[56] = ctx->lo;
+ ctx->buffer[57] = ctx->lo >> 8;
+ ctx->buffer[58] = ctx->lo >> 16;
+ ctx->buffer[59] = ctx->lo >> 24;
+ ctx->buffer[60] = ctx->hi;
+ ctx->buffer[61] = ctx->hi >> 8;
+ ctx->buffer[62] = ctx->hi >> 16;
+ ctx->buffer[63] = ctx->hi >> 24;
+
+ body(ctx, ctx->buffer, 64);
+
+ result[0] = ctx->a;
+ result[1] = ctx->a >> 8;
+ result[2] = ctx->a >> 16;
+ result[3] = ctx->a >> 24;
+ result[4] = ctx->b;
+ result[5] = ctx->b >> 8;
+ result[6] = ctx->b >> 16;
+ result[7] = ctx->b >> 24;
+ result[8] = ctx->c;
+ result[9] = ctx->c >> 8;
+ result[10] = ctx->c >> 16;
+ result[11] = ctx->c >> 24;
+ result[12] = ctx->d;
+ result[13] = ctx->d >> 8;
+ result[14] = ctx->d >> 16;
+ result[15] = ctx->d >> 24;
+
+ internal_memset(ctx, 0, sizeof(*ctx));
+}
+
+MD5Hash md5_hash(const void *data, uptr size) {
+ MD5Hash res;
+ MD5_CTX ctx;
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, data, size);
+ MD5_Final((unsigned char*)&res.hash[0], &ctx);
+ return res;
+}
+} // namespace __tsan
+++ /dev/null
-//===-- tsan_mman.cc ------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_allocator_checks.h"
-#include "sanitizer_common/sanitizer_allocator_interface.h"
-#include "sanitizer_common/sanitizer_allocator_report.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "tsan_mman.h"
-#include "tsan_rtl.h"
-#include "tsan_report.h"
-#include "tsan_flags.h"
-
-// May be overriden by front-end.
-SANITIZER_WEAK_DEFAULT_IMPL
-void __sanitizer_malloc_hook(void *ptr, uptr size) {
- (void)ptr;
- (void)size;
-}
-
-SANITIZER_WEAK_DEFAULT_IMPL
-void __sanitizer_free_hook(void *ptr) {
- (void)ptr;
-}
-
-namespace __tsan {
-
-struct MapUnmapCallback {
- void OnMap(uptr p, uptr size) const { }
- void OnUnmap(uptr p, uptr size) const {
- // We are about to unmap a chunk of user memory.
- // Mark the corresponding shadow memory as not needed.
- DontNeedShadowFor(p, size);
- // Mark the corresponding meta shadow memory as not needed.
- // Note the block does not contain any meta info at this point
- // (this happens after free).
- const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
- const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
- // Block came from LargeMmapAllocator, so must be large.
- // We rely on this in the calculations below.
- CHECK_GE(size, 2 * kPageSize);
- uptr diff = RoundUp(p, kPageSize) - p;
- if (diff != 0) {
- p += diff;
- size -= diff;
- }
- diff = p + size - RoundDown(p + size, kPageSize);
- if (diff != 0)
- size -= diff;
- uptr p_meta = (uptr)MemToMeta(p);
- ReleaseMemoryPagesToOS(p_meta, p_meta + size / kMetaRatio);
- }
-};
-
-static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
-Allocator *allocator() {
- return reinterpret_cast<Allocator*>(&allocator_placeholder);
-}
-
-struct GlobalProc {
- Mutex mtx;
- Processor *proc;
-
- GlobalProc()
- : mtx(MutexTypeGlobalProc, StatMtxGlobalProc)
- , proc(ProcCreate()) {
- }
-};
-
-static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64);
-GlobalProc *global_proc() {
- return reinterpret_cast<GlobalProc*>(&global_proc_placeholder);
-}
-
-ScopedGlobalProcessor::ScopedGlobalProcessor() {
- GlobalProc *gp = global_proc();
- ThreadState *thr = cur_thread();
- if (thr->proc())
- return;
- // If we don't have a proc, use the global one.
- // There are currently only two known case where this path is triggered:
- // __interceptor_free
- // __nptl_deallocate_tsd
- // start_thread
- // clone
- // and:
- // ResetRange
- // __interceptor_munmap
- // __deallocate_stack
- // start_thread
- // clone
- // Ideally, we destroy thread state (and unwire proc) when a thread actually
- // exits (i.e. when we join/wait it). Then we would not need the global proc
- gp->mtx.Lock();
- ProcWire(gp->proc, thr);
-}
-
-ScopedGlobalProcessor::~ScopedGlobalProcessor() {
- GlobalProc *gp = global_proc();
- ThreadState *thr = cur_thread();
- if (thr->proc() != gp->proc)
- return;
- ProcUnwire(gp->proc, thr);
- gp->mtx.Unlock();
-}
-
-void InitializeAllocator() {
- SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
- allocator()->Init(common_flags()->allocator_release_to_os_interval_ms);
-}
-
-void InitializeAllocatorLate() {
- new(global_proc()) GlobalProc();
-}
-
-void AllocatorProcStart(Processor *proc) {
- allocator()->InitCache(&proc->alloc_cache);
- internal_allocator()->InitCache(&proc->internal_alloc_cache);
-}
-
-void AllocatorProcFinish(Processor *proc) {
- allocator()->DestroyCache(&proc->alloc_cache);
- internal_allocator()->DestroyCache(&proc->internal_alloc_cache);
-}
-
-void AllocatorPrintStats() {
- allocator()->PrintStats();
-}
-
-static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
- if (atomic_load_relaxed(&thr->in_signal_handler) == 0 ||
- !flags()->report_signal_unsafe)
- return;
- VarSizeStackTrace stack;
- ObtainCurrentStack(thr, pc, &stack);
- if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
- return;
- ThreadRegistryLock l(ctx->thread_registry);
- ScopedReport rep(ReportTypeSignalUnsafe);
- rep.AddStack(stack, true);
- OutputReport(thr, rep);
-}
-
-static constexpr uptr kMaxAllowedMallocSize = 1ull << 40;
-
-void *user_alloc_internal(ThreadState *thr, uptr pc, uptr sz, uptr align,
- bool signal) {
- if (sz >= kMaxAllowedMallocSize || align >= kMaxAllowedMallocSize) {
- if (AllocatorMayReturnNull())
- return nullptr;
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportAllocationSizeTooBig(sz, kMaxAllowedMallocSize, &stack);
- }
- void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align);
- if (UNLIKELY(!p)) {
- SetAllocatorOutOfMemory();
- if (AllocatorMayReturnNull())
- return nullptr;
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportOutOfMemory(sz, &stack);
- }
- if (ctx && ctx->initialized)
- OnUserAlloc(thr, pc, (uptr)p, sz, true);
- if (signal)
- SignalUnsafeCall(thr, pc);
- return p;
-}
-
-void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
- ScopedGlobalProcessor sgp;
- if (ctx && ctx->initialized)
- OnUserFree(thr, pc, (uptr)p, true);
- allocator()->Deallocate(&thr->proc()->alloc_cache, p);
- if (signal)
- SignalUnsafeCall(thr, pc);
-}
-
-void *user_alloc(ThreadState *thr, uptr pc, uptr sz) {
- return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, kDefaultAlignment));
-}
-
-void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) {
- if (UNLIKELY(CheckForCallocOverflow(size, n))) {
- if (AllocatorMayReturnNull())
- return SetErrnoOnNull(nullptr);
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportCallocOverflow(n, size, &stack);
- }
- void *p = user_alloc_internal(thr, pc, n * size);
- if (p)
- internal_memset(p, 0, n * size);
- return SetErrnoOnNull(p);
-}
-
-void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {
- DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
- ctx->metamap.AllocBlock(thr, pc, p, sz);
- if (write && thr->ignore_reads_and_writes == 0)
- MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
- else
- MemoryResetRange(thr, pc, (uptr)p, sz);
-}
-
-void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) {
- CHECK_NE(p, (void*)0);
- uptr sz = ctx->metamap.FreeBlock(thr->proc(), p);
- DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz);
- if (write && thr->ignore_reads_and_writes == 0)
- MemoryRangeFreed(thr, pc, (uptr)p, sz);
-}
-
-void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
- // FIXME: Handle "shrinking" more efficiently,
- // it seems that some software actually does this.
- if (!p)
- return SetErrnoOnNull(user_alloc_internal(thr, pc, sz));
- if (!sz) {
- user_free(thr, pc, p);
- return nullptr;
- }
- void *new_p = user_alloc_internal(thr, pc, sz);
- if (new_p) {
- uptr old_sz = user_alloc_usable_size(p);
- internal_memcpy(new_p, p, min(old_sz, sz));
- user_free(thr, pc, p);
- }
- return SetErrnoOnNull(new_p);
-}
-
-void *user_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz) {
- if (UNLIKELY(!IsPowerOfTwo(align))) {
- errno = errno_EINVAL;
- if (AllocatorMayReturnNull())
- return nullptr;
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportInvalidAllocationAlignment(align, &stack);
- }
- return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align));
-}
-
-int user_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align,
- uptr sz) {
- if (UNLIKELY(!CheckPosixMemalignAlignment(align))) {
- if (AllocatorMayReturnNull())
- return errno_EINVAL;
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportInvalidPosixMemalignAlignment(align, &stack);
- }
- void *ptr = user_alloc_internal(thr, pc, sz, align);
- if (UNLIKELY(!ptr))
- // OOM error is already taken care of by user_alloc_internal.
- return errno_ENOMEM;
- CHECK(IsAligned((uptr)ptr, align));
- *memptr = ptr;
- return 0;
-}
-
-void *user_aligned_alloc(ThreadState *thr, uptr pc, uptr align, uptr sz) {
- if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(align, sz))) {
- errno = errno_EINVAL;
- if (AllocatorMayReturnNull())
- return nullptr;
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportInvalidAlignedAllocAlignment(sz, align, &stack);
- }
- return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align));
-}
-
-void *user_valloc(ThreadState *thr, uptr pc, uptr sz) {
- return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, GetPageSizeCached()));
-}
-
-void *user_pvalloc(ThreadState *thr, uptr pc, uptr sz) {
- uptr PageSize = GetPageSizeCached();
- if (UNLIKELY(CheckForPvallocOverflow(sz, PageSize))) {
- errno = errno_ENOMEM;
- if (AllocatorMayReturnNull())
- return nullptr;
- GET_STACK_TRACE_FATAL(thr, pc);
- ReportPvallocOverflow(sz, &stack);
- }
- // pvalloc(0) should allocate one page.
- sz = sz ? RoundUpTo(sz, PageSize) : PageSize;
- return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, PageSize));
-}
-
-uptr user_alloc_usable_size(const void *p) {
- if (p == 0)
- return 0;
- MBlock *b = ctx->metamap.GetBlock((uptr)p);
- if (!b)
- return 0; // Not a valid pointer.
- if (b->siz == 0)
- return 1; // Zero-sized allocations are actually 1 byte.
- return b->siz;
-}
-
-void invoke_malloc_hook(void *ptr, uptr size) {
- ThreadState *thr = cur_thread();
- if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
- return;
- __sanitizer_malloc_hook(ptr, size);
- RunMallocHooks(ptr, size);
-}
-
-void invoke_free_hook(void *ptr) {
- ThreadState *thr = cur_thread();
- if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
- return;
- __sanitizer_free_hook(ptr);
- RunFreeHooks(ptr);
-}
-
-void *internal_alloc(MBlockType typ, uptr sz) {
- ThreadState *thr = cur_thread();
- if (thr->nomalloc) {
- thr->nomalloc = 0; // CHECK calls internal_malloc().
- CHECK(0);
- }
- return InternalAlloc(sz, &thr->proc()->internal_alloc_cache);
-}
-
-void internal_free(void *p) {
- ThreadState *thr = cur_thread();
- if (thr->nomalloc) {
- thr->nomalloc = 0; // CHECK calls internal_malloc().
- CHECK(0);
- }
- InternalFree(p, &thr->proc()->internal_alloc_cache);
-}
-
-} // namespace __tsan
-
-using namespace __tsan;
-
-extern "C" {
-uptr __sanitizer_get_current_allocated_bytes() {
- uptr stats[AllocatorStatCount];
- allocator()->GetStats(stats);
- return stats[AllocatorStatAllocated];
-}
-
-uptr __sanitizer_get_heap_size() {
- uptr stats[AllocatorStatCount];
- allocator()->GetStats(stats);
- return stats[AllocatorStatMapped];
-}
-
-uptr __sanitizer_get_free_bytes() {
- return 1;
-}
-
-uptr __sanitizer_get_unmapped_bytes() {
- return 1;
-}
-
-uptr __sanitizer_get_estimated_allocated_size(uptr size) {
- return size;
-}
-
-int __sanitizer_get_ownership(const void *p) {
- return allocator()->GetBlockBegin(p) != 0;
-}
-
-uptr __sanitizer_get_allocated_size(const void *p) {
- return user_alloc_usable_size(p);
-}
-
-void __tsan_on_thread_idle() {
- ThreadState *thr = cur_thread();
- thr->clock.ResetCached(&thr->proc()->clock_cache);
- thr->last_sleep_clock.ResetCached(&thr->proc()->clock_cache);
- allocator()->SwallowCache(&thr->proc()->alloc_cache);
- internal_allocator()->SwallowCache(&thr->proc()->internal_alloc_cache);
- ctx->metamap.OnProcIdle(thr->proc());
-}
-} // extern "C"
--- /dev/null
+//===-- tsan_mman.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_mman.h"
+#include "tsan_rtl.h"
+#include "tsan_report.h"
+#include "tsan_flags.h"
+
+// May be overriden by front-end.
+SANITIZER_WEAK_DEFAULT_IMPL
+void __sanitizer_malloc_hook(void *ptr, uptr size) {
+ (void)ptr;
+ (void)size;
+}
+
+SANITIZER_WEAK_DEFAULT_IMPL
+void __sanitizer_free_hook(void *ptr) {
+ (void)ptr;
+}
+
+namespace __tsan {
+
+struct MapUnmapCallback {
+ void OnMap(uptr p, uptr size) const { }
+ void OnUnmap(uptr p, uptr size) const {
+ // We are about to unmap a chunk of user memory.
+ // Mark the corresponding shadow memory as not needed.
+ DontNeedShadowFor(p, size);
+ // Mark the corresponding meta shadow memory as not needed.
+ // Note the block does not contain any meta info at this point
+ // (this happens after free).
+ const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
+ const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
+ // Block came from LargeMmapAllocator, so must be large.
+ // We rely on this in the calculations below.
+ CHECK_GE(size, 2 * kPageSize);
+ uptr diff = RoundUp(p, kPageSize) - p;
+ if (diff != 0) {
+ p += diff;
+ size -= diff;
+ }
+ diff = p + size - RoundDown(p + size, kPageSize);
+ if (diff != 0)
+ size -= diff;
+ uptr p_meta = (uptr)MemToMeta(p);
+ ReleaseMemoryPagesToOS(p_meta, p_meta + size / kMetaRatio);
+ }
+};
+
+static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
+Allocator *allocator() {
+ return reinterpret_cast<Allocator*>(&allocator_placeholder);
+}
+
+struct GlobalProc {
+ Mutex mtx;
+ Processor *proc;
+
+ GlobalProc()
+ : mtx(MutexTypeGlobalProc, StatMtxGlobalProc)
+ , proc(ProcCreate()) {
+ }
+};
+
+static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64);
+GlobalProc *global_proc() {
+ return reinterpret_cast<GlobalProc*>(&global_proc_placeholder);
+}
+
+ScopedGlobalProcessor::ScopedGlobalProcessor() {
+ GlobalProc *gp = global_proc();
+ ThreadState *thr = cur_thread();
+ if (thr->proc())
+ return;
+ // If we don't have a proc, use the global one.
+ // There are currently only two known case where this path is triggered:
+ // __interceptor_free
+ // __nptl_deallocate_tsd
+ // start_thread
+ // clone
+ // and:
+ // ResetRange
+ // __interceptor_munmap
+ // __deallocate_stack
+ // start_thread
+ // clone
+ // Ideally, we destroy thread state (and unwire proc) when a thread actually
+ // exits (i.e. when we join/wait it). Then we would not need the global proc
+ gp->mtx.Lock();
+ ProcWire(gp->proc, thr);
+}
+
+ScopedGlobalProcessor::~ScopedGlobalProcessor() {
+ GlobalProc *gp = global_proc();
+ ThreadState *thr = cur_thread();
+ if (thr->proc() != gp->proc)
+ return;
+ ProcUnwire(gp->proc, thr);
+ gp->mtx.Unlock();
+}
+
+void InitializeAllocator() {
+ SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
+ allocator()->Init(common_flags()->allocator_release_to_os_interval_ms);
+}
+
+void InitializeAllocatorLate() {
+ new(global_proc()) GlobalProc();
+}
+
+void AllocatorProcStart(Processor *proc) {
+ allocator()->InitCache(&proc->alloc_cache);
+ internal_allocator()->InitCache(&proc->internal_alloc_cache);
+}
+
+void AllocatorProcFinish(Processor *proc) {
+ allocator()->DestroyCache(&proc->alloc_cache);
+ internal_allocator()->DestroyCache(&proc->internal_alloc_cache);
+}
+
+void AllocatorPrintStats() {
+ allocator()->PrintStats();
+}
+
+static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
+ if (atomic_load_relaxed(&thr->in_signal_handler) == 0 ||
+ !flags()->report_signal_unsafe)
+ return;
+ VarSizeStackTrace stack;
+ ObtainCurrentStack(thr, pc, &stack);
+ if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(ReportTypeSignalUnsafe);
+ rep.AddStack(stack, true);
+ OutputReport(thr, rep);
+}
+
+static constexpr uptr kMaxAllowedMallocSize = 1ull << 40;
+
+void *user_alloc_internal(ThreadState *thr, uptr pc, uptr sz, uptr align,
+ bool signal) {
+ if (sz >= kMaxAllowedMallocSize || align >= kMaxAllowedMallocSize) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportAllocationSizeTooBig(sz, kMaxAllowedMallocSize, &stack);
+ }
+ void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align);
+ if (UNLIKELY(!p)) {
+ SetAllocatorOutOfMemory();
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportOutOfMemory(sz, &stack);
+ }
+ if (ctx && ctx->initialized)
+ OnUserAlloc(thr, pc, (uptr)p, sz, true);
+ if (signal)
+ SignalUnsafeCall(thr, pc);
+ return p;
+}
+
+void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
+ ScopedGlobalProcessor sgp;
+ if (ctx && ctx->initialized)
+ OnUserFree(thr, pc, (uptr)p, true);
+ allocator()->Deallocate(&thr->proc()->alloc_cache, p);
+ if (signal)
+ SignalUnsafeCall(thr, pc);
+}
+
+void *user_alloc(ThreadState *thr, uptr pc, uptr sz) {
+ return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, kDefaultAlignment));
+}
+
+void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) {
+ if (UNLIKELY(CheckForCallocOverflow(size, n))) {
+ if (AllocatorMayReturnNull())
+ return SetErrnoOnNull(nullptr);
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportCallocOverflow(n, size, &stack);
+ }
+ void *p = user_alloc_internal(thr, pc, n * size);
+ if (p)
+ internal_memset(p, 0, n * size);
+ return SetErrnoOnNull(p);
+}
+
+void *user_reallocarray(ThreadState *thr, uptr pc, void *p, uptr size, uptr n) {
+ if (UNLIKELY(CheckForCallocOverflow(size, n))) {
+ if (AllocatorMayReturnNull())
+ return SetErrnoOnNull(nullptr);
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportReallocArrayOverflow(size, n, &stack);
+ }
+ return user_realloc(thr, pc, p, size * n);
+}
+
+void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {
+ DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
+ ctx->metamap.AllocBlock(thr, pc, p, sz);
+ if (write && thr->ignore_reads_and_writes == 0)
+ MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
+ else
+ MemoryResetRange(thr, pc, (uptr)p, sz);
+}
+
+void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) {
+ CHECK_NE(p, (void*)0);
+ uptr sz = ctx->metamap.FreeBlock(thr->proc(), p);
+ DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz);
+ if (write && thr->ignore_reads_and_writes == 0)
+ MemoryRangeFreed(thr, pc, (uptr)p, sz);
+}
+
+void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
+ // FIXME: Handle "shrinking" more efficiently,
+ // it seems that some software actually does this.
+ if (!p)
+ return SetErrnoOnNull(user_alloc_internal(thr, pc, sz));
+ if (!sz) {
+ user_free(thr, pc, p);
+ return nullptr;
+ }
+ void *new_p = user_alloc_internal(thr, pc, sz);
+ if (new_p) {
+ uptr old_sz = user_alloc_usable_size(p);
+ internal_memcpy(new_p, p, min(old_sz, sz));
+ user_free(thr, pc, p);
+ }
+ return SetErrnoOnNull(new_p);
+}
+
+void *user_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz) {
+ if (UNLIKELY(!IsPowerOfTwo(align))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportInvalidAllocationAlignment(align, &stack);
+ }
+ return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align));
+}
+
+int user_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align,
+ uptr sz) {
+ if (UNLIKELY(!CheckPosixMemalignAlignment(align))) {
+ if (AllocatorMayReturnNull())
+ return errno_EINVAL;
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportInvalidPosixMemalignAlignment(align, &stack);
+ }
+ void *ptr = user_alloc_internal(thr, pc, sz, align);
+ if (UNLIKELY(!ptr))
+ // OOM error is already taken care of by user_alloc_internal.
+ return errno_ENOMEM;
+ CHECK(IsAligned((uptr)ptr, align));
+ *memptr = ptr;
+ return 0;
+}
+
+void *user_aligned_alloc(ThreadState *thr, uptr pc, uptr align, uptr sz) {
+ if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(align, sz))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportInvalidAlignedAllocAlignment(sz, align, &stack);
+ }
+ return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, align));
+}
+
+void *user_valloc(ThreadState *thr, uptr pc, uptr sz) {
+ return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, GetPageSizeCached()));
+}
+
+void *user_pvalloc(ThreadState *thr, uptr pc, uptr sz) {
+ uptr PageSize = GetPageSizeCached();
+ if (UNLIKELY(CheckForPvallocOverflow(sz, PageSize))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ GET_STACK_TRACE_FATAL(thr, pc);
+ ReportPvallocOverflow(sz, &stack);
+ }
+ // pvalloc(0) should allocate one page.
+ sz = sz ? RoundUpTo(sz, PageSize) : PageSize;
+ return SetErrnoOnNull(user_alloc_internal(thr, pc, sz, PageSize));
+}
+
+uptr user_alloc_usable_size(const void *p) {
+ if (p == 0)
+ return 0;
+ MBlock *b = ctx->metamap.GetBlock((uptr)p);
+ if (!b)
+ return 0; // Not a valid pointer.
+ if (b->siz == 0)
+ return 1; // Zero-sized allocations are actually 1 byte.
+ return b->siz;
+}
+
+void invoke_malloc_hook(void *ptr, uptr size) {
+ ThreadState *thr = cur_thread();
+ if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
+ return;
+ __sanitizer_malloc_hook(ptr, size);
+ RunMallocHooks(ptr, size);
+}
+
+void invoke_free_hook(void *ptr) {
+ ThreadState *thr = cur_thread();
+ if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
+ return;
+ __sanitizer_free_hook(ptr);
+ RunFreeHooks(ptr);
+}
+
+void *internal_alloc(MBlockType typ, uptr sz) {
+ ThreadState *thr = cur_thread();
+ if (thr->nomalloc) {
+ thr->nomalloc = 0; // CHECK calls internal_malloc().
+ CHECK(0);
+ }
+ return InternalAlloc(sz, &thr->proc()->internal_alloc_cache);
+}
+
+void internal_free(void *p) {
+ ThreadState *thr = cur_thread();
+ if (thr->nomalloc) {
+ thr->nomalloc = 0; // CHECK calls internal_malloc().
+ CHECK(0);
+ }
+ InternalFree(p, &thr->proc()->internal_alloc_cache);
+}
+
+} // namespace __tsan
+
+using namespace __tsan;
+
+extern "C" {
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ allocator()->GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ allocator()->GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+uptr __sanitizer_get_free_bytes() {
+ return 1;
+}
+
+uptr __sanitizer_get_unmapped_bytes() {
+ return 1;
+}
+
+uptr __sanitizer_get_estimated_allocated_size(uptr size) {
+ return size;
+}
+
+int __sanitizer_get_ownership(const void *p) {
+ return allocator()->GetBlockBegin(p) != 0;
+}
+
+uptr __sanitizer_get_allocated_size(const void *p) {
+ return user_alloc_usable_size(p);
+}
+
+void __tsan_on_thread_idle() {
+ ThreadState *thr = cur_thread();
+ thr->clock.ResetCached(&thr->proc()->clock_cache);
+ thr->last_sleep_clock.ResetCached(&thr->proc()->clock_cache);
+ allocator()->SwallowCache(&thr->proc()->alloc_cache);
+ internal_allocator()->SwallowCache(&thr->proc()->internal_alloc_cache);
+ ctx->metamap.OnProcIdle(thr->proc());
+}
+} // extern "C"
//===-- tsan_mman.h ---------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void *user_alloc(ThreadState *thr, uptr pc, uptr sz);
void *user_calloc(ThreadState *thr, uptr pc, uptr sz, uptr n);
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
+void *user_reallocarray(ThreadState *thr, uptr pc, void *p, uptr sz, uptr n);
void *user_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz);
int user_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align,
uptr sz);
+++ /dev/null
-//===-- tsan_mutex.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_libc.h"
-#include "tsan_mutex.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-
-namespace __tsan {
-
-// Simple reader-writer spin-mutex. Optimized for not-so-contended case.
-// Readers have preference, can possibly starvate writers.
-
-// The table fixes what mutexes can be locked under what mutexes.
-// E.g. if the row for MutexTypeThreads contains MutexTypeReport,
-// then Report mutex can be locked while under Threads mutex.
-// The leaf mutexes can be locked under any other mutexes.
-// Recursive locking is not supported.
-#if SANITIZER_DEBUG && !SANITIZER_GO
-const MutexType MutexTypeLeaf = (MutexType)-1;
-static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
- /*0 MutexTypeInvalid*/ {},
- /*1 MutexTypeTrace*/ {MutexTypeLeaf},
- /*2 MutexTypeThreads*/ {MutexTypeReport},
- /*3 MutexTypeReport*/ {MutexTypeSyncVar,
- MutexTypeMBlock, MutexTypeJavaMBlock},
- /*4 MutexTypeSyncVar*/ {MutexTypeDDetector},
- /*5 MutexTypeSyncTab*/ {}, // unused
- /*6 MutexTypeSlab*/ {MutexTypeLeaf},
- /*7 MutexTypeAnnotations*/ {},
- /*8 MutexTypeAtExit*/ {MutexTypeSyncVar},
- /*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
- /*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar},
- /*11 MutexTypeDDetector*/ {},
- /*12 MutexTypeFired*/ {MutexTypeLeaf},
- /*13 MutexTypeRacy*/ {MutexTypeLeaf},
- /*14 MutexTypeGlobalProc*/ {},
-};
-
-static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
-#endif
-
-void InitializeMutex() {
-#if SANITIZER_DEBUG && !SANITIZER_GO
- // Build the "can lock" adjacency matrix.
- // If [i][j]==true, then one can lock mutex j while under mutex i.
- const int N = MutexTypeCount;
- int cnt[N] = {};
- bool leaf[N] = {};
- for (int i = 1; i < N; i++) {
- for (int j = 0; j < N; j++) {
- MutexType z = CanLockTab[i][j];
- if (z == MutexTypeInvalid)
- continue;
- if (z == MutexTypeLeaf) {
- CHECK(!leaf[i]);
- leaf[i] = true;
- continue;
- }
- CHECK(!CanLockAdj[i][(int)z]);
- CanLockAdj[i][(int)z] = true;
- cnt[i]++;
- }
- }
- for (int i = 0; i < N; i++) {
- CHECK(!leaf[i] || cnt[i] == 0);
- }
- // Add leaf mutexes.
- for (int i = 0; i < N; i++) {
- if (!leaf[i])
- continue;
- for (int j = 0; j < N; j++) {
- if (i == j || leaf[j] || j == MutexTypeInvalid)
- continue;
- CHECK(!CanLockAdj[j][i]);
- CanLockAdj[j][i] = true;
- }
- }
- // Build the transitive closure.
- bool CanLockAdj2[MutexTypeCount][MutexTypeCount];
- for (int i = 0; i < N; i++) {
- for (int j = 0; j < N; j++) {
- CanLockAdj2[i][j] = CanLockAdj[i][j];
- }
- }
- for (int k = 0; k < N; k++) {
- for (int i = 0; i < N; i++) {
- for (int j = 0; j < N; j++) {
- if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) {
- CanLockAdj2[i][j] = true;
- }
- }
- }
- }
-#if 0
- Printf("Can lock graph:\n");
- for (int i = 0; i < N; i++) {
- for (int j = 0; j < N; j++) {
- Printf("%d ", CanLockAdj[i][j]);
- }
- Printf("\n");
- }
- Printf("Can lock graph closure:\n");
- for (int i = 0; i < N; i++) {
- for (int j = 0; j < N; j++) {
- Printf("%d ", CanLockAdj2[i][j]);
- }
- Printf("\n");
- }
-#endif
- // Verify that the graph is acyclic.
- for (int i = 0; i < N; i++) {
- if (CanLockAdj2[i][i]) {
- Printf("Mutex %d participates in a cycle\n", i);
- Die();
- }
- }
-#endif
-}
-
-InternalDeadlockDetector::InternalDeadlockDetector() {
- // Rely on zero initialization because some mutexes can be locked before ctor.
-}
-
-#if SANITIZER_DEBUG && !SANITIZER_GO
-void InternalDeadlockDetector::Lock(MutexType t) {
- // Printf("LOCK %d @%zu\n", t, seq_ + 1);
- CHECK_GT(t, MutexTypeInvalid);
- CHECK_LT(t, MutexTypeCount);
- u64 max_seq = 0;
- u64 max_idx = MutexTypeInvalid;
- for (int i = 0; i != MutexTypeCount; i++) {
- if (locked_[i] == 0)
- continue;
- CHECK_NE(locked_[i], max_seq);
- if (max_seq < locked_[i]) {
- max_seq = locked_[i];
- max_idx = i;
- }
- }
- locked_[t] = ++seq_;
- if (max_idx == MutexTypeInvalid)
- return;
- // Printf(" last %d @%zu\n", max_idx, max_seq);
- if (!CanLockAdj[max_idx][t]) {
- Printf("ThreadSanitizer: internal deadlock detected\n");
- Printf("ThreadSanitizer: can't lock %d while under %zu\n",
- t, (uptr)max_idx);
- CHECK(0);
- }
-}
-
-void InternalDeadlockDetector::Unlock(MutexType t) {
- // Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
- CHECK(locked_[t]);
- locked_[t] = 0;
-}
-
-void InternalDeadlockDetector::CheckNoLocks() {
- for (int i = 0; i != MutexTypeCount; i++) {
- CHECK_EQ(locked_[i], 0);
- }
-}
-#endif
-
-void CheckNoLocks(ThreadState *thr) {
-#if SANITIZER_DEBUG && !SANITIZER_GO
- thr->internal_deadlock_detector.CheckNoLocks();
-#endif
-}
-
-const uptr kUnlocked = 0;
-const uptr kWriteLock = 1;
-const uptr kReadLock = 2;
-
-class Backoff {
- public:
- Backoff()
- : iter_() {
- }
-
- bool Do() {
- if (iter_++ < kActiveSpinIters)
- proc_yield(kActiveSpinCnt);
- else
- internal_sched_yield();
- return true;
- }
-
- u64 Contention() const {
- u64 active = iter_ % kActiveSpinIters;
- u64 passive = iter_ - active;
- return active + 10 * passive;
- }
-
- private:
- int iter_;
- static const int kActiveSpinIters = 10;
- static const int kActiveSpinCnt = 20;
-};
-
-Mutex::Mutex(MutexType type, StatType stat_type) {
- CHECK_GT(type, MutexTypeInvalid);
- CHECK_LT(type, MutexTypeCount);
-#if SANITIZER_DEBUG
- type_ = type;
-#endif
-#if TSAN_COLLECT_STATS
- stat_type_ = stat_type;
-#endif
- atomic_store(&state_, kUnlocked, memory_order_relaxed);
-}
-
-Mutex::~Mutex() {
- CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
-}
-
-void Mutex::Lock() {
-#if SANITIZER_DEBUG && !SANITIZER_GO
- cur_thread()->internal_deadlock_detector.Lock(type_);
-#endif
- uptr cmp = kUnlocked;
- if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
- memory_order_acquire))
- return;
- for (Backoff backoff; backoff.Do();) {
- if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) {
- cmp = kUnlocked;
- if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
- memory_order_acquire)) {
-#if TSAN_COLLECT_STATS && !SANITIZER_GO
- StatInc(cur_thread(), stat_type_, backoff.Contention());
-#endif
- return;
- }
- }
- }
-}
-
-void Mutex::Unlock() {
- uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
- (void)prev;
- DCHECK_NE(prev & kWriteLock, 0);
-#if SANITIZER_DEBUG && !SANITIZER_GO
- cur_thread()->internal_deadlock_detector.Unlock(type_);
-#endif
-}
-
-void Mutex::ReadLock() {
-#if SANITIZER_DEBUG && !SANITIZER_GO
- cur_thread()->internal_deadlock_detector.Lock(type_);
-#endif
- uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
- if ((prev & kWriteLock) == 0)
- return;
- for (Backoff backoff; backoff.Do();) {
- prev = atomic_load(&state_, memory_order_acquire);
- if ((prev & kWriteLock) == 0) {
-#if TSAN_COLLECT_STATS && !SANITIZER_GO
- StatInc(cur_thread(), stat_type_, backoff.Contention());
-#endif
- return;
- }
- }
-}
-
-void Mutex::ReadUnlock() {
- uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
- (void)prev;
- DCHECK_EQ(prev & kWriteLock, 0);
- DCHECK_GT(prev & ~kWriteLock, 0);
-#if SANITIZER_DEBUG && !SANITIZER_GO
- cur_thread()->internal_deadlock_detector.Unlock(type_);
-#endif
-}
-
-void Mutex::CheckLocked() {
- CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0);
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_mutex.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_libc.h"
+#include "tsan_mutex.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+// Simple reader-writer spin-mutex. Optimized for not-so-contended case.
+// Readers have preference, can possibly starvate writers.
+
+// The table fixes what mutexes can be locked under what mutexes.
+// E.g. if the row for MutexTypeThreads contains MutexTypeReport,
+// then Report mutex can be locked while under Threads mutex.
+// The leaf mutexes can be locked under any other mutexes.
+// Recursive locking is not supported.
+#if SANITIZER_DEBUG && !SANITIZER_GO
+const MutexType MutexTypeLeaf = (MutexType)-1;
+static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
+ /*0 MutexTypeInvalid*/ {},
+ /*1 MutexTypeTrace*/ {MutexTypeLeaf},
+ /*2 MutexTypeThreads*/ {MutexTypeReport},
+ /*3 MutexTypeReport*/ {MutexTypeSyncVar,
+ MutexTypeMBlock, MutexTypeJavaMBlock},
+ /*4 MutexTypeSyncVar*/ {MutexTypeDDetector},
+ /*5 MutexTypeSyncTab*/ {}, // unused
+ /*6 MutexTypeSlab*/ {MutexTypeLeaf},
+ /*7 MutexTypeAnnotations*/ {},
+ /*8 MutexTypeAtExit*/ {MutexTypeSyncVar},
+ /*9 MutexTypeMBlock*/ {MutexTypeSyncVar},
+ /*10 MutexTypeJavaMBlock*/ {MutexTypeSyncVar},
+ /*11 MutexTypeDDetector*/ {},
+ /*12 MutexTypeFired*/ {MutexTypeLeaf},
+ /*13 MutexTypeRacy*/ {MutexTypeLeaf},
+ /*14 MutexTypeGlobalProc*/ {},
+};
+
+static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
+#endif
+
+void InitializeMutex() {
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ // Build the "can lock" adjacency matrix.
+ // If [i][j]==true, then one can lock mutex j while under mutex i.
+ const int N = MutexTypeCount;
+ int cnt[N] = {};
+ bool leaf[N] = {};
+ for (int i = 1; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ MutexType z = CanLockTab[i][j];
+ if (z == MutexTypeInvalid)
+ continue;
+ if (z == MutexTypeLeaf) {
+ CHECK(!leaf[i]);
+ leaf[i] = true;
+ continue;
+ }
+ CHECK(!CanLockAdj[i][(int)z]);
+ CanLockAdj[i][(int)z] = true;
+ cnt[i]++;
+ }
+ }
+ for (int i = 0; i < N; i++) {
+ CHECK(!leaf[i] || cnt[i] == 0);
+ }
+ // Add leaf mutexes.
+ for (int i = 0; i < N; i++) {
+ if (!leaf[i])
+ continue;
+ for (int j = 0; j < N; j++) {
+ if (i == j || leaf[j] || j == MutexTypeInvalid)
+ continue;
+ CHECK(!CanLockAdj[j][i]);
+ CanLockAdj[j][i] = true;
+ }
+ }
+ // Build the transitive closure.
+ bool CanLockAdj2[MutexTypeCount][MutexTypeCount];
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ CanLockAdj2[i][j] = CanLockAdj[i][j];
+ }
+ }
+ for (int k = 0; k < N; k++) {
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) {
+ CanLockAdj2[i][j] = true;
+ }
+ }
+ }
+ }
+#if 0
+ Printf("Can lock graph:\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ Printf("%d ", CanLockAdj[i][j]);
+ }
+ Printf("\n");
+ }
+ Printf("Can lock graph closure:\n");
+ for (int i = 0; i < N; i++) {
+ for (int j = 0; j < N; j++) {
+ Printf("%d ", CanLockAdj2[i][j]);
+ }
+ Printf("\n");
+ }
+#endif
+ // Verify that the graph is acyclic.
+ for (int i = 0; i < N; i++) {
+ if (CanLockAdj2[i][i]) {
+ Printf("Mutex %d participates in a cycle\n", i);
+ Die();
+ }
+ }
+#endif
+}
+
+InternalDeadlockDetector::InternalDeadlockDetector() {
+ // Rely on zero initialization because some mutexes can be locked before ctor.
+}
+
+#if SANITIZER_DEBUG && !SANITIZER_GO
+void InternalDeadlockDetector::Lock(MutexType t) {
+ // Printf("LOCK %d @%zu\n", t, seq_ + 1);
+ CHECK_GT(t, MutexTypeInvalid);
+ CHECK_LT(t, MutexTypeCount);
+ u64 max_seq = 0;
+ u64 max_idx = MutexTypeInvalid;
+ for (int i = 0; i != MutexTypeCount; i++) {
+ if (locked_[i] == 0)
+ continue;
+ CHECK_NE(locked_[i], max_seq);
+ if (max_seq < locked_[i]) {
+ max_seq = locked_[i];
+ max_idx = i;
+ }
+ }
+ locked_[t] = ++seq_;
+ if (max_idx == MutexTypeInvalid)
+ return;
+ // Printf(" last %d @%zu\n", max_idx, max_seq);
+ if (!CanLockAdj[max_idx][t]) {
+ Printf("ThreadSanitizer: internal deadlock detected\n");
+ Printf("ThreadSanitizer: can't lock %d while under %zu\n",
+ t, (uptr)max_idx);
+ CHECK(0);
+ }
+}
+
+void InternalDeadlockDetector::Unlock(MutexType t) {
+ // Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
+ CHECK(locked_[t]);
+ locked_[t] = 0;
+}
+
+void InternalDeadlockDetector::CheckNoLocks() {
+ for (int i = 0; i != MutexTypeCount; i++) {
+ CHECK_EQ(locked_[i], 0);
+ }
+}
+#endif
+
+void CheckNoLocks(ThreadState *thr) {
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ thr->internal_deadlock_detector.CheckNoLocks();
+#endif
+}
+
+const uptr kUnlocked = 0;
+const uptr kWriteLock = 1;
+const uptr kReadLock = 2;
+
+class Backoff {
+ public:
+ Backoff()
+ : iter_() {
+ }
+
+ bool Do() {
+ if (iter_++ < kActiveSpinIters)
+ proc_yield(kActiveSpinCnt);
+ else
+ internal_sched_yield();
+ return true;
+ }
+
+ u64 Contention() const {
+ u64 active = iter_ % kActiveSpinIters;
+ u64 passive = iter_ - active;
+ return active + 10 * passive;
+ }
+
+ private:
+ int iter_;
+ static const int kActiveSpinIters = 10;
+ static const int kActiveSpinCnt = 20;
+};
+
+Mutex::Mutex(MutexType type, StatType stat_type) {
+ CHECK_GT(type, MutexTypeInvalid);
+ CHECK_LT(type, MutexTypeCount);
+#if SANITIZER_DEBUG
+ type_ = type;
+#endif
+#if TSAN_COLLECT_STATS
+ stat_type_ = stat_type;
+#endif
+ atomic_store(&state_, kUnlocked, memory_order_relaxed);
+}
+
+Mutex::~Mutex() {
+ CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
+}
+
+void Mutex::Lock() {
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Lock(type_);
+#endif
+ uptr cmp = kUnlocked;
+ if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
+ memory_order_acquire))
+ return;
+ for (Backoff backoff; backoff.Do();) {
+ if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) {
+ cmp = kUnlocked;
+ if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
+ memory_order_acquire)) {
+#if TSAN_COLLECT_STATS && !SANITIZER_GO
+ StatInc(cur_thread(), stat_type_, backoff.Contention());
+#endif
+ return;
+ }
+ }
+ }
+}
+
+void Mutex::Unlock() {
+ uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
+ (void)prev;
+ DCHECK_NE(prev & kWriteLock, 0);
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Unlock(type_);
+#endif
+}
+
+void Mutex::ReadLock() {
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Lock(type_);
+#endif
+ uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
+ if ((prev & kWriteLock) == 0)
+ return;
+ for (Backoff backoff; backoff.Do();) {
+ prev = atomic_load(&state_, memory_order_acquire);
+ if ((prev & kWriteLock) == 0) {
+#if TSAN_COLLECT_STATS && !SANITIZER_GO
+ StatInc(cur_thread(), stat_type_, backoff.Contention());
+#endif
+ return;
+ }
+ }
+}
+
+void Mutex::ReadUnlock() {
+ uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
+ (void)prev;
+ DCHECK_EQ(prev & kWriteLock, 0);
+ DCHECK_GT(prev & ~kWriteLock, 0);
+#if SANITIZER_DEBUG && !SANITIZER_GO
+ cur_thread()->internal_deadlock_detector.Unlock(type_);
+#endif
+}
+
+void Mutex::CheckLocked() {
+ CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0);
+}
+
+} // namespace __tsan
//===-- tsan_mutex.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_mutexset.cc --------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_mutexset.h"
-#include "tsan_rtl.h"
-
-namespace __tsan {
-
-const uptr MutexSet::kMaxSize;
-
-MutexSet::MutexSet() {
- size_ = 0;
- internal_memset(&descs_, 0, sizeof(descs_));
-}
-
-void MutexSet::Add(u64 id, bool write, u64 epoch) {
- // Look up existing mutex with the same id.
- for (uptr i = 0; i < size_; i++) {
- if (descs_[i].id == id) {
- descs_[i].count++;
- descs_[i].epoch = epoch;
- return;
- }
- }
- // On overflow, find the oldest mutex and drop it.
- if (size_ == kMaxSize) {
- u64 minepoch = (u64)-1;
- u64 mini = (u64)-1;
- for (uptr i = 0; i < size_; i++) {
- if (descs_[i].epoch < minepoch) {
- minepoch = descs_[i].epoch;
- mini = i;
- }
- }
- RemovePos(mini);
- CHECK_EQ(size_, kMaxSize - 1);
- }
- // Add new mutex descriptor.
- descs_[size_].id = id;
- descs_[size_].write = write;
- descs_[size_].epoch = epoch;
- descs_[size_].count = 1;
- size_++;
-}
-
-void MutexSet::Del(u64 id, bool write) {
- for (uptr i = 0; i < size_; i++) {
- if (descs_[i].id == id) {
- if (--descs_[i].count == 0)
- RemovePos(i);
- return;
- }
- }
-}
-
-void MutexSet::Remove(u64 id) {
- for (uptr i = 0; i < size_; i++) {
- if (descs_[i].id == id) {
- RemovePos(i);
- return;
- }
- }
-}
-
-void MutexSet::RemovePos(uptr i) {
- CHECK_LT(i, size_);
- descs_[i] = descs_[size_ - 1];
- size_--;
-}
-
-uptr MutexSet::Size() const {
- return size_;
-}
-
-MutexSet::Desc MutexSet::Get(uptr i) const {
- CHECK_LT(i, size_);
- return descs_[i];
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_mutexset.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_mutexset.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+const uptr MutexSet::kMaxSize;
+
+MutexSet::MutexSet() {
+ size_ = 0;
+ internal_memset(&descs_, 0, sizeof(descs_));
+}
+
+void MutexSet::Add(u64 id, bool write, u64 epoch) {
+ // Look up existing mutex with the same id.
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].id == id) {
+ descs_[i].count++;
+ descs_[i].epoch = epoch;
+ return;
+ }
+ }
+ // On overflow, find the oldest mutex and drop it.
+ if (size_ == kMaxSize) {
+ u64 minepoch = (u64)-1;
+ u64 mini = (u64)-1;
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].epoch < minepoch) {
+ minepoch = descs_[i].epoch;
+ mini = i;
+ }
+ }
+ RemovePos(mini);
+ CHECK_EQ(size_, kMaxSize - 1);
+ }
+ // Add new mutex descriptor.
+ descs_[size_].id = id;
+ descs_[size_].write = write;
+ descs_[size_].epoch = epoch;
+ descs_[size_].count = 1;
+ size_++;
+}
+
+void MutexSet::Del(u64 id, bool write) {
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].id == id) {
+ if (--descs_[i].count == 0)
+ RemovePos(i);
+ return;
+ }
+ }
+}
+
+void MutexSet::Remove(u64 id) {
+ for (uptr i = 0; i < size_; i++) {
+ if (descs_[i].id == id) {
+ RemovePos(i);
+ return;
+ }
+ }
+}
+
+void MutexSet::RemovePos(uptr i) {
+ CHECK_LT(i, size_);
+ descs_[i] = descs_[size_ - 1];
+ size_--;
+}
+
+uptr MutexSet::Size() const {
+ return size_;
+}
+
+MutexSet::Desc MutexSet::Get(uptr i) const {
+ CHECK_LT(i, size_);
+ return descs_[i];
+}
+
+} // namespace __tsan
//===-- tsan_mutexset.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_new_delete.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Interceptors for operators new and delete.
-//===----------------------------------------------------------------------===//
-#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_allocator.h"
-#include "sanitizer_common/sanitizer_allocator_report.h"
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "tsan_interceptors.h"
-#include "tsan_rtl.h"
-
-using namespace __tsan; // NOLINT
-
-namespace std {
-struct nothrow_t {};
-enum class align_val_t: __sanitizer::uptr {};
-} // namespace std
-
-DECLARE_REAL(void *, malloc, uptr size)
-DECLARE_REAL(void, free, void *ptr)
-
-// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
-#define OPERATOR_NEW_BODY(mangled_name, nothrow) \
- if (cur_thread()->in_symbolizer) \
- return InternalAlloc(size); \
- void *p = 0; \
- { \
- SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
- p = user_alloc(thr, pc, size); \
- if (!nothrow && UNLIKELY(!p)) { \
- GET_STACK_TRACE_FATAL(thr, pc); \
- ReportOutOfMemory(size, &stack); \
- } \
- } \
- invoke_malloc_hook(p, size); \
- return p;
-
-#define OPERATOR_NEW_BODY_ALIGN(mangled_name, nothrow) \
- if (cur_thread()->in_symbolizer) \
- return InternalAlloc(size, nullptr, (uptr)align); \
- void *p = 0; \
- { \
- SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
- p = user_memalign(thr, pc, (uptr)align, size); \
- if (!nothrow && UNLIKELY(!p)) { \
- GET_STACK_TRACE_FATAL(thr, pc); \
- ReportOutOfMemory(size, &stack); \
- } \
- } \
- invoke_malloc_hook(p, size); \
- return p;
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size);
-void *operator new(__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znwm, false /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size);
-void *operator new[](__sanitizer::uptr size) {
- OPERATOR_NEW_BODY(_Znam, false /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
-void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t, true /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
-void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
- OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t, true /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size, std::align_val_t align);
-void *operator new(__sanitizer::uptr size, std::align_val_t align) {
- OPERATOR_NEW_BODY_ALIGN(_ZnwmSt11align_val_t, false /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size, std::align_val_t align);
-void *operator new[](__sanitizer::uptr size, std::align_val_t align) {
- OPERATOR_NEW_BODY_ALIGN(_ZnamSt11align_val_t, false /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new(__sanitizer::uptr size, std::align_val_t align,
- std::nothrow_t const&);
-void *operator new(__sanitizer::uptr size, std::align_val_t align,
- std::nothrow_t const&) {
- OPERATOR_NEW_BODY_ALIGN(_ZnwmSt11align_val_tRKSt9nothrow_t,
- true /*nothrow*/);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void *operator new[](__sanitizer::uptr size, std::align_val_t align,
- std::nothrow_t const&);
-void *operator new[](__sanitizer::uptr size, std::align_val_t align,
- std::nothrow_t const&) {
- OPERATOR_NEW_BODY_ALIGN(_ZnamSt11align_val_tRKSt9nothrow_t,
- true /*nothrow*/);
-}
-
-#define OPERATOR_DELETE_BODY(mangled_name) \
- if (ptr == 0) return; \
- if (cur_thread()->in_symbolizer) \
- return InternalFree(ptr); \
- invoke_free_hook(ptr); \
- SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
- user_free(thr, pc, ptr);
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr) NOEXCEPT;
-void operator delete(void *ptr) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdlPv);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr) NOEXCEPT;
-void operator delete[](void *ptr) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdaPv);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, std::nothrow_t const&);
-void operator delete(void *ptr, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, std::nothrow_t const&);
-void operator delete[](void *ptr, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, __sanitizer::uptr size) NOEXCEPT;
-void operator delete(void *ptr, __sanitizer::uptr size) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdlPvm);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, __sanitizer::uptr size) NOEXCEPT;
-void operator delete[](void *ptr, __sanitizer::uptr size) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdaPvm);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, std::align_val_t align) NOEXCEPT;
-void operator delete(void *ptr, std::align_val_t align) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdlPvSt11align_val_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT;
-void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdaPvSt11align_val_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&);
-void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdlPvSt11align_val_tRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, std::align_val_t align,
- std::nothrow_t const&);
-void operator delete[](void *ptr, std::align_val_t align,
- std::nothrow_t const&) {
- OPERATOR_DELETE_BODY(_ZdaPvSt11align_val_tRKSt9nothrow_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete(void *ptr, __sanitizer::uptr size,
- std::align_val_t align) NOEXCEPT;
-void operator delete(void *ptr, __sanitizer::uptr size,
- std::align_val_t align) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdlPvmSt11align_val_t);
-}
-
-SANITIZER_INTERFACE_ATTRIBUTE
-void operator delete[](void *ptr, __sanitizer::uptr size,
- std::align_val_t align) NOEXCEPT;
-void operator delete[](void *ptr, __sanitizer::uptr size,
- std::align_val_t align) NOEXCEPT {
- OPERATOR_DELETE_BODY(_ZdaPvmSt11align_val_t);
-}
--- /dev/null
+//===-- tsan_new_delete.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "tsan_interceptors.h"
+#include "tsan_rtl.h"
+
+using namespace __tsan; // NOLINT
+
+namespace std {
+struct nothrow_t {};
+enum class align_val_t: __sanitizer::uptr {};
+} // namespace std
+
+DECLARE_REAL(void *, malloc, uptr size)
+DECLARE_REAL(void, free, void *ptr)
+
+// TODO(alekseys): throw std::bad_alloc instead of dying on OOM.
+#define OPERATOR_NEW_BODY(mangled_name, nothrow) \
+ if (in_symbolizer()) \
+ return InternalAlloc(size); \
+ void *p = 0; \
+ { \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
+ p = user_alloc(thr, pc, size); \
+ if (!nothrow && UNLIKELY(!p)) { \
+ GET_STACK_TRACE_FATAL(thr, pc); \
+ ReportOutOfMemory(size, &stack); \
+ } \
+ } \
+ invoke_malloc_hook(p, size); \
+ return p;
+
+#define OPERATOR_NEW_BODY_ALIGN(mangled_name, nothrow) \
+ if (in_symbolizer()) \
+ return InternalAlloc(size, nullptr, (uptr)align); \
+ void *p = 0; \
+ { \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \
+ p = user_memalign(thr, pc, (uptr)align, size); \
+ if (!nothrow && UNLIKELY(!p)) { \
+ GET_STACK_TRACE_FATAL(thr, pc); \
+ ReportOutOfMemory(size, &stack); \
+ } \
+ } \
+ invoke_malloc_hook(p, size); \
+ return p;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size);
+void *operator new(__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znwm, false /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size);
+void *operator new[](__sanitizer::uptr size) {
+ OPERATOR_NEW_BODY(_Znam, false /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&);
+void *operator new(__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t, true /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&);
+void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) {
+ OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t, true /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size, std::align_val_t align);
+void *operator new(__sanitizer::uptr size, std::align_val_t align) {
+ OPERATOR_NEW_BODY_ALIGN(_ZnwmSt11align_val_t, false /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size, std::align_val_t align);
+void *operator new[](__sanitizer::uptr size, std::align_val_t align) {
+ OPERATOR_NEW_BODY_ALIGN(_ZnamSt11align_val_t, false /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new(__sanitizer::uptr size, std::align_val_t align,
+ std::nothrow_t const&);
+void *operator new(__sanitizer::uptr size, std::align_val_t align,
+ std::nothrow_t const&) {
+ OPERATOR_NEW_BODY_ALIGN(_ZnwmSt11align_val_tRKSt9nothrow_t,
+ true /*nothrow*/);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void *operator new[](__sanitizer::uptr size, std::align_val_t align,
+ std::nothrow_t const&);
+void *operator new[](__sanitizer::uptr size, std::align_val_t align,
+ std::nothrow_t const&) {
+ OPERATOR_NEW_BODY_ALIGN(_ZnamSt11align_val_tRKSt9nothrow_t,
+ true /*nothrow*/);
+}
+
+#define OPERATOR_DELETE_BODY(mangled_name) \
+ if (ptr == 0) return; \
+ if (in_symbolizer()) \
+ return InternalFree(ptr); \
+ invoke_free_hook(ptr); \
+ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \
+ user_free(thr, pc, ptr);
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT;
+void operator delete(void *ptr) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdlPv);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT;
+void operator delete[](void *ptr) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdaPv);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const&);
+void operator delete(void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const&);
+void operator delete[](void *ptr, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, __sanitizer::uptr size) NOEXCEPT;
+void operator delete(void *ptr, __sanitizer::uptr size) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdlPvm);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, __sanitizer::uptr size) NOEXCEPT;
+void operator delete[](void *ptr, __sanitizer::uptr size) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdaPvm);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align) NOEXCEPT;
+void operator delete(void *ptr, std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdlPvSt11align_val_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT;
+void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdaPvSt11align_val_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&);
+void operator delete(void *ptr, std::align_val_t align, std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdlPvSt11align_val_tRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align,
+ std::nothrow_t const&);
+void operator delete[](void *ptr, std::align_val_t align,
+ std::nothrow_t const&) {
+ OPERATOR_DELETE_BODY(_ZdaPvSt11align_val_tRKSt9nothrow_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete(void *ptr, __sanitizer::uptr size,
+ std::align_val_t align) NOEXCEPT;
+void operator delete(void *ptr, __sanitizer::uptr size,
+ std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdlPvmSt11align_val_t);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void operator delete[](void *ptr, __sanitizer::uptr size,
+ std::align_val_t align) NOEXCEPT;
+void operator delete[](void *ptr, __sanitizer::uptr size,
+ std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY(_ZdaPvmSt11align_val_t);
+}
//===-- tsan_platform.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
int ExtractResolvFDs(void *state, int *fds, int nfd);
int ExtractRecvmsgFDs(void *msg, int *fds, int nfd);
+uptr ExtractLongJmpSp(uptr *env);
void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size);
int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
+++ /dev/null
-//===-- tsan_platform_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 ThreadSanitizer (TSan), a race detector.
-//
-// Linux- and FreeBSD-specific code.
-//===----------------------------------------------------------------------===//
-
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_linux.h"
-#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
-#include "sanitizer_common/sanitizer_platform_limits_posix.h"
-#include "sanitizer_common/sanitizer_posix.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-#include "sanitizer_common/sanitizer_stoptheworld.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-#include "tsan_flags.h"
-
-#include <fcntl.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <sys/mman.h>
-#if SANITIZER_LINUX
-#include <sys/personality.h>
-#include <setjmp.h>
-#endif
-#include <sys/syscall.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <sched.h>
-#include <dlfcn.h>
-#if SANITIZER_LINUX
-#define __need_res_state
-#include <resolv.h>
-#endif
-
-#ifdef sa_handler
-# undef sa_handler
-#endif
-
-#ifdef sa_sigaction
-# undef sa_sigaction
-#endif
-
-#if SANITIZER_FREEBSD
-extern "C" void *__libc_stack_end;
-void *__libc_stack_end = 0;
-#endif
-
-#if SANITIZER_LINUX && defined(__aarch64__)
-void InitializeGuardPtr() __attribute__((visibility("hidden")));
-#endif
-
-namespace __tsan {
-
-#ifdef TSAN_RUNTIME_VMA
-// Runtime detected VMA size.
-uptr vmaSize;
-#endif
-
-enum {
- MemTotal = 0,
- MemShadow = 1,
- MemMeta = 2,
- MemFile = 3,
- MemMmap = 4,
- MemTrace = 5,
- MemHeap = 6,
- MemOther = 7,
- MemCount = 8,
-};
-
-void FillProfileCallback(uptr p, uptr rss, bool file,
- uptr *mem, uptr stats_size) {
- mem[MemTotal] += rss;
- if (p >= ShadowBeg() && p < ShadowEnd())
- mem[MemShadow] += rss;
- else if (p >= MetaShadowBeg() && p < MetaShadowEnd())
- mem[MemMeta] += rss;
-#if !SANITIZER_GO
- else if (p >= HeapMemBeg() && p < HeapMemEnd())
- mem[MemHeap] += rss;
- else if (p >= LoAppMemBeg() && p < LoAppMemEnd())
- mem[file ? MemFile : MemMmap] += rss;
- else if (p >= HiAppMemBeg() && p < HiAppMemEnd())
- mem[file ? MemFile : MemMmap] += rss;
-#else
- else if (p >= AppMemBeg() && p < AppMemEnd())
- mem[file ? MemFile : MemMmap] += rss;
-#endif
- else if (p >= TraceMemBeg() && p < TraceMemEnd())
- mem[MemTrace] += rss;
- else
- mem[MemOther] += rss;
-}
-
-void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
- uptr mem[MemCount];
- internal_memset(mem, 0, sizeof(mem[0]) * MemCount);
- __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
- StackDepotStats *stacks = StackDepotGetStats();
- internal_snprintf(buf, buf_size,
- "RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd"
- " trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n",
- mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20,
- mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20,
- mem[MemHeap] >> 20, mem[MemOther] >> 20,
- stacks->allocated >> 20, stacks->n_uniq_ids,
- nlive, nthread);
-}
-
-#if SANITIZER_LINUX
-void FlushShadowMemoryCallback(
- const SuspendedThreadsList &suspended_threads_list,
- void *argument) {
- ReleaseMemoryPagesToOS(ShadowBeg(), ShadowEnd());
-}
-#endif
-
-void FlushShadowMemory() {
-#if SANITIZER_LINUX
- StopTheWorld(FlushShadowMemoryCallback, 0);
-#endif
-}
-
-#if !SANITIZER_GO
-// Mark shadow for .rodata sections with the special kShadowRodata marker.
-// Accesses to .rodata can't race, so this saves time, memory and trace space.
-static void MapRodata() {
- // First create temp file.
- const char *tmpdir = GetEnv("TMPDIR");
- if (tmpdir == 0)
- tmpdir = GetEnv("TEST_TMPDIR");
-#ifdef P_tmpdir
- if (tmpdir == 0)
- tmpdir = P_tmpdir;
-#endif
- if (tmpdir == 0)
- return;
- char name[256];
- internal_snprintf(name, sizeof(name), "%s/tsan.rodata.%d",
- tmpdir, (int)internal_getpid());
- uptr openrv = internal_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (internal_iserror(openrv))
- return;
- internal_unlink(name); // Unlink it now, so that we can reuse the buffer.
- fd_t fd = openrv;
- // Fill the file with kShadowRodata.
- const uptr kMarkerSize = 512 * 1024 / sizeof(u64);
- InternalMmapVector<u64> marker(kMarkerSize);
- // volatile to prevent insertion of memset
- for (volatile u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++)
- *p = kShadowRodata;
- internal_write(fd, marker.data(), marker.size() * sizeof(u64));
- // Map the file into memory.
- uptr page = internal_mmap(0, GetPageSizeCached(), PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
- if (internal_iserror(page)) {
- internal_close(fd);
- return;
- }
- // Map the file into shadow of .rodata sections.
- MemoryMappingLayout proc_maps(/*cache_enabled*/true);
- // Reusing the buffer 'name'.
- MemoryMappedSegment segment(name, ARRAY_SIZE(name));
- while (proc_maps.Next(&segment)) {
- if (segment.filename[0] != 0 && segment.filename[0] != '[' &&
- segment.IsReadable() && segment.IsExecutable() &&
- !segment.IsWritable() && IsAppMem(segment.start)) {
- // Assume it's .rodata
- char *shadow_start = (char *)MemToShadow(segment.start);
- char *shadow_end = (char *)MemToShadow(segment.end);
- for (char *p = shadow_start; p < shadow_end;
- p += marker.size() * sizeof(u64)) {
- internal_mmap(p, Min<uptr>(marker.size() * sizeof(u64), shadow_end - p),
- PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
- }
- }
- }
- internal_close(fd);
-}
-
-void InitializeShadowMemoryPlatform() {
- MapRodata();
-}
-
-#endif // #if !SANITIZER_GO
-
-void InitializePlatformEarly() {
-#ifdef TSAN_RUNTIME_VMA
- vmaSize =
- (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
-#if defined(__aarch64__)
-# if !SANITIZER_GO
- if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {
- Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
- Printf("FATAL: Found %zd - Supported 39, 42 and 48\n", vmaSize);
- Die();
- }
-#else
- if (vmaSize != 48) {
- Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
- Printf("FATAL: Found %zd - Supported 48\n", vmaSize);
- Die();
- }
-#endif
-#elif defined(__powerpc64__)
-# if !SANITIZER_GO
- if (vmaSize != 44 && vmaSize != 46 && vmaSize != 47) {
- Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
- Printf("FATAL: Found %zd - Supported 44, 46, and 47\n", vmaSize);
- Die();
- }
-# else
- if (vmaSize != 46 && vmaSize != 47) {
- Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
- Printf("FATAL: Found %zd - Supported 46, and 47\n", vmaSize);
- Die();
- }
-# endif
-#endif
-#endif
-}
-
-void InitializePlatform() {
- DisableCoreDumperIfNecessary();
-
- // Go maps shadow memory lazily and works fine with limited address space.
- // Unlimited stack is not a problem as well, because the executable
- // is not compiled with -pie.
- if (!SANITIZER_GO) {
- bool reexec = false;
- // TSan doesn't play well with unlimited stack size (as stack
- // overlaps with shadow memory). If we detect unlimited stack size,
- // we re-exec the program with limited stack size as a best effort.
- if (StackSizeIsUnlimited()) {
- const uptr kMaxStackSize = 32 * 1024 * 1024;
- VReport(1, "Program is run with unlimited stack size, which wouldn't "
- "work with ThreadSanitizer.\n"
- "Re-execing with stack size limited to %zd bytes.\n",
- kMaxStackSize);
- SetStackSizeLimitInBytes(kMaxStackSize);
- reexec = true;
- }
-
- if (!AddressSpaceIsUnlimited()) {
- Report("WARNING: Program is run with limited virtual address space,"
- " which wouldn't work with ThreadSanitizer.\n");
- Report("Re-execing with unlimited virtual address space.\n");
- SetAddressSpaceUnlimited();
- reexec = true;
- }
-#if SANITIZER_LINUX && defined(__aarch64__)
- // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in
- // linux kernel, the random gap between stack and mapped area is increased
- // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover
- // this big range, we should disable randomized virtual space on aarch64.
- int old_personality = personality(0xffffffff);
- if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) {
- VReport(1, "WARNING: Program is run with randomized virtual address "
- "space, which wouldn't work with ThreadSanitizer.\n"
- "Re-execing with fixed virtual address space.\n");
- CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
- reexec = true;
- }
- // Initialize the guard pointer used in {sig}{set,long}jump.
- InitializeGuardPtr();
-#endif
- if (reexec)
- ReExec();
- }
-
-#if !SANITIZER_GO
- CheckAndProtect();
- InitTlsSize();
-#endif
-}
-
-#if !SANITIZER_GO
-// Extract file descriptors passed to glibc internal __res_iclose function.
-// This is required to properly "close" the fds, because we do not see internal
-// closes within glibc. The code is a pure hack.
-int ExtractResolvFDs(void *state, int *fds, int nfd) {
-#if SANITIZER_LINUX && !SANITIZER_ANDROID
- int cnt = 0;
- struct __res_state *statp = (struct __res_state*)state;
- for (int i = 0; i < MAXNS && cnt < nfd; i++) {
- if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1)
- fds[cnt++] = statp->_u._ext.nssocks[i];
- }
- return cnt;
-#else
- return 0;
-#endif
-}
-
-// Extract file descriptors passed via UNIX domain sockets.
-// This is requried to properly handle "open" of these fds.
-// see 'man recvmsg' and 'man 3 cmsg'.
-int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
- int res = 0;
- msghdr *msg = (msghdr*)msgp;
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
- for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
- if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
- continue;
- int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]);
- for (int i = 0; i < n; i++) {
- fds[res++] = ((int*)CMSG_DATA(cmsg))[i];
- if (res == nfd)
- return res;
- }
- }
- return res;
-}
-
-void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
- // Check that the thr object is in tls;
- const uptr thr_beg = (uptr)thr;
- const uptr thr_end = (uptr)thr + sizeof(*thr);
- CHECK_GE(thr_beg, tls_addr);
- CHECK_LE(thr_beg, tls_addr + tls_size);
- CHECK_GE(thr_end, tls_addr);
- CHECK_LE(thr_end, tls_addr + tls_size);
- // Since the thr object is huge, skip it.
- MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, thr_beg - tls_addr);
- MemoryRangeImitateWrite(thr, /*pc=*/2, thr_end,
- tls_addr + tls_size - thr_end);
-}
-
-// Note: this function runs with async signals enabled,
-// so it must not touch any tsan state.
-int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
- void *abstime), void *c, void *m, void *abstime,
- void(*cleanup)(void *arg), void *arg) {
- // pthread_cleanup_push/pop are hardcore macros mess.
- // We can't intercept nor call them w/o including pthread.h.
- int res;
- pthread_cleanup_push(cleanup, arg);
- res = fn(c, m, abstime);
- pthread_cleanup_pop(0);
- return res;
-}
-#endif
-
-#if !SANITIZER_GO
-void ReplaceSystemMalloc() { }
-#endif
-
-#if !SANITIZER_GO
-#if SANITIZER_ANDROID
-// On Android, one thread can call intercepted functions after
-// DestroyThreadState(), so add a fake thread state for "dead" threads.
-static ThreadState *dead_thread_state = nullptr;
-
-ThreadState *cur_thread() {
- ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
- if (thr == nullptr) {
- __sanitizer_sigset_t emptyset;
- internal_sigfillset(&emptyset);
- __sanitizer_sigset_t oldset;
- CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset));
- thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
- if (thr == nullptr) {
- thr = reinterpret_cast<ThreadState*>(MmapOrDie(sizeof(ThreadState),
- "ThreadState"));
- *get_android_tls_ptr() = reinterpret_cast<uptr>(thr);
- if (dead_thread_state == nullptr) {
- dead_thread_state = reinterpret_cast<ThreadState*>(
- MmapOrDie(sizeof(ThreadState), "ThreadState"));
- dead_thread_state->fast_state.SetIgnoreBit();
- dead_thread_state->ignore_interceptors = 1;
- dead_thread_state->is_dead = true;
- *const_cast<int*>(&dead_thread_state->tid) = -1;
- CHECK_EQ(0, internal_mprotect(dead_thread_state, sizeof(ThreadState),
- PROT_READ));
- }
- }
- CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
- }
- return thr;
-}
-
-void cur_thread_finalize() {
- __sanitizer_sigset_t emptyset;
- internal_sigfillset(&emptyset);
- __sanitizer_sigset_t oldset;
- CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset));
- ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
- if (thr != dead_thread_state) {
- *get_android_tls_ptr() = reinterpret_cast<uptr>(dead_thread_state);
- UnmapOrDie(thr, sizeof(ThreadState));
- }
- CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
-}
-#endif // SANITIZER_ANDROID
-#endif // if !SANITIZER_GO
-
-} // namespace __tsan
-
-#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
--- /dev/null
+//===-- tsan_platform_linux.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Linux- and FreeBSD-specific code.
+//===----------------------------------------------------------------------===//
+
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_linux.h"
+#include "sanitizer_common/sanitizer_platform_limits_netbsd.h"
+#include "sanitizer_common/sanitizer_platform_limits_posix.h"
+#include "sanitizer_common/sanitizer_posix.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stoptheworld.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#if SANITIZER_LINUX
+#include <sys/personality.h>
+#include <setjmp.h>
+#endif
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sched.h>
+#include <dlfcn.h>
+#if SANITIZER_LINUX
+#define __need_res_state
+#include <resolv.h>
+#endif
+
+#ifdef sa_handler
+# undef sa_handler
+#endif
+
+#ifdef sa_sigaction
+# undef sa_sigaction
+#endif
+
+#if SANITIZER_FREEBSD
+extern "C" void *__libc_stack_end;
+void *__libc_stack_end = 0;
+#endif
+
+#if SANITIZER_LINUX && defined(__aarch64__) && !SANITIZER_GO
+# define INIT_LONGJMP_XOR_KEY 1
+#else
+# define INIT_LONGJMP_XOR_KEY 0
+#endif
+
+#if INIT_LONGJMP_XOR_KEY
+#include "interception/interception.h"
+// Must be declared outside of other namespaces.
+DECLARE_REAL(int, _setjmp, void *env)
+#endif
+
+namespace __tsan {
+
+#if INIT_LONGJMP_XOR_KEY
+static void InitializeLongjmpXorKey();
+static uptr longjmp_xor_key;
+#endif
+
+#ifdef TSAN_RUNTIME_VMA
+// Runtime detected VMA size.
+uptr vmaSize;
+#endif
+
+enum {
+ MemTotal = 0,
+ MemShadow = 1,
+ MemMeta = 2,
+ MemFile = 3,
+ MemMmap = 4,
+ MemTrace = 5,
+ MemHeap = 6,
+ MemOther = 7,
+ MemCount = 8,
+};
+
+void FillProfileCallback(uptr p, uptr rss, bool file,
+ uptr *mem, uptr stats_size) {
+ mem[MemTotal] += rss;
+ if (p >= ShadowBeg() && p < ShadowEnd())
+ mem[MemShadow] += rss;
+ else if (p >= MetaShadowBeg() && p < MetaShadowEnd())
+ mem[MemMeta] += rss;
+#if !SANITIZER_GO
+ else if (p >= HeapMemBeg() && p < HeapMemEnd())
+ mem[MemHeap] += rss;
+ else if (p >= LoAppMemBeg() && p < LoAppMemEnd())
+ mem[file ? MemFile : MemMmap] += rss;
+ else if (p >= HiAppMemBeg() && p < HiAppMemEnd())
+ mem[file ? MemFile : MemMmap] += rss;
+#else
+ else if (p >= AppMemBeg() && p < AppMemEnd())
+ mem[file ? MemFile : MemMmap] += rss;
+#endif
+ else if (p >= TraceMemBeg() && p < TraceMemEnd())
+ mem[MemTrace] += rss;
+ else
+ mem[MemOther] += rss;
+}
+
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
+ uptr mem[MemCount];
+ internal_memset(mem, 0, sizeof(mem[0]) * MemCount);
+ __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
+ StackDepotStats *stacks = StackDepotGetStats();
+ internal_snprintf(buf, buf_size,
+ "RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd"
+ " trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n",
+ mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20,
+ mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20,
+ mem[MemHeap] >> 20, mem[MemOther] >> 20,
+ stacks->allocated >> 20, stacks->n_uniq_ids,
+ nlive, nthread);
+}
+
+#if SANITIZER_LINUX
+void FlushShadowMemoryCallback(
+ const SuspendedThreadsList &suspended_threads_list,
+ void *argument) {
+ ReleaseMemoryPagesToOS(ShadowBeg(), ShadowEnd());
+}
+#endif
+
+void FlushShadowMemory() {
+#if SANITIZER_LINUX
+ StopTheWorld(FlushShadowMemoryCallback, 0);
+#endif
+}
+
+#if !SANITIZER_GO
+// Mark shadow for .rodata sections with the special kShadowRodata marker.
+// Accesses to .rodata can't race, so this saves time, memory and trace space.
+static void MapRodata() {
+ // First create temp file.
+ const char *tmpdir = GetEnv("TMPDIR");
+ if (tmpdir == 0)
+ tmpdir = GetEnv("TEST_TMPDIR");
+#ifdef P_tmpdir
+ if (tmpdir == 0)
+ tmpdir = P_tmpdir;
+#endif
+ if (tmpdir == 0)
+ return;
+ char name[256];
+ internal_snprintf(name, sizeof(name), "%s/tsan.rodata.%d",
+ tmpdir, (int)internal_getpid());
+ uptr openrv = internal_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (internal_iserror(openrv))
+ return;
+ internal_unlink(name); // Unlink it now, so that we can reuse the buffer.
+ fd_t fd = openrv;
+ // Fill the file with kShadowRodata.
+ const uptr kMarkerSize = 512 * 1024 / sizeof(u64);
+ InternalMmapVector<u64> marker(kMarkerSize);
+ // volatile to prevent insertion of memset
+ for (volatile u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++)
+ *p = kShadowRodata;
+ internal_write(fd, marker.data(), marker.size() * sizeof(u64));
+ // Map the file into memory.
+ uptr page = internal_mmap(0, GetPageSizeCached(), PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
+ if (internal_iserror(page)) {
+ internal_close(fd);
+ return;
+ }
+ // Map the file into shadow of .rodata sections.
+ MemoryMappingLayout proc_maps(/*cache_enabled*/true);
+ // Reusing the buffer 'name'.
+ MemoryMappedSegment segment(name, ARRAY_SIZE(name));
+ while (proc_maps.Next(&segment)) {
+ if (segment.filename[0] != 0 && segment.filename[0] != '[' &&
+ segment.IsReadable() && segment.IsExecutable() &&
+ !segment.IsWritable() && IsAppMem(segment.start)) {
+ // Assume it's .rodata
+ char *shadow_start = (char *)MemToShadow(segment.start);
+ char *shadow_end = (char *)MemToShadow(segment.end);
+ for (char *p = shadow_start; p < shadow_end;
+ p += marker.size() * sizeof(u64)) {
+ internal_mmap(p, Min<uptr>(marker.size() * sizeof(u64), shadow_end - p),
+ PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
+ }
+ }
+ }
+ internal_close(fd);
+}
+
+void InitializeShadowMemoryPlatform() {
+ MapRodata();
+}
+
+#endif // #if !SANITIZER_GO
+
+void InitializePlatformEarly() {
+#ifdef TSAN_RUNTIME_VMA
+ vmaSize =
+ (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
+#if defined(__aarch64__)
+# if !SANITIZER_GO
+ if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {
+ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+ Printf("FATAL: Found %zd - Supported 39, 42 and 48\n", vmaSize);
+ Die();
+ }
+#else
+ if (vmaSize != 48) {
+ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+ Printf("FATAL: Found %zd - Supported 48\n", vmaSize);
+ Die();
+ }
+#endif
+#elif defined(__powerpc64__)
+# if !SANITIZER_GO
+ if (vmaSize != 44 && vmaSize != 46 && vmaSize != 47) {
+ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+ Printf("FATAL: Found %zd - Supported 44, 46, and 47\n", vmaSize);
+ Die();
+ }
+# else
+ if (vmaSize != 46 && vmaSize != 47) {
+ Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
+ Printf("FATAL: Found %zd - Supported 46, and 47\n", vmaSize);
+ Die();
+ }
+# endif
+#endif
+#endif
+}
+
+void InitializePlatform() {
+ DisableCoreDumperIfNecessary();
+
+ // Go maps shadow memory lazily and works fine with limited address space.
+ // Unlimited stack is not a problem as well, because the executable
+ // is not compiled with -pie.
+#if !SANITIZER_GO
+ {
+ bool reexec = false;
+ // TSan doesn't play well with unlimited stack size (as stack
+ // overlaps with shadow memory). If we detect unlimited stack size,
+ // we re-exec the program with limited stack size as a best effort.
+ if (StackSizeIsUnlimited()) {
+ const uptr kMaxStackSize = 32 * 1024 * 1024;
+ VReport(1, "Program is run with unlimited stack size, which wouldn't "
+ "work with ThreadSanitizer.\n"
+ "Re-execing with stack size limited to %zd bytes.\n",
+ kMaxStackSize);
+ SetStackSizeLimitInBytes(kMaxStackSize);
+ reexec = true;
+ }
+
+ if (!AddressSpaceIsUnlimited()) {
+ Report("WARNING: Program is run with limited virtual address space,"
+ " which wouldn't work with ThreadSanitizer.\n");
+ Report("Re-execing with unlimited virtual address space.\n");
+ SetAddressSpaceUnlimited();
+ reexec = true;
+ }
+#if SANITIZER_LINUX && defined(__aarch64__)
+ // After patch "arm64: mm: support ARCH_MMAP_RND_BITS." is introduced in
+ // linux kernel, the random gap between stack and mapped area is increased
+ // from 128M to 36G on 39-bit aarch64. As it is almost impossible to cover
+ // this big range, we should disable randomized virtual space on aarch64.
+ int old_personality = personality(0xffffffff);
+ if (old_personality != -1 && (old_personality & ADDR_NO_RANDOMIZE) == 0) {
+ VReport(1, "WARNING: Program is run with randomized virtual address "
+ "space, which wouldn't work with ThreadSanitizer.\n"
+ "Re-execing with fixed virtual address space.\n");
+ CHECK_NE(personality(old_personality | ADDR_NO_RANDOMIZE), -1);
+ reexec = true;
+ }
+ // Initialize the xor key used in {sig}{set,long}jump.
+ InitializeLongjmpXorKey();
+#endif
+ if (reexec)
+ ReExec();
+ }
+
+ CheckAndProtect();
+ InitTlsSize();
+#endif // !SANITIZER_GO
+}
+
+#if !SANITIZER_GO
+// Extract file descriptors passed to glibc internal __res_iclose function.
+// This is required to properly "close" the fds, because we do not see internal
+// closes within glibc. The code is a pure hack.
+int ExtractResolvFDs(void *state, int *fds, int nfd) {
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+ int cnt = 0;
+ struct __res_state *statp = (struct __res_state*)state;
+ for (int i = 0; i < MAXNS && cnt < nfd; i++) {
+ if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1)
+ fds[cnt++] = statp->_u._ext.nssocks[i];
+ }
+ return cnt;
+#else
+ return 0;
+#endif
+}
+
+// Extract file descriptors passed via UNIX domain sockets.
+// This is requried to properly handle "open" of these fds.
+// see 'man recvmsg' and 'man 3 cmsg'.
+int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
+ int res = 0;
+ msghdr *msg = (msghdr*)msgp;
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+ for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
+ continue;
+ int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]);
+ for (int i = 0; i < n; i++) {
+ fds[res++] = ((int*)CMSG_DATA(cmsg))[i];
+ if (res == nfd)
+ return res;
+ }
+ }
+ return res;
+}
+
+// Reverse operation of libc stack pointer mangling
+static uptr UnmangleLongJmpSp(uptr mangled_sp) {
+#if defined(__x86_64__)
+# if SANITIZER_LINUX
+ // Reverse of:
+ // xor %fs:0x30, %rsi
+ // rol $0x11, %rsi
+ uptr sp;
+ asm("ror $0x11, %0 \n"
+ "xor %%fs:0x30, %0 \n"
+ : "=r" (sp)
+ : "0" (mangled_sp));
+ return sp;
+# else
+ return mangled_sp;
+# endif
+#elif defined(__aarch64__)
+# if SANITIZER_LINUX
+ return mangled_sp ^ longjmp_xor_key;
+# else
+ return mangled_sp;
+# endif
+#elif defined(__powerpc64__)
+ // Reverse of:
+ // ld r4, -28696(r13)
+ // xor r4, r3, r4
+ uptr xor_key;
+ asm("ld %0, -28696(%%r13)" : "=r" (xor_key));
+ return mangled_sp ^ xor_key;
+#elif defined(__mips__)
+ return mangled_sp;
+#else
+ #error "Unknown platform"
+#endif
+}
+
+#ifdef __powerpc__
+# define LONG_JMP_SP_ENV_SLOT 0
+#elif SANITIZER_FREEBSD
+# define LONG_JMP_SP_ENV_SLOT 2
+#elif SANITIZER_NETBSD
+# define LONG_JMP_SP_ENV_SLOT 6
+#elif SANITIZER_LINUX
+# ifdef __aarch64__
+# define LONG_JMP_SP_ENV_SLOT 13
+# elif defined(__mips64)
+# define LONG_JMP_SP_ENV_SLOT 1
+# else
+# define LONG_JMP_SP_ENV_SLOT 6
+# endif
+#endif
+
+uptr ExtractLongJmpSp(uptr *env) {
+ uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT];
+ return UnmangleLongJmpSp(mangled_sp);
+}
+
+#if INIT_LONGJMP_XOR_KEY
+// GLIBC mangles the function pointers in jmp_buf (used in {set,long}*jmp
+// functions) by XORing them with a random key. For AArch64 it is a global
+// variable rather than a TCB one (as for x86_64/powerpc). We obtain the key by
+// issuing a setjmp and XORing the SP pointer values to derive the key.
+static void InitializeLongjmpXorKey() {
+ // 1. Call REAL(setjmp), which stores the mangled SP in env.
+ jmp_buf env;
+ REAL(_setjmp)(env);
+
+ // 2. Retrieve vanilla/mangled SP.
+ uptr sp;
+ asm("mov %0, sp" : "=r" (sp));
+ uptr mangled_sp = ((uptr *)&env)[LONG_JMP_SP_ENV_SLOT];
+
+ // 3. xor SPs to obtain key.
+ longjmp_xor_key = mangled_sp ^ sp;
+}
+#endif
+
+void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
+ // Check that the thr object is in tls;
+ const uptr thr_beg = (uptr)thr;
+ const uptr thr_end = (uptr)thr + sizeof(*thr);
+ CHECK_GE(thr_beg, tls_addr);
+ CHECK_LE(thr_beg, tls_addr + tls_size);
+ CHECK_GE(thr_end, tls_addr);
+ CHECK_LE(thr_end, tls_addr + tls_size);
+ // Since the thr object is huge, skip it.
+ MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, thr_beg - tls_addr);
+ MemoryRangeImitateWrite(thr, /*pc=*/2, thr_end,
+ tls_addr + tls_size - thr_end);
+}
+
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
+int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
+ void *abstime), void *c, void *m, void *abstime,
+ void(*cleanup)(void *arg), void *arg) {
+ // pthread_cleanup_push/pop are hardcore macros mess.
+ // We can't intercept nor call them w/o including pthread.h.
+ int res;
+ pthread_cleanup_push(cleanup, arg);
+ res = fn(c, m, abstime);
+ pthread_cleanup_pop(0);
+ return res;
+}
+#endif // !SANITIZER_GO
+
+#if !SANITIZER_GO
+void ReplaceSystemMalloc() { }
+#endif
+
+#if !SANITIZER_GO
+#if SANITIZER_ANDROID
+// On Android, one thread can call intercepted functions after
+// DestroyThreadState(), so add a fake thread state for "dead" threads.
+static ThreadState *dead_thread_state = nullptr;
+
+ThreadState *cur_thread() {
+ ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
+ if (thr == nullptr) {
+ __sanitizer_sigset_t emptyset;
+ internal_sigfillset(&emptyset);
+ __sanitizer_sigset_t oldset;
+ CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset));
+ thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
+ if (thr == nullptr) {
+ thr = reinterpret_cast<ThreadState*>(MmapOrDie(sizeof(ThreadState),
+ "ThreadState"));
+ *get_android_tls_ptr() = reinterpret_cast<uptr>(thr);
+ if (dead_thread_state == nullptr) {
+ dead_thread_state = reinterpret_cast<ThreadState*>(
+ MmapOrDie(sizeof(ThreadState), "ThreadState"));
+ dead_thread_state->fast_state.SetIgnoreBit();
+ dead_thread_state->ignore_interceptors = 1;
+ dead_thread_state->is_dead = true;
+ *const_cast<int*>(&dead_thread_state->tid) = -1;
+ CHECK_EQ(0, internal_mprotect(dead_thread_state, sizeof(ThreadState),
+ PROT_READ));
+ }
+ }
+ CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
+ }
+ return thr;
+}
+
+void set_cur_thread(ThreadState *thr) {
+ *get_android_tls_ptr() = reinterpret_cast<uptr>(thr);
+}
+
+void cur_thread_finalize() {
+ __sanitizer_sigset_t emptyset;
+ internal_sigfillset(&emptyset);
+ __sanitizer_sigset_t oldset;
+ CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset));
+ ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
+ if (thr != dead_thread_state) {
+ *get_android_tls_ptr() = reinterpret_cast<uptr>(dead_thread_state);
+ UnmapOrDie(thr, sizeof(ThreadState));
+ }
+ CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
+}
+#endif // SANITIZER_ANDROID
+#endif // if !SANITIZER_GO
+
+} // namespace __tsan
+
+#endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
+++ /dev/null
-//===-- tsan_platform_mac.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Mac-specific code.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_MAC
-
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_posix.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-#include "tsan_flags.h"
-
-#include <mach/mach.h>
-#include <pthread.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sched.h>
-
-namespace __tsan {
-
-#if !SANITIZER_GO
-static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
- atomic_uintptr_t *a = (atomic_uintptr_t *)dst;
- void *val = (void *)atomic_load_relaxed(a);
- atomic_signal_fence(memory_order_acquire); // Turns the previous load into
- // acquire wrt signals.
- if (UNLIKELY(val == nullptr)) {
- val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON, -1, 0);
- CHECK(val);
- void *cmp = nullptr;
- if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val,
- memory_order_acq_rel)) {
- internal_munmap(val, size);
- val = cmp;
- }
- }
- return val;
-}
-
-// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
-// problematic, because there are several places where interceptors are called
-// when TLVs are not accessible (early process startup, thread cleanup, ...).
-// The following provides a "poor man's TLV" implementation, where we use the
-// shadow memory of the pointer returned by pthread_self() to store a pointer to
-// the ThreadState object. The main thread's ThreadState is stored separately
-// in a static variable, because we need to access it even before the
-// shadow memory is set up.
-static uptr main_thread_identity = 0;
-ALIGNED(64) static char main_thread_state[sizeof(ThreadState)];
-
-ThreadState **cur_thread_location() {
- ThreadState **thread_identity = (ThreadState **)pthread_self();
- return ((uptr)thread_identity == main_thread_identity) ? nullptr
- : thread_identity;
-}
-
-ThreadState *cur_thread() {
- ThreadState **thr_state_loc = cur_thread_location();
- if (thr_state_loc == nullptr || main_thread_identity == 0) {
- return (ThreadState *)&main_thread_state;
- }
- ThreadState **fake_tls = (ThreadState **)MemToShadow((uptr)thr_state_loc);
- ThreadState *thr = (ThreadState *)SignalSafeGetOrAllocate(
- (uptr *)fake_tls, sizeof(ThreadState));
- return thr;
-}
-
-// TODO(kuba.brecka): This is not async-signal-safe. In particular, we call
-// munmap first and then clear `fake_tls`; if we receive a signal in between,
-// handler will try to access the unmapped ThreadState.
-void cur_thread_finalize() {
- ThreadState **thr_state_loc = cur_thread_location();
- if (thr_state_loc == nullptr) {
- // Calling dispatch_main() or xpc_main() actually invokes pthread_exit to
- // exit the main thread. Let's keep the main thread's ThreadState.
- return;
- }
- ThreadState **fake_tls = (ThreadState **)MemToShadow((uptr)thr_state_loc);
- internal_munmap(*fake_tls, sizeof(ThreadState));
- *fake_tls = nullptr;
-}
-#endif
-
-void FlushShadowMemory() {
-}
-
-static void RegionMemUsage(uptr start, uptr end, uptr *res, uptr *dirty) {
- vm_address_t address = start;
- vm_address_t end_address = end;
- uptr resident_pages = 0;
- uptr dirty_pages = 0;
- while (address < end_address) {
- vm_size_t vm_region_size;
- mach_msg_type_number_t count = VM_REGION_EXTENDED_INFO_COUNT;
- vm_region_extended_info_data_t vm_region_info;
- mach_port_t object_name;
- kern_return_t ret = vm_region_64(
- mach_task_self(), &address, &vm_region_size, VM_REGION_EXTENDED_INFO,
- (vm_region_info_t)&vm_region_info, &count, &object_name);
- if (ret != KERN_SUCCESS) break;
-
- resident_pages += vm_region_info.pages_resident;
- dirty_pages += vm_region_info.pages_dirtied;
-
- address += vm_region_size;
- }
- *res = resident_pages * GetPageSizeCached();
- *dirty = dirty_pages * GetPageSizeCached();
-}
-
-void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
- uptr shadow_res, shadow_dirty;
- uptr meta_res, meta_dirty;
- uptr trace_res, trace_dirty;
- RegionMemUsage(ShadowBeg(), ShadowEnd(), &shadow_res, &shadow_dirty);
- RegionMemUsage(MetaShadowBeg(), MetaShadowEnd(), &meta_res, &meta_dirty);
- RegionMemUsage(TraceMemBeg(), TraceMemEnd(), &trace_res, &trace_dirty);
-
-#if !SANITIZER_GO
- uptr low_res, low_dirty;
- uptr high_res, high_dirty;
- uptr heap_res, heap_dirty;
- RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &low_res, &low_dirty);
- RegionMemUsage(HiAppMemBeg(), HiAppMemEnd(), &high_res, &high_dirty);
- RegionMemUsage(HeapMemBeg(), HeapMemEnd(), &heap_res, &heap_dirty);
-#else // !SANITIZER_GO
- uptr app_res, app_dirty;
- RegionMemUsage(AppMemBeg(), AppMemEnd(), &app_res, &app_dirty);
-#endif
-
- StackDepotStats *stacks = StackDepotGetStats();
- internal_snprintf(buf, buf_size,
- "shadow (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
- "meta (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
- "traces (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
-#if !SANITIZER_GO
- "low app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
- "high app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
- "heap (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
-#else // !SANITIZER_GO
- "app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
-#endif
- "stacks: %zd unique IDs, %zd kB allocated\n"
- "threads: %zd total, %zd live\n"
- "------------------------------\n",
- ShadowBeg(), ShadowEnd(), shadow_res / 1024, shadow_dirty / 1024,
- MetaShadowBeg(), MetaShadowEnd(), meta_res / 1024, meta_dirty / 1024,
- TraceMemBeg(), TraceMemEnd(), trace_res / 1024, trace_dirty / 1024,
-#if !SANITIZER_GO
- LoAppMemBeg(), LoAppMemEnd(), low_res / 1024, low_dirty / 1024,
- HiAppMemBeg(), HiAppMemEnd(), high_res / 1024, high_dirty / 1024,
- HeapMemBeg(), HeapMemEnd(), heap_res / 1024, heap_dirty / 1024,
-#else // !SANITIZER_GO
- AppMemBeg(), AppMemEnd(), app_res / 1024, app_dirty / 1024,
-#endif
- stacks->n_uniq_ids, stacks->allocated / 1024,
- nthread, nlive);
-}
-
-#if !SANITIZER_GO
-void InitializeShadowMemoryPlatform() { }
-
-// On OS X, GCD worker threads are created without a call to pthread_create. We
-// need to properly register these threads with ThreadCreate and ThreadStart.
-// These threads don't have a parent thread, as they are created "spuriously".
-// We're using a libpthread API that notifies us about a newly created thread.
-// The `thread == pthread_self()` check indicates this is actually a worker
-// thread. If it's just a regular thread, this hook is called on the parent
-// thread.
-typedef void (*pthread_introspection_hook_t)(unsigned int event,
- pthread_t thread, void *addr,
- size_t size);
-extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
- pthread_introspection_hook_t hook);
-static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
-static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE = 3;
-static pthread_introspection_hook_t prev_pthread_introspection_hook;
-static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
- void *addr, size_t size) {
- if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
- if (thread == pthread_self()) {
- // The current thread is a newly created GCD worker thread.
- ThreadState *thr = cur_thread();
- Processor *proc = ProcCreate();
- ProcWire(proc, thr);
- ThreadState *parent_thread_state = nullptr; // No parent.
- int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
- CHECK_NE(tid, 0);
- ThreadStart(thr, tid, GetTid(), /*workerthread*/ true);
- }
- } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) {
- if (thread == pthread_self()) {
- ThreadState *thr = cur_thread();
- if (thr->tctx) {
- DestroyThreadState();
- }
- }
- }
-
- if (prev_pthread_introspection_hook != nullptr)
- prev_pthread_introspection_hook(event, thread, addr, size);
-}
-#endif
-
-void InitializePlatformEarly() {
-#if defined(__aarch64__)
- uptr max_vm = GetMaxUserVirtualAddress() + 1;
- if (max_vm != Mapping::kHiAppMemEnd) {
- Printf("ThreadSanitizer: unsupported vm address limit %p, expected %p.\n",
- max_vm, Mapping::kHiAppMemEnd);
- Die();
- }
-#endif
-}
-
-static const uptr kPthreadSetjmpXorKeySlot = 0x7;
-extern "C" uptr __tsan_darwin_setjmp_xor_key = 0;
-
-void InitializePlatform() {
- DisableCoreDumperIfNecessary();
-#if !SANITIZER_GO
- CheckAndProtect();
-
- CHECK_EQ(main_thread_identity, 0);
- main_thread_identity = (uptr)pthread_self();
-
- prev_pthread_introspection_hook =
- pthread_introspection_hook_install(&my_pthread_introspection_hook);
-#endif
-
- if (GetMacosVersion() >= MACOS_VERSION_MOJAVE) {
- __tsan_darwin_setjmp_xor_key =
- (uptr)pthread_getspecific(kPthreadSetjmpXorKeySlot);
- }
-}
-
-#if !SANITIZER_GO
-void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
- // The pointer to the ThreadState object is stored in the shadow memory
- // of the tls.
- uptr tls_end = tls_addr + tls_size;
- ThreadState **thr_state_loc = cur_thread_location();
- if (thr_state_loc == nullptr) {
- MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, tls_size);
- } else {
- uptr thr_state_start = (uptr)thr_state_loc;
- uptr thr_state_end = thr_state_start + sizeof(uptr);
- CHECK_GE(thr_state_start, tls_addr);
- CHECK_LE(thr_state_start, tls_addr + tls_size);
- CHECK_GE(thr_state_end, tls_addr);
- CHECK_LE(thr_state_end, tls_addr + tls_size);
- MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr,
- thr_state_start - tls_addr);
- MemoryRangeImitateWrite(thr, /*pc=*/2, thr_state_end,
- tls_end - thr_state_end);
- }
-}
-#endif
-
-#if !SANITIZER_GO
-// Note: this function runs with async signals enabled,
-// so it must not touch any tsan state.
-int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
- void *abstime), void *c, void *m, void *abstime,
- void(*cleanup)(void *arg), void *arg) {
- // pthread_cleanup_push/pop are hardcore macros mess.
- // We can't intercept nor call them w/o including pthread.h.
- int res;
- pthread_cleanup_push(cleanup, arg);
- res = fn(c, m, abstime);
- pthread_cleanup_pop(0);
- return res;
-}
-#endif
-
-} // namespace __tsan
-
-#endif // SANITIZER_MAC
--- /dev/null
+//===-- tsan_platform_mac.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Mac-specific code.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_MAC
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_posix.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+
+#include <mach/mach.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sched.h>
+
+namespace __tsan {
+
+#if !SANITIZER_GO
+static void *SignalSafeGetOrAllocate(uptr *dst, uptr size) {
+ atomic_uintptr_t *a = (atomic_uintptr_t *)dst;
+ void *val = (void *)atomic_load_relaxed(a);
+ atomic_signal_fence(memory_order_acquire); // Turns the previous load into
+ // acquire wrt signals.
+ if (UNLIKELY(val == nullptr)) {
+ val = (void *)internal_mmap(nullptr, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0);
+ CHECK(val);
+ void *cmp = nullptr;
+ if (!atomic_compare_exchange_strong(a, (uintptr_t *)&cmp, (uintptr_t)val,
+ memory_order_acq_rel)) {
+ internal_munmap(val, size);
+ val = cmp;
+ }
+ }
+ return val;
+}
+
+// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is
+// problematic, because there are several places where interceptors are called
+// when TLVs are not accessible (early process startup, thread cleanup, ...).
+// The following provides a "poor man's TLV" implementation, where we use the
+// shadow memory of the pointer returned by pthread_self() to store a pointer to
+// the ThreadState object. The main thread's ThreadState is stored separately
+// in a static variable, because we need to access it even before the
+// shadow memory is set up.
+static uptr main_thread_identity = 0;
+ALIGNED(64) static char main_thread_state[sizeof(ThreadState)];
+static ThreadState *main_thread_state_loc = (ThreadState *)main_thread_state;
+
+static ThreadState **cur_thread_location() {
+ uptr thread_identity = (uptr)pthread_self();
+ if (thread_identity == main_thread_identity || main_thread_identity == 0)
+ return &main_thread_state_loc;
+ return (ThreadState **)MemToShadow(thread_identity);
+}
+
+ThreadState *cur_thread() {
+ return (ThreadState *)SignalSafeGetOrAllocate(
+ (uptr *)cur_thread_location(), sizeof(ThreadState));
+}
+
+void set_cur_thread(ThreadState *thr) {
+ *cur_thread_location() = thr;
+}
+
+// TODO(kuba.brecka): This is not async-signal-safe. In particular, we call
+// munmap first and then clear `fake_tls`; if we receive a signal in between,
+// handler will try to access the unmapped ThreadState.
+void cur_thread_finalize() {
+ ThreadState **thr_state_loc = cur_thread_location();
+ if (thr_state_loc == &main_thread_state_loc) {
+ // Calling dispatch_main() or xpc_main() actually invokes pthread_exit to
+ // exit the main thread. Let's keep the main thread's ThreadState.
+ return;
+ }
+ internal_munmap(*thr_state_loc, sizeof(ThreadState));
+ *thr_state_loc = nullptr;
+}
+#endif
+
+void FlushShadowMemory() {
+}
+
+static void RegionMemUsage(uptr start, uptr end, uptr *res, uptr *dirty) {
+ vm_address_t address = start;
+ vm_address_t end_address = end;
+ uptr resident_pages = 0;
+ uptr dirty_pages = 0;
+ while (address < end_address) {
+ vm_size_t vm_region_size;
+ mach_msg_type_number_t count = VM_REGION_EXTENDED_INFO_COUNT;
+ vm_region_extended_info_data_t vm_region_info;
+ mach_port_t object_name;
+ kern_return_t ret = vm_region_64(
+ mach_task_self(), &address, &vm_region_size, VM_REGION_EXTENDED_INFO,
+ (vm_region_info_t)&vm_region_info, &count, &object_name);
+ if (ret != KERN_SUCCESS) break;
+
+ resident_pages += vm_region_info.pages_resident;
+ dirty_pages += vm_region_info.pages_dirtied;
+
+ address += vm_region_size;
+ }
+ *res = resident_pages * GetPageSizeCached();
+ *dirty = dirty_pages * GetPageSizeCached();
+}
+
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
+ uptr shadow_res, shadow_dirty;
+ uptr meta_res, meta_dirty;
+ uptr trace_res, trace_dirty;
+ RegionMemUsage(ShadowBeg(), ShadowEnd(), &shadow_res, &shadow_dirty);
+ RegionMemUsage(MetaShadowBeg(), MetaShadowEnd(), &meta_res, &meta_dirty);
+ RegionMemUsage(TraceMemBeg(), TraceMemEnd(), &trace_res, &trace_dirty);
+
+#if !SANITIZER_GO
+ uptr low_res, low_dirty;
+ uptr high_res, high_dirty;
+ uptr heap_res, heap_dirty;
+ RegionMemUsage(LoAppMemBeg(), LoAppMemEnd(), &low_res, &low_dirty);
+ RegionMemUsage(HiAppMemBeg(), HiAppMemEnd(), &high_res, &high_dirty);
+ RegionMemUsage(HeapMemBeg(), HeapMemEnd(), &heap_res, &heap_dirty);
+#else // !SANITIZER_GO
+ uptr app_res, app_dirty;
+ RegionMemUsage(AppMemBeg(), AppMemEnd(), &app_res, &app_dirty);
+#endif
+
+ StackDepotStats *stacks = StackDepotGetStats();
+ internal_snprintf(buf, buf_size,
+ "shadow (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+ "meta (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+ "traces (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+#if !SANITIZER_GO
+ "low app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+ "high app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+ "heap (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+#else // !SANITIZER_GO
+ "app (0x%016zx-0x%016zx): resident %zd kB, dirty %zd kB\n"
+#endif
+ "stacks: %zd unique IDs, %zd kB allocated\n"
+ "threads: %zd total, %zd live\n"
+ "------------------------------\n",
+ ShadowBeg(), ShadowEnd(), shadow_res / 1024, shadow_dirty / 1024,
+ MetaShadowBeg(), MetaShadowEnd(), meta_res / 1024, meta_dirty / 1024,
+ TraceMemBeg(), TraceMemEnd(), trace_res / 1024, trace_dirty / 1024,
+#if !SANITIZER_GO
+ LoAppMemBeg(), LoAppMemEnd(), low_res / 1024, low_dirty / 1024,
+ HiAppMemBeg(), HiAppMemEnd(), high_res / 1024, high_dirty / 1024,
+ HeapMemBeg(), HeapMemEnd(), heap_res / 1024, heap_dirty / 1024,
+#else // !SANITIZER_GO
+ AppMemBeg(), AppMemEnd(), app_res / 1024, app_dirty / 1024,
+#endif
+ stacks->n_uniq_ids, stacks->allocated / 1024,
+ nthread, nlive);
+}
+
+#if !SANITIZER_GO
+void InitializeShadowMemoryPlatform() { }
+
+// On OS X, GCD worker threads are created without a call to pthread_create. We
+// need to properly register these threads with ThreadCreate and ThreadStart.
+// These threads don't have a parent thread, as they are created "spuriously".
+// We're using a libpthread API that notifies us about a newly created thread.
+// The `thread == pthread_self()` check indicates this is actually a worker
+// thread. If it's just a regular thread, this hook is called on the parent
+// thread.
+typedef void (*pthread_introspection_hook_t)(unsigned int event,
+ pthread_t thread, void *addr,
+ size_t size);
+extern "C" pthread_introspection_hook_t pthread_introspection_hook_install(
+ pthread_introspection_hook_t hook);
+static const uptr PTHREAD_INTROSPECTION_THREAD_CREATE = 1;
+static const uptr PTHREAD_INTROSPECTION_THREAD_TERMINATE = 3;
+static pthread_introspection_hook_t prev_pthread_introspection_hook;
+static void my_pthread_introspection_hook(unsigned int event, pthread_t thread,
+ void *addr, size_t size) {
+ if (event == PTHREAD_INTROSPECTION_THREAD_CREATE) {
+ if (thread == pthread_self()) {
+ // The current thread is a newly created GCD worker thread.
+ ThreadState *thr = cur_thread();
+ Processor *proc = ProcCreate();
+ ProcWire(proc, thr);
+ ThreadState *parent_thread_state = nullptr; // No parent.
+ int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true);
+ CHECK_NE(tid, 0);
+ ThreadStart(thr, tid, GetTid(), ThreadType::Worker);
+ }
+ } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) {
+ if (thread == pthread_self()) {
+ ThreadState *thr = cur_thread();
+ if (thr->tctx) {
+ DestroyThreadState();
+ }
+ }
+ }
+
+ if (prev_pthread_introspection_hook != nullptr)
+ prev_pthread_introspection_hook(event, thread, addr, size);
+}
+#endif
+
+void InitializePlatformEarly() {
+#if defined(__aarch64__)
+ uptr max_vm = GetMaxUserVirtualAddress() + 1;
+ if (max_vm != Mapping::kHiAppMemEnd) {
+ Printf("ThreadSanitizer: unsupported vm address limit %p, expected %p.\n",
+ max_vm, Mapping::kHiAppMemEnd);
+ Die();
+ }
+#endif
+}
+
+static uptr longjmp_xor_key = 0;
+
+void InitializePlatform() {
+ DisableCoreDumperIfNecessary();
+#if !SANITIZER_GO
+ CheckAndProtect();
+
+ CHECK_EQ(main_thread_identity, 0);
+ main_thread_identity = (uptr)pthread_self();
+
+ prev_pthread_introspection_hook =
+ pthread_introspection_hook_install(&my_pthread_introspection_hook);
+#endif
+
+ if (GetMacosVersion() >= MACOS_VERSION_MOJAVE) {
+ // Libsystem currently uses a process-global key; this might change.
+ const unsigned kTLSLongjmpXorKeySlot = 0x7;
+ longjmp_xor_key = (uptr)pthread_getspecific(kTLSLongjmpXorKeySlot);
+ }
+}
+
+#ifdef __aarch64__
+# define LONG_JMP_SP_ENV_SLOT \
+ ((GetMacosVersion() >= MACOS_VERSION_MOJAVE) ? 12 : 13)
+#else
+# define LONG_JMP_SP_ENV_SLOT 2
+#endif
+
+uptr ExtractLongJmpSp(uptr *env) {
+ uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT];
+ uptr sp = mangled_sp ^ longjmp_xor_key;
+ return sp;
+}
+
+#if !SANITIZER_GO
+void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) {
+ // The pointer to the ThreadState object is stored in the shadow memory
+ // of the tls.
+ uptr tls_end = tls_addr + tls_size;
+ uptr thread_identity = (uptr)pthread_self();
+ if (thread_identity == main_thread_identity) {
+ MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr, tls_size);
+ } else {
+ uptr thr_state_start = thread_identity;
+ uptr thr_state_end = thr_state_start + sizeof(uptr);
+ CHECK_GE(thr_state_start, tls_addr);
+ CHECK_LE(thr_state_start, tls_addr + tls_size);
+ CHECK_GE(thr_state_end, tls_addr);
+ CHECK_LE(thr_state_end, tls_addr + tls_size);
+ MemoryRangeImitateWrite(thr, /*pc=*/2, tls_addr,
+ thr_state_start - tls_addr);
+ MemoryRangeImitateWrite(thr, /*pc=*/2, thr_state_end,
+ tls_end - thr_state_end);
+ }
+}
+#endif
+
+#if !SANITIZER_GO
+// Note: this function runs with async signals enabled,
+// so it must not touch any tsan state.
+int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
+ void *abstime), void *c, void *m, void *abstime,
+ void(*cleanup)(void *arg), void *arg) {
+ // pthread_cleanup_push/pop are hardcore macros mess.
+ // We can't intercept nor call them w/o including pthread.h.
+ int res;
+ pthread_cleanup_push(cleanup, arg);
+ res = fn(c, m, abstime);
+ pthread_cleanup_pop(0);
+ return res;
+}
+#endif
+
+} // namespace __tsan
+
+#endif // SANITIZER_MAC
+++ /dev/null
-//===-- tsan_platform_posix.cc --------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// POSIX-specific code.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_POSIX
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_errno.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_procmaps.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-
-namespace __tsan {
-
-static const char kShadowMemoryMappingWarning[] =
- "FATAL: %s can not madvise shadow region [%zx, %zx] with %s (errno: %d)\n";
-static const char kShadowMemoryMappingHint[] =
- "HINT: if %s is not supported in your environment, you may set "
- "TSAN_OPTIONS=%s=0\n";
-
-static void NoHugePagesInShadow(uptr addr, uptr size) {
- if (common_flags()->no_huge_pages_for_shadow)
- if (!NoHugePagesInRegion(addr, size)) {
- Printf(kShadowMemoryMappingWarning, SanitizerToolName, addr, addr + size,
- "MADV_NOHUGEPAGE", errno);
- Printf(kShadowMemoryMappingHint, "MADV_NOHUGEPAGE",
- "no_huge_pages_for_shadow");
- Die();
- }
-}
-
-static void DontDumpShadow(uptr addr, uptr size) {
- if (common_flags()->use_madv_dontdump)
- if (!DontDumpShadowMemory(addr, size)) {
- Printf(kShadowMemoryMappingWarning, SanitizerToolName, addr, addr + size,
- "MADV_DONTDUMP", errno);
- Printf(kShadowMemoryMappingHint, "MADV_DONTDUMP", "use_madv_dontdump");
- Die();
- }
-}
-
-#if !SANITIZER_GO
-void InitializeShadowMemory() {
- // Map memory shadow.
- if (!MmapFixedNoReserve(ShadowBeg(), ShadowEnd() - ShadowBeg(), "shadow")) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
- Die();
- }
- // This memory range is used for thread stacks and large user mmaps.
- // Frequently a thread uses only a small part of stack and similarly
- // a program uses a small part of large mmap. On some programs
- // we see 20% memory usage reduction without huge pages for this range.
- // FIXME: don't use constants here.
-#if defined(__x86_64__)
- const uptr kMadviseRangeBeg = 0x7f0000000000ull;
- const uptr kMadviseRangeSize = 0x010000000000ull;
-#elif defined(__mips64)
- const uptr kMadviseRangeBeg = 0xff00000000ull;
- const uptr kMadviseRangeSize = 0x0100000000ull;
-#elif defined(__aarch64__) && defined(__APPLE__)
- uptr kMadviseRangeBeg = LoAppMemBeg();
- uptr kMadviseRangeSize = LoAppMemEnd() - LoAppMemBeg();
-#elif defined(__aarch64__)
- uptr kMadviseRangeBeg = 0;
- uptr kMadviseRangeSize = 0;
- if (vmaSize == 39) {
- kMadviseRangeBeg = 0x7d00000000ull;
- kMadviseRangeSize = 0x0300000000ull;
- } else if (vmaSize == 42) {
- kMadviseRangeBeg = 0x3f000000000ull;
- kMadviseRangeSize = 0x01000000000ull;
- } else {
- DCHECK(0);
- }
-#elif defined(__powerpc64__)
- uptr kMadviseRangeBeg = 0;
- uptr kMadviseRangeSize = 0;
- if (vmaSize == 44) {
- kMadviseRangeBeg = 0x0f60000000ull;
- kMadviseRangeSize = 0x0010000000ull;
- } else if (vmaSize == 46) {
- kMadviseRangeBeg = 0x3f0000000000ull;
- kMadviseRangeSize = 0x010000000000ull;
- } else {
- DCHECK(0);
- }
-#endif
- NoHugePagesInShadow(MemToShadow(kMadviseRangeBeg),
- kMadviseRangeSize * kShadowMultiplier);
- DontDumpShadow(ShadowBeg(), ShadowEnd() - ShadowBeg());
- DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
- ShadowBeg(), ShadowEnd(),
- (ShadowEnd() - ShadowBeg()) >> 30);
-
- // Map meta shadow.
- const uptr meta = MetaShadowBeg();
- const uptr meta_size = MetaShadowEnd() - meta;
- if (!MmapFixedNoReserve(meta, meta_size, "meta shadow")) {
- Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
- Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
- Die();
- }
- NoHugePagesInShadow(meta, meta_size);
- DontDumpShadow(meta, meta_size);
- DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
- meta, meta + meta_size, meta_size >> 30);
-
- InitializeShadowMemoryPlatform();
-}
-
-static void ProtectRange(uptr beg, uptr end) {
- CHECK_LE(beg, end);
- if (beg == end)
- return;
- if (beg != (uptr)MmapFixedNoAccess(beg, end - beg)) {
- Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
- Printf("FATAL: Make sure you are not using unlimited stack\n");
- Die();
- }
-}
-
-void CheckAndProtect() {
- // Ensure that the binary is indeed compiled with -pie.
- MemoryMappingLayout proc_maps(true);
- MemoryMappedSegment segment;
- while (proc_maps.Next(&segment)) {
- if (IsAppMem(segment.start)) continue;
- if (segment.start >= HeapMemEnd() && segment.start < HeapEnd()) continue;
- if (segment.protection == 0) // Zero page or mprotected.
- continue;
- if (segment.start >= VdsoBeg()) // vdso
- break;
- Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n",
- segment.start, segment.end);
- Die();
- }
-
-#if defined(__aarch64__) && defined(__APPLE__)
- ProtectRange(HeapMemEnd(), ShadowBeg());
- ProtectRange(ShadowEnd(), MetaShadowBeg());
- ProtectRange(MetaShadowEnd(), TraceMemBeg());
-#else
- ProtectRange(LoAppMemEnd(), ShadowBeg());
- ProtectRange(ShadowEnd(), MetaShadowBeg());
-#ifdef TSAN_MID_APP_RANGE
- ProtectRange(MetaShadowEnd(), MidAppMemBeg());
- ProtectRange(MidAppMemEnd(), TraceMemBeg());
-#else
- ProtectRange(MetaShadowEnd(), TraceMemBeg());
-#endif
- // Memory for traces is mapped lazily in MapThreadTrace.
- // Protect the whole range for now, so that user does not map something here.
- ProtectRange(TraceMemBeg(), TraceMemEnd());
- ProtectRange(TraceMemEnd(), HeapMemBeg());
- ProtectRange(HeapEnd(), HiAppMemBeg());
-#endif
-}
-#endif
-
-} // namespace __tsan
-
-#endif // SANITIZER_POSIX
--- /dev/null
+//===-- tsan_platform_posix.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// POSIX-specific code.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_POSIX
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_errno.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_procmaps.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+static const char kShadowMemoryMappingWarning[] =
+ "FATAL: %s can not madvise shadow region [%zx, %zx] with %s (errno: %d)\n";
+static const char kShadowMemoryMappingHint[] =
+ "HINT: if %s is not supported in your environment, you may set "
+ "TSAN_OPTIONS=%s=0\n";
+
+static void NoHugePagesInShadow(uptr addr, uptr size) {
+ SetShadowRegionHugePageMode(addr, size);
+}
+
+static void DontDumpShadow(uptr addr, uptr size) {
+ if (common_flags()->use_madv_dontdump)
+ if (!DontDumpShadowMemory(addr, size)) {
+ Printf(kShadowMemoryMappingWarning, SanitizerToolName, addr, addr + size,
+ "MADV_DONTDUMP", errno);
+ Printf(kShadowMemoryMappingHint, "MADV_DONTDUMP", "use_madv_dontdump");
+ Die();
+ }
+}
+
+#if !SANITIZER_GO
+void InitializeShadowMemory() {
+ // Map memory shadow.
+ if (!MmapFixedNoReserve(ShadowBeg(), ShadowEnd() - ShadowBeg(), "shadow")) {
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
+ Die();
+ }
+ // This memory range is used for thread stacks and large user mmaps.
+ // Frequently a thread uses only a small part of stack and similarly
+ // a program uses a small part of large mmap. On some programs
+ // we see 20% memory usage reduction without huge pages for this range.
+ // FIXME: don't use constants here.
+#if defined(__x86_64__)
+ const uptr kMadviseRangeBeg = 0x7f0000000000ull;
+ const uptr kMadviseRangeSize = 0x010000000000ull;
+#elif defined(__mips64)
+ const uptr kMadviseRangeBeg = 0xff00000000ull;
+ const uptr kMadviseRangeSize = 0x0100000000ull;
+#elif defined(__aarch64__) && defined(__APPLE__)
+ uptr kMadviseRangeBeg = LoAppMemBeg();
+ uptr kMadviseRangeSize = LoAppMemEnd() - LoAppMemBeg();
+#elif defined(__aarch64__)
+ uptr kMadviseRangeBeg = 0;
+ uptr kMadviseRangeSize = 0;
+ if (vmaSize == 39) {
+ kMadviseRangeBeg = 0x7d00000000ull;
+ kMadviseRangeSize = 0x0300000000ull;
+ } else if (vmaSize == 42) {
+ kMadviseRangeBeg = 0x3f000000000ull;
+ kMadviseRangeSize = 0x01000000000ull;
+ } else {
+ DCHECK(0);
+ }
+#elif defined(__powerpc64__)
+ uptr kMadviseRangeBeg = 0;
+ uptr kMadviseRangeSize = 0;
+ if (vmaSize == 44) {
+ kMadviseRangeBeg = 0x0f60000000ull;
+ kMadviseRangeSize = 0x0010000000ull;
+ } else if (vmaSize == 46) {
+ kMadviseRangeBeg = 0x3f0000000000ull;
+ kMadviseRangeSize = 0x010000000000ull;
+ } else {
+ DCHECK(0);
+ }
+#endif
+ NoHugePagesInShadow(MemToShadow(kMadviseRangeBeg),
+ kMadviseRangeSize * kShadowMultiplier);
+ DontDumpShadow(ShadowBeg(), ShadowEnd() - ShadowBeg());
+ DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
+ ShadowBeg(), ShadowEnd(),
+ (ShadowEnd() - ShadowBeg()) >> 30);
+
+ // Map meta shadow.
+ const uptr meta = MetaShadowBeg();
+ const uptr meta_size = MetaShadowEnd() - meta;
+ if (!MmapFixedNoReserve(meta, meta_size, "meta shadow")) {
+ Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
+ Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n");
+ Die();
+ }
+ NoHugePagesInShadow(meta, meta_size);
+ DontDumpShadow(meta, meta_size);
+ DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
+ meta, meta + meta_size, meta_size >> 30);
+
+ InitializeShadowMemoryPlatform();
+}
+
+static void ProtectRange(uptr beg, uptr end) {
+ CHECK_LE(beg, end);
+ if (beg == end)
+ return;
+ if (beg != (uptr)MmapFixedNoAccess(beg, end - beg)) {
+ Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end);
+ Printf("FATAL: Make sure you are not using unlimited stack\n");
+ Die();
+ }
+}
+
+void CheckAndProtect() {
+ // Ensure that the binary is indeed compiled with -pie.
+ MemoryMappingLayout proc_maps(true);
+ MemoryMappedSegment segment;
+ while (proc_maps.Next(&segment)) {
+ if (IsAppMem(segment.start)) continue;
+ if (segment.start >= HeapMemEnd() && segment.start < HeapEnd()) continue;
+ if (segment.protection == 0) // Zero page or mprotected.
+ continue;
+ if (segment.start >= VdsoBeg()) // vdso
+ break;
+ Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n",
+ segment.start, segment.end);
+ Die();
+ }
+
+#if defined(__aarch64__) && defined(__APPLE__)
+ ProtectRange(HeapMemEnd(), ShadowBeg());
+ ProtectRange(ShadowEnd(), MetaShadowBeg());
+ ProtectRange(MetaShadowEnd(), TraceMemBeg());
+#else
+ ProtectRange(LoAppMemEnd(), ShadowBeg());
+ ProtectRange(ShadowEnd(), MetaShadowBeg());
+#ifdef TSAN_MID_APP_RANGE
+ ProtectRange(MetaShadowEnd(), MidAppMemBeg());
+ ProtectRange(MidAppMemEnd(), TraceMemBeg());
+#else
+ ProtectRange(MetaShadowEnd(), TraceMemBeg());
+#endif
+ // Memory for traces is mapped lazily in MapThreadTrace.
+ // Protect the whole range for now, so that user does not map something here.
+ ProtectRange(TraceMemBeg(), TraceMemEnd());
+ ProtectRange(TraceMemEnd(), HeapMemBeg());
+ ProtectRange(HeapEnd(), HiAppMemBeg());
+#endif
+}
+#endif
+
+} // namespace __tsan
+
+#endif // SANITIZER_POSIX
+++ /dev/null
-//===-- tsan_platform_windows.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-// Windows-specific code.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#if SANITIZER_WINDOWS
-
-#include "tsan_platform.h"
-
-#include <stdlib.h>
-
-namespace __tsan {
-
-void FlushShadowMemory() {
-}
-
-void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
-}
-
-void InitializePlatformEarly() {
-}
-
-void InitializePlatform() {
-}
-
-} // namespace __tsan
-
-#endif // SANITIZER_WINDOWS
--- /dev/null
+//===-- tsan_platform_windows.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Windows-specific code.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#if SANITIZER_WINDOWS
+
+#include "tsan_platform.h"
+
+#include <stdlib.h>
+
+namespace __tsan {
+
+void FlushShadowMemory() {
+}
+
+void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
+}
+
+void InitializePlatformEarly() {
+}
+
+void InitializePlatform() {
+}
+
+} // namespace __tsan
+
+#endif // SANITIZER_WINDOWS
+++ /dev/null
-//===-- tsan_preinit.cc ---------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer.
-//
-// Call __tsan_init at the very early stage of process startup.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "tsan_interface.h"
-
-#if SANITIZER_CAN_USE_PREINIT_ARRAY
-
-// The symbol is called __local_tsan_preinit, because it's not intended to be
-// exported.
-// This code linked into the main executable when -fsanitize=thread is in
-// the link flags. It can only use exported interface functions.
-__attribute__((section(".preinit_array"), used))
-void (*__local_tsan_preinit)(void) = __tsan_init;
-
-#endif
--- /dev/null
+//===-- tsan_preinit.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer.
+//
+// Call __tsan_init at the very early stage of process startup.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "tsan_interface.h"
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+
+// The symbol is called __local_tsan_preinit, because it's not intended to be
+// exported.
+// This code linked into the main executable when -fsanitize=thread is in
+// the link flags. It can only use exported interface functions.
+__attribute__((section(".preinit_array"), used))
+void (*__local_tsan_preinit)(void) = __tsan_init;
+
+#endif
+++ /dev/null
-//===-- tsan_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 ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_report.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-#include "sanitizer_common/sanitizer_file.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_report_decorator.h"
-#include "sanitizer_common/sanitizer_stacktrace_printer.h"
-
-namespace __tsan {
-
-ReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
-
-ReportStack *ReportStack::New() {
- void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
- return new(mem) ReportStack();
-}
-
-ReportLocation::ReportLocation(ReportLocationType type)
- : type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
- fd(0), suppressable(false), stack(nullptr) {}
-
-ReportLocation *ReportLocation::New(ReportLocationType type) {
- void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
- return new(mem) ReportLocation(type);
-}
-
-class Decorator: public __sanitizer::SanitizerCommonDecorator {
- public:
- Decorator() : SanitizerCommonDecorator() { }
- const char *Access() { return Blue(); }
- const char *ThreadDescription() { return Cyan(); }
- const char *Location() { return Green(); }
- const char *Sleep() { return Yellow(); }
- const char *Mutex() { return Magenta(); }
-};
-
-ReportDesc::ReportDesc()
- : tag(kExternalTagNone)
- , stacks()
- , mops()
- , locs()
- , mutexes()
- , threads()
- , unique_tids()
- , sleep()
- , count() {
-}
-
-ReportMop::ReportMop()
- : mset() {
-}
-
-ReportDesc::~ReportDesc() {
- // FIXME(dvyukov): it must be leaking a lot of memory.
-}
-
-#if !SANITIZER_GO
-
-const int kThreadBufSize = 32;
-const char *thread_name(char *buf, int tid) {
- if (tid == 0)
- return "main thread";
- internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
- return buf;
-}
-
-static const char *ReportTypeString(ReportType typ, uptr tag) {
- if (typ == ReportTypeRace)
- return "data race";
- if (typ == ReportTypeVptrRace)
- return "data race on vptr (ctor/dtor vs virtual call)";
- if (typ == ReportTypeUseAfterFree)
- return "heap-use-after-free";
- if (typ == ReportTypeVptrUseAfterFree)
- return "heap-use-after-free (virtual call vs free)";
- if (typ == ReportTypeExternalRace) {
- const char *str = GetReportHeaderFromTag(tag);
- return str ? str : "race on external object";
- }
- if (typ == ReportTypeThreadLeak)
- return "thread leak";
- if (typ == ReportTypeMutexDestroyLocked)
- return "destroy of a locked mutex";
- if (typ == ReportTypeMutexDoubleLock)
- return "double lock of a mutex";
- if (typ == ReportTypeMutexInvalidAccess)
- return "use of an invalid mutex (e.g. uninitialized or destroyed)";
- if (typ == ReportTypeMutexBadUnlock)
- return "unlock of an unlocked mutex (or by a wrong thread)";
- if (typ == ReportTypeMutexBadReadLock)
- return "read lock of a write locked mutex";
- if (typ == ReportTypeMutexBadReadUnlock)
- return "read unlock of a write locked mutex";
- if (typ == ReportTypeSignalUnsafe)
- return "signal-unsafe call inside of a signal";
- if (typ == ReportTypeErrnoInSignal)
- return "signal handler spoils errno";
- if (typ == ReportTypeDeadlock)
- return "lock-order-inversion (potential deadlock)";
- return "";
-}
-
-#if SANITIZER_MAC
-static const char *const kInterposedFunctionPrefix = "wrap_";
-#else
-static const char *const kInterposedFunctionPrefix = "__interceptor_";
-#endif
-
-void PrintStack(const ReportStack *ent) {
- if (ent == 0 || ent->frames == 0) {
- Printf(" [failed to restore the stack]\n\n");
- return;
- }
- SymbolizedStack *frame = ent->frames;
- for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
- InternalScopedString res(2 * GetPageSizeCached());
- RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
- common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
- Printf("%s\n", res.data());
- }
- Printf("\n");
-}
-
-static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
- for (uptr i = 0; i < mset.Size(); i++) {
- if (i == 0)
- Printf(" (mutexes:");
- const ReportMopMutex m = mset[i];
- Printf(" %s M%llu", m.write ? "write" : "read", m.id);
- Printf(i == mset.Size() - 1 ? ")" : ",");
- }
-}
-
-static const char *MopDesc(bool first, bool write, bool atomic) {
- return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
- : (write ? "Previous atomic write" : "Previous atomic read"))
- : (first ? (write ? "Write" : "Read")
- : (write ? "Previous write" : "Previous read"));
-}
-
-static const char *ExternalMopDesc(bool first, bool write) {
- return first ? (write ? "Modifying" : "Read-only")
- : (write ? "Previous modifying" : "Previous read-only");
-}
-
-static void PrintMop(const ReportMop *mop, bool first) {
- Decorator d;
- char thrbuf[kThreadBufSize];
- Printf("%s", d.Access());
- if (mop->external_tag == kExternalTagNone) {
- Printf(" %s of size %d at %p by %s",
- MopDesc(first, mop->write, mop->atomic), mop->size,
- (void *)mop->addr, thread_name(thrbuf, mop->tid));
- } else {
- const char *object_type = GetObjectTypeFromTag(mop->external_tag);
- if (object_type == nullptr)
- object_type = "external object";
- Printf(" %s access of %s at %p by %s",
- ExternalMopDesc(first, mop->write), object_type,
- (void *)mop->addr, thread_name(thrbuf, mop->tid));
- }
- PrintMutexSet(mop->mset);
- Printf(":\n");
- Printf("%s", d.Default());
- PrintStack(mop->stack);
-}
-
-static void PrintLocation(const ReportLocation *loc) {
- Decorator d;
- char thrbuf[kThreadBufSize];
- bool print_stack = false;
- Printf("%s", d.Location());
- if (loc->type == ReportLocationGlobal) {
- const DataInfo &global = loc->global;
- if (global.size != 0)
- Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
- global.name, global.size, global.start,
- StripModuleName(global.module), global.module_offset);
- else
- Printf(" Location is global '%s' at %p (%s+%p)\n\n", global.name,
- global.start, StripModuleName(global.module),
- global.module_offset);
- } else if (loc->type == ReportLocationHeap) {
- char thrbuf[kThreadBufSize];
- const char *object_type = GetObjectTypeFromTag(loc->external_tag);
- if (!object_type) {
- Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
- loc->heap_chunk_size, loc->heap_chunk_start,
- thread_name(thrbuf, loc->tid));
- } else {
- Printf(" Location is %s of size %zu at %p allocated by %s:\n",
- object_type, loc->heap_chunk_size, loc->heap_chunk_start,
- thread_name(thrbuf, loc->tid));
- }
- print_stack = true;
- } else if (loc->type == ReportLocationStack) {
- Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
- } else if (loc->type == ReportLocationTLS) {
- Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
- } else if (loc->type == ReportLocationFD) {
- Printf(" Location is file descriptor %d created by %s at:\n",
- loc->fd, thread_name(thrbuf, loc->tid));
- print_stack = true;
- }
- Printf("%s", d.Default());
- if (print_stack)
- PrintStack(loc->stack);
-}
-
-static void PrintMutexShort(const ReportMutex *rm, const char *after) {
- Decorator d;
- Printf("%sM%zd%s%s", d.Mutex(), rm->id, d.Default(), after);
-}
-
-static void PrintMutexShortWithAddress(const ReportMutex *rm,
- const char *after) {
- Decorator d;
- Printf("%sM%zd (%p)%s%s", d.Mutex(), rm->id, rm->addr, d.Default(), after);
-}
-
-static void PrintMutex(const ReportMutex *rm) {
- Decorator d;
- if (rm->destroyed) {
- Printf("%s", d.Mutex());
- Printf(" Mutex M%llu is already destroyed.\n\n", rm->id);
- Printf("%s", d.Default());
- } else {
- Printf("%s", d.Mutex());
- Printf(" Mutex M%llu (%p) created at:\n", rm->id, rm->addr);
- Printf("%s", d.Default());
- PrintStack(rm->stack);
- }
-}
-
-static void PrintThread(const ReportThread *rt) {
- Decorator d;
- if (rt->id == 0) // Little sense in describing the main thread.
- return;
- Printf("%s", d.ThreadDescription());
- Printf(" Thread T%d", rt->id);
- if (rt->name && rt->name[0] != '\0')
- Printf(" '%s'", rt->name);
- char thrbuf[kThreadBufSize];
- const char *thread_status = rt->running ? "running" : "finished";
- if (rt->workerthread) {
- Printf(" (tid=%zu, %s) is a GCD worker thread\n", rt->os_id, thread_status);
- Printf("\n");
- Printf("%s", d.Default());
- return;
- }
- Printf(" (tid=%zu, %s) created by %s", rt->os_id, thread_status,
- thread_name(thrbuf, rt->parent_tid));
- if (rt->stack)
- Printf(" at:");
- Printf("\n");
- Printf("%s", d.Default());
- PrintStack(rt->stack);
-}
-
-static void PrintSleep(const ReportStack *s) {
- Decorator d;
- Printf("%s", d.Sleep());
- Printf(" As if synchronized via sleep:\n");
- Printf("%s", d.Default());
- PrintStack(s);
-}
-
-static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
- if (rep->mops.Size())
- return rep->mops[0]->stack;
- if (rep->stacks.Size())
- return rep->stacks[0];
- if (rep->mutexes.Size())
- return rep->mutexes[0]->stack;
- if (rep->threads.Size())
- return rep->threads[0]->stack;
- return 0;
-}
-
-static bool FrameIsInternal(const SymbolizedStack *frame) {
- if (frame == 0)
- return false;
- const char *file = frame->info.file;
- const char *module = frame->info.module;
- if (file != 0 &&
- (internal_strstr(file, "tsan_interceptors.cc") ||
- internal_strstr(file, "sanitizer_common_interceptors.inc") ||
- internal_strstr(file, "tsan_interface_")))
- return true;
- if (module != 0 && (internal_strstr(module, "libclang_rt.tsan_")))
- return true;
- return false;
-}
-
-static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
- while (FrameIsInternal(frames) && frames->next)
- frames = frames->next;
- return frames;
-}
-
-void PrintReport(const ReportDesc *rep) {
- Decorator d;
- Printf("==================\n");
- const char *rep_typ_str = ReportTypeString(rep->typ, rep->tag);
- Printf("%s", d.Warning());
- Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,
- (int)internal_getpid());
- Printf("%s", d.Default());
-
- if (rep->typ == ReportTypeDeadlock) {
- char thrbuf[kThreadBufSize];
- Printf(" Cycle in lock order graph: ");
- for (uptr i = 0; i < rep->mutexes.Size(); i++)
- PrintMutexShortWithAddress(rep->mutexes[i], " => ");
- PrintMutexShort(rep->mutexes[0], "\n\n");
- CHECK_GT(rep->mutexes.Size(), 0U);
- CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
- rep->stacks.Size());
- for (uptr i = 0; i < rep->mutexes.Size(); i++) {
- Printf(" Mutex ");
- PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
- " acquired here while holding mutex ");
- PrintMutexShort(rep->mutexes[i], " in ");
- Printf("%s", d.ThreadDescription());
- Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
- Printf("%s", d.Default());
- if (flags()->second_deadlock_stack) {
- PrintStack(rep->stacks[2*i]);
- Printf(" Mutex ");
- PrintMutexShort(rep->mutexes[i],
- " previously acquired by the same thread here:\n");
- PrintStack(rep->stacks[2*i+1]);
- } else {
- PrintStack(rep->stacks[i]);
- if (i == 0)
- Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
- "to get more informative warning message\n\n");
- }
- }
- } else {
- for (uptr i = 0; i < rep->stacks.Size(); i++) {
- if (i)
- Printf(" and:\n");
- PrintStack(rep->stacks[i]);
- }
- }
-
- for (uptr i = 0; i < rep->mops.Size(); i++)
- PrintMop(rep->mops[i], i == 0);
-
- if (rep->sleep)
- PrintSleep(rep->sleep);
-
- for (uptr i = 0; i < rep->locs.Size(); i++)
- PrintLocation(rep->locs[i]);
-
- if (rep->typ != ReportTypeDeadlock) {
- for (uptr i = 0; i < rep->mutexes.Size(); i++)
- PrintMutex(rep->mutexes[i]);
- }
-
- for (uptr i = 0; i < rep->threads.Size(); i++)
- PrintThread(rep->threads[i]);
-
- if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
- Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
-
- if (ReportStack *stack = ChooseSummaryStack(rep)) {
- if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
- ReportErrorSummary(rep_typ_str, frame->info);
- }
-
- if (common_flags()->print_module_map == 2) PrintModuleMap();
-
- Printf("==================\n");
-}
-
-#else // #if !SANITIZER_GO
-
-const int kMainThreadId = 1;
-
-void PrintStack(const ReportStack *ent) {
- if (ent == 0 || ent->frames == 0) {
- Printf(" [failed to restore the stack]\n");
- return;
- }
- SymbolizedStack *frame = ent->frames;
- for (int i = 0; frame; frame = frame->next, i++) {
- const AddressInfo &info = frame->info;
- Printf(" %s()\n %s:%d +0x%zx\n", info.function,
- StripPathPrefix(info.file, common_flags()->strip_path_prefix),
- info.line, (void *)info.module_offset);
- }
-}
-
-static void PrintMop(const ReportMop *mop, bool first) {
- Printf("\n");
- Printf("%s at %p by ",
- (first ? (mop->write ? "Write" : "Read")
- : (mop->write ? "Previous write" : "Previous read")), mop->addr);
- if (mop->tid == kMainThreadId)
- Printf("main goroutine:\n");
- else
- Printf("goroutine %d:\n", mop->tid);
- PrintStack(mop->stack);
-}
-
-static void PrintLocation(const ReportLocation *loc) {
- switch (loc->type) {
- case ReportLocationHeap: {
- Printf("\n");
- Printf("Heap block of size %zu at %p allocated by ",
- loc->heap_chunk_size, loc->heap_chunk_start);
- if (loc->tid == kMainThreadId)
- Printf("main goroutine:\n");
- else
- Printf("goroutine %d:\n", loc->tid);
- PrintStack(loc->stack);
- break;
- }
- case ReportLocationGlobal: {
- Printf("\n");
- Printf("Global var %s of size %zu at %p declared at %s:%zu\n",
- loc->global.name, loc->global.size, loc->global.start,
- loc->global.file, loc->global.line);
- break;
- }
- default:
- break;
- }
-}
-
-static void PrintThread(const ReportThread *rt) {
- if (rt->id == kMainThreadId)
- return;
- Printf("\n");
- Printf("Goroutine %d (%s) created at:\n",
- rt->id, rt->running ? "running" : "finished");
- PrintStack(rt->stack);
-}
-
-void PrintReport(const ReportDesc *rep) {
- Printf("==================\n");
- if (rep->typ == ReportTypeRace) {
- Printf("WARNING: DATA RACE");
- for (uptr i = 0; i < rep->mops.Size(); i++)
- PrintMop(rep->mops[i], i == 0);
- for (uptr i = 0; i < rep->locs.Size(); i++)
- PrintLocation(rep->locs[i]);
- for (uptr i = 0; i < rep->threads.Size(); i++)
- PrintThread(rep->threads[i]);
- } else if (rep->typ == ReportTypeDeadlock) {
- Printf("WARNING: DEADLOCK\n");
- for (uptr i = 0; i < rep->mutexes.Size(); i++) {
- Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
- 999, rep->mutexes[i]->id,
- rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
- PrintStack(rep->stacks[2*i]);
- Printf("\n");
- Printf("Mutex %d was previously locked here:\n",
- rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
- PrintStack(rep->stacks[2*i + 1]);
- Printf("\n");
- }
- }
- Printf("==================\n");
-}
-
-#endif
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_report.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_report.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "sanitizer_common/sanitizer_file.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+
+namespace __tsan {
+
+ReportStack::ReportStack() : frames(nullptr), suppressable(false) {}
+
+ReportStack *ReportStack::New() {
+ void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
+ return new(mem) ReportStack();
+}
+
+ReportLocation::ReportLocation(ReportLocationType type)
+ : type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
+ fd(0), suppressable(false), stack(nullptr) {}
+
+ReportLocation *ReportLocation::New(ReportLocationType type) {
+ void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
+ return new(mem) ReportLocation(type);
+}
+
+class Decorator: public __sanitizer::SanitizerCommonDecorator {
+ public:
+ Decorator() : SanitizerCommonDecorator() { }
+ const char *Access() { return Blue(); }
+ const char *ThreadDescription() { return Cyan(); }
+ const char *Location() { return Green(); }
+ const char *Sleep() { return Yellow(); }
+ const char *Mutex() { return Magenta(); }
+};
+
+ReportDesc::ReportDesc()
+ : tag(kExternalTagNone)
+ , stacks()
+ , mops()
+ , locs()
+ , mutexes()
+ , threads()
+ , unique_tids()
+ , sleep()
+ , count() {
+}
+
+ReportMop::ReportMop()
+ : mset() {
+}
+
+ReportDesc::~ReportDesc() {
+ // FIXME(dvyukov): it must be leaking a lot of memory.
+}
+
+#if !SANITIZER_GO
+
+const int kThreadBufSize = 32;
+const char *thread_name(char *buf, int tid) {
+ if (tid == 0)
+ return "main thread";
+ internal_snprintf(buf, kThreadBufSize, "thread T%d", tid);
+ return buf;
+}
+
+static const char *ReportTypeString(ReportType typ, uptr tag) {
+ switch (typ) {
+ case ReportTypeRace:
+ return "data race";
+ case ReportTypeVptrRace:
+ return "data race on vptr (ctor/dtor vs virtual call)";
+ case ReportTypeUseAfterFree:
+ return "heap-use-after-free";
+ case ReportTypeVptrUseAfterFree:
+ return "heap-use-after-free (virtual call vs free)";
+ case ReportTypeExternalRace: {
+ const char *str = GetReportHeaderFromTag(tag);
+ return str ? str : "race on external object";
+ }
+ case ReportTypeThreadLeak:
+ return "thread leak";
+ case ReportTypeMutexDestroyLocked:
+ return "destroy of a locked mutex";
+ case ReportTypeMutexDoubleLock:
+ return "double lock of a mutex";
+ case ReportTypeMutexInvalidAccess:
+ return "use of an invalid mutex (e.g. uninitialized or destroyed)";
+ case ReportTypeMutexBadUnlock:
+ return "unlock of an unlocked mutex (or by a wrong thread)";
+ case ReportTypeMutexBadReadLock:
+ return "read lock of a write locked mutex";
+ case ReportTypeMutexBadReadUnlock:
+ return "read unlock of a write locked mutex";
+ case ReportTypeSignalUnsafe:
+ return "signal-unsafe call inside of a signal";
+ case ReportTypeErrnoInSignal:
+ return "signal handler spoils errno";
+ case ReportTypeDeadlock:
+ return "lock-order-inversion (potential deadlock)";
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
+}
+
+#if SANITIZER_MAC
+static const char *const kInterposedFunctionPrefix = "wrap_";
+#else
+static const char *const kInterposedFunctionPrefix = "__interceptor_";
+#endif
+
+void PrintStack(const ReportStack *ent) {
+ if (ent == 0 || ent->frames == 0) {
+ Printf(" [failed to restore the stack]\n\n");
+ return;
+ }
+ SymbolizedStack *frame = ent->frames;
+ for (int i = 0; frame && frame->info.address; frame = frame->next, i++) {
+ InternalScopedString res(2 * GetPageSizeCached());
+ RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix, kInterposedFunctionPrefix);
+ Printf("%s\n", res.data());
+ }
+ Printf("\n");
+}
+
+static void PrintMutexSet(Vector<ReportMopMutex> const& mset) {
+ for (uptr i = 0; i < mset.Size(); i++) {
+ if (i == 0)
+ Printf(" (mutexes:");
+ const ReportMopMutex m = mset[i];
+ Printf(" %s M%llu", m.write ? "write" : "read", m.id);
+ Printf(i == mset.Size() - 1 ? ")" : ",");
+ }
+}
+
+static const char *MopDesc(bool first, bool write, bool atomic) {
+ return atomic ? (first ? (write ? "Atomic write" : "Atomic read")
+ : (write ? "Previous atomic write" : "Previous atomic read"))
+ : (first ? (write ? "Write" : "Read")
+ : (write ? "Previous write" : "Previous read"));
+}
+
+static const char *ExternalMopDesc(bool first, bool write) {
+ return first ? (write ? "Modifying" : "Read-only")
+ : (write ? "Previous modifying" : "Previous read-only");
+}
+
+static void PrintMop(const ReportMop *mop, bool first) {
+ Decorator d;
+ char thrbuf[kThreadBufSize];
+ Printf("%s", d.Access());
+ if (mop->external_tag == kExternalTagNone) {
+ Printf(" %s of size %d at %p by %s",
+ MopDesc(first, mop->write, mop->atomic), mop->size,
+ (void *)mop->addr, thread_name(thrbuf, mop->tid));
+ } else {
+ const char *object_type = GetObjectTypeFromTag(mop->external_tag);
+ if (object_type == nullptr)
+ object_type = "external object";
+ Printf(" %s access of %s at %p by %s",
+ ExternalMopDesc(first, mop->write), object_type,
+ (void *)mop->addr, thread_name(thrbuf, mop->tid));
+ }
+ PrintMutexSet(mop->mset);
+ Printf(":\n");
+ Printf("%s", d.Default());
+ PrintStack(mop->stack);
+}
+
+static void PrintLocation(const ReportLocation *loc) {
+ Decorator d;
+ char thrbuf[kThreadBufSize];
+ bool print_stack = false;
+ Printf("%s", d.Location());
+ if (loc->type == ReportLocationGlobal) {
+ const DataInfo &global = loc->global;
+ if (global.size != 0)
+ Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
+ global.name, global.size, global.start,
+ StripModuleName(global.module), global.module_offset);
+ else
+ Printf(" Location is global '%s' at %p (%s+%p)\n\n", global.name,
+ global.start, StripModuleName(global.module),
+ global.module_offset);
+ } else if (loc->type == ReportLocationHeap) {
+ char thrbuf[kThreadBufSize];
+ const char *object_type = GetObjectTypeFromTag(loc->external_tag);
+ if (!object_type) {
+ Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
+ loc->heap_chunk_size, loc->heap_chunk_start,
+ thread_name(thrbuf, loc->tid));
+ } else {
+ Printf(" Location is %s of size %zu at %p allocated by %s:\n",
+ object_type, loc->heap_chunk_size, loc->heap_chunk_start,
+ thread_name(thrbuf, loc->tid));
+ }
+ print_stack = true;
+ } else if (loc->type == ReportLocationStack) {
+ Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
+ } else if (loc->type == ReportLocationTLS) {
+ Printf(" Location is TLS of %s.\n\n", thread_name(thrbuf, loc->tid));
+ } else if (loc->type == ReportLocationFD) {
+ Printf(" Location is file descriptor %d created by %s at:\n",
+ loc->fd, thread_name(thrbuf, loc->tid));
+ print_stack = true;
+ }
+ Printf("%s", d.Default());
+ if (print_stack)
+ PrintStack(loc->stack);
+}
+
+static void PrintMutexShort(const ReportMutex *rm, const char *after) {
+ Decorator d;
+ Printf("%sM%zd%s%s", d.Mutex(), rm->id, d.Default(), after);
+}
+
+static void PrintMutexShortWithAddress(const ReportMutex *rm,
+ const char *after) {
+ Decorator d;
+ Printf("%sM%zd (%p)%s%s", d.Mutex(), rm->id, rm->addr, d.Default(), after);
+}
+
+static void PrintMutex(const ReportMutex *rm) {
+ Decorator d;
+ if (rm->destroyed) {
+ Printf("%s", d.Mutex());
+ Printf(" Mutex M%llu is already destroyed.\n\n", rm->id);
+ Printf("%s", d.Default());
+ } else {
+ Printf("%s", d.Mutex());
+ Printf(" Mutex M%llu (%p) created at:\n", rm->id, rm->addr);
+ Printf("%s", d.Default());
+ PrintStack(rm->stack);
+ }
+}
+
+static void PrintThread(const ReportThread *rt) {
+ Decorator d;
+ if (rt->id == 0) // Little sense in describing the main thread.
+ return;
+ Printf("%s", d.ThreadDescription());
+ Printf(" Thread T%d", rt->id);
+ if (rt->name && rt->name[0] != '\0')
+ Printf(" '%s'", rt->name);
+ char thrbuf[kThreadBufSize];
+ const char *thread_status = rt->running ? "running" : "finished";
+ if (rt->thread_type == ThreadType::Worker) {
+ Printf(" (tid=%zu, %s) is a GCD worker thread\n", rt->os_id, thread_status);
+ Printf("\n");
+ Printf("%s", d.Default());
+ return;
+ }
+ Printf(" (tid=%zu, %s) created by %s", rt->os_id, thread_status,
+ thread_name(thrbuf, rt->parent_tid));
+ if (rt->stack)
+ Printf(" at:");
+ Printf("\n");
+ Printf("%s", d.Default());
+ PrintStack(rt->stack);
+}
+
+static void PrintSleep(const ReportStack *s) {
+ Decorator d;
+ Printf("%s", d.Sleep());
+ Printf(" As if synchronized via sleep:\n");
+ Printf("%s", d.Default());
+ PrintStack(s);
+}
+
+static ReportStack *ChooseSummaryStack(const ReportDesc *rep) {
+ if (rep->mops.Size())
+ return rep->mops[0]->stack;
+ if (rep->stacks.Size())
+ return rep->stacks[0];
+ if (rep->mutexes.Size())
+ return rep->mutexes[0]->stack;
+ if (rep->threads.Size())
+ return rep->threads[0]->stack;
+ return 0;
+}
+
+static bool FrameIsInternal(const SymbolizedStack *frame) {
+ if (frame == 0)
+ return false;
+ const char *file = frame->info.file;
+ const char *module = frame->info.module;
+ if (file != 0 &&
+ (internal_strstr(file, "tsan_interceptors.cpp") ||
+ internal_strstr(file, "sanitizer_common_interceptors.inc") ||
+ internal_strstr(file, "tsan_interface_")))
+ return true;
+ if (module != 0 && (internal_strstr(module, "libclang_rt.tsan_")))
+ return true;
+ return false;
+}
+
+static SymbolizedStack *SkipTsanInternalFrames(SymbolizedStack *frames) {
+ while (FrameIsInternal(frames) && frames->next)
+ frames = frames->next;
+ return frames;
+}
+
+void PrintReport(const ReportDesc *rep) {
+ Decorator d;
+ Printf("==================\n");
+ const char *rep_typ_str = ReportTypeString(rep->typ, rep->tag);
+ Printf("%s", d.Warning());
+ Printf("WARNING: ThreadSanitizer: %s (pid=%d)\n", rep_typ_str,
+ (int)internal_getpid());
+ Printf("%s", d.Default());
+
+ if (rep->typ == ReportTypeDeadlock) {
+ char thrbuf[kThreadBufSize];
+ Printf(" Cycle in lock order graph: ");
+ for (uptr i = 0; i < rep->mutexes.Size(); i++)
+ PrintMutexShortWithAddress(rep->mutexes[i], " => ");
+ PrintMutexShort(rep->mutexes[0], "\n\n");
+ CHECK_GT(rep->mutexes.Size(), 0U);
+ CHECK_EQ(rep->mutexes.Size() * (flags()->second_deadlock_stack ? 2 : 1),
+ rep->stacks.Size());
+ for (uptr i = 0; i < rep->mutexes.Size(); i++) {
+ Printf(" Mutex ");
+ PrintMutexShort(rep->mutexes[(i + 1) % rep->mutexes.Size()],
+ " acquired here while holding mutex ");
+ PrintMutexShort(rep->mutexes[i], " in ");
+ Printf("%s", d.ThreadDescription());
+ Printf("%s:\n", thread_name(thrbuf, rep->unique_tids[i]));
+ Printf("%s", d.Default());
+ if (flags()->second_deadlock_stack) {
+ PrintStack(rep->stacks[2*i]);
+ Printf(" Mutex ");
+ PrintMutexShort(rep->mutexes[i],
+ " previously acquired by the same thread here:\n");
+ PrintStack(rep->stacks[2*i+1]);
+ } else {
+ PrintStack(rep->stacks[i]);
+ if (i == 0)
+ Printf(" Hint: use TSAN_OPTIONS=second_deadlock_stack=1 "
+ "to get more informative warning message\n\n");
+ }
+ }
+ } else {
+ for (uptr i = 0; i < rep->stacks.Size(); i++) {
+ if (i)
+ Printf(" and:\n");
+ PrintStack(rep->stacks[i]);
+ }
+ }
+
+ for (uptr i = 0; i < rep->mops.Size(); i++)
+ PrintMop(rep->mops[i], i == 0);
+
+ if (rep->sleep)
+ PrintSleep(rep->sleep);
+
+ for (uptr i = 0; i < rep->locs.Size(); i++)
+ PrintLocation(rep->locs[i]);
+
+ if (rep->typ != ReportTypeDeadlock) {
+ for (uptr i = 0; i < rep->mutexes.Size(); i++)
+ PrintMutex(rep->mutexes[i]);
+ }
+
+ for (uptr i = 0; i < rep->threads.Size(); i++)
+ PrintThread(rep->threads[i]);
+
+ if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
+ Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
+
+ if (ReportStack *stack = ChooseSummaryStack(rep)) {
+ if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames))
+ ReportErrorSummary(rep_typ_str, frame->info);
+ }
+
+ if (common_flags()->print_module_map == 2) PrintModuleMap();
+
+ Printf("==================\n");
+}
+
+#else // #if !SANITIZER_GO
+
+const int kMainThreadId = 1;
+
+void PrintStack(const ReportStack *ent) {
+ if (ent == 0 || ent->frames == 0) {
+ Printf(" [failed to restore the stack]\n");
+ return;
+ }
+ SymbolizedStack *frame = ent->frames;
+ for (int i = 0; frame; frame = frame->next, i++) {
+ const AddressInfo &info = frame->info;
+ Printf(" %s()\n %s:%d +0x%zx\n", info.function,
+ StripPathPrefix(info.file, common_flags()->strip_path_prefix),
+ info.line, (void *)info.module_offset);
+ }
+}
+
+static void PrintMop(const ReportMop *mop, bool first) {
+ Printf("\n");
+ Printf("%s at %p by ",
+ (first ? (mop->write ? "Write" : "Read")
+ : (mop->write ? "Previous write" : "Previous read")), mop->addr);
+ if (mop->tid == kMainThreadId)
+ Printf("main goroutine:\n");
+ else
+ Printf("goroutine %d:\n", mop->tid);
+ PrintStack(mop->stack);
+}
+
+static void PrintLocation(const ReportLocation *loc) {
+ switch (loc->type) {
+ case ReportLocationHeap: {
+ Printf("\n");
+ Printf("Heap block of size %zu at %p allocated by ",
+ loc->heap_chunk_size, loc->heap_chunk_start);
+ if (loc->tid == kMainThreadId)
+ Printf("main goroutine:\n");
+ else
+ Printf("goroutine %d:\n", loc->tid);
+ PrintStack(loc->stack);
+ break;
+ }
+ case ReportLocationGlobal: {
+ Printf("\n");
+ Printf("Global var %s of size %zu at %p declared at %s:%zu\n",
+ loc->global.name, loc->global.size, loc->global.start,
+ loc->global.file, loc->global.line);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void PrintThread(const ReportThread *rt) {
+ if (rt->id == kMainThreadId)
+ return;
+ Printf("\n");
+ Printf("Goroutine %d (%s) created at:\n",
+ rt->id, rt->running ? "running" : "finished");
+ PrintStack(rt->stack);
+}
+
+void PrintReport(const ReportDesc *rep) {
+ Printf("==================\n");
+ if (rep->typ == ReportTypeRace) {
+ Printf("WARNING: DATA RACE");
+ for (uptr i = 0; i < rep->mops.Size(); i++)
+ PrintMop(rep->mops[i], i == 0);
+ for (uptr i = 0; i < rep->locs.Size(); i++)
+ PrintLocation(rep->locs[i]);
+ for (uptr i = 0; i < rep->threads.Size(); i++)
+ PrintThread(rep->threads[i]);
+ } else if (rep->typ == ReportTypeDeadlock) {
+ Printf("WARNING: DEADLOCK\n");
+ for (uptr i = 0; i < rep->mutexes.Size(); i++) {
+ Printf("Goroutine %d lock mutex %d while holding mutex %d:\n",
+ 999, rep->mutexes[i]->id,
+ rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
+ PrintStack(rep->stacks[2*i]);
+ Printf("\n");
+ Printf("Mutex %d was previously locked here:\n",
+ rep->mutexes[(i+1) % rep->mutexes.Size()]->id);
+ PrintStack(rep->stacks[2*i + 1]);
+ Printf("\n");
+ }
+ }
+ Printf("==================\n");
+}
+
+#endif
+
+} // namespace __tsan
//===-- tsan_report.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#define TSAN_REPORT_H
#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "sanitizer_common/sanitizer_thread_registry.h"
#include "sanitizer_common/sanitizer_vector.h"
#include "tsan_defs.h"
int id;
tid_t os_id;
bool running;
- bool workerthread;
+ ThreadType thread_type;
char *name;
u32 parent_tid;
ReportStack *stack;
+++ /dev/null
-//===-- tsan_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 ThreadSanitizer (TSan), a race detector.
-//
-// Main file (entry points) for the TSan run-time.
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_atomic.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_file.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-#include "tsan_defs.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-#include "tsan_suppressions.h"
-#include "tsan_symbolize.h"
-#include "ubsan/ubsan_init.h"
-
-#ifdef __SSE3__
-// <emmintrin.h> transitively includes <stdlib.h>,
-// and it's prohibited to include std headers into tsan runtime.
-// So we do this dirty trick.
-#define _MM_MALLOC_H_INCLUDED
-#define __MM_MALLOC_H
-#include <emmintrin.h>
-typedef __m128i m128;
-#endif
-
-volatile int __tsan_resumed = 0;
-
-extern "C" void __tsan_resume() {
- __tsan_resumed = 1;
-}
-
-namespace __tsan {
-
-#if !SANITIZER_GO && !SANITIZER_MAC
-__attribute__((tls_model("initial-exec")))
-THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
-#endif
-static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
-Context *ctx;
-
-// Can be overriden by a front-end.
-#ifdef TSAN_EXTERNAL_HOOKS
-bool OnFinalize(bool failed);
-void OnInitialize();
-#else
-SANITIZER_WEAK_CXX_DEFAULT_IMPL
-bool OnFinalize(bool failed) {
- return failed;
-}
-SANITIZER_WEAK_CXX_DEFAULT_IMPL
-void OnInitialize() {}
-#endif
-
-static char thread_registry_placeholder[sizeof(ThreadRegistry)];
-
-static ThreadContextBase *CreateThreadContext(u32 tid) {
- // Map thread trace when context is created.
- char name[50];
- internal_snprintf(name, sizeof(name), "trace %u", tid);
- MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event), name);
- const uptr hdr = GetThreadTraceHeader(tid);
- internal_snprintf(name, sizeof(name), "trace header %u", tid);
- MapThreadTrace(hdr, sizeof(Trace), name);
- new((void*)hdr) Trace();
- // We are going to use only a small part of the trace with the default
- // value of history_size. However, the constructor writes to the whole trace.
- // Unmap the unused part.
- uptr hdr_end = hdr + sizeof(Trace);
- hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts());
- hdr_end = RoundUp(hdr_end, GetPageSizeCached());
- if (hdr_end < hdr + sizeof(Trace))
- UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end);
- void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
- return new(mem) ThreadContext(tid);
-}
-
-#if !SANITIZER_GO
-static const u32 kThreadQuarantineSize = 16;
-#else
-static const u32 kThreadQuarantineSize = 64;
-#endif
-
-Context::Context()
- : initialized()
- , report_mtx(MutexTypeReport, StatMtxReport)
- , nreported()
- , nmissed_expected()
- , thread_registry(new(thread_registry_placeholder) ThreadRegistry(
- CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse))
- , racy_mtx(MutexTypeRacy, StatMtxRacy)
- , racy_stacks()
- , racy_addresses()
- , fired_suppressions_mtx(MutexTypeFired, StatMtxFired)
- , clock_alloc("clock allocator") {
- fired_suppressions.reserve(8);
-}
-
-// The objects are allocated in TLS, so one may rely on zero-initialization.
-ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
- unsigned reuse_count,
- uptr stk_addr, uptr stk_size,
- uptr tls_addr, uptr tls_size)
- : fast_state(tid, epoch)
- // Do not touch these, rely on zero initialization,
- // they may be accessed before the ctor.
- // , ignore_reads_and_writes()
- // , ignore_interceptors()
- , clock(tid, reuse_count)
-#if !SANITIZER_GO
- , jmp_bufs()
-#endif
- , tid(tid)
- , unique_id(unique_id)
- , stk_addr(stk_addr)
- , stk_size(stk_size)
- , tls_addr(tls_addr)
- , tls_size(tls_size)
-#if !SANITIZER_GO
- , last_sleep_clock(tid)
-#endif
-{
-}
-
-#if !SANITIZER_GO
-static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
- uptr n_threads;
- uptr n_running_threads;
- ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads);
- InternalMmapVector<char> buf(4096);
- WriteMemoryProfile(buf.data(), buf.size(), n_threads, n_running_threads);
- WriteToFile(fd, buf.data(), internal_strlen(buf.data()));
-}
-
-static void BackgroundThread(void *arg) {
- // This is a non-initialized non-user thread, nothing to see here.
- // We don't use ScopedIgnoreInterceptors, because we want ignores to be
- // enabled even when the thread function exits (e.g. during pthread thread
- // shutdown code).
- cur_thread()->ignore_interceptors++;
- const u64 kMs2Ns = 1000 * 1000;
-
- fd_t mprof_fd = kInvalidFd;
- if (flags()->profile_memory && flags()->profile_memory[0]) {
- if (internal_strcmp(flags()->profile_memory, "stdout") == 0) {
- mprof_fd = 1;
- } else if (internal_strcmp(flags()->profile_memory, "stderr") == 0) {
- mprof_fd = 2;
- } else {
- InternalScopedString filename(kMaxPathLength);
- filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid());
- fd_t fd = OpenFile(filename.data(), WrOnly);
- if (fd == kInvalidFd) {
- Printf("ThreadSanitizer: failed to open memory profile file '%s'\n",
- &filename[0]);
- } else {
- mprof_fd = fd;
- }
- }
- }
-
- u64 last_flush = NanoTime();
- uptr last_rss = 0;
- for (int i = 0;
- atomic_load(&ctx->stop_background_thread, memory_order_relaxed) == 0;
- i++) {
- SleepForMillis(100);
- u64 now = NanoTime();
-
- // Flush memory if requested.
- if (flags()->flush_memory_ms > 0) {
- if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) {
- VPrintf(1, "ThreadSanitizer: periodic memory flush\n");
- FlushShadowMemory();
- last_flush = NanoTime();
- }
- }
- // GetRSS can be expensive on huge programs, so don't do it every 100ms.
- if (flags()->memory_limit_mb > 0) {
- uptr rss = GetRSS();
- uptr limit = uptr(flags()->memory_limit_mb) << 20;
- VPrintf(1, "ThreadSanitizer: memory flush check"
- " RSS=%llu LAST=%llu LIMIT=%llu\n",
- (u64)rss >> 20, (u64)last_rss >> 20, (u64)limit >> 20);
- if (2 * rss > limit + last_rss) {
- VPrintf(1, "ThreadSanitizer: flushing memory due to RSS\n");
- FlushShadowMemory();
- rss = GetRSS();
- VPrintf(1, "ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
- }
- last_rss = rss;
- }
-
- // Write memory profile if requested.
- if (mprof_fd != kInvalidFd)
- MemoryProfiler(ctx, mprof_fd, i);
-
- // Flush symbolizer cache if requested.
- if (flags()->flush_symbolizer_ms > 0) {
- u64 last = atomic_load(&ctx->last_symbolize_time_ns,
- memory_order_relaxed);
- if (last != 0 && last + flags()->flush_symbolizer_ms * kMs2Ns < now) {
- Lock l(&ctx->report_mtx);
- ScopedErrorReportLock l2;
- SymbolizeFlush();
- atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed);
- }
- }
- }
-}
-
-static void StartBackgroundThread() {
- ctx->background_thread = internal_start_thread(&BackgroundThread, 0);
-}
-
-#ifndef __mips__
-static void StopBackgroundThread() {
- atomic_store(&ctx->stop_background_thread, 1, memory_order_relaxed);
- internal_join_thread(ctx->background_thread);
- ctx->background_thread = 0;
-}
-#endif
-#endif
-
-void DontNeedShadowFor(uptr addr, uptr size) {
- ReleaseMemoryPagesToOS(MemToShadow(addr), MemToShadow(addr + size));
-}
-
-void MapShadow(uptr addr, uptr size) {
- // Global data is not 64K aligned, but there are no adjacent mappings,
- // so we can get away with unaligned mapping.
- // CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
- const uptr kPageSize = GetPageSizeCached();
- uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), kPageSize);
- uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), kPageSize);
- if (!MmapFixedNoReserve(shadow_begin, shadow_end - shadow_begin, "shadow"))
- Die();
-
- // Meta shadow is 2:1, so tread carefully.
- static bool data_mapped = false;
- static uptr mapped_meta_end = 0;
- uptr meta_begin = (uptr)MemToMeta(addr);
- uptr meta_end = (uptr)MemToMeta(addr + size);
- meta_begin = RoundDownTo(meta_begin, 64 << 10);
- meta_end = RoundUpTo(meta_end, 64 << 10);
- if (!data_mapped) {
- // First call maps data+bss.
- data_mapped = true;
- if (!MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow"))
- Die();
- } else {
- // Mapping continous heap.
- // Windows wants 64K alignment.
- meta_begin = RoundDownTo(meta_begin, 64 << 10);
- meta_end = RoundUpTo(meta_end, 64 << 10);
- if (meta_end <= mapped_meta_end)
- return;
- if (meta_begin < mapped_meta_end)
- meta_begin = mapped_meta_end;
- if (!MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow"))
- Die();
- mapped_meta_end = meta_end;
- }
- VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n",
- addr, addr+size, meta_begin, meta_end);
-}
-
-void MapThreadTrace(uptr addr, uptr size, const char *name) {
- DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
- CHECK_GE(addr, TraceMemBeg());
- CHECK_LE(addr + size, TraceMemEnd());
- CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
- if (!MmapFixedNoReserve(addr, size, name)) {
- Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p)\n",
- addr, size);
- Die();
- }
-}
-
-static void CheckShadowMapping() {
- uptr beg, end;
- for (int i = 0; GetUserRegion(i, &beg, &end); i++) {
- // Skip cases for empty regions (heap definition for architectures that
- // do not use 64-bit allocator).
- if (beg == end)
- continue;
- VPrintf(3, "checking shadow region %p-%p\n", beg, end);
- uptr prev = 0;
- for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) {
- for (int x = -(int)kShadowCell; x <= (int)kShadowCell; x += kShadowCell) {
- const uptr p = RoundDown(p0 + x, kShadowCell);
- if (p < beg || p >= end)
- continue;
- const uptr s = MemToShadow(p);
- const uptr m = (uptr)MemToMeta(p);
- VPrintf(3, " checking pointer %p: shadow=%p meta=%p\n", p, s, m);
- CHECK(IsAppMem(p));
- CHECK(IsShadowMem(s));
- CHECK_EQ(p, ShadowToMem(s));
- CHECK(IsMetaMem(m));
- if (prev) {
- // Ensure that shadow and meta mappings are linear within a single
- // user range. Lots of code that processes memory ranges assumes it.
- const uptr prev_s = MemToShadow(prev);
- const uptr prev_m = (uptr)MemToMeta(prev);
- CHECK_EQ(s - prev_s, (p - prev) * kShadowMultiplier);
- CHECK_EQ((m - prev_m) / kMetaShadowSize,
- (p - prev) / kMetaShadowCell);
- }
- prev = p;
- }
- }
- }
-}
-
-#if !SANITIZER_GO
-static void OnStackUnwind(const SignalContext &sig, const void *,
- BufferedStackTrace *stack) {
- uptr top = 0;
- uptr bottom = 0;
- bool fast = common_flags()->fast_unwind_on_fatal;
- if (fast) GetThreadStackTopAndBottom(false, &top, &bottom);
- stack->Unwind(kStackTraceMax, sig.pc, sig.bp, sig.context, top, bottom, fast);
-}
-
-static void TsanOnDeadlySignal(int signo, void *siginfo, void *context) {
- HandleDeadlySignal(siginfo, context, GetTid(), &OnStackUnwind, nullptr);
-}
-#endif
-
-void Initialize(ThreadState *thr) {
- // Thread safe because done before all threads exist.
- static bool is_initialized = false;
- if (is_initialized)
- return;
- is_initialized = true;
- // We are not ready to handle interceptors yet.
- ScopedIgnoreInterceptors ignore;
- SanitizerToolName = "ThreadSanitizer";
- // Install tool-specific callbacks in sanitizer_common.
- SetCheckFailedCallback(TsanCheckFailed);
-
- ctx = new(ctx_placeholder) Context;
- const char *options = GetEnv(SANITIZER_GO ? "GORACE" : "TSAN_OPTIONS");
- CacheBinaryName();
- CheckASLR();
- InitializeFlags(&ctx->flags, options);
- AvoidCVE_2016_2143();
- InitializePlatformEarly();
-#if !SANITIZER_GO
- // Re-exec ourselves if we need to set additional env or command line args.
- MaybeReexec();
-
- InitializeAllocator();
- ReplaceSystemMalloc();
-#endif
- if (common_flags()->detect_deadlocks)
- ctx->dd = DDetector::Create(flags());
- Processor *proc = ProcCreate();
- ProcWire(proc, thr);
- InitializeInterceptors();
- CheckShadowMapping();
- InitializePlatform();
- InitializeMutex();
- InitializeDynamicAnnotations();
-#if !SANITIZER_GO
- InitializeShadowMemory();
- InitializeAllocatorLate();
- InstallDeadlySignalHandlers(TsanOnDeadlySignal);
-#endif
- // Setup correct file descriptor for error reports.
- __sanitizer_set_report_path(common_flags()->log_path);
- InitializeSuppressions();
-#if !SANITIZER_GO
- InitializeLibIgnore();
- Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
-#endif
-
- VPrintf(1, "***** Running under ThreadSanitizer v2 (pid %d) *****\n",
- (int)internal_getpid());
-
- // Initialize thread 0.
- int tid = ThreadCreate(thr, 0, 0, true);
- CHECK_EQ(tid, 0);
- ThreadStart(thr, tid, GetTid(), /*workerthread*/ false);
-#if TSAN_CONTAINS_UBSAN
- __ubsan::InitAsPlugin();
-#endif
- ctx->initialized = true;
-
-#if !SANITIZER_GO
- Symbolizer::LateInitialize();
-#endif
-
- if (flags()->stop_on_start) {
- Printf("ThreadSanitizer is suspended at startup (pid %d)."
- " Call __tsan_resume().\n",
- (int)internal_getpid());
- while (__tsan_resumed == 0) {}
- }
-
- OnInitialize();
-}
-
-void MaybeSpawnBackgroundThread() {
- // On MIPS, TSan initialization is run before
- // __pthread_initialize_minimal_internal() is finished, so we can not spawn
- // new threads.
-#if !SANITIZER_GO && !defined(__mips__)
- static atomic_uint32_t bg_thread = {};
- if (atomic_load(&bg_thread, memory_order_relaxed) == 0 &&
- atomic_exchange(&bg_thread, 1, memory_order_relaxed) == 0) {
- StartBackgroundThread();
- SetSandboxingCallback(StopBackgroundThread);
- }
-#endif
-}
-
-
-int Finalize(ThreadState *thr) {
- bool failed = false;
-
- if (common_flags()->print_module_map == 1) PrintModuleMap();
-
- if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
- SleepForMillis(flags()->atexit_sleep_ms);
-
- // Wait for pending reports.
- ctx->report_mtx.Lock();
- { ScopedErrorReportLock l; }
- ctx->report_mtx.Unlock();
-
-#if !SANITIZER_GO
- if (Verbosity()) AllocatorPrintStats();
-#endif
-
- ThreadFinalize(thr);
-
- if (ctx->nreported) {
- failed = true;
-#if !SANITIZER_GO
- Printf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
-#else
- Printf("Found %d data race(s)\n", ctx->nreported);
-#endif
- }
-
- if (ctx->nmissed_expected) {
- failed = true;
- Printf("ThreadSanitizer: missed %d expected races\n",
- ctx->nmissed_expected);
- }
-
- if (common_flags()->print_suppressions)
- PrintMatchedSuppressions();
-#if !SANITIZER_GO
- if (flags()->print_benign)
- PrintMatchedBenignRaces();
-#endif
-
- failed = OnFinalize(failed);
-
-#if TSAN_COLLECT_STATS
- StatAggregate(ctx->stat, thr->stat);
- StatOutput(ctx->stat);
-#endif
-
- return failed ? common_flags()->exitcode : 0;
-}
-
-#if !SANITIZER_GO
-void ForkBefore(ThreadState *thr, uptr pc) {
- ctx->thread_registry->Lock();
- ctx->report_mtx.Lock();
-}
-
-void ForkParentAfter(ThreadState *thr, uptr pc) {
- ctx->report_mtx.Unlock();
- ctx->thread_registry->Unlock();
-}
-
-void ForkChildAfter(ThreadState *thr, uptr pc) {
- ctx->report_mtx.Unlock();
- ctx->thread_registry->Unlock();
-
- uptr nthread = 0;
- ctx->thread_registry->GetNumberOfThreads(0, 0, &nthread /* alive threads */);
- VPrintf(1, "ThreadSanitizer: forked new process with pid %d,"
- " parent had %d threads\n", (int)internal_getpid(), (int)nthread);
- if (nthread == 1) {
- StartBackgroundThread();
- } else {
- // We've just forked a multi-threaded process. We cannot reasonably function
- // after that (some mutexes may be locked before fork). So just enable
- // ignores for everything in the hope that we will exec soon.
- ctx->after_multithreaded_fork = true;
- thr->ignore_interceptors++;
- ThreadIgnoreBegin(thr, pc);
- ThreadIgnoreSyncBegin(thr, pc);
- }
-}
-#endif
-
-#if SANITIZER_GO
-NOINLINE
-void GrowShadowStack(ThreadState *thr) {
- const int sz = thr->shadow_stack_end - thr->shadow_stack;
- const int newsz = 2 * sz;
- uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack,
- newsz * sizeof(uptr));
- internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr));
- internal_free(thr->shadow_stack);
- thr->shadow_stack = newstack;
- thr->shadow_stack_pos = newstack + sz;
- thr->shadow_stack_end = newstack + newsz;
-}
-#endif
-
-u32 CurrentStackId(ThreadState *thr, uptr pc) {
- if (!thr->is_inited) // May happen during bootstrap.
- return 0;
- if (pc != 0) {
-#if !SANITIZER_GO
- DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
-#else
- if (thr->shadow_stack_pos == thr->shadow_stack_end)
- GrowShadowStack(thr);
-#endif
- thr->shadow_stack_pos[0] = pc;
- thr->shadow_stack_pos++;
- }
- u32 id = StackDepotPut(
- StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack));
- if (pc != 0)
- thr->shadow_stack_pos--;
- return id;
-}
-
-void TraceSwitch(ThreadState *thr) {
-#if !SANITIZER_GO
- if (ctx->after_multithreaded_fork)
- return;
-#endif
- thr->nomalloc++;
- Trace *thr_trace = ThreadTrace(thr->tid);
- Lock l(&thr_trace->mtx);
- unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
- TraceHeader *hdr = &thr_trace->headers[trace];
- hdr->epoch0 = thr->fast_state.epoch();
- ObtainCurrentStack(thr, 0, &hdr->stack0);
- hdr->mset0 = thr->mset;
- thr->nomalloc--;
-}
-
-Trace *ThreadTrace(int tid) {
- return (Trace*)GetThreadTraceHeader(tid);
-}
-
-uptr TraceTopPC(ThreadState *thr) {
- Event *events = (Event*)GetThreadTrace(thr->tid);
- uptr pc = events[thr->fast_state.GetTracePos()];
- return pc;
-}
-
-uptr TraceSize() {
- return (uptr)(1ull << (kTracePartSizeBits + flags()->history_size + 1));
-}
-
-uptr TraceParts() {
- return TraceSize() / kTracePartSize;
-}
-
-#if !SANITIZER_GO
-extern "C" void __tsan_trace_switch() {
- TraceSwitch(cur_thread());
-}
-
-extern "C" void __tsan_report_race() {
- ReportRace(cur_thread());
-}
-#endif
-
-ALWAYS_INLINE
-Shadow LoadShadow(u64 *p) {
- u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed);
- return Shadow(raw);
-}
-
-ALWAYS_INLINE
-void StoreShadow(u64 *sp, u64 s) {
- atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed);
-}
-
-ALWAYS_INLINE
-void StoreIfNotYetStored(u64 *sp, u64 *s) {
- StoreShadow(sp, *s);
- *s = 0;
-}
-
-ALWAYS_INLINE
-void HandleRace(ThreadState *thr, u64 *shadow_mem,
- Shadow cur, Shadow old) {
- thr->racy_state[0] = cur.raw();
- thr->racy_state[1] = old.raw();
- thr->racy_shadow_addr = shadow_mem;
-#if !SANITIZER_GO
- HACKY_CALL(__tsan_report_race);
-#else
- ReportRace(thr);
-#endif
-}
-
-static inline bool HappensBefore(Shadow old, ThreadState *thr) {
- return thr->clock.get(old.TidWithIgnore()) >= old.epoch();
-}
-
-ALWAYS_INLINE
-void MemoryAccessImpl1(ThreadState *thr, uptr addr,
- int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic,
- u64 *shadow_mem, Shadow cur) {
- StatInc(thr, StatMop);
- StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
- StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
-
- // This potentially can live in an MMX/SSE scratch register.
- // The required intrinsics are:
- // __m128i _mm_move_epi64(__m128i*);
- // _mm_storel_epi64(u64*, __m128i);
- u64 store_word = cur.raw();
-
- // scan all the shadow values and dispatch to 4 categories:
- // same, replace, candidate and race (see comments below).
- // we consider only 3 cases regarding access sizes:
- // equal, intersect and not intersect. initially I considered
- // larger and smaller as well, it allowed to replace some
- // 'candidates' with 'same' or 'replace', but I think
- // it's just not worth it (performance- and complexity-wise).
-
- Shadow old(0);
-
- // It release mode we manually unroll the loop,
- // because empirically gcc generates better code this way.
- // However, we can't afford unrolling in debug mode, because the function
- // consumes almost 4K of stack. Gtest gives only 4K of stack to death test
- // threads, which is not enough for the unrolled loop.
-#if SANITIZER_DEBUG
- for (int idx = 0; idx < 4; idx++) {
-#include "tsan_update_shadow_word_inl.h"
- }
-#else
- int idx = 0;
-#include "tsan_update_shadow_word_inl.h"
- idx = 1;
-#include "tsan_update_shadow_word_inl.h"
- idx = 2;
-#include "tsan_update_shadow_word_inl.h"
- idx = 3;
-#include "tsan_update_shadow_word_inl.h"
-#endif
-
- // we did not find any races and had already stored
- // the current access info, so we are done
- if (LIKELY(store_word == 0))
- return;
- // choose a random candidate slot and replace it
- StoreShadow(shadow_mem + (cur.epoch() % kShadowCnt), store_word);
- StatInc(thr, StatShadowReplace);
- return;
- RACE:
- HandleRace(thr, shadow_mem, cur, old);
- return;
-}
-
-void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr,
- int size, bool kAccessIsWrite, bool kIsAtomic) {
- while (size) {
- int size1 = 1;
- int kAccessSizeLog = kSizeLog1;
- if (size >= 8 && (addr & ~7) == ((addr + 7) & ~7)) {
- size1 = 8;
- kAccessSizeLog = kSizeLog8;
- } else if (size >= 4 && (addr & ~7) == ((addr + 3) & ~7)) {
- size1 = 4;
- kAccessSizeLog = kSizeLog4;
- } else if (size >= 2 && (addr & ~7) == ((addr + 1) & ~7)) {
- size1 = 2;
- kAccessSizeLog = kSizeLog2;
- }
- MemoryAccess(thr, pc, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic);
- addr += size1;
- size -= size1;
- }
-}
-
-ALWAYS_INLINE
-bool ContainsSameAccessSlow(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
- Shadow cur(a);
- for (uptr i = 0; i < kShadowCnt; i++) {
- Shadow old(LoadShadow(&s[i]));
- if (Shadow::Addr0AndSizeAreEqual(cur, old) &&
- old.TidWithIgnore() == cur.TidWithIgnore() &&
- old.epoch() > sync_epoch &&
- old.IsAtomic() == cur.IsAtomic() &&
- old.IsRead() <= cur.IsRead())
- return true;
- }
- return false;
-}
-
-#if defined(__SSE3__)
-#define SHUF(v0, v1, i0, i1, i2, i3) _mm_castps_si128(_mm_shuffle_ps( \
- _mm_castsi128_ps(v0), _mm_castsi128_ps(v1), \
- (i0)*1 + (i1)*4 + (i2)*16 + (i3)*64))
-ALWAYS_INLINE
-bool ContainsSameAccessFast(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
- // This is an optimized version of ContainsSameAccessSlow.
- // load current access into access[0:63]
- const m128 access = _mm_cvtsi64_si128(a);
- // duplicate high part of access in addr0:
- // addr0[0:31] = access[32:63]
- // addr0[32:63] = access[32:63]
- // addr0[64:95] = access[32:63]
- // addr0[96:127] = access[32:63]
- const m128 addr0 = SHUF(access, access, 1, 1, 1, 1);
- // load 4 shadow slots
- const m128 shadow0 = _mm_load_si128((__m128i*)s);
- const m128 shadow1 = _mm_load_si128((__m128i*)s + 1);
- // load high parts of 4 shadow slots into addr_vect:
- // addr_vect[0:31] = shadow0[32:63]
- // addr_vect[32:63] = shadow0[96:127]
- // addr_vect[64:95] = shadow1[32:63]
- // addr_vect[96:127] = shadow1[96:127]
- m128 addr_vect = SHUF(shadow0, shadow1, 1, 3, 1, 3);
- if (!is_write) {
- // set IsRead bit in addr_vect
- const m128 rw_mask1 = _mm_cvtsi64_si128(1<<15);
- const m128 rw_mask = SHUF(rw_mask1, rw_mask1, 0, 0, 0, 0);
- addr_vect = _mm_or_si128(addr_vect, rw_mask);
- }
- // addr0 == addr_vect?
- const m128 addr_res = _mm_cmpeq_epi32(addr0, addr_vect);
- // epoch1[0:63] = sync_epoch
- const m128 epoch1 = _mm_cvtsi64_si128(sync_epoch);
- // epoch[0:31] = sync_epoch[0:31]
- // epoch[32:63] = sync_epoch[0:31]
- // epoch[64:95] = sync_epoch[0:31]
- // epoch[96:127] = sync_epoch[0:31]
- const m128 epoch = SHUF(epoch1, epoch1, 0, 0, 0, 0);
- // load low parts of shadow cell epochs into epoch_vect:
- // epoch_vect[0:31] = shadow0[0:31]
- // epoch_vect[32:63] = shadow0[64:95]
- // epoch_vect[64:95] = shadow1[0:31]
- // epoch_vect[96:127] = shadow1[64:95]
- const m128 epoch_vect = SHUF(shadow0, shadow1, 0, 2, 0, 2);
- // epoch_vect >= sync_epoch?
- const m128 epoch_res = _mm_cmpgt_epi32(epoch_vect, epoch);
- // addr_res & epoch_res
- const m128 res = _mm_and_si128(addr_res, epoch_res);
- // mask[0] = res[7]
- // mask[1] = res[15]
- // ...
- // mask[15] = res[127]
- const int mask = _mm_movemask_epi8(res);
- return mask != 0;
-}
-#endif
-
-ALWAYS_INLINE
-bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
-#if defined(__SSE3__)
- bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write);
- // NOTE: this check can fail if the shadow is concurrently mutated
- // by other threads. But it still can be useful if you modify
- // ContainsSameAccessFast and want to ensure that it's not completely broken.
- // DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write));
- return res;
-#else
- return ContainsSameAccessSlow(s, a, sync_epoch, is_write);
-#endif
-}
-
-ALWAYS_INLINE USED
-void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
- int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) {
- u64 *shadow_mem = (u64*)MemToShadow(addr);
- DPrintf2("#%d: MemoryAccess: @%p %p size=%d"
- " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n",
- (int)thr->fast_state.tid(), (void*)pc, (void*)addr,
- (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
- (uptr)shadow_mem[0], (uptr)shadow_mem[1],
- (uptr)shadow_mem[2], (uptr)shadow_mem[3]);
-#if SANITIZER_DEBUG
- if (!IsAppMem(addr)) {
- Printf("Access to non app mem %zx\n", addr);
- DCHECK(IsAppMem(addr));
- }
- if (!IsShadowMem((uptr)shadow_mem)) {
- Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
- DCHECK(IsShadowMem((uptr)shadow_mem));
- }
-#endif
-
- if (!SANITIZER_GO && *shadow_mem == kShadowRodata) {
- // Access to .rodata section, no races here.
- // Measurements show that it can be 10-20% of all memory accesses.
- StatInc(thr, StatMop);
- StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
- StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
- StatInc(thr, StatMopRodata);
- return;
- }
-
- FastState fast_state = thr->fast_state;
- if (fast_state.GetIgnoreBit()) {
- StatInc(thr, StatMop);
- StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
- StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
- StatInc(thr, StatMopIgnored);
- return;
- }
-
- Shadow cur(fast_state);
- cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog);
- cur.SetWrite(kAccessIsWrite);
- cur.SetAtomic(kIsAtomic);
-
- if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(),
- thr->fast_synch_epoch, kAccessIsWrite))) {
- StatInc(thr, StatMop);
- StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
- StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
- StatInc(thr, StatMopSame);
- return;
- }
-
- if (kCollectHistory) {
- fast_state.IncrementEpoch();
- thr->fast_state = fast_state;
- TraceAddEvent(thr, fast_state, EventTypeMop, pc);
- cur.IncrementEpoch();
- }
-
- MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
- shadow_mem, cur);
-}
-
-// Called by MemoryAccessRange in tsan_rtl_thread.cc
-ALWAYS_INLINE USED
-void MemoryAccessImpl(ThreadState *thr, uptr addr,
- int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic,
- u64 *shadow_mem, Shadow cur) {
- if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(),
- thr->fast_synch_epoch, kAccessIsWrite))) {
- StatInc(thr, StatMop);
- StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
- StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
- StatInc(thr, StatMopSame);
- return;
- }
-
- MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
- shadow_mem, cur);
-}
-
-static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size,
- u64 val) {
- (void)thr;
- (void)pc;
- if (size == 0)
- return;
- // FIXME: fix me.
- uptr offset = addr % kShadowCell;
- if (offset) {
- offset = kShadowCell - offset;
- if (size <= offset)
- return;
- addr += offset;
- size -= offset;
- }
- DCHECK_EQ(addr % 8, 0);
- // If a user passes some insane arguments (memset(0)),
- // let it just crash as usual.
- if (!IsAppMem(addr) || !IsAppMem(addr + size - 1))
- return;
- // Don't want to touch lots of shadow memory.
- // If a program maps 10MB stack, there is no need reset the whole range.
- size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1);
- // UnmapOrDie/MmapFixedNoReserve does not work on Windows.
- if (SANITIZER_WINDOWS || size < common_flags()->clear_shadow_mmap_threshold) {
- u64 *p = (u64*)MemToShadow(addr);
- CHECK(IsShadowMem((uptr)p));
- CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1)));
- // FIXME: may overwrite a part outside the region
- for (uptr i = 0; i < size / kShadowCell * kShadowCnt;) {
- p[i++] = val;
- for (uptr j = 1; j < kShadowCnt; j++)
- p[i++] = 0;
- }
- } else {
- // The region is big, reset only beginning and end.
- const uptr kPageSize = GetPageSizeCached();
- u64 *begin = (u64*)MemToShadow(addr);
- u64 *end = begin + size / kShadowCell * kShadowCnt;
- u64 *p = begin;
- // Set at least first kPageSize/2 to page boundary.
- while ((p < begin + kPageSize / kShadowSize / 2) || ((uptr)p % kPageSize)) {
- *p++ = val;
- for (uptr j = 1; j < kShadowCnt; j++)
- *p++ = 0;
- }
- // Reset middle part.
- u64 *p1 = p;
- p = RoundDown(end, kPageSize);
- UnmapOrDie((void*)p1, (uptr)p - (uptr)p1);
- if (!MmapFixedNoReserve((uptr)p1, (uptr)p - (uptr)p1))
- Die();
- // Set the ending.
- while (p < end) {
- *p++ = val;
- for (uptr j = 1; j < kShadowCnt; j++)
- *p++ = 0;
- }
- }
-}
-
-void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) {
- MemoryRangeSet(thr, pc, addr, size, 0);
-}
-
-void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) {
- // Processing more than 1k (4k of shadow) is expensive,
- // can cause excessive memory consumption (user does not necessary touch
- // the whole range) and most likely unnecessary.
- if (size > 1024)
- size = 1024;
- CHECK_EQ(thr->is_freeing, false);
- thr->is_freeing = true;
- MemoryAccessRange(thr, pc, addr, size, true);
- thr->is_freeing = false;
- if (kCollectHistory) {
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
- }
- Shadow s(thr->fast_state);
- s.ClearIgnoreBit();
- s.MarkAsFreed();
- s.SetWrite(true);
- s.SetAddr0AndSizeLog(0, 3);
- MemoryRangeSet(thr, pc, addr, size, s.raw());
-}
-
-void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
- if (kCollectHistory) {
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
- }
- Shadow s(thr->fast_state);
- s.ClearIgnoreBit();
- s.SetWrite(true);
- s.SetAddr0AndSizeLog(0, 3);
- MemoryRangeSet(thr, pc, addr, size, s.raw());
-}
-
-ALWAYS_INLINE USED
-void FuncEntry(ThreadState *thr, uptr pc) {
- StatInc(thr, StatFuncEnter);
- DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
- if (kCollectHistory) {
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc);
- }
-
- // Shadow stack maintenance can be replaced with
- // stack unwinding during trace switch (which presumably must be faster).
- DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack);
-#if !SANITIZER_GO
- DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
-#else
- if (thr->shadow_stack_pos == thr->shadow_stack_end)
- GrowShadowStack(thr);
-#endif
- thr->shadow_stack_pos[0] = pc;
- thr->shadow_stack_pos++;
-}
-
-ALWAYS_INLINE USED
-void FuncExit(ThreadState *thr) {
- StatInc(thr, StatFuncExit);
- DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
- if (kCollectHistory) {
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0);
- }
-
- DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack);
-#if !SANITIZER_GO
- DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
-#endif
- thr->shadow_stack_pos--;
-}
-
-void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack) {
- DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid);
- thr->ignore_reads_and_writes++;
- CHECK_GT(thr->ignore_reads_and_writes, 0);
- thr->fast_state.SetIgnoreBit();
-#if !SANITIZER_GO
- if (save_stack && !ctx->after_multithreaded_fork)
- thr->mop_ignore_set.Add(CurrentStackId(thr, pc));
-#endif
-}
-
-void ThreadIgnoreEnd(ThreadState *thr, uptr pc) {
- DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid);
- CHECK_GT(thr->ignore_reads_and_writes, 0);
- thr->ignore_reads_and_writes--;
- if (thr->ignore_reads_and_writes == 0) {
- thr->fast_state.ClearIgnoreBit();
-#if !SANITIZER_GO
- thr->mop_ignore_set.Reset();
-#endif
- }
-}
-
-#if !SANITIZER_GO
-extern "C" SANITIZER_INTERFACE_ATTRIBUTE
-uptr __tsan_testonly_shadow_stack_current_size() {
- ThreadState *thr = cur_thread();
- return thr->shadow_stack_pos - thr->shadow_stack;
-}
-#endif
-
-void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc, bool save_stack) {
- DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid);
- thr->ignore_sync++;
- CHECK_GT(thr->ignore_sync, 0);
-#if !SANITIZER_GO
- if (save_stack && !ctx->after_multithreaded_fork)
- thr->sync_ignore_set.Add(CurrentStackId(thr, pc));
-#endif
-}
-
-void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc) {
- DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid);
- CHECK_GT(thr->ignore_sync, 0);
- thr->ignore_sync--;
-#if !SANITIZER_GO
- if (thr->ignore_sync == 0)
- thr->sync_ignore_set.Reset();
-#endif
-}
-
-bool MD5Hash::operator==(const MD5Hash &other) const {
- return hash[0] == other.hash[0] && hash[1] == other.hash[1];
-}
-
-#if SANITIZER_DEBUG
-void build_consistency_debug() {}
-#else
-void build_consistency_release() {}
-#endif
-
-#if TSAN_COLLECT_STATS
-void build_consistency_stats() {}
-#else
-void build_consistency_nostats() {}
-#endif
-
-} // namespace __tsan
-
-#if !SANITIZER_GO
-// Must be included in this file to make sure everything is inlined.
-#include "tsan_interface_inl.h"
-#endif
--- /dev/null
+//===-- tsan_rtl.cpp ------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+// Main file (entry points) for the TSan run-time.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_atomic.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_file.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "tsan_defs.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_suppressions.h"
+#include "tsan_symbolize.h"
+#include "ubsan/ubsan_init.h"
+
+#ifdef __SSE3__
+// <emmintrin.h> transitively includes <stdlib.h>,
+// and it's prohibited to include std headers into tsan runtime.
+// So we do this dirty trick.
+#define _MM_MALLOC_H_INCLUDED
+#define __MM_MALLOC_H
+#include <emmintrin.h>
+typedef __m128i m128;
+#endif
+
+volatile int __tsan_resumed = 0;
+
+extern "C" void __tsan_resume() {
+ __tsan_resumed = 1;
+}
+
+namespace __tsan {
+
+#if !SANITIZER_GO && !SANITIZER_MAC
+__attribute__((tls_model("initial-exec")))
+THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64);
+#endif
+static char ctx_placeholder[sizeof(Context)] ALIGNED(64);
+Context *ctx;
+
+// Can be overriden by a front-end.
+#ifdef TSAN_EXTERNAL_HOOKS
+bool OnFinalize(bool failed);
+void OnInitialize();
+#else
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+bool OnFinalize(bool failed) {
+ return failed;
+}
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+void OnInitialize() {}
+#endif
+
+static char thread_registry_placeholder[sizeof(ThreadRegistry)];
+
+static ThreadContextBase *CreateThreadContext(u32 tid) {
+ // Map thread trace when context is created.
+ char name[50];
+ internal_snprintf(name, sizeof(name), "trace %u", tid);
+ MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event), name);
+ const uptr hdr = GetThreadTraceHeader(tid);
+ internal_snprintf(name, sizeof(name), "trace header %u", tid);
+ MapThreadTrace(hdr, sizeof(Trace), name);
+ new((void*)hdr) Trace();
+ // We are going to use only a small part of the trace with the default
+ // value of history_size. However, the constructor writes to the whole trace.
+ // Unmap the unused part.
+ uptr hdr_end = hdr + sizeof(Trace);
+ hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts());
+ hdr_end = RoundUp(hdr_end, GetPageSizeCached());
+ if (hdr_end < hdr + sizeof(Trace))
+ UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end);
+ void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext));
+ return new(mem) ThreadContext(tid);
+}
+
+#if !SANITIZER_GO
+static const u32 kThreadQuarantineSize = 16;
+#else
+static const u32 kThreadQuarantineSize = 64;
+#endif
+
+Context::Context()
+ : initialized()
+ , report_mtx(MutexTypeReport, StatMtxReport)
+ , nreported()
+ , nmissed_expected()
+ , thread_registry(new(thread_registry_placeholder) ThreadRegistry(
+ CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse))
+ , racy_mtx(MutexTypeRacy, StatMtxRacy)
+ , racy_stacks()
+ , racy_addresses()
+ , fired_suppressions_mtx(MutexTypeFired, StatMtxFired)
+ , clock_alloc("clock allocator") {
+ fired_suppressions.reserve(8);
+}
+
+// The objects are allocated in TLS, so one may rely on zero-initialization.
+ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch,
+ unsigned reuse_count,
+ uptr stk_addr, uptr stk_size,
+ uptr tls_addr, uptr tls_size)
+ : fast_state(tid, epoch)
+ // Do not touch these, rely on zero initialization,
+ // they may be accessed before the ctor.
+ // , ignore_reads_and_writes()
+ // , ignore_interceptors()
+ , clock(tid, reuse_count)
+#if !SANITIZER_GO
+ , jmp_bufs()
+#endif
+ , tid(tid)
+ , unique_id(unique_id)
+ , stk_addr(stk_addr)
+ , stk_size(stk_size)
+ , tls_addr(tls_addr)
+ , tls_size(tls_size)
+#if !SANITIZER_GO
+ , last_sleep_clock(tid)
+#endif
+{
+}
+
+#if !SANITIZER_GO
+static void MemoryProfiler(Context *ctx, fd_t fd, int i) {
+ uptr n_threads;
+ uptr n_running_threads;
+ ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads);
+ InternalMmapVector<char> buf(4096);
+ WriteMemoryProfile(buf.data(), buf.size(), n_threads, n_running_threads);
+ WriteToFile(fd, buf.data(), internal_strlen(buf.data()));
+}
+
+static void BackgroundThread(void *arg) {
+ // This is a non-initialized non-user thread, nothing to see here.
+ // We don't use ScopedIgnoreInterceptors, because we want ignores to be
+ // enabled even when the thread function exits (e.g. during pthread thread
+ // shutdown code).
+ cur_thread_init();
+ cur_thread()->ignore_interceptors++;
+ const u64 kMs2Ns = 1000 * 1000;
+
+ fd_t mprof_fd = kInvalidFd;
+ if (flags()->profile_memory && flags()->profile_memory[0]) {
+ if (internal_strcmp(flags()->profile_memory, "stdout") == 0) {
+ mprof_fd = 1;
+ } else if (internal_strcmp(flags()->profile_memory, "stderr") == 0) {
+ mprof_fd = 2;
+ } else {
+ InternalScopedString filename(kMaxPathLength);
+ filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid());
+ fd_t fd = OpenFile(filename.data(), WrOnly);
+ if (fd == kInvalidFd) {
+ Printf("ThreadSanitizer: failed to open memory profile file '%s'\n",
+ &filename[0]);
+ } else {
+ mprof_fd = fd;
+ }
+ }
+ }
+
+ u64 last_flush = NanoTime();
+ uptr last_rss = 0;
+ for (int i = 0;
+ atomic_load(&ctx->stop_background_thread, memory_order_relaxed) == 0;
+ i++) {
+ SleepForMillis(100);
+ u64 now = NanoTime();
+
+ // Flush memory if requested.
+ if (flags()->flush_memory_ms > 0) {
+ if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) {
+ VPrintf(1, "ThreadSanitizer: periodic memory flush\n");
+ FlushShadowMemory();
+ last_flush = NanoTime();
+ }
+ }
+ // GetRSS can be expensive on huge programs, so don't do it every 100ms.
+ if (flags()->memory_limit_mb > 0) {
+ uptr rss = GetRSS();
+ uptr limit = uptr(flags()->memory_limit_mb) << 20;
+ VPrintf(1, "ThreadSanitizer: memory flush check"
+ " RSS=%llu LAST=%llu LIMIT=%llu\n",
+ (u64)rss >> 20, (u64)last_rss >> 20, (u64)limit >> 20);
+ if (2 * rss > limit + last_rss) {
+ VPrintf(1, "ThreadSanitizer: flushing memory due to RSS\n");
+ FlushShadowMemory();
+ rss = GetRSS();
+ VPrintf(1, "ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20);
+ }
+ last_rss = rss;
+ }
+
+ // Write memory profile if requested.
+ if (mprof_fd != kInvalidFd)
+ MemoryProfiler(ctx, mprof_fd, i);
+
+ // Flush symbolizer cache if requested.
+ if (flags()->flush_symbolizer_ms > 0) {
+ u64 last = atomic_load(&ctx->last_symbolize_time_ns,
+ memory_order_relaxed);
+ if (last != 0 && last + flags()->flush_symbolizer_ms * kMs2Ns < now) {
+ Lock l(&ctx->report_mtx);
+ ScopedErrorReportLock l2;
+ SymbolizeFlush();
+ atomic_store(&ctx->last_symbolize_time_ns, 0, memory_order_relaxed);
+ }
+ }
+ }
+}
+
+static void StartBackgroundThread() {
+ ctx->background_thread = internal_start_thread(&BackgroundThread, 0);
+}
+
+#ifndef __mips__
+static void StopBackgroundThread() {
+ atomic_store(&ctx->stop_background_thread, 1, memory_order_relaxed);
+ internal_join_thread(ctx->background_thread);
+ ctx->background_thread = 0;
+}
+#endif
+#endif
+
+void DontNeedShadowFor(uptr addr, uptr size) {
+ ReleaseMemoryPagesToOS(MemToShadow(addr), MemToShadow(addr + size));
+}
+
+void MapShadow(uptr addr, uptr size) {
+ // Global data is not 64K aligned, but there are no adjacent mappings,
+ // so we can get away with unaligned mapping.
+ // CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
+ const uptr kPageSize = GetPageSizeCached();
+ uptr shadow_begin = RoundDownTo((uptr)MemToShadow(addr), kPageSize);
+ uptr shadow_end = RoundUpTo((uptr)MemToShadow(addr + size), kPageSize);
+ if (!MmapFixedNoReserve(shadow_begin, shadow_end - shadow_begin, "shadow"))
+ Die();
+
+ // Meta shadow is 2:1, so tread carefully.
+ static bool data_mapped = false;
+ static uptr mapped_meta_end = 0;
+ uptr meta_begin = (uptr)MemToMeta(addr);
+ uptr meta_end = (uptr)MemToMeta(addr + size);
+ meta_begin = RoundDownTo(meta_begin, 64 << 10);
+ meta_end = RoundUpTo(meta_end, 64 << 10);
+ if (!data_mapped) {
+ // First call maps data+bss.
+ data_mapped = true;
+ if (!MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow"))
+ Die();
+ } else {
+ // Mapping continous heap.
+ // Windows wants 64K alignment.
+ meta_begin = RoundDownTo(meta_begin, 64 << 10);
+ meta_end = RoundUpTo(meta_end, 64 << 10);
+ if (meta_end <= mapped_meta_end)
+ return;
+ if (meta_begin < mapped_meta_end)
+ meta_begin = mapped_meta_end;
+ if (!MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow"))
+ Die();
+ mapped_meta_end = meta_end;
+ }
+ VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n",
+ addr, addr+size, meta_begin, meta_end);
+}
+
+void MapThreadTrace(uptr addr, uptr size, const char *name) {
+ DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
+ CHECK_GE(addr, TraceMemBeg());
+ CHECK_LE(addr + size, TraceMemEnd());
+ CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
+ if (!MmapFixedNoReserve(addr, size, name)) {
+ Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p)\n",
+ addr, size);
+ Die();
+ }
+}
+
+static void CheckShadowMapping() {
+ uptr beg, end;
+ for (int i = 0; GetUserRegion(i, &beg, &end); i++) {
+ // Skip cases for empty regions (heap definition for architectures that
+ // do not use 64-bit allocator).
+ if (beg == end)
+ continue;
+ VPrintf(3, "checking shadow region %p-%p\n", beg, end);
+ uptr prev = 0;
+ for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) {
+ for (int x = -(int)kShadowCell; x <= (int)kShadowCell; x += kShadowCell) {
+ const uptr p = RoundDown(p0 + x, kShadowCell);
+ if (p < beg || p >= end)
+ continue;
+ const uptr s = MemToShadow(p);
+ const uptr m = (uptr)MemToMeta(p);
+ VPrintf(3, " checking pointer %p: shadow=%p meta=%p\n", p, s, m);
+ CHECK(IsAppMem(p));
+ CHECK(IsShadowMem(s));
+ CHECK_EQ(p, ShadowToMem(s));
+ CHECK(IsMetaMem(m));
+ if (prev) {
+ // Ensure that shadow and meta mappings are linear within a single
+ // user range. Lots of code that processes memory ranges assumes it.
+ const uptr prev_s = MemToShadow(prev);
+ const uptr prev_m = (uptr)MemToMeta(prev);
+ CHECK_EQ(s - prev_s, (p - prev) * kShadowMultiplier);
+ CHECK_EQ((m - prev_m) / kMetaShadowSize,
+ (p - prev) / kMetaShadowCell);
+ }
+ prev = p;
+ }
+ }
+ }
+}
+
+#if !SANITIZER_GO
+static void OnStackUnwind(const SignalContext &sig, const void *,
+ BufferedStackTrace *stack) {
+ stack->Unwind(sig.pc, sig.bp, sig.context,
+ common_flags()->fast_unwind_on_fatal);
+}
+
+static void TsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ HandleDeadlySignal(siginfo, context, GetTid(), &OnStackUnwind, nullptr);
+}
+#endif
+
+void Initialize(ThreadState *thr) {
+ // Thread safe because done before all threads exist.
+ static bool is_initialized = false;
+ if (is_initialized)
+ return;
+ is_initialized = true;
+ // We are not ready to handle interceptors yet.
+ ScopedIgnoreInterceptors ignore;
+ SanitizerToolName = "ThreadSanitizer";
+ // Install tool-specific callbacks in sanitizer_common.
+ SetCheckFailedCallback(TsanCheckFailed);
+
+ ctx = new(ctx_placeholder) Context;
+ const char *env_name = SANITIZER_GO ? "GORACE" : "TSAN_OPTIONS";
+ const char *options = GetEnv(env_name);
+ CacheBinaryName();
+ CheckASLR();
+ InitializeFlags(&ctx->flags, options, env_name);
+ AvoidCVE_2016_2143();
+ __sanitizer::InitializePlatformEarly();
+ __tsan::InitializePlatformEarly();
+
+#if !SANITIZER_GO
+ // Re-exec ourselves if we need to set additional env or command line args.
+ MaybeReexec();
+
+ InitializeAllocator();
+ ReplaceSystemMalloc();
+#endif
+ if (common_flags()->detect_deadlocks)
+ ctx->dd = DDetector::Create(flags());
+ Processor *proc = ProcCreate();
+ ProcWire(proc, thr);
+ InitializeInterceptors();
+ CheckShadowMapping();
+ InitializePlatform();
+ InitializeMutex();
+ InitializeDynamicAnnotations();
+#if !SANITIZER_GO
+ InitializeShadowMemory();
+ InitializeAllocatorLate();
+ InstallDeadlySignalHandlers(TsanOnDeadlySignal);
+#endif
+ // Setup correct file descriptor for error reports.
+ __sanitizer_set_report_path(common_flags()->log_path);
+ InitializeSuppressions();
+#if !SANITIZER_GO
+ InitializeLibIgnore();
+ Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer);
+#endif
+
+ VPrintf(1, "***** Running under ThreadSanitizer v2 (pid %d) *****\n",
+ (int)internal_getpid());
+
+ // Initialize thread 0.
+ int tid = ThreadCreate(thr, 0, 0, true);
+ CHECK_EQ(tid, 0);
+ ThreadStart(thr, tid, GetTid(), ThreadType::Regular);
+#if TSAN_CONTAINS_UBSAN
+ __ubsan::InitAsPlugin();
+#endif
+ ctx->initialized = true;
+
+#if !SANITIZER_GO
+ Symbolizer::LateInitialize();
+#endif
+
+ if (flags()->stop_on_start) {
+ Printf("ThreadSanitizer is suspended at startup (pid %d)."
+ " Call __tsan_resume().\n",
+ (int)internal_getpid());
+ while (__tsan_resumed == 0) {}
+ }
+
+ OnInitialize();
+}
+
+void MaybeSpawnBackgroundThread() {
+ // On MIPS, TSan initialization is run before
+ // __pthread_initialize_minimal_internal() is finished, so we can not spawn
+ // new threads.
+#if !SANITIZER_GO && !defined(__mips__)
+ static atomic_uint32_t bg_thread = {};
+ if (atomic_load(&bg_thread, memory_order_relaxed) == 0 &&
+ atomic_exchange(&bg_thread, 1, memory_order_relaxed) == 0) {
+ StartBackgroundThread();
+ SetSandboxingCallback(StopBackgroundThread);
+ }
+#endif
+}
+
+
+int Finalize(ThreadState *thr) {
+ bool failed = false;
+
+ if (common_flags()->print_module_map == 1) PrintModuleMap();
+
+ if (flags()->atexit_sleep_ms > 0 && ThreadCount(thr) > 1)
+ SleepForMillis(flags()->atexit_sleep_ms);
+
+ // Wait for pending reports.
+ ctx->report_mtx.Lock();
+ { ScopedErrorReportLock l; }
+ ctx->report_mtx.Unlock();
+
+#if !SANITIZER_GO
+ if (Verbosity()) AllocatorPrintStats();
+#endif
+
+ ThreadFinalize(thr);
+
+ if (ctx->nreported) {
+ failed = true;
+#if !SANITIZER_GO
+ Printf("ThreadSanitizer: reported %d warnings\n", ctx->nreported);
+#else
+ Printf("Found %d data race(s)\n", ctx->nreported);
+#endif
+ }
+
+ if (ctx->nmissed_expected) {
+ failed = true;
+ Printf("ThreadSanitizer: missed %d expected races\n",
+ ctx->nmissed_expected);
+ }
+
+ if (common_flags()->print_suppressions)
+ PrintMatchedSuppressions();
+#if !SANITIZER_GO
+ if (flags()->print_benign)
+ PrintMatchedBenignRaces();
+#endif
+
+ failed = OnFinalize(failed);
+
+#if TSAN_COLLECT_STATS
+ StatAggregate(ctx->stat, thr->stat);
+ StatOutput(ctx->stat);
+#endif
+
+ return failed ? common_flags()->exitcode : 0;
+}
+
+#if !SANITIZER_GO
+void ForkBefore(ThreadState *thr, uptr pc) {
+ ctx->thread_registry->Lock();
+ ctx->report_mtx.Lock();
+}
+
+void ForkParentAfter(ThreadState *thr, uptr pc) {
+ ctx->report_mtx.Unlock();
+ ctx->thread_registry->Unlock();
+}
+
+void ForkChildAfter(ThreadState *thr, uptr pc) {
+ ctx->report_mtx.Unlock();
+ ctx->thread_registry->Unlock();
+
+ uptr nthread = 0;
+ ctx->thread_registry->GetNumberOfThreads(0, 0, &nthread /* alive threads */);
+ VPrintf(1, "ThreadSanitizer: forked new process with pid %d,"
+ " parent had %d threads\n", (int)internal_getpid(), (int)nthread);
+ if (nthread == 1) {
+ StartBackgroundThread();
+ } else {
+ // We've just forked a multi-threaded process. We cannot reasonably function
+ // after that (some mutexes may be locked before fork). So just enable
+ // ignores for everything in the hope that we will exec soon.
+ ctx->after_multithreaded_fork = true;
+ thr->ignore_interceptors++;
+ ThreadIgnoreBegin(thr, pc);
+ ThreadIgnoreSyncBegin(thr, pc);
+ }
+}
+#endif
+
+#if SANITIZER_GO
+NOINLINE
+void GrowShadowStack(ThreadState *thr) {
+ const int sz = thr->shadow_stack_end - thr->shadow_stack;
+ const int newsz = 2 * sz;
+ uptr *newstack = (uptr*)internal_alloc(MBlockShadowStack,
+ newsz * sizeof(uptr));
+ internal_memcpy(newstack, thr->shadow_stack, sz * sizeof(uptr));
+ internal_free(thr->shadow_stack);
+ thr->shadow_stack = newstack;
+ thr->shadow_stack_pos = newstack + sz;
+ thr->shadow_stack_end = newstack + newsz;
+}
+#endif
+
+u32 CurrentStackId(ThreadState *thr, uptr pc) {
+ if (!thr->is_inited) // May happen during bootstrap.
+ return 0;
+ if (pc != 0) {
+#if !SANITIZER_GO
+ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
+#else
+ if (thr->shadow_stack_pos == thr->shadow_stack_end)
+ GrowShadowStack(thr);
+#endif
+ thr->shadow_stack_pos[0] = pc;
+ thr->shadow_stack_pos++;
+ }
+ u32 id = StackDepotPut(
+ StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack));
+ if (pc != 0)
+ thr->shadow_stack_pos--;
+ return id;
+}
+
+void TraceSwitch(ThreadState *thr) {
+#if !SANITIZER_GO
+ if (ctx->after_multithreaded_fork)
+ return;
+#endif
+ thr->nomalloc++;
+ Trace *thr_trace = ThreadTrace(thr->tid);
+ Lock l(&thr_trace->mtx);
+ unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
+ TraceHeader *hdr = &thr_trace->headers[trace];
+ hdr->epoch0 = thr->fast_state.epoch();
+ ObtainCurrentStack(thr, 0, &hdr->stack0);
+ hdr->mset0 = thr->mset;
+ thr->nomalloc--;
+}
+
+Trace *ThreadTrace(int tid) {
+ return (Trace*)GetThreadTraceHeader(tid);
+}
+
+uptr TraceTopPC(ThreadState *thr) {
+ Event *events = (Event*)GetThreadTrace(thr->tid);
+ uptr pc = events[thr->fast_state.GetTracePos()];
+ return pc;
+}
+
+uptr TraceSize() {
+ return (uptr)(1ull << (kTracePartSizeBits + flags()->history_size + 1));
+}
+
+uptr TraceParts() {
+ return TraceSize() / kTracePartSize;
+}
+
+#if !SANITIZER_GO
+extern "C" void __tsan_trace_switch() {
+ TraceSwitch(cur_thread());
+}
+
+extern "C" void __tsan_report_race() {
+ ReportRace(cur_thread());
+}
+#endif
+
+ALWAYS_INLINE
+Shadow LoadShadow(u64 *p) {
+ u64 raw = atomic_load((atomic_uint64_t*)p, memory_order_relaxed);
+ return Shadow(raw);
+}
+
+ALWAYS_INLINE
+void StoreShadow(u64 *sp, u64 s) {
+ atomic_store((atomic_uint64_t*)sp, s, memory_order_relaxed);
+}
+
+ALWAYS_INLINE
+void StoreIfNotYetStored(u64 *sp, u64 *s) {
+ StoreShadow(sp, *s);
+ *s = 0;
+}
+
+ALWAYS_INLINE
+void HandleRace(ThreadState *thr, u64 *shadow_mem,
+ Shadow cur, Shadow old) {
+ thr->racy_state[0] = cur.raw();
+ thr->racy_state[1] = old.raw();
+ thr->racy_shadow_addr = shadow_mem;
+#if !SANITIZER_GO
+ HACKY_CALL(__tsan_report_race);
+#else
+ ReportRace(thr);
+#endif
+}
+
+static inline bool HappensBefore(Shadow old, ThreadState *thr) {
+ return thr->clock.get(old.TidWithIgnore()) >= old.epoch();
+}
+
+ALWAYS_INLINE
+void MemoryAccessImpl1(ThreadState *thr, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic,
+ u64 *shadow_mem, Shadow cur) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+
+ // This potentially can live in an MMX/SSE scratch register.
+ // The required intrinsics are:
+ // __m128i _mm_move_epi64(__m128i*);
+ // _mm_storel_epi64(u64*, __m128i);
+ u64 store_word = cur.raw();
+ bool stored = false;
+
+ // scan all the shadow values and dispatch to 4 categories:
+ // same, replace, candidate and race (see comments below).
+ // we consider only 3 cases regarding access sizes:
+ // equal, intersect and not intersect. initially I considered
+ // larger and smaller as well, it allowed to replace some
+ // 'candidates' with 'same' or 'replace', but I think
+ // it's just not worth it (performance- and complexity-wise).
+
+ Shadow old(0);
+
+ // It release mode we manually unroll the loop,
+ // because empirically gcc generates better code this way.
+ // However, we can't afford unrolling in debug mode, because the function
+ // consumes almost 4K of stack. Gtest gives only 4K of stack to death test
+ // threads, which is not enough for the unrolled loop.
+#if SANITIZER_DEBUG
+ for (int idx = 0; idx < 4; idx++) {
+#include "tsan_update_shadow_word_inl.h"
+ }
+#else
+ int idx = 0;
+#include "tsan_update_shadow_word_inl.h"
+ idx = 1;
+ if (stored) {
+#include "tsan_update_shadow_word_inl.h"
+ } else {
+#include "tsan_update_shadow_word_inl.h"
+ }
+ idx = 2;
+ if (stored) {
+#include "tsan_update_shadow_word_inl.h"
+ } else {
+#include "tsan_update_shadow_word_inl.h"
+ }
+ idx = 3;
+ if (stored) {
+#include "tsan_update_shadow_word_inl.h"
+ } else {
+#include "tsan_update_shadow_word_inl.h"
+ }
+#endif
+
+ // we did not find any races and had already stored
+ // the current access info, so we are done
+ if (LIKELY(stored))
+ return;
+ // choose a random candidate slot and replace it
+ StoreShadow(shadow_mem + (cur.epoch() % kShadowCnt), store_word);
+ StatInc(thr, StatShadowReplace);
+ return;
+ RACE:
+ HandleRace(thr, shadow_mem, cur, old);
+ return;
+}
+
+void UnalignedMemoryAccess(ThreadState *thr, uptr pc, uptr addr,
+ int size, bool kAccessIsWrite, bool kIsAtomic) {
+ while (size) {
+ int size1 = 1;
+ int kAccessSizeLog = kSizeLog1;
+ if (size >= 8 && (addr & ~7) == ((addr + 7) & ~7)) {
+ size1 = 8;
+ kAccessSizeLog = kSizeLog8;
+ } else if (size >= 4 && (addr & ~7) == ((addr + 3) & ~7)) {
+ size1 = 4;
+ kAccessSizeLog = kSizeLog4;
+ } else if (size >= 2 && (addr & ~7) == ((addr + 1) & ~7)) {
+ size1 = 2;
+ kAccessSizeLog = kSizeLog2;
+ }
+ MemoryAccess(thr, pc, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic);
+ addr += size1;
+ size -= size1;
+ }
+}
+
+ALWAYS_INLINE
+bool ContainsSameAccessSlow(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
+ Shadow cur(a);
+ for (uptr i = 0; i < kShadowCnt; i++) {
+ Shadow old(LoadShadow(&s[i]));
+ if (Shadow::Addr0AndSizeAreEqual(cur, old) &&
+ old.TidWithIgnore() == cur.TidWithIgnore() &&
+ old.epoch() > sync_epoch &&
+ old.IsAtomic() == cur.IsAtomic() &&
+ old.IsRead() <= cur.IsRead())
+ return true;
+ }
+ return false;
+}
+
+#if defined(__SSE3__)
+#define SHUF(v0, v1, i0, i1, i2, i3) _mm_castps_si128(_mm_shuffle_ps( \
+ _mm_castsi128_ps(v0), _mm_castsi128_ps(v1), \
+ (i0)*1 + (i1)*4 + (i2)*16 + (i3)*64))
+ALWAYS_INLINE
+bool ContainsSameAccessFast(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
+ // This is an optimized version of ContainsSameAccessSlow.
+ // load current access into access[0:63]
+ const m128 access = _mm_cvtsi64_si128(a);
+ // duplicate high part of access in addr0:
+ // addr0[0:31] = access[32:63]
+ // addr0[32:63] = access[32:63]
+ // addr0[64:95] = access[32:63]
+ // addr0[96:127] = access[32:63]
+ const m128 addr0 = SHUF(access, access, 1, 1, 1, 1);
+ // load 4 shadow slots
+ const m128 shadow0 = _mm_load_si128((__m128i*)s);
+ const m128 shadow1 = _mm_load_si128((__m128i*)s + 1);
+ // load high parts of 4 shadow slots into addr_vect:
+ // addr_vect[0:31] = shadow0[32:63]
+ // addr_vect[32:63] = shadow0[96:127]
+ // addr_vect[64:95] = shadow1[32:63]
+ // addr_vect[96:127] = shadow1[96:127]
+ m128 addr_vect = SHUF(shadow0, shadow1, 1, 3, 1, 3);
+ if (!is_write) {
+ // set IsRead bit in addr_vect
+ const m128 rw_mask1 = _mm_cvtsi64_si128(1<<15);
+ const m128 rw_mask = SHUF(rw_mask1, rw_mask1, 0, 0, 0, 0);
+ addr_vect = _mm_or_si128(addr_vect, rw_mask);
+ }
+ // addr0 == addr_vect?
+ const m128 addr_res = _mm_cmpeq_epi32(addr0, addr_vect);
+ // epoch1[0:63] = sync_epoch
+ const m128 epoch1 = _mm_cvtsi64_si128(sync_epoch);
+ // epoch[0:31] = sync_epoch[0:31]
+ // epoch[32:63] = sync_epoch[0:31]
+ // epoch[64:95] = sync_epoch[0:31]
+ // epoch[96:127] = sync_epoch[0:31]
+ const m128 epoch = SHUF(epoch1, epoch1, 0, 0, 0, 0);
+ // load low parts of shadow cell epochs into epoch_vect:
+ // epoch_vect[0:31] = shadow0[0:31]
+ // epoch_vect[32:63] = shadow0[64:95]
+ // epoch_vect[64:95] = shadow1[0:31]
+ // epoch_vect[96:127] = shadow1[64:95]
+ const m128 epoch_vect = SHUF(shadow0, shadow1, 0, 2, 0, 2);
+ // epoch_vect >= sync_epoch?
+ const m128 epoch_res = _mm_cmpgt_epi32(epoch_vect, epoch);
+ // addr_res & epoch_res
+ const m128 res = _mm_and_si128(addr_res, epoch_res);
+ // mask[0] = res[7]
+ // mask[1] = res[15]
+ // ...
+ // mask[15] = res[127]
+ const int mask = _mm_movemask_epi8(res);
+ return mask != 0;
+}
+#endif
+
+ALWAYS_INLINE
+bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
+#if defined(__SSE3__)
+ bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write);
+ // NOTE: this check can fail if the shadow is concurrently mutated
+ // by other threads. But it still can be useful if you modify
+ // ContainsSameAccessFast and want to ensure that it's not completely broken.
+ // DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write));
+ return res;
+#else
+ return ContainsSameAccessSlow(s, a, sync_epoch, is_write);
+#endif
+}
+
+ALWAYS_INLINE USED
+void MemoryAccess(ThreadState *thr, uptr pc, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic) {
+ u64 *shadow_mem = (u64*)MemToShadow(addr);
+ DPrintf2("#%d: MemoryAccess: @%p %p size=%d"
+ " is_write=%d shadow_mem=%p {%zx, %zx, %zx, %zx}\n",
+ (int)thr->fast_state.tid(), (void*)pc, (void*)addr,
+ (int)(1 << kAccessSizeLog), kAccessIsWrite, shadow_mem,
+ (uptr)shadow_mem[0], (uptr)shadow_mem[1],
+ (uptr)shadow_mem[2], (uptr)shadow_mem[3]);
+#if SANITIZER_DEBUG
+ if (!IsAppMem(addr)) {
+ Printf("Access to non app mem %zx\n", addr);
+ DCHECK(IsAppMem(addr));
+ }
+ if (!IsShadowMem((uptr)shadow_mem)) {
+ Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+ DCHECK(IsShadowMem((uptr)shadow_mem));
+ }
+#endif
+
+ if (!SANITIZER_GO && !kAccessIsWrite && *shadow_mem == kShadowRodata) {
+ // Access to .rodata section, no races here.
+ // Measurements show that it can be 10-20% of all memory accesses.
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopRodata);
+ return;
+ }
+
+ FastState fast_state = thr->fast_state;
+ if (UNLIKELY(fast_state.GetIgnoreBit())) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopIgnored);
+ return;
+ }
+
+ Shadow cur(fast_state);
+ cur.SetAddr0AndSizeLog(addr & 7, kAccessSizeLog);
+ cur.SetWrite(kAccessIsWrite);
+ cur.SetAtomic(kIsAtomic);
+
+ if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(),
+ thr->fast_synch_epoch, kAccessIsWrite))) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopSame);
+ return;
+ }
+
+ if (kCollectHistory) {
+ fast_state.IncrementEpoch();
+ thr->fast_state = fast_state;
+ TraceAddEvent(thr, fast_state, EventTypeMop, pc);
+ cur.IncrementEpoch();
+ }
+
+ MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
+ shadow_mem, cur);
+}
+
+// Called by MemoryAccessRange in tsan_rtl_thread.cpp
+ALWAYS_INLINE USED
+void MemoryAccessImpl(ThreadState *thr, uptr addr,
+ int kAccessSizeLog, bool kAccessIsWrite, bool kIsAtomic,
+ u64 *shadow_mem, Shadow cur) {
+ if (LIKELY(ContainsSameAccess(shadow_mem, cur.raw(),
+ thr->fast_synch_epoch, kAccessIsWrite))) {
+ StatInc(thr, StatMop);
+ StatInc(thr, kAccessIsWrite ? StatMopWrite : StatMopRead);
+ StatInc(thr, (StatType)(StatMop1 + kAccessSizeLog));
+ StatInc(thr, StatMopSame);
+ return;
+ }
+
+ MemoryAccessImpl1(thr, addr, kAccessSizeLog, kAccessIsWrite, kIsAtomic,
+ shadow_mem, cur);
+}
+
+static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size,
+ u64 val) {
+ (void)thr;
+ (void)pc;
+ if (size == 0)
+ return;
+ // FIXME: fix me.
+ uptr offset = addr % kShadowCell;
+ if (offset) {
+ offset = kShadowCell - offset;
+ if (size <= offset)
+ return;
+ addr += offset;
+ size -= offset;
+ }
+ DCHECK_EQ(addr % 8, 0);
+ // If a user passes some insane arguments (memset(0)),
+ // let it just crash as usual.
+ if (!IsAppMem(addr) || !IsAppMem(addr + size - 1))
+ return;
+ // Don't want to touch lots of shadow memory.
+ // If a program maps 10MB stack, there is no need reset the whole range.
+ size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1);
+ // UnmapOrDie/MmapFixedNoReserve does not work on Windows.
+ if (SANITIZER_WINDOWS || size < common_flags()->clear_shadow_mmap_threshold) {
+ u64 *p = (u64*)MemToShadow(addr);
+ CHECK(IsShadowMem((uptr)p));
+ CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1)));
+ // FIXME: may overwrite a part outside the region
+ for (uptr i = 0; i < size / kShadowCell * kShadowCnt;) {
+ p[i++] = val;
+ for (uptr j = 1; j < kShadowCnt; j++)
+ p[i++] = 0;
+ }
+ } else {
+ // The region is big, reset only beginning and end.
+ const uptr kPageSize = GetPageSizeCached();
+ u64 *begin = (u64*)MemToShadow(addr);
+ u64 *end = begin + size / kShadowCell * kShadowCnt;
+ u64 *p = begin;
+ // Set at least first kPageSize/2 to page boundary.
+ while ((p < begin + kPageSize / kShadowSize / 2) || ((uptr)p % kPageSize)) {
+ *p++ = val;
+ for (uptr j = 1; j < kShadowCnt; j++)
+ *p++ = 0;
+ }
+ // Reset middle part.
+ u64 *p1 = p;
+ p = RoundDown(end, kPageSize);
+ UnmapOrDie((void*)p1, (uptr)p - (uptr)p1);
+ if (!MmapFixedNoReserve((uptr)p1, (uptr)p - (uptr)p1))
+ Die();
+ // Set the ending.
+ while (p < end) {
+ *p++ = val;
+ for (uptr j = 1; j < kShadowCnt; j++)
+ *p++ = 0;
+ }
+ }
+}
+
+void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+ MemoryRangeSet(thr, pc, addr, size, 0);
+}
+
+void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+ // Processing more than 1k (4k of shadow) is expensive,
+ // can cause excessive memory consumption (user does not necessary touch
+ // the whole range) and most likely unnecessary.
+ if (size > 1024)
+ size = 1024;
+ CHECK_EQ(thr->is_freeing, false);
+ thr->is_freeing = true;
+ MemoryAccessRange(thr, pc, addr, size, true);
+ thr->is_freeing = false;
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
+ }
+ Shadow s(thr->fast_state);
+ s.ClearIgnoreBit();
+ s.MarkAsFreed();
+ s.SetWrite(true);
+ s.SetAddr0AndSizeLog(0, 3);
+ MemoryRangeSet(thr, pc, addr, size, s.raw());
+}
+
+void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, pc);
+ }
+ Shadow s(thr->fast_state);
+ s.ClearIgnoreBit();
+ s.SetWrite(true);
+ s.SetAddr0AndSizeLog(0, 3);
+ MemoryRangeSet(thr, pc, addr, size, s.raw());
+}
+
+ALWAYS_INLINE USED
+void FuncEntry(ThreadState *thr, uptr pc) {
+ StatInc(thr, StatFuncEnter);
+ DPrintf2("#%d: FuncEntry %p\n", (int)thr->fast_state.tid(), (void*)pc);
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeFuncEnter, pc);
+ }
+
+ // Shadow stack maintenance can be replaced with
+ // stack unwinding during trace switch (which presumably must be faster).
+ DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack);
+#if !SANITIZER_GO
+ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
+#else
+ if (thr->shadow_stack_pos == thr->shadow_stack_end)
+ GrowShadowStack(thr);
+#endif
+ thr->shadow_stack_pos[0] = pc;
+ thr->shadow_stack_pos++;
+}
+
+ALWAYS_INLINE USED
+void FuncExit(ThreadState *thr) {
+ StatInc(thr, StatFuncExit);
+ DPrintf2("#%d: FuncExit\n", (int)thr->fast_state.tid());
+ if (kCollectHistory) {
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0);
+ }
+
+ DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack);
+#if !SANITIZER_GO
+ DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end);
+#endif
+ thr->shadow_stack_pos--;
+}
+
+void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack) {
+ DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid);
+ thr->ignore_reads_and_writes++;
+ CHECK_GT(thr->ignore_reads_and_writes, 0);
+ thr->fast_state.SetIgnoreBit();
+#if !SANITIZER_GO
+ if (save_stack && !ctx->after_multithreaded_fork)
+ thr->mop_ignore_set.Add(CurrentStackId(thr, pc));
+#endif
+}
+
+void ThreadIgnoreEnd(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid);
+ CHECK_GT(thr->ignore_reads_and_writes, 0);
+ thr->ignore_reads_and_writes--;
+ if (thr->ignore_reads_and_writes == 0) {
+ thr->fast_state.ClearIgnoreBit();
+#if !SANITIZER_GO
+ thr->mop_ignore_set.Reset();
+#endif
+ }
+}
+
+#if !SANITIZER_GO
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+uptr __tsan_testonly_shadow_stack_current_size() {
+ ThreadState *thr = cur_thread();
+ return thr->shadow_stack_pos - thr->shadow_stack;
+}
+#endif
+
+void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc, bool save_stack) {
+ DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid);
+ thr->ignore_sync++;
+ CHECK_GT(thr->ignore_sync, 0);
+#if !SANITIZER_GO
+ if (save_stack && !ctx->after_multithreaded_fork)
+ thr->sync_ignore_set.Add(CurrentStackId(thr, pc));
+#endif
+}
+
+void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid);
+ CHECK_GT(thr->ignore_sync, 0);
+ thr->ignore_sync--;
+#if !SANITIZER_GO
+ if (thr->ignore_sync == 0)
+ thr->sync_ignore_set.Reset();
+#endif
+}
+
+bool MD5Hash::operator==(const MD5Hash &other) const {
+ return hash[0] == other.hash[0] && hash[1] == other.hash[1];
+}
+
+#if SANITIZER_DEBUG
+void build_consistency_debug() {}
+#else
+void build_consistency_release() {}
+#endif
+
+#if TSAN_COLLECT_STATS
+void build_consistency_stats() {}
+#else
+void build_consistency_nostats() {}
+#endif
+
+} // namespace __tsan
+
+#if !SANITIZER_GO
+// Must be included in this file to make sure everything is inlined.
+#include "tsan_interface_inl.h"
+#endif
//===-- tsan_rtl.h ----------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#if !SANITIZER_GO
struct MapUnmapCallback;
#if defined(__mips64) || defined(__aarch64__) || defined(__powerpc__)
-static const uptr kAllocatorRegionSizeLog = 20;
-static const uptr kAllocatorNumRegions =
- SANITIZER_MMAP_RANGE_SIZE >> kAllocatorRegionSizeLog;
-typedef TwoLevelByteMap<(kAllocatorNumRegions >> 12), 1 << 12,
- MapUnmapCallback> ByteMap;
+
struct AP32 {
static const uptr kSpaceBeg = 0;
static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
static const uptr kMetadataSize = 0;
typedef __sanitizer::CompactSizeClassMap SizeClassMap;
- static const uptr kRegionSizeLog = kAllocatorRegionSizeLog;
- typedef __tsan::ByteMap ByteMap;
+ static const uptr kRegionSizeLog = 20;
+ using AddressSpaceView = LocalAddressSpaceView;
typedef __tsan::MapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
};
typedef DefaultSizeClassMap SizeClassMap;
typedef __tsan::MapUnmapCallback MapUnmapCallback;
static const uptr kFlags = 0;
+ using AddressSpaceView = LocalAddressSpaceView;
};
typedef SizeClassAllocator64<AP64> PrimaryAllocator;
#endif
-typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
-typedef LargeMmapAllocator<MapUnmapCallback> SecondaryAllocator;
-typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
- SecondaryAllocator> Allocator;
+typedef CombinedAllocator<PrimaryAllocator> Allocator;
+typedef Allocator::AllocatorCache AllocatorCache;
Allocator *allocator();
#endif
struct JmpBuf {
uptr sp;
- uptr mangled_sp;
int int_signal_send;
bool in_blocking_func;
uptr in_signal_handler;
// taken by epoch between synchs.
// This way we can save one load from tls.
u64 fast_synch_epoch;
+ // Technically `current` should be a separate THREADLOCAL variable;
+ // but it is placed here in order to share cache line with previous fields.
+ ThreadState* current;
// This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read.
// We do not distinguish beteween ignoring reads and writes
// for better performance.
#if !SANITIZER_GO
#if SANITIZER_MAC || SANITIZER_ANDROID
ThreadState *cur_thread();
+void set_cur_thread(ThreadState *thr);
void cur_thread_finalize();
+INLINE void cur_thread_init() { }
#else
__attribute__((tls_model("initial-exec")))
extern THREADLOCAL char cur_thread_placeholder[];
INLINE ThreadState *cur_thread() {
- return reinterpret_cast<ThreadState *>(&cur_thread_placeholder);
+ return reinterpret_cast<ThreadState *>(cur_thread_placeholder)->current;
+}
+INLINE void cur_thread_init() {
+ ThreadState *thr = reinterpret_cast<ThreadState *>(cur_thread_placeholder);
+ if (UNLIKELY(!thr->current))
+ thr->current = thr;
+}
+INLINE void set_cur_thread(ThreadState *thr) {
+ reinterpret_cast<ThreadState *>(cur_thread_placeholder)->current = thr;
}
INLINE void cur_thread_finalize() { }
#endif // SANITIZER_MAC || SANITIZER_ANDROID
void FuncExit(ThreadState *thr);
int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
-void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread);
+void ThreadStart(ThreadState *thr, int tid, tid_t os_id,
+ ThreadType thread_type);
void ThreadFinish(ThreadState *thr);
int ThreadTid(ThreadState *thr, uptr pc, uptr uid);
void ThreadJoin(ThreadState *thr, uptr pc, int tid);
void ThreadSetName(ThreadState *thr, const char *name);
int ThreadCount(ThreadState *thr);
void ProcessPendingSignals(ThreadState *thr);
+void ThreadNotJoined(ThreadState *thr, uptr pc, int tid, uptr uid);
Processor *ProcCreate();
void ProcDestroy(Processor *proc);
}
#endif
+ThreadState *FiberCreate(ThreadState *thr, uptr pc, unsigned flags);
+void FiberDestroy(ThreadState *thr, uptr pc, ThreadState *fiber);
+void FiberSwitch(ThreadState *thr, uptr pc, ThreadState *fiber, unsigned flags);
+
+// These need to match __tsan_switch_to_fiber_* flags defined in
+// tsan_interface.h. See documentation there as well.
+enum FiberSwitchFlags {
+ FiberSwitchFlagNoSync = 1 << 0, // __tsan_switch_to_fiber_no_sync
+};
+
} // namespace __tsan
#endif // TSAN_RTL_H
#include "sanitizer_common/sanitizer_asm.h"
-#if !defined(__APPLE__)
-.section .bss
-.type __tsan_pointer_chk_guard, %object
-ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__tsan_pointer_chk_guard))
-__tsan_pointer_chk_guard:
-.zero 8
-#endif
-
#if defined(__APPLE__)
.align 2
.align 3
#endif
-#if !defined(__APPLE__)
-// GLIBC mangles the function pointers in jmp_buf (used in {set,long}*jmp
-// functions) by XORing them with a random guard pointer. For AArch64 it is a
-// global variable rather than a TCB one (as for x86_64/powerpc) and althought
-// its value is exported by the loader, it lies within a private GLIBC
-// namespace (meaning it should be only used by GLIBC itself and the ABI is
-// not stable). So InitializeGuardPtr obtains the pointer guard value by
-// issuing a setjmp and checking the resulting pointers values against the
-// original ones.
-ASM_HIDDEN(_Z18InitializeGuardPtrv)
-.global _Z18InitializeGuardPtrv
-ASM_TYPE_FUNCTION(ASM_SYMBOL_INTERCEPTOR(_Z18InitializeGuardPtrv))
-_Z18InitializeGuardPtrv:
- CFI_STARTPROC
- // Allocates a jmp_buf for the setjmp call.
- stp x29, x30, [sp, -336]!
- CFI_DEF_CFA_OFFSET (336)
- CFI_OFFSET (29, -336)
- CFI_OFFSET (30, -328)
- add x29, sp, 0
- CFI_DEF_CFA_REGISTER (29)
- add x0, x29, 24
-
- // Call libc setjmp that mangle the stack pointer value
- adrp x1, :got:_ZN14__interception12real__setjmpE
- ldr x1, [x1, #:got_lo12:_ZN14__interception12real__setjmpE]
- ldr x1, [x1]
- blr x1
-
- // glibc setjmp mangles both the frame pointer (FP, pc+4 on blr) and the
- // stack pointer (SP). FP will be placed on ((uintptr*)jmp_buf)[11] and
- // SP at ((uintptr*)jmp_buf)[13].
- // The mangle operation is just 'value' xor 'pointer guard value' and
- // if we know the original value (SP) and the expected one, we can derive
- // the guard pointer value.
- mov x0, sp
-
- // Loads the mangled SP pointer.
- ldr x1, [x29, 128]
- eor x0, x0, x1
- adrp x2, __tsan_pointer_chk_guard
- str x0, [x2, #:lo12:__tsan_pointer_chk_guard]
- ldp x29, x30, [sp], 336
- CFI_RESTORE (30)
- CFI_RESTORE (19)
- CFI_DEF_CFA (31, 0)
- ret
- CFI_ENDPROC
-ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(_Z18InitializeGuardPtrv))
-#endif
-
ASM_HIDDEN(__tsan_setjmp)
.comm _ZN14__interception11real_setjmpE,8,8
.globl ASM_SYMBOL_INTERCEPTOR(setjmp)
ASM_SYMBOL_INTERCEPTOR(setjmp):
CFI_STARTPROC
- // save env parameters for function call
+ // Save frame/link register
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
- // Save jmp_buf
- str x19, [sp, 16]
- CFI_OFFSET (19, -16)
- mov x19, x0
+ // Save env parameter
+ str x0, [sp, 16]
+ CFI_OFFSET (0, -16)
-#if !defined(__APPLE__)
- // SP pointer mangling (see glibc setjmp)
- adrp x2, __tsan_pointer_chk_guard
- ldr x2, [x2, #:lo12:__tsan_pointer_chk_guard]
- add x0, x29, 32
- eor x1, x2, x0
-#else
- adrp x2, ___tsan_darwin_setjmp_xor_key@page
- ldr x2, [x2, ___tsan_darwin_setjmp_xor_key@pageoff]
+ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
add x0, x29, 32
- eor x1, x2, x0
-#endif
// call tsan interceptor
bl ASM_SYMBOL(__tsan_setjmp)
- // restore env parameter
- mov x0, x19
- ldr x19, [sp, 16]
+ // Restore env parameter
+ ldr x0, [sp, 16]
+ CFI_RESTORE (0)
+
+ // Restore frame/link register
ldp x29, x30, [sp], 32
+ CFI_RESTORE (29)
CFI_RESTORE (30)
- CFI_RESTORE (19)
CFI_DEF_CFA (31, 0)
// tail jump to libc setjmp
ASM_SYMBOL_INTERCEPTOR(_setjmp):
CFI_STARTPROC
- // save env parameters for function call
+ // Save frame/link register
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
- // Save jmp_buf
- str x19, [sp, 16]
- CFI_OFFSET (19, -16)
- mov x19, x0
+ // Save env parameter
+ str x0, [sp, 16]
+ CFI_OFFSET (0, -16)
-#if !defined(__APPLE__)
- // SP pointer mangling (see glibc setjmp)
- adrp x2, __tsan_pointer_chk_guard
- ldr x2, [x2, #:lo12:__tsan_pointer_chk_guard]
+ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
add x0, x29, 32
- eor x1, x2, x0
-#else
- adrp x2, ___tsan_darwin_setjmp_xor_key@page
- ldr x2, [x2, ___tsan_darwin_setjmp_xor_key@pageoff]
- add x0, x29, 32
- eor x1, x2, x0
-#endif
// call tsan interceptor
bl ASM_SYMBOL(__tsan_setjmp)
- // Restore jmp_buf parameter
- mov x0, x19
- ldr x19, [sp, 16]
+ // Restore env parameter
+ ldr x0, [sp, 16]
+ CFI_RESTORE (0)
+
+ // Restore frame/link register
ldp x29, x30, [sp], 32
+ CFI_RESTORE (29)
CFI_RESTORE (30)
- CFI_RESTORE (19)
CFI_DEF_CFA (31, 0)
// tail jump to libc setjmp
ASM_SYMBOL_INTERCEPTOR(sigsetjmp):
CFI_STARTPROC
- // save env parameters for function call
+ // Save frame/link register
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
- // Save jmp_buf and savesigs
- stp x19, x20, [sp, 16]
- CFI_OFFSET (19, -16)
- CFI_OFFSET (20, -8)
- mov w20, w1
- mov x19, x0
+ // Save env and savesigs parameter
+ stp x0, x1, [sp, 16]
+ CFI_OFFSET (0, -16)
+ CFI_OFFSET (1, -8)
-#if !defined(__APPLE__)
- // SP pointer mangling (see glibc setjmp)
- adrp x2, __tsan_pointer_chk_guard
- ldr x2, [x2, #:lo12:__tsan_pointer_chk_guard]
- add x0, x29, 32
- eor x1, x2, x0
-#else
- adrp x2, ___tsan_darwin_setjmp_xor_key@page
- ldr x2, [x2, ___tsan_darwin_setjmp_xor_key@pageoff]
+ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
add x0, x29, 32
- eor x1, x2, x0
-#endif
// call tsan interceptor
bl ASM_SYMBOL(__tsan_setjmp)
- // restore env parameter
- mov w1, w20
- mov x0, x19
- ldp x19, x20, [sp, 16]
+ // Restore env and savesigs parameter
+ ldp x0, x1, [sp, 16]
+ CFI_RESTORE (0)
+ CFI_RESTORE (1)
+
+ // Restore frame/link register
ldp x29, x30, [sp], 32
- CFI_RESTORE (30)
CFI_RESTORE (29)
- CFI_RESTORE (19)
- CFI_RESTORE (20)
+ CFI_RESTORE (30)
CFI_DEF_CFA (31, 0)
// tail jump to libc sigsetjmp
ASM_SYMBOL_INTERCEPTOR(__sigsetjmp):
CFI_STARTPROC
- // save env parameters for function call
+ // Save frame/link register
stp x29, x30, [sp, -32]!
CFI_DEF_CFA_OFFSET (32)
CFI_OFFSET (29, -32)
add x29, sp, 0
CFI_DEF_CFA_REGISTER (29)
- // Save jmp_buf and savesigs
- stp x19, x20, [sp, 16]
- CFI_OFFSET (19, -16)
- CFI_OFFSET (20, -8)
- mov w20, w1
- mov x19, x0
+ // Save env and savesigs parameter
+ stp x0, x1, [sp, 16]
+ CFI_OFFSET (0, -16)
+ CFI_OFFSET (1, -8)
-#if !defined(__APPLE__)
- // SP pointer mangling (see glibc setjmp)
- adrp x2, __tsan_pointer_chk_guard
- ldr x2, [x2, #:lo12:__tsan_pointer_chk_guard]
+ // Obtain SP, first argument to `void __tsan_setjmp(uptr sp)`
add x0, x29, 32
- eor x1, x2, x0
-#endif
// call tsan interceptor
bl ASM_SYMBOL(__tsan_setjmp)
- mov w1, w20
- mov x0, x19
- ldp x19, x20, [sp, 16]
+ // Restore env and savesigs parameter
+ ldp x0, x1, [sp, 16]
+ CFI_RESTORE (0)
+ CFI_RESTORE (1)
+
+ // Restore frame/link register
ldp x29, x30, [sp], 32
- CFI_RESTORE (30)
CFI_RESTORE (29)
- CFI_RESTORE (19)
- CFI_RESTORE (20)
+ CFI_RESTORE (30)
CFI_DEF_CFA (31, 0)
// tail jump to libc __sigsetjmp
ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp))
#endif
-#if defined(__linux__)
-/* We do not need executable stack. */
-.section .note.GNU-stack,"",@progbits
-#endif
+NO_EXEC_STACK_DIRECTIVE
#endif
push %rdi
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
- // obtain %rsp
+ // obtain SP, store in %rdi, first argument to `void __tsan_setjmp(uptr sp)`
#if defined(__FreeBSD__) || defined(__NetBSD__)
lea 8(%rsp), %rdi
- mov %rdi, %rsi
-#elif defined(__APPLE__)
+#elif defined(__linux__) || defined(__APPLE__)
lea 16(%rsp), %rdi
- mov %rdi, %rsi
- xorq ___tsan_darwin_setjmp_xor_key(%rip), %rsi
-#elif defined(__linux__)
- lea 16(%rsp), %rdi
- mov %rdi, %rsi
- xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
- rol $0x11, %rsi
#else
# error "Unknown platform"
#endif
push %rdi
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
- // obtain %rsp
+ // obtain SP, store in %rdi, first argument to `void __tsan_setjmp(uptr sp)`
#if defined(__FreeBSD__) || defined(__NetBSD__)
lea 8(%rsp), %rdi
- mov %rdi, %rsi
-#elif defined(__APPLE__)
- lea 16(%rsp), %rdi
- mov %rdi, %rsi
- xorq ___tsan_darwin_setjmp_xor_key(%rip), %rsi
-#elif defined(__linux__)
+#elif defined(__linux__) || defined(__APPLE__)
lea 16(%rsp), %rdi
- mov %rdi, %rsi
- xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
- rol $0x11, %rsi
#else
# error "Unknown platform"
#endif
// align stack frame
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
- // obtain %rsp
+ // obtain SP, store in %rdi, first argument to `void __tsan_setjmp(uptr sp)`
#if defined(__FreeBSD__) || defined(__NetBSD__)
lea 24(%rsp), %rdi
- mov %rdi, %rsi
-#elif defined(__APPLE__)
+#elif defined(__linux__) || defined(__APPLE__)
lea 32(%rsp), %rdi
- mov %rdi, %rsi
- xorq ___tsan_darwin_setjmp_xor_key(%rip), %rsi
-#elif defined(__linux__)
- lea 32(%rsp), %rdi
- mov %rdi, %rsi
- xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
- rol $0x11, %rsi
#else
# error "Unknown platform"
#endif
// align stack frame
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
- // obtain %rsp
+ // obtain SP, store in %rdi, first argument to `void __tsan_setjmp(uptr sp)`
#if defined(__FreeBSD__)
lea 24(%rsp), %rdi
- mov %rdi, %rsi
#else
lea 32(%rsp), %rdi
- mov %rdi, %rsi
- xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
- rol $0x11, %rsi
#endif
// call tsan interceptor
call ASM_SYMBOL(__tsan_setjmp)
ASM_SIZE(ASM_SYMBOL_INTERCEPTOR(__sigsetjmp))
#endif // !defined(__APPLE__) && !defined(__NetBSD__)
-#if defined(__FreeBSD__) || defined(__linux__)
-/* We do not need executable stack. */
-/* This note is not needed on NetBSD. */
-.section .note.GNU-stack,"",@progbits
-#endif
+NO_EXEC_STACK_DIRECTIVE
#endif
+++ /dev/null
-//===-- tsan_rtl_mutex.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
-#include <sanitizer_common/sanitizer_stackdepot.h>
-
-#include "tsan_rtl.h"
-#include "tsan_flags.h"
-#include "tsan_sync.h"
-#include "tsan_report.h"
-#include "tsan_symbolize.h"
-#include "tsan_platform.h"
-
-namespace __tsan {
-
-void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r);
-
-struct Callback : DDCallback {
- ThreadState *thr;
- uptr pc;
-
- Callback(ThreadState *thr, uptr pc)
- : thr(thr)
- , pc(pc) {
- DDCallback::pt = thr->proc()->dd_pt;
- DDCallback::lt = thr->dd_lt;
- }
-
- u32 Unwind() override { return CurrentStackId(thr, pc); }
- int UniqueTid() override { return thr->unique_id; }
-};
-
-void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
- Callback cb(thr, pc);
- ctx->dd->MutexInit(&cb, &s->dd);
- s->dd.ctx = s->GetId();
-}
-
-static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
- uptr addr, u64 mid) {
- // In Go, these misuses are either impossible, or detected by std lib,
- // or false positives (e.g. unlock in a different thread).
- if (SANITIZER_GO)
- return;
- ThreadRegistryLock l(ctx->thread_registry);
- ScopedReport rep(typ);
- rep.AddMutex(mid);
- VarSizeStackTrace trace;
- ObtainCurrentStack(thr, pc, &trace);
- rep.AddStack(trace, true);
- rep.AddLocation(addr, 1);
- OutputReport(thr, rep);
-}
-
-void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
- DPrintf("#%d: MutexCreate %zx flagz=0x%x\n", thr->tid, addr, flagz);
- StatInc(thr, StatMutexCreate);
- if (!(flagz & MutexFlagLinkerInit) && IsAppMem(addr)) {
- CHECK(!thr->is_freeing);
- thr->is_freeing = true;
- MemoryWrite(thr, pc, addr, kSizeLog1);
- thr->is_freeing = false;
- }
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- s->SetFlags(flagz & MutexCreationFlagMask);
- if (!SANITIZER_GO && s->creation_stack_id == 0)
- s->creation_stack_id = CurrentStackId(thr, pc);
- s->mtx.Unlock();
-}
-
-void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
- DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
- StatInc(thr, StatMutexDestroy);
- SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
- if (s == 0)
- return;
- if ((flagz & MutexFlagLinkerInit)
- || s->IsFlagSet(MutexFlagLinkerInit)
- || ((flagz & MutexFlagNotStatic) && !s->IsFlagSet(MutexFlagNotStatic))) {
- // Destroy is no-op for linker-initialized mutexes.
- s->mtx.Unlock();
- return;
- }
- if (common_flags()->detect_deadlocks) {
- Callback cb(thr, pc);
- ctx->dd->MutexDestroy(&cb, &s->dd);
- ctx->dd->MutexInit(&cb, &s->dd);
- }
- bool unlock_locked = false;
- if (flags()->report_destroy_locked
- && s->owner_tid != SyncVar::kInvalidTid
- && !s->IsFlagSet(MutexFlagBroken)) {
- s->SetFlags(MutexFlagBroken);
- unlock_locked = true;
- }
- u64 mid = s->GetId();
- u64 last_lock = s->last_lock;
- if (!unlock_locked)
- s->Reset(thr->proc()); // must not reset it before the report is printed
- s->mtx.Unlock();
- if (unlock_locked) {
- ThreadRegistryLock l(ctx->thread_registry);
- ScopedReport rep(ReportTypeMutexDestroyLocked);
- rep.AddMutex(mid);
- VarSizeStackTrace trace;
- ObtainCurrentStack(thr, pc, &trace);
- rep.AddStack(trace, true);
- FastState last(last_lock);
- RestoreStack(last.tid(), last.epoch(), &trace, 0);
- rep.AddStack(trace, true);
- rep.AddLocation(addr, 1);
- OutputReport(thr, rep);
-
- SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
- if (s != 0) {
- s->Reset(thr->proc());
- s->mtx.Unlock();
- }
- }
- thr->mset.Remove(mid);
- // Imitate a memory write to catch unlock-destroy races.
- // Do this outside of sync mutex, because it can report a race which locks
- // sync mutexes.
- if (IsAppMem(addr)) {
- CHECK(!thr->is_freeing);
- thr->is_freeing = true;
- MemoryWrite(thr, pc, addr, kSizeLog1);
- thr->is_freeing = false;
- }
- // s will be destroyed and freed in MetaMap::FreeBlock.
-}
-
-void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
- DPrintf("#%d: MutexPreLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
- if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) {
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
- s->UpdateFlags(flagz);
- if (s->owner_tid != thr->tid) {
- Callback cb(thr, pc);
- ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
- s->mtx.ReadUnlock();
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- } else {
- s->mtx.ReadUnlock();
- }
- }
-}
-
-void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) {
- DPrintf("#%d: MutexPostLock %zx flag=0x%x rec=%d\n",
- thr->tid, addr, flagz, rec);
- if (flagz & MutexFlagRecursiveLock)
- CHECK_GT(rec, 0);
- else
- rec = 1;
- if (IsAppMem(addr))
- MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- s->UpdateFlags(flagz);
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
- bool report_double_lock = false;
- if (s->owner_tid == SyncVar::kInvalidTid) {
- CHECK_EQ(s->recursion, 0);
- s->owner_tid = thr->tid;
- s->last_lock = thr->fast_state.raw();
- } else if (s->owner_tid == thr->tid) {
- CHECK_GT(s->recursion, 0);
- } else if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
- s->SetFlags(MutexFlagBroken);
- report_double_lock = true;
- }
- const bool first = s->recursion == 0;
- s->recursion += rec;
- if (first) {
- StatInc(thr, StatMutexLock);
- AcquireImpl(thr, pc, &s->clock);
- AcquireImpl(thr, pc, &s->read_clock);
- } else if (!s->IsFlagSet(MutexFlagWriteReentrant)) {
- StatInc(thr, StatMutexRecLock);
- }
- thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
- bool pre_lock = false;
- if (first && common_flags()->detect_deadlocks) {
- pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) &&
- !(flagz & MutexFlagTryLock);
- Callback cb(thr, pc);
- if (pre_lock)
- ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
- ctx->dd->MutexAfterLock(&cb, &s->dd, true, flagz & MutexFlagTryLock);
- }
- u64 mid = s->GetId();
- s->mtx.Unlock();
- // Can't touch s after this point.
- s = 0;
- if (report_double_lock)
- ReportMutexMisuse(thr, pc, ReportTypeMutexDoubleLock, addr, mid);
- if (first && pre_lock && common_flags()->detect_deadlocks) {
- Callback cb(thr, pc);
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- }
-}
-
-int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
- DPrintf("#%d: MutexUnlock %zx flagz=0x%x\n", thr->tid, addr, flagz);
- if (IsAppMem(addr))
- MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
- int rec = 0;
- bool report_bad_unlock = false;
- if (!SANITIZER_GO && (s->recursion == 0 || s->owner_tid != thr->tid)) {
- if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
- s->SetFlags(MutexFlagBroken);
- report_bad_unlock = true;
- }
- } else {
- rec = (flagz & MutexFlagRecursiveUnlock) ? s->recursion : 1;
- s->recursion -= rec;
- if (s->recursion == 0) {
- StatInc(thr, StatMutexUnlock);
- s->owner_tid = SyncVar::kInvalidTid;
- ReleaseStoreImpl(thr, pc, &s->clock);
- } else {
- StatInc(thr, StatMutexRecUnlock);
- }
- }
- thr->mset.Del(s->GetId(), true);
- if (common_flags()->detect_deadlocks && s->recursion == 0 &&
- !report_bad_unlock) {
- Callback cb(thr, pc);
- ctx->dd->MutexBeforeUnlock(&cb, &s->dd, true);
- }
- u64 mid = s->GetId();
- s->mtx.Unlock();
- // Can't touch s after this point.
- if (report_bad_unlock)
- ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
- if (common_flags()->detect_deadlocks && !report_bad_unlock) {
- Callback cb(thr, pc);
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- }
- return rec;
-}
-
-void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
- DPrintf("#%d: MutexPreReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
- if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) {
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
- s->UpdateFlags(flagz);
- Callback cb(thr, pc);
- ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
- s->mtx.ReadUnlock();
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- }
-}
-
-void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
- DPrintf("#%d: MutexPostReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
- StatInc(thr, StatMutexReadLock);
- if (IsAppMem(addr))
- MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
- s->UpdateFlags(flagz);
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
- bool report_bad_lock = false;
- if (s->owner_tid != SyncVar::kInvalidTid) {
- if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
- s->SetFlags(MutexFlagBroken);
- report_bad_lock = true;
- }
- }
- AcquireImpl(thr, pc, &s->clock);
- s->last_lock = thr->fast_state.raw();
- thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
- bool pre_lock = false;
- if (common_flags()->detect_deadlocks) {
- pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) &&
- !(flagz & MutexFlagTryLock);
- Callback cb(thr, pc);
- if (pre_lock)
- ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
- ctx->dd->MutexAfterLock(&cb, &s->dd, false, flagz & MutexFlagTryLock);
- }
- u64 mid = s->GetId();
- s->mtx.ReadUnlock();
- // Can't touch s after this point.
- s = 0;
- if (report_bad_lock)
- ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadLock, addr, mid);
- if (pre_lock && common_flags()->detect_deadlocks) {
- Callback cb(thr, pc);
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- }
-}
-
-void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
- StatInc(thr, StatMutexReadUnlock);
- if (IsAppMem(addr))
- MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
- bool report_bad_unlock = false;
- if (s->owner_tid != SyncVar::kInvalidTid) {
- if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
- s->SetFlags(MutexFlagBroken);
- report_bad_unlock = true;
- }
- }
- ReleaseImpl(thr, pc, &s->read_clock);
- if (common_flags()->detect_deadlocks && s->recursion == 0) {
- Callback cb(thr, pc);
- ctx->dd->MutexBeforeUnlock(&cb, &s->dd, false);
- }
- u64 mid = s->GetId();
- s->mtx.Unlock();
- // Can't touch s after this point.
- thr->mset.Del(mid, false);
- if (report_bad_unlock)
- ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadUnlock, addr, mid);
- if (common_flags()->detect_deadlocks) {
- Callback cb(thr, pc);
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- }
-}
-
-void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
- if (IsAppMem(addr))
- MemoryReadAtomic(thr, pc, addr, kSizeLog1);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- bool write = true;
- bool report_bad_unlock = false;
- if (s->owner_tid == SyncVar::kInvalidTid) {
- // Seems to be read unlock.
- write = false;
- StatInc(thr, StatMutexReadUnlock);
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
- ReleaseImpl(thr, pc, &s->read_clock);
- } else if (s->owner_tid == thr->tid) {
- // Seems to be write unlock.
- thr->fast_state.IncrementEpoch();
- TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
- CHECK_GT(s->recursion, 0);
- s->recursion--;
- if (s->recursion == 0) {
- StatInc(thr, StatMutexUnlock);
- s->owner_tid = SyncVar::kInvalidTid;
- ReleaseStoreImpl(thr, pc, &s->clock);
- } else {
- StatInc(thr, StatMutexRecUnlock);
- }
- } else if (!s->IsFlagSet(MutexFlagBroken)) {
- s->SetFlags(MutexFlagBroken);
- report_bad_unlock = true;
- }
- thr->mset.Del(s->GetId(), write);
- if (common_flags()->detect_deadlocks && s->recursion == 0) {
- Callback cb(thr, pc);
- ctx->dd->MutexBeforeUnlock(&cb, &s->dd, write);
- }
- u64 mid = s->GetId();
- s->mtx.Unlock();
- // Can't touch s after this point.
- if (report_bad_unlock)
- ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
- if (common_flags()->detect_deadlocks) {
- Callback cb(thr, pc);
- ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
- }
-}
-
-void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- s->owner_tid = SyncVar::kInvalidTid;
- s->recursion = 0;
- s->mtx.Unlock();
-}
-
-void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: MutexInvalidAccess %zx\n", thr->tid, addr);
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- u64 mid = s->GetId();
- s->mtx.Unlock();
- ReportMutexMisuse(thr, pc, ReportTypeMutexInvalidAccess, addr, mid);
-}
-
-void Acquire(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
- if (thr->ignore_sync)
- return;
- SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, false);
- if (!s)
- return;
- AcquireImpl(thr, pc, &s->clock);
- s->mtx.ReadUnlock();
-}
-
-static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) {
- ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
- ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
- u64 epoch = tctx->epoch1;
- if (tctx->status == ThreadStatusRunning)
- epoch = tctx->thr->fast_state.epoch();
- thr->clock.set(&thr->proc()->clock_cache, tctx->tid, epoch);
-}
-
-void AcquireGlobal(ThreadState *thr, uptr pc) {
- DPrintf("#%d: AcquireGlobal\n", thr->tid);
- if (thr->ignore_sync)
- return;
- ThreadRegistryLock l(ctx->thread_registry);
- ctx->thread_registry->RunCallbackForEachThreadLocked(
- UpdateClockCallback, thr);
-}
-
-void Release(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: Release %zx\n", thr->tid, addr);
- if (thr->ignore_sync)
- return;
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- ReleaseImpl(thr, pc, &s->clock);
- s->mtx.Unlock();
-}
-
-void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
- DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
- if (thr->ignore_sync)
- return;
- SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
- thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- ReleaseStoreImpl(thr, pc, &s->clock);
- s->mtx.Unlock();
-}
-
-#if !SANITIZER_GO
-static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
- ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
- ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
- u64 epoch = tctx->epoch1;
- if (tctx->status == ThreadStatusRunning)
- epoch = tctx->thr->fast_state.epoch();
- thr->last_sleep_clock.set(&thr->proc()->clock_cache, tctx->tid, epoch);
-}
-
-void AfterSleep(ThreadState *thr, uptr pc) {
- DPrintf("#%d: AfterSleep %zx\n", thr->tid);
- if (thr->ignore_sync)
- return;
- thr->last_sleep_stack_id = CurrentStackId(thr, pc);
- ThreadRegistryLock l(ctx->thread_registry);
- ctx->thread_registry->RunCallbackForEachThreadLocked(
- UpdateSleepClockCallback, thr);
-}
-#endif
-
-void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) {
- if (thr->ignore_sync)
- return;
- thr->clock.set(thr->fast_state.epoch());
- thr->clock.acquire(&thr->proc()->clock_cache, c);
- StatInc(thr, StatSyncAcquire);
-}
-
-void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
- if (thr->ignore_sync)
- return;
- thr->clock.set(thr->fast_state.epoch());
- thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.release(&thr->proc()->clock_cache, c);
- StatInc(thr, StatSyncRelease);
-}
-
-void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) {
- if (thr->ignore_sync)
- return;
- thr->clock.set(thr->fast_state.epoch());
- thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.ReleaseStore(&thr->proc()->clock_cache, c);
- StatInc(thr, StatSyncRelease);
-}
-
-void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
- if (thr->ignore_sync)
- return;
- thr->clock.set(thr->fast_state.epoch());
- thr->fast_synch_epoch = thr->fast_state.epoch();
- thr->clock.acq_rel(&thr->proc()->clock_cache, c);
- StatInc(thr, StatSyncAcquire);
- StatInc(thr, StatSyncRelease);
-}
-
-void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
- if (r == 0)
- return;
- ThreadRegistryLock l(ctx->thread_registry);
- ScopedReport rep(ReportTypeDeadlock);
- for (int i = 0; i < r->n; i++) {
- rep.AddMutex(r->loop[i].mtx_ctx0);
- rep.AddUniqueTid((int)r->loop[i].thr_ctx);
- rep.AddThread((int)r->loop[i].thr_ctx);
- }
- uptr dummy_pc = 0x42;
- for (int i = 0; i < r->n; i++) {
- for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
- u32 stk = r->loop[i].stk[j];
- if (stk && stk != 0xffffffff) {
- rep.AddStack(StackDepotGet(stk), true);
- } else {
- // Sometimes we fail to extract the stack trace (FIXME: investigate),
- // but we should still produce some stack trace in the report.
- rep.AddStack(StackTrace(&dummy_pc, 1), true);
- }
- }
- }
- OutputReport(thr, rep);
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_rtl_mutex.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
+#include <sanitizer_common/sanitizer_stackdepot.h>
+
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+#include "tsan_sync.h"
+#include "tsan_report.h"
+#include "tsan_symbolize.h"
+#include "tsan_platform.h"
+
+namespace __tsan {
+
+void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r);
+
+struct Callback : DDCallback {
+ ThreadState *thr;
+ uptr pc;
+
+ Callback(ThreadState *thr, uptr pc)
+ : thr(thr)
+ , pc(pc) {
+ DDCallback::pt = thr->proc()->dd_pt;
+ DDCallback::lt = thr->dd_lt;
+ }
+
+ u32 Unwind() override { return CurrentStackId(thr, pc); }
+ int UniqueTid() override { return thr->unique_id; }
+};
+
+void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexInit(&cb, &s->dd);
+ s->dd.ctx = s->GetId();
+}
+
+static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
+ uptr addr, u64 mid) {
+ // In Go, these misuses are either impossible, or detected by std lib,
+ // or false positives (e.g. unlock in a different thread).
+ if (SANITIZER_GO)
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(typ);
+ rep.AddMutex(mid);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ rep.AddStack(trace, true);
+ rep.AddLocation(addr, 1);
+ OutputReport(thr, rep);
+}
+
+void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
+ DPrintf("#%d: MutexCreate %zx flagz=0x%x\n", thr->tid, addr, flagz);
+ StatInc(thr, StatMutexCreate);
+ if (!(flagz & MutexFlagLinkerInit) && IsAppMem(addr)) {
+ CHECK(!thr->is_freeing);
+ thr->is_freeing = true;
+ MemoryWrite(thr, pc, addr, kSizeLog1);
+ thr->is_freeing = false;
+ }
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ s->SetFlags(flagz & MutexCreationFlagMask);
+ if (!SANITIZER_GO && s->creation_stack_id == 0)
+ s->creation_stack_id = CurrentStackId(thr, pc);
+ s->mtx.Unlock();
+}
+
+void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
+ DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
+ StatInc(thr, StatMutexDestroy);
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
+ if (s == 0)
+ return;
+ if ((flagz & MutexFlagLinkerInit)
+ || s->IsFlagSet(MutexFlagLinkerInit)
+ || ((flagz & MutexFlagNotStatic) && !s->IsFlagSet(MutexFlagNotStatic))) {
+ // Destroy is no-op for linker-initialized mutexes.
+ s->mtx.Unlock();
+ return;
+ }
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexDestroy(&cb, &s->dd);
+ ctx->dd->MutexInit(&cb, &s->dd);
+ }
+ bool unlock_locked = false;
+ if (flags()->report_destroy_locked
+ && s->owner_tid != SyncVar::kInvalidTid
+ && !s->IsFlagSet(MutexFlagBroken)) {
+ s->SetFlags(MutexFlagBroken);
+ unlock_locked = true;
+ }
+ u64 mid = s->GetId();
+ u64 last_lock = s->last_lock;
+ if (!unlock_locked)
+ s->Reset(thr->proc()); // must not reset it before the report is printed
+ s->mtx.Unlock();
+ if (unlock_locked) {
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(ReportTypeMutexDestroyLocked);
+ rep.AddMutex(mid);
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ rep.AddStack(trace, true);
+ FastState last(last_lock);
+ RestoreStack(last.tid(), last.epoch(), &trace, 0);
+ rep.AddStack(trace, true);
+ rep.AddLocation(addr, 1);
+ OutputReport(thr, rep);
+
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
+ if (s != 0) {
+ s->Reset(thr->proc());
+ s->mtx.Unlock();
+ }
+ }
+ thr->mset.Remove(mid);
+ // Imitate a memory write to catch unlock-destroy races.
+ // Do this outside of sync mutex, because it can report a race which locks
+ // sync mutexes.
+ if (IsAppMem(addr)) {
+ CHECK(!thr->is_freeing);
+ thr->is_freeing = true;
+ MemoryWrite(thr, pc, addr, kSizeLog1);
+ thr->is_freeing = false;
+ }
+ // s will be destroyed and freed in MetaMap::FreeBlock.
+}
+
+void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
+ DPrintf("#%d: MutexPreLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
+ if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) {
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
+ s->UpdateFlags(flagz);
+ if (s->owner_tid != thr->tid) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
+ s->mtx.ReadUnlock();
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ } else {
+ s->mtx.ReadUnlock();
+ }
+ }
+}
+
+void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) {
+ DPrintf("#%d: MutexPostLock %zx flag=0x%x rec=%d\n",
+ thr->tid, addr, flagz, rec);
+ if (flagz & MutexFlagRecursiveLock)
+ CHECK_GT(rec, 0);
+ else
+ rec = 1;
+ if (IsAppMem(addr))
+ MemoryReadAtomic(thr, pc, addr, kSizeLog1);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ s->UpdateFlags(flagz);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId());
+ bool report_double_lock = false;
+ if (s->owner_tid == SyncVar::kInvalidTid) {
+ CHECK_EQ(s->recursion, 0);
+ s->owner_tid = thr->tid;
+ s->last_lock = thr->fast_state.raw();
+ } else if (s->owner_tid == thr->tid) {
+ CHECK_GT(s->recursion, 0);
+ } else if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
+ s->SetFlags(MutexFlagBroken);
+ report_double_lock = true;
+ }
+ const bool first = s->recursion == 0;
+ s->recursion += rec;
+ if (first) {
+ StatInc(thr, StatMutexLock);
+ AcquireImpl(thr, pc, &s->clock);
+ AcquireImpl(thr, pc, &s->read_clock);
+ } else if (!s->IsFlagSet(MutexFlagWriteReentrant)) {
+ StatInc(thr, StatMutexRecLock);
+ }
+ thr->mset.Add(s->GetId(), true, thr->fast_state.epoch());
+ bool pre_lock = false;
+ if (first && common_flags()->detect_deadlocks) {
+ pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) &&
+ !(flagz & MutexFlagTryLock);
+ Callback cb(thr, pc);
+ if (pre_lock)
+ ctx->dd->MutexBeforeLock(&cb, &s->dd, true);
+ ctx->dd->MutexAfterLock(&cb, &s->dd, true, flagz & MutexFlagTryLock);
+ }
+ u64 mid = s->GetId();
+ s->mtx.Unlock();
+ // Can't touch s after this point.
+ s = 0;
+ if (report_double_lock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexDoubleLock, addr, mid);
+ if (first && pre_lock && common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
+}
+
+int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
+ DPrintf("#%d: MutexUnlock %zx flagz=0x%x\n", thr->tid, addr, flagz);
+ if (IsAppMem(addr))
+ MemoryReadAtomic(thr, pc, addr, kSizeLog1);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
+ int rec = 0;
+ bool report_bad_unlock = false;
+ if (!SANITIZER_GO && (s->recursion == 0 || s->owner_tid != thr->tid)) {
+ if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
+ s->SetFlags(MutexFlagBroken);
+ report_bad_unlock = true;
+ }
+ } else {
+ rec = (flagz & MutexFlagRecursiveUnlock) ? s->recursion : 1;
+ s->recursion -= rec;
+ if (s->recursion == 0) {
+ StatInc(thr, StatMutexUnlock);
+ s->owner_tid = SyncVar::kInvalidTid;
+ ReleaseStoreImpl(thr, pc, &s->clock);
+ } else {
+ StatInc(thr, StatMutexRecUnlock);
+ }
+ }
+ thr->mset.Del(s->GetId(), true);
+ if (common_flags()->detect_deadlocks && s->recursion == 0 &&
+ !report_bad_unlock) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeUnlock(&cb, &s->dd, true);
+ }
+ u64 mid = s->GetId();
+ s->mtx.Unlock();
+ // Can't touch s after this point.
+ if (report_bad_unlock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
+ if (common_flags()->detect_deadlocks && !report_bad_unlock) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
+ return rec;
+}
+
+void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
+ DPrintf("#%d: MutexPreReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
+ if (!(flagz & MutexFlagTryLock) && common_flags()->detect_deadlocks) {
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
+ s->UpdateFlags(flagz);
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
+ s->mtx.ReadUnlock();
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
+}
+
+void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
+ DPrintf("#%d: MutexPostReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
+ StatInc(thr, StatMutexReadLock);
+ if (IsAppMem(addr))
+ MemoryReadAtomic(thr, pc, addr, kSizeLog1);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, false);
+ s->UpdateFlags(flagz);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId());
+ bool report_bad_lock = false;
+ if (s->owner_tid != SyncVar::kInvalidTid) {
+ if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
+ s->SetFlags(MutexFlagBroken);
+ report_bad_lock = true;
+ }
+ }
+ AcquireImpl(thr, pc, &s->clock);
+ s->last_lock = thr->fast_state.raw();
+ thr->mset.Add(s->GetId(), false, thr->fast_state.epoch());
+ bool pre_lock = false;
+ if (common_flags()->detect_deadlocks) {
+ pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) &&
+ !(flagz & MutexFlagTryLock);
+ Callback cb(thr, pc);
+ if (pre_lock)
+ ctx->dd->MutexBeforeLock(&cb, &s->dd, false);
+ ctx->dd->MutexAfterLock(&cb, &s->dd, false, flagz & MutexFlagTryLock);
+ }
+ u64 mid = s->GetId();
+ s->mtx.ReadUnlock();
+ // Can't touch s after this point.
+ s = 0;
+ if (report_bad_lock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadLock, addr, mid);
+ if (pre_lock && common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
+}
+
+void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
+ StatInc(thr, StatMutexReadUnlock);
+ if (IsAppMem(addr))
+ MemoryReadAtomic(thr, pc, addr, kSizeLog1);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
+ bool report_bad_unlock = false;
+ if (s->owner_tid != SyncVar::kInvalidTid) {
+ if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) {
+ s->SetFlags(MutexFlagBroken);
+ report_bad_unlock = true;
+ }
+ }
+ ReleaseImpl(thr, pc, &s->read_clock);
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeUnlock(&cb, &s->dd, false);
+ }
+ u64 mid = s->GetId();
+ s->mtx.Unlock();
+ // Can't touch s after this point.
+ thr->mset.Del(mid, false);
+ if (report_bad_unlock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadReadUnlock, addr, mid);
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
+}
+
+void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
+ if (IsAppMem(addr))
+ MemoryReadAtomic(thr, pc, addr, kSizeLog1);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ bool write = true;
+ bool report_bad_unlock = false;
+ if (s->owner_tid == SyncVar::kInvalidTid) {
+ // Seems to be read unlock.
+ write = false;
+ StatInc(thr, StatMutexReadUnlock);
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId());
+ ReleaseImpl(thr, pc, &s->read_clock);
+ } else if (s->owner_tid == thr->tid) {
+ // Seems to be write unlock.
+ thr->fast_state.IncrementEpoch();
+ TraceAddEvent(thr, thr->fast_state, EventTypeUnlock, s->GetId());
+ CHECK_GT(s->recursion, 0);
+ s->recursion--;
+ if (s->recursion == 0) {
+ StatInc(thr, StatMutexUnlock);
+ s->owner_tid = SyncVar::kInvalidTid;
+ ReleaseStoreImpl(thr, pc, &s->clock);
+ } else {
+ StatInc(thr, StatMutexRecUnlock);
+ }
+ } else if (!s->IsFlagSet(MutexFlagBroken)) {
+ s->SetFlags(MutexFlagBroken);
+ report_bad_unlock = true;
+ }
+ thr->mset.Del(s->GetId(), write);
+ if (common_flags()->detect_deadlocks && s->recursion == 0) {
+ Callback cb(thr, pc);
+ ctx->dd->MutexBeforeUnlock(&cb, &s->dd, write);
+ }
+ u64 mid = s->GetId();
+ s->mtx.Unlock();
+ // Can't touch s after this point.
+ if (report_bad_unlock)
+ ReportMutexMisuse(thr, pc, ReportTypeMutexBadUnlock, addr, mid);
+ if (common_flags()->detect_deadlocks) {
+ Callback cb(thr, pc);
+ ReportDeadlock(thr, pc, ctx->dd->GetReport(&cb));
+ }
+}
+
+void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ s->owner_tid = SyncVar::kInvalidTid;
+ s->recursion = 0;
+ s->mtx.Unlock();
+}
+
+void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: MutexInvalidAccess %zx\n", thr->tid, addr);
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ u64 mid = s->GetId();
+ s->mtx.Unlock();
+ ReportMutexMisuse(thr, pc, ReportTypeMutexInvalidAccess, addr, mid);
+}
+
+void Acquire(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
+ if (thr->ignore_sync)
+ return;
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, false);
+ if (!s)
+ return;
+ AcquireImpl(thr, pc, &s->clock);
+ s->mtx.ReadUnlock();
+}
+
+static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) {
+ ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
+ ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
+ u64 epoch = tctx->epoch1;
+ if (tctx->status == ThreadStatusRunning)
+ epoch = tctx->thr->fast_state.epoch();
+ thr->clock.set(&thr->proc()->clock_cache, tctx->tid, epoch);
+}
+
+void AcquireGlobal(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: AcquireGlobal\n", thr->tid);
+ if (thr->ignore_sync)
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ ctx->thread_registry->RunCallbackForEachThreadLocked(
+ UpdateClockCallback, thr);
+}
+
+void Release(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: Release %zx\n", thr->tid, addr);
+ if (thr->ignore_sync)
+ return;
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseImpl(thr, pc, &s->clock);
+ s->mtx.Unlock();
+}
+
+void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
+ DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
+ if (thr->ignore_sync)
+ return;
+ SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true);
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseStoreImpl(thr, pc, &s->clock);
+ s->mtx.Unlock();
+}
+
+#if !SANITIZER_GO
+static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) {
+ ThreadState *thr = reinterpret_cast<ThreadState*>(arg);
+ ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
+ u64 epoch = tctx->epoch1;
+ if (tctx->status == ThreadStatusRunning)
+ epoch = tctx->thr->fast_state.epoch();
+ thr->last_sleep_clock.set(&thr->proc()->clock_cache, tctx->tid, epoch);
+}
+
+void AfterSleep(ThreadState *thr, uptr pc) {
+ DPrintf("#%d: AfterSleep %zx\n", thr->tid);
+ if (thr->ignore_sync)
+ return;
+ thr->last_sleep_stack_id = CurrentStackId(thr, pc);
+ ThreadRegistryLock l(ctx->thread_registry);
+ ctx->thread_registry->RunCallbackForEachThreadLocked(
+ UpdateSleepClockCallback, thr);
+}
+#endif
+
+void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->fast_state.epoch());
+ thr->clock.acquire(&thr->proc()->clock_cache, c);
+ StatInc(thr, StatSyncAcquire);
+}
+
+void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.release(&thr->proc()->clock_cache, c);
+ StatInc(thr, StatSyncRelease);
+}
+
+void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.ReleaseStore(&thr->proc()->clock_cache, c);
+ StatInc(thr, StatSyncRelease);
+}
+
+void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) {
+ if (thr->ignore_sync)
+ return;
+ thr->clock.set(thr->fast_state.epoch());
+ thr->fast_synch_epoch = thr->fast_state.epoch();
+ thr->clock.acq_rel(&thr->proc()->clock_cache, c);
+ StatInc(thr, StatSyncAcquire);
+ StatInc(thr, StatSyncRelease);
+}
+
+void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
+ if (r == 0)
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ ScopedReport rep(ReportTypeDeadlock);
+ for (int i = 0; i < r->n; i++) {
+ rep.AddMutex(r->loop[i].mtx_ctx0);
+ rep.AddUniqueTid((int)r->loop[i].thr_ctx);
+ rep.AddThread((int)r->loop[i].thr_ctx);
+ }
+ uptr dummy_pc = 0x42;
+ for (int i = 0; i < r->n; i++) {
+ for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
+ u32 stk = r->loop[i].stk[j];
+ if (stk && stk != 0xffffffff) {
+ rep.AddStack(StackDepotGet(stk), true);
+ } else {
+ // Sometimes we fail to extract the stack trace (FIXME: investigate),
+ // but we should still produce some stack trace in the report.
+ rep.AddStack(StackTrace(&dummy_pc, 1), true);
+ }
+ }
+ }
+ OutputReport(thr, rep);
+}
+
+} // namespace __tsan
#include "tsan_ppc_regs.h"
- .machine altivec
.section .text
.hidden __tsan_setjmp
.globl _setjmp
+++ /dev/null
-//===-- tsan_rtl_proc.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-#include "tsan_flags.h"
-
-namespace __tsan {
-
-Processor *ProcCreate() {
- void *mem = InternalAlloc(sizeof(Processor));
- internal_memset(mem, 0, sizeof(Processor));
- Processor *proc = new(mem) Processor;
- proc->thr = nullptr;
-#if !SANITIZER_GO
- AllocatorProcStart(proc);
-#endif
- if (common_flags()->detect_deadlocks)
- proc->dd_pt = ctx->dd->CreatePhysicalThread();
- return proc;
-}
-
-void ProcDestroy(Processor *proc) {
- CHECK_EQ(proc->thr, nullptr);
-#if !SANITIZER_GO
- AllocatorProcFinish(proc);
-#endif
- ctx->clock_alloc.FlushCache(&proc->clock_cache);
- ctx->metamap.OnProcIdle(proc);
- if (common_flags()->detect_deadlocks)
- ctx->dd->DestroyPhysicalThread(proc->dd_pt);
- proc->~Processor();
- InternalFree(proc);
-}
-
-void ProcWire(Processor *proc, ThreadState *thr) {
- CHECK_EQ(thr->proc1, nullptr);
- CHECK_EQ(proc->thr, nullptr);
- thr->proc1 = proc;
- proc->thr = thr;
-}
-
-void ProcUnwire(Processor *proc, ThreadState *thr) {
- CHECK_EQ(thr->proc1, proc);
- CHECK_EQ(proc->thr, thr);
- thr->proc1 = nullptr;
- proc->thr = nullptr;
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_rtl_proc.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+
+namespace __tsan {
+
+Processor *ProcCreate() {
+ void *mem = InternalAlloc(sizeof(Processor));
+ internal_memset(mem, 0, sizeof(Processor));
+ Processor *proc = new(mem) Processor;
+ proc->thr = nullptr;
+#if !SANITIZER_GO
+ AllocatorProcStart(proc);
+#endif
+ if (common_flags()->detect_deadlocks)
+ proc->dd_pt = ctx->dd->CreatePhysicalThread();
+ return proc;
+}
+
+void ProcDestroy(Processor *proc) {
+ CHECK_EQ(proc->thr, nullptr);
+#if !SANITIZER_GO
+ AllocatorProcFinish(proc);
+#endif
+ ctx->clock_alloc.FlushCache(&proc->clock_cache);
+ ctx->metamap.OnProcIdle(proc);
+ if (common_flags()->detect_deadlocks)
+ ctx->dd->DestroyPhysicalThread(proc->dd_pt);
+ proc->~Processor();
+ InternalFree(proc);
+}
+
+void ProcWire(Processor *proc, ThreadState *thr) {
+ CHECK_EQ(thr->proc1, nullptr);
+ CHECK_EQ(proc->thr, nullptr);
+ thr->proc1 = proc;
+ proc->thr = thr;
+}
+
+void ProcUnwire(Processor *proc, ThreadState *thr) {
+ CHECK_EQ(thr->proc1, proc);
+ CHECK_EQ(proc->thr, thr);
+ thr->proc1 = nullptr;
+ proc->thr = nullptr;
+}
+
+} // namespace __tsan
+++ /dev/null
-//===-- tsan_rtl_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 ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_stackdepot.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "tsan_platform.h"
-#include "tsan_rtl.h"
-#include "tsan_suppressions.h"
-#include "tsan_symbolize.h"
-#include "tsan_report.h"
-#include "tsan_sync.h"
-#include "tsan_mman.h"
-#include "tsan_flags.h"
-#include "tsan_fd.h"
-
-namespace __tsan {
-
-using namespace __sanitizer; // NOLINT
-
-static ReportStack *SymbolizeStack(StackTrace trace);
-
-void TsanCheckFailed(const char *file, int line, const char *cond,
- u64 v1, u64 v2) {
- // There is high probability that interceptors will check-fail as well,
- // on the other hand there is no sense in processing interceptors
- // since we are going to die soon.
- ScopedIgnoreInterceptors ignore;
-#if !SANITIZER_GO
- cur_thread()->ignore_sync++;
- cur_thread()->ignore_reads_and_writes++;
-#endif
- Printf("FATAL: ThreadSanitizer CHECK failed: "
- "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
- file, line, cond, (uptr)v1, (uptr)v2);
- PrintCurrentStackSlow(StackTrace::GetCurrentPc());
- Die();
-}
-
-// Can be overriden by an application/test to intercept reports.
-#ifdef TSAN_EXTERNAL_HOOKS
-bool OnReport(const ReportDesc *rep, bool suppressed);
-#else
-SANITIZER_WEAK_CXX_DEFAULT_IMPL
-bool OnReport(const ReportDesc *rep, bool suppressed) {
- (void)rep;
- return suppressed;
-}
-#endif
-
-SANITIZER_WEAK_DEFAULT_IMPL
-void __tsan_on_report(const ReportDesc *rep) {
- (void)rep;
-}
-
-static void StackStripMain(SymbolizedStack *frames) {
- SymbolizedStack *last_frame = nullptr;
- SymbolizedStack *last_frame2 = nullptr;
- for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
- last_frame2 = last_frame;
- last_frame = cur;
- }
-
- if (last_frame2 == 0)
- return;
-#if !SANITIZER_GO
- const char *last = last_frame->info.function;
- const char *last2 = last_frame2->info.function;
- // Strip frame above 'main'
- if (last2 && 0 == internal_strcmp(last2, "main")) {
- last_frame->ClearAll();
- last_frame2->next = nullptr;
- // Strip our internal thread start routine.
- } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
- last_frame->ClearAll();
- last_frame2->next = nullptr;
- // Strip global ctors init.
- } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) {
- last_frame->ClearAll();
- last_frame2->next = nullptr;
- // If both are 0, then we probably just failed to symbolize.
- } else if (last || last2) {
- // Ensure that we recovered stack completely. Trimmed stack
- // can actually happen if we do not instrument some code,
- // so it's only a debug print. However we must try hard to not miss it
- // due to our fault.
- DPrintf("Bottom stack frame is missed\n");
- }
-#else
- // The last frame always point into runtime (gosched0, goexit0, runtime.main).
- last_frame->ClearAll();
- last_frame2->next = nullptr;
-#endif
-}
-
-ReportStack *SymbolizeStackId(u32 stack_id) {
- if (stack_id == 0)
- return 0;
- StackTrace stack = StackDepotGet(stack_id);
- if (stack.trace == nullptr)
- return nullptr;
- return SymbolizeStack(stack);
-}
-
-static ReportStack *SymbolizeStack(StackTrace trace) {
- if (trace.size == 0)
- return 0;
- SymbolizedStack *top = nullptr;
- for (uptr si = 0; si < trace.size; si++) {
- const uptr pc = trace.trace[si];
- uptr pc1 = pc;
- // We obtain the return address, but we're interested in the previous
- // instruction.
- if ((pc & kExternalPCBit) == 0)
- pc1 = StackTrace::GetPreviousInstructionPc(pc);
- SymbolizedStack *ent = SymbolizeCode(pc1);
- CHECK_NE(ent, 0);
- SymbolizedStack *last = ent;
- while (last->next) {
- last->info.address = pc; // restore original pc for report
- last = last->next;
- }
- last->info.address = pc; // restore original pc for report
- last->next = top;
- top = ent;
- }
- StackStripMain(top);
-
- ReportStack *stack = ReportStack::New();
- stack->frames = top;
- return stack;
-}
-
-ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) {
- ctx->thread_registry->CheckLocked();
- void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
- rep_ = new(mem) ReportDesc;
- rep_->typ = typ;
- rep_->tag = tag;
- ctx->report_mtx.Lock();
-}
-
-ScopedReportBase::~ScopedReportBase() {
- ctx->report_mtx.Unlock();
- DestroyAndFree(rep_);
-}
-
-void ScopedReportBase::AddStack(StackTrace stack, bool suppressable) {
- ReportStack **rs = rep_->stacks.PushBack();
- *rs = SymbolizeStack(stack);
- (*rs)->suppressable = suppressable;
-}
-
-void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
- StackTrace stack, const MutexSet *mset) {
- void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
- ReportMop *mop = new(mem) ReportMop;
- rep_->mops.PushBack(mop);
- mop->tid = s.tid();
- mop->addr = addr + s.addr0();
- mop->size = s.size();
- mop->write = s.IsWrite();
- mop->atomic = s.IsAtomic();
- mop->stack = SymbolizeStack(stack);
- mop->external_tag = external_tag;
- if (mop->stack)
- mop->stack->suppressable = true;
- for (uptr i = 0; i < mset->Size(); i++) {
- MutexSet::Desc d = mset->Get(i);
- u64 mid = this->AddMutex(d.id);
- ReportMopMutex mtx = {mid, d.write};
- mop->mset.PushBack(mtx);
- }
-}
-
-void ScopedReportBase::AddUniqueTid(int unique_tid) {
- rep_->unique_tids.PushBack(unique_tid);
-}
-
-void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
- for (uptr i = 0; i < rep_->threads.Size(); i++) {
- if ((u32)rep_->threads[i]->id == tctx->tid)
- return;
- }
- void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
- ReportThread *rt = new(mem) ReportThread;
- rep_->threads.PushBack(rt);
- rt->id = tctx->tid;
- rt->os_id = tctx->os_id;
- rt->running = (tctx->status == ThreadStatusRunning);
- rt->name = internal_strdup(tctx->name);
- rt->parent_tid = tctx->parent_tid;
- rt->workerthread = tctx->workerthread;
- rt->stack = 0;
- rt->stack = SymbolizeStackId(tctx->creation_stack_id);
- if (rt->stack)
- rt->stack->suppressable = suppressable;
-}
-
-#if !SANITIZER_GO
-static bool FindThreadByUidLockedCallback(ThreadContextBase *tctx, void *arg) {
- int unique_id = *(int *)arg;
- return tctx->unique_id == (u32)unique_id;
-}
-
-static ThreadContext *FindThreadByUidLocked(int unique_id) {
- ctx->thread_registry->CheckLocked();
- return static_cast<ThreadContext *>(
- ctx->thread_registry->FindThreadContextLocked(
- FindThreadByUidLockedCallback, &unique_id));
-}
-
-static ThreadContext *FindThreadByTidLocked(int tid) {
- ctx->thread_registry->CheckLocked();
- return static_cast<ThreadContext*>(
- ctx->thread_registry->GetThreadLocked(tid));
-}
-
-static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) {
- uptr addr = (uptr)arg;
- ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
- if (tctx->status != ThreadStatusRunning)
- return false;
- ThreadState *thr = tctx->thr;
- CHECK(thr);
- return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) ||
- (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size));
-}
-
-ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
- ctx->thread_registry->CheckLocked();
- ThreadContext *tctx = static_cast<ThreadContext*>(
- ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls,
- (void*)addr));
- if (!tctx)
- return 0;
- ThreadState *thr = tctx->thr;
- CHECK(thr);
- *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size);
- return tctx;
-}
-#endif
-
-void ScopedReportBase::AddThread(int unique_tid, bool suppressable) {
-#if !SANITIZER_GO
- if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid))
- AddThread(tctx, suppressable);
-#endif
-}
-
-void ScopedReportBase::AddMutex(const SyncVar *s) {
- for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
- if (rep_->mutexes[i]->id == s->uid)
- return;
- }
- void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
- ReportMutex *rm = new(mem) ReportMutex;
- rep_->mutexes.PushBack(rm);
- rm->id = s->uid;
- rm->addr = s->addr;
- rm->destroyed = false;
- rm->stack = SymbolizeStackId(s->creation_stack_id);
-}
-
-u64 ScopedReportBase::AddMutex(u64 id) {
- u64 uid = 0;
- u64 mid = id;
- uptr addr = SyncVar::SplitId(id, &uid);
- SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
- // Check that the mutex is still alive.
- // Another mutex can be created at the same address,
- // so check uid as well.
- if (s && s->CheckId(uid)) {
- mid = s->uid;
- AddMutex(s);
- } else {
- AddDeadMutex(id);
- }
- if (s)
- s->mtx.Unlock();
- return mid;
-}
-
-void ScopedReportBase::AddDeadMutex(u64 id) {
- for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
- if (rep_->mutexes[i]->id == id)
- return;
- }
- void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
- ReportMutex *rm = new(mem) ReportMutex;
- rep_->mutexes.PushBack(rm);
- rm->id = id;
- rm->addr = 0;
- rm->destroyed = true;
- rm->stack = 0;
-}
-
-void ScopedReportBase::AddLocation(uptr addr, uptr size) {
- if (addr == 0)
- return;
-#if !SANITIZER_GO
- int fd = -1;
- int creat_tid = kInvalidTid;
- u32 creat_stack = 0;
- if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) {
- ReportLocation *loc = ReportLocation::New(ReportLocationFD);
- loc->fd = fd;
- loc->tid = creat_tid;
- loc->stack = SymbolizeStackId(creat_stack);
- rep_->locs.PushBack(loc);
- ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
- if (tctx)
- AddThread(tctx);
- return;
- }
- MBlock *b = 0;
- Allocator *a = allocator();
- if (a->PointerIsMine((void*)addr)) {
- void *block_begin = a->GetBlockBegin((void*)addr);
- if (block_begin)
- b = ctx->metamap.GetBlock((uptr)block_begin);
- }
- if (b != 0) {
- ThreadContext *tctx = FindThreadByTidLocked(b->tid);
- ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
- loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
- loc->heap_chunk_size = b->siz;
- loc->external_tag = b->tag;
- loc->tid = tctx ? tctx->tid : b->tid;
- loc->stack = SymbolizeStackId(b->stk);
- rep_->locs.PushBack(loc);
- if (tctx)
- AddThread(tctx);
- return;
- }
- bool is_stack = false;
- if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
- ReportLocation *loc =
- ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS);
- loc->tid = tctx->tid;
- rep_->locs.PushBack(loc);
- AddThread(tctx);
- }
-#endif
- if (ReportLocation *loc = SymbolizeData(addr)) {
- loc->suppressable = true;
- rep_->locs.PushBack(loc);
- return;
- }
-}
-
-#if !SANITIZER_GO
-void ScopedReportBase::AddSleep(u32 stack_id) {
- rep_->sleep = SymbolizeStackId(stack_id);
-}
-#endif
-
-void ScopedReportBase::SetCount(int count) { rep_->count = count; }
-
-const ReportDesc *ScopedReportBase::GetReport() const { return rep_; }
-
-ScopedReport::ScopedReport(ReportType typ, uptr tag)
- : ScopedReportBase(typ, tag) {}
-
-ScopedReport::~ScopedReport() {}
-
-void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
- MutexSet *mset, uptr *tag) {
- // This function restores stack trace and mutex set for the thread/epoch.
- // It does so by getting stack trace and mutex set at the beginning of
- // trace part, and then replaying the trace till the given epoch.
- Trace* trace = ThreadTrace(tid);
- ReadLock l(&trace->mtx);
- const int partidx = (epoch / kTracePartSize) % TraceParts();
- TraceHeader* hdr = &trace->headers[partidx];
- if (epoch < hdr->epoch0 || epoch >= hdr->epoch0 + kTracePartSize)
- return;
- CHECK_EQ(RoundDown(epoch, kTracePartSize), hdr->epoch0);
- const u64 epoch0 = RoundDown(epoch, TraceSize());
- const u64 eend = epoch % TraceSize();
- const u64 ebegin = RoundDown(eend, kTracePartSize);
- DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
- tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
- Vector<uptr> stack;
- stack.Resize(hdr->stack0.size + 64);
- for (uptr i = 0; i < hdr->stack0.size; i++) {
- stack[i] = hdr->stack0.trace[i];
- DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]);
- }
- if (mset)
- *mset = hdr->mset0;
- uptr pos = hdr->stack0.size;
- Event *events = (Event*)GetThreadTrace(tid);
- for (uptr i = ebegin; i <= eend; i++) {
- Event ev = events[i];
- EventType typ = (EventType)(ev >> kEventPCBits);
- uptr pc = (uptr)(ev & ((1ull << kEventPCBits) - 1));
- DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc);
- if (typ == EventTypeMop) {
- stack[pos] = pc;
- } else if (typ == EventTypeFuncEnter) {
- if (stack.Size() < pos + 2)
- stack.Resize(pos + 2);
- stack[pos++] = pc;
- } else if (typ == EventTypeFuncExit) {
- if (pos > 0)
- pos--;
- }
- if (mset) {
- if (typ == EventTypeLock) {
- mset->Add(pc, true, epoch0 + i);
- } else if (typ == EventTypeUnlock) {
- mset->Del(pc, true);
- } else if (typ == EventTypeRLock) {
- mset->Add(pc, false, epoch0 + i);
- } else if (typ == EventTypeRUnlock) {
- mset->Del(pc, false);
- }
- }
- for (uptr j = 0; j <= pos; j++)
- DPrintf2(" #%zu: %zx\n", j, stack[j]);
- }
- if (pos == 0 && stack[0] == 0)
- return;
- pos++;
- stk->Init(&stack[0], pos);
- ExtractTagFromStack(stk, tag);
-}
-
-static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
- uptr addr_min, uptr addr_max) {
- bool equal_stack = false;
- RacyStacks hash;
- bool equal_address = false;
- RacyAddress ra0 = {addr_min, addr_max};
- {
- ReadLock lock(&ctx->racy_mtx);
- if (flags()->suppress_equal_stacks) {
- hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
- hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
- for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
- if (hash == ctx->racy_stacks[i]) {
- VPrintf(2,
- "ThreadSanitizer: suppressing report as doubled (stack)\n");
- equal_stack = true;
- break;
- }
- }
- }
- if (flags()->suppress_equal_addresses) {
- for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
- RacyAddress ra2 = ctx->racy_addresses[i];
- uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
- uptr minend = min(ra0.addr_max, ra2.addr_max);
- if (maxbeg < minend) {
- VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n");
- equal_address = true;
- break;
- }
- }
- }
- }
- if (!equal_stack && !equal_address)
- return false;
- if (!equal_stack) {
- Lock lock(&ctx->racy_mtx);
- ctx->racy_stacks.PushBack(hash);
- }
- if (!equal_address) {
- Lock lock(&ctx->racy_mtx);
- ctx->racy_addresses.PushBack(ra0);
- }
- return true;
-}
-
-static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
- uptr addr_min, uptr addr_max) {
- Lock lock(&ctx->racy_mtx);
- if (flags()->suppress_equal_stacks) {
- RacyStacks hash;
- hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
- hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
- ctx->racy_stacks.PushBack(hash);
- }
- if (flags()->suppress_equal_addresses) {
- RacyAddress ra0 = {addr_min, addr_max};
- ctx->racy_addresses.PushBack(ra0);
- }
-}
-
-bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
- if (!flags()->report_bugs || thr->suppress_reports)
- return false;
- atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime());
- const ReportDesc *rep = srep.GetReport();
- CHECK_EQ(thr->current_report, nullptr);
- thr->current_report = rep;
- Suppression *supp = 0;
- uptr pc_or_addr = 0;
- for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++)
- pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp);
- for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++)
- pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp);
- for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++)
- pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp);
- for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++)
- pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp);
- if (pc_or_addr != 0) {
- Lock lock(&ctx->fired_suppressions_mtx);
- FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp};
- ctx->fired_suppressions.push_back(s);
- }
- {
- bool old_is_freeing = thr->is_freeing;
- thr->is_freeing = false;
- bool suppressed = OnReport(rep, pc_or_addr != 0);
- thr->is_freeing = old_is_freeing;
- if (suppressed) {
- thr->current_report = nullptr;
- return false;
- }
- }
- PrintReport(rep);
- __tsan_on_report(rep);
- ctx->nreported++;
- if (flags()->halt_on_error)
- Die();
- thr->current_report = nullptr;
- return true;
-}
-
-bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) {
- ReadLock lock(&ctx->fired_suppressions_mtx);
- for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
- if (ctx->fired_suppressions[k].type != type)
- continue;
- for (uptr j = 0; j < trace.size; j++) {
- FiredSuppression *s = &ctx->fired_suppressions[k];
- if (trace.trace[j] == s->pc_or_addr) {
- if (s->supp)
- atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
- return true;
- }
- }
- }
- return false;
-}
-
-static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) {
- ReadLock lock(&ctx->fired_suppressions_mtx);
- for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
- if (ctx->fired_suppressions[k].type != type)
- continue;
- FiredSuppression *s = &ctx->fired_suppressions[k];
- if (addr == s->pc_or_addr) {
- if (s->supp)
- atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
- return true;
- }
- }
- return false;
-}
-
-static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
- Shadow s0(thr->racy_state[0]);
- Shadow s1(thr->racy_state[1]);
- CHECK(!(s0.IsAtomic() && s1.IsAtomic()));
- if (!s0.IsAtomic() && !s1.IsAtomic())
- return true;
- if (s0.IsAtomic() && s1.IsFreed())
- return true;
- if (s1.IsAtomic() && thr->is_freeing)
- return true;
- return false;
-}
-
-void ReportRace(ThreadState *thr) {
- CheckNoLocks(thr);
-
- // Symbolizer makes lots of intercepted calls. If we try to process them,
- // at best it will cause deadlocks on internal mutexes.
- ScopedIgnoreInterceptors ignore;
-
- if (!flags()->report_bugs)
- return;
- if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr))
- return;
-
- bool freed = false;
- {
- Shadow s(thr->racy_state[1]);
- freed = s.GetFreedAndReset();
- thr->racy_state[1] = s.raw();
- }
-
- uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr);
- uptr addr_min = 0;
- uptr addr_max = 0;
- {
- uptr a0 = addr + Shadow(thr->racy_state[0]).addr0();
- uptr a1 = addr + Shadow(thr->racy_state[1]).addr0();
- uptr e0 = a0 + Shadow(thr->racy_state[0]).size();
- uptr e1 = a1 + Shadow(thr->racy_state[1]).size();
- addr_min = min(a0, a1);
- addr_max = max(e0, e1);
- if (IsExpectedReport(addr_min, addr_max - addr_min))
- return;
- }
-
- ReportType typ = ReportTypeRace;
- if (thr->is_vptr_access && freed)
- typ = ReportTypeVptrUseAfterFree;
- else if (thr->is_vptr_access)
- typ = ReportTypeVptrRace;
- else if (freed)
- typ = ReportTypeUseAfterFree;
-
- if (IsFiredSuppression(ctx, typ, addr))
- return;
-
- const uptr kMop = 2;
- VarSizeStackTrace traces[kMop];
- uptr tags[kMop] = {kExternalTagNone};
- uptr toppc = TraceTopPC(thr);
- if (toppc >> kEventPCBits) {
- // This is a work-around for a known issue.
- // The scenario where this happens is rather elaborate and requires
- // an instrumented __sanitizer_report_error_summary callback and
- // a __tsan_symbolize_external callback and a race during a range memory
- // access larger than 8 bytes. MemoryAccessRange adds the current PC to
- // the trace and starts processing memory accesses. A first memory access
- // triggers a race, we report it and call the instrumented
- // __sanitizer_report_error_summary, which adds more stuff to the trace
- // since it is intrumented. Then a second memory access in MemoryAccessRange
- // also triggers a race and we get here and call TraceTopPC to get the
- // current PC, however now it contains some unrelated events from the
- // callback. Most likely, TraceTopPC will now return a EventTypeFuncExit
- // event. Later we subtract -1 from it (in GetPreviousInstructionPc)
- // and the resulting PC has kExternalPCBit set, so we pass it to
- // __tsan_symbolize_external_ex. __tsan_symbolize_external_ex is within its
- // rights to crash since the PC is completely bogus.
- // test/tsan/double_race.cc contains a test case for this.
- toppc = 0;
- }
- ObtainCurrentStack(thr, toppc, &traces[0], &tags[0]);
- if (IsFiredSuppression(ctx, typ, traces[0]))
- return;
-
- // MutexSet is too large to live on stack.
- Vector<u64> mset_buffer;
- mset_buffer.Resize(sizeof(MutexSet) / sizeof(u64) + 1);
- MutexSet *mset2 = new(&mset_buffer[0]) MutexSet();
-
- Shadow s2(thr->racy_state[1]);
- RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2, &tags[1]);
- if (IsFiredSuppression(ctx, typ, traces[1]))
- return;
-
- if (HandleRacyStacks(thr, traces, addr_min, addr_max))
- return;
-
- // If any of the accesses has a tag, treat this as an "external" race.
- uptr tag = kExternalTagNone;
- for (uptr i = 0; i < kMop; i++) {
- if (tags[i] != kExternalTagNone) {
- typ = ReportTypeExternalRace;
- tag = tags[i];
- break;
- }
- }
-
- ThreadRegistryLock l0(ctx->thread_registry);
- ScopedReport rep(typ, tag);
- for (uptr i = 0; i < kMop; i++) {
- Shadow s(thr->racy_state[i]);
- rep.AddMemoryAccess(addr, tags[i], s, traces[i],
- i == 0 ? &thr->mset : mset2);
- }
-
- for (uptr i = 0; i < kMop; i++) {
- FastState s(thr->racy_state[i]);
- ThreadContext *tctx = static_cast<ThreadContext*>(
- ctx->thread_registry->GetThreadLocked(s.tid()));
- if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1)
- continue;
- rep.AddThread(tctx);
- }
-
- rep.AddLocation(addr_min, addr_max - addr_min);
-
-#if !SANITIZER_GO
- { // NOLINT
- Shadow s(thr->racy_state[1]);
- if (s.epoch() <= thr->last_sleep_clock.get(s.tid()))
- rep.AddSleep(thr->last_sleep_stack_id);
- }
-#endif
-
- if (!OutputReport(thr, rep))
- return;
-
- AddRacyStacks(thr, traces, addr_min, addr_max);
-}
-
-void PrintCurrentStack(ThreadState *thr, uptr pc) {
- VarSizeStackTrace trace;
- ObtainCurrentStack(thr, pc, &trace);
- PrintStack(SymbolizeStack(trace));
-}
-
-// Always inlining PrintCurrentStackSlow, because LocatePcInTrace assumes
-// __sanitizer_print_stack_trace exists in the actual unwinded stack, but
-// tail-call to PrintCurrentStackSlow breaks this assumption because
-// __sanitizer_print_stack_trace disappears after tail-call.
-// However, this solution is not reliable enough, please see dvyukov's comment
-// http://reviews.llvm.org/D19148#406208
-// Also see PR27280 comment 2 and 3 for breaking examples and analysis.
-ALWAYS_INLINE
-void PrintCurrentStackSlow(uptr pc) {
-#if !SANITIZER_GO
- BufferedStackTrace *ptrace =
- new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace)))
- BufferedStackTrace();
- ptrace->Unwind(kStackTraceMax, pc, 0, 0, 0, 0, false);
- for (uptr i = 0; i < ptrace->size / 2; i++) {
- uptr tmp = ptrace->trace_buffer[i];
- ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
- ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
- }
- PrintStack(SymbolizeStack(*ptrace));
-#endif
-}
-
-} // namespace __tsan
-
-using namespace __tsan;
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_print_stack_trace() {
- PrintCurrentStackSlow(StackTrace::GetCurrentPc());
-}
-} // extern "C"
--- /dev/null
+//===-- tsan_rtl_report.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "tsan_platform.h"
+#include "tsan_rtl.h"
+#include "tsan_suppressions.h"
+#include "tsan_symbolize.h"
+#include "tsan_report.h"
+#include "tsan_sync.h"
+#include "tsan_mman.h"
+#include "tsan_flags.h"
+#include "tsan_fd.h"
+
+namespace __tsan {
+
+using namespace __sanitizer; // NOLINT
+
+static ReportStack *SymbolizeStack(StackTrace trace);
+
+void TsanCheckFailed(const char *file, int line, const char *cond,
+ u64 v1, u64 v2) {
+ // There is high probability that interceptors will check-fail as well,
+ // on the other hand there is no sense in processing interceptors
+ // since we are going to die soon.
+ ScopedIgnoreInterceptors ignore;
+#if !SANITIZER_GO
+ cur_thread()->ignore_sync++;
+ cur_thread()->ignore_reads_and_writes++;
+#endif
+ Printf("FATAL: ThreadSanitizer CHECK failed: "
+ "%s:%d \"%s\" (0x%zx, 0x%zx)\n",
+ file, line, cond, (uptr)v1, (uptr)v2);
+ PrintCurrentStackSlow(StackTrace::GetCurrentPc());
+ Die();
+}
+
+// Can be overriden by an application/test to intercept reports.
+#ifdef TSAN_EXTERNAL_HOOKS
+bool OnReport(const ReportDesc *rep, bool suppressed);
+#else
+SANITIZER_WEAK_CXX_DEFAULT_IMPL
+bool OnReport(const ReportDesc *rep, bool suppressed) {
+ (void)rep;
+ return suppressed;
+}
+#endif
+
+SANITIZER_WEAK_DEFAULT_IMPL
+void __tsan_on_report(const ReportDesc *rep) {
+ (void)rep;
+}
+
+static void StackStripMain(SymbolizedStack *frames) {
+ SymbolizedStack *last_frame = nullptr;
+ SymbolizedStack *last_frame2 = nullptr;
+ for (SymbolizedStack *cur = frames; cur; cur = cur->next) {
+ last_frame2 = last_frame;
+ last_frame = cur;
+ }
+
+ if (last_frame2 == 0)
+ return;
+#if !SANITIZER_GO
+ const char *last = last_frame->info.function;
+ const char *last2 = last_frame2->info.function;
+ // Strip frame above 'main'
+ if (last2 && 0 == internal_strcmp(last2, "main")) {
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
+ // Strip our internal thread start routine.
+ } else if (last && 0 == internal_strcmp(last, "__tsan_thread_start_func")) {
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
+ // Strip global ctors init.
+ } else if (last && 0 == internal_strcmp(last, "__do_global_ctors_aux")) {
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
+ // If both are 0, then we probably just failed to symbolize.
+ } else if (last || last2) {
+ // Ensure that we recovered stack completely. Trimmed stack
+ // can actually happen if we do not instrument some code,
+ // so it's only a debug print. However we must try hard to not miss it
+ // due to our fault.
+ DPrintf("Bottom stack frame is missed\n");
+ }
+#else
+ // The last frame always point into runtime (gosched0, goexit0, runtime.main).
+ last_frame->ClearAll();
+ last_frame2->next = nullptr;
+#endif
+}
+
+ReportStack *SymbolizeStackId(u32 stack_id) {
+ if (stack_id == 0)
+ return 0;
+ StackTrace stack = StackDepotGet(stack_id);
+ if (stack.trace == nullptr)
+ return nullptr;
+ return SymbolizeStack(stack);
+}
+
+static ReportStack *SymbolizeStack(StackTrace trace) {
+ if (trace.size == 0)
+ return 0;
+ SymbolizedStack *top = nullptr;
+ for (uptr si = 0; si < trace.size; si++) {
+ const uptr pc = trace.trace[si];
+ uptr pc1 = pc;
+ // We obtain the return address, but we're interested in the previous
+ // instruction.
+ if ((pc & kExternalPCBit) == 0)
+ pc1 = StackTrace::GetPreviousInstructionPc(pc);
+ SymbolizedStack *ent = SymbolizeCode(pc1);
+ CHECK_NE(ent, 0);
+ SymbolizedStack *last = ent;
+ while (last->next) {
+ last->info.address = pc; // restore original pc for report
+ last = last->next;
+ }
+ last->info.address = pc; // restore original pc for report
+ last->next = top;
+ top = ent;
+ }
+ StackStripMain(top);
+
+ ReportStack *stack = ReportStack::New();
+ stack->frames = top;
+ return stack;
+}
+
+ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) {
+ ctx->thread_registry->CheckLocked();
+ void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc));
+ rep_ = new(mem) ReportDesc;
+ rep_->typ = typ;
+ rep_->tag = tag;
+ ctx->report_mtx.Lock();
+}
+
+ScopedReportBase::~ScopedReportBase() {
+ ctx->report_mtx.Unlock();
+ DestroyAndFree(rep_);
+}
+
+void ScopedReportBase::AddStack(StackTrace stack, bool suppressable) {
+ ReportStack **rs = rep_->stacks.PushBack();
+ *rs = SymbolizeStack(stack);
+ (*rs)->suppressable = suppressable;
+}
+
+void ScopedReportBase::AddMemoryAccess(uptr addr, uptr external_tag, Shadow s,
+ StackTrace stack, const MutexSet *mset) {
+ void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
+ ReportMop *mop = new(mem) ReportMop;
+ rep_->mops.PushBack(mop);
+ mop->tid = s.tid();
+ mop->addr = addr + s.addr0();
+ mop->size = s.size();
+ mop->write = s.IsWrite();
+ mop->atomic = s.IsAtomic();
+ mop->stack = SymbolizeStack(stack);
+ mop->external_tag = external_tag;
+ if (mop->stack)
+ mop->stack->suppressable = true;
+ for (uptr i = 0; i < mset->Size(); i++) {
+ MutexSet::Desc d = mset->Get(i);
+ u64 mid = this->AddMutex(d.id);
+ ReportMopMutex mtx = {mid, d.write};
+ mop->mset.PushBack(mtx);
+ }
+}
+
+void ScopedReportBase::AddUniqueTid(int unique_tid) {
+ rep_->unique_tids.PushBack(unique_tid);
+}
+
+void ScopedReportBase::AddThread(const ThreadContext *tctx, bool suppressable) {
+ for (uptr i = 0; i < rep_->threads.Size(); i++) {
+ if ((u32)rep_->threads[i]->id == tctx->tid)
+ return;
+ }
+ void *mem = internal_alloc(MBlockReportThread, sizeof(ReportThread));
+ ReportThread *rt = new(mem) ReportThread;
+ rep_->threads.PushBack(rt);
+ rt->id = tctx->tid;
+ rt->os_id = tctx->os_id;
+ rt->running = (tctx->status == ThreadStatusRunning);
+ rt->name = internal_strdup(tctx->name);
+ rt->parent_tid = tctx->parent_tid;
+ rt->thread_type = tctx->thread_type;
+ rt->stack = 0;
+ rt->stack = SymbolizeStackId(tctx->creation_stack_id);
+ if (rt->stack)
+ rt->stack->suppressable = suppressable;
+}
+
+#if !SANITIZER_GO
+static bool FindThreadByUidLockedCallback(ThreadContextBase *tctx, void *arg) {
+ int unique_id = *(int *)arg;
+ return tctx->unique_id == (u32)unique_id;
+}
+
+static ThreadContext *FindThreadByUidLocked(int unique_id) {
+ ctx->thread_registry->CheckLocked();
+ return static_cast<ThreadContext *>(
+ ctx->thread_registry->FindThreadContextLocked(
+ FindThreadByUidLockedCallback, &unique_id));
+}
+
+static ThreadContext *FindThreadByTidLocked(int tid) {
+ ctx->thread_registry->CheckLocked();
+ return static_cast<ThreadContext*>(
+ ctx->thread_registry->GetThreadLocked(tid));
+}
+
+static bool IsInStackOrTls(ThreadContextBase *tctx_base, void *arg) {
+ uptr addr = (uptr)arg;
+ ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
+ if (tctx->status != ThreadStatusRunning)
+ return false;
+ ThreadState *thr = tctx->thr;
+ CHECK(thr);
+ return ((addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size) ||
+ (addr >= thr->tls_addr && addr < thr->tls_addr + thr->tls_size));
+}
+
+ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack) {
+ ctx->thread_registry->CheckLocked();
+ ThreadContext *tctx = static_cast<ThreadContext*>(
+ ctx->thread_registry->FindThreadContextLocked(IsInStackOrTls,
+ (void*)addr));
+ if (!tctx)
+ return 0;
+ ThreadState *thr = tctx->thr;
+ CHECK(thr);
+ *is_stack = (addr >= thr->stk_addr && addr < thr->stk_addr + thr->stk_size);
+ return tctx;
+}
+#endif
+
+void ScopedReportBase::AddThread(int unique_tid, bool suppressable) {
+#if !SANITIZER_GO
+ if (const ThreadContext *tctx = FindThreadByUidLocked(unique_tid))
+ AddThread(tctx, suppressable);
+#endif
+}
+
+void ScopedReportBase::AddMutex(const SyncVar *s) {
+ for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
+ if (rep_->mutexes[i]->id == s->uid)
+ return;
+ }
+ void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
+ ReportMutex *rm = new(mem) ReportMutex;
+ rep_->mutexes.PushBack(rm);
+ rm->id = s->uid;
+ rm->addr = s->addr;
+ rm->destroyed = false;
+ rm->stack = SymbolizeStackId(s->creation_stack_id);
+}
+
+u64 ScopedReportBase::AddMutex(u64 id) {
+ u64 uid = 0;
+ u64 mid = id;
+ uptr addr = SyncVar::SplitId(id, &uid);
+ SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true);
+ // Check that the mutex is still alive.
+ // Another mutex can be created at the same address,
+ // so check uid as well.
+ if (s && s->CheckId(uid)) {
+ mid = s->uid;
+ AddMutex(s);
+ } else {
+ AddDeadMutex(id);
+ }
+ if (s)
+ s->mtx.Unlock();
+ return mid;
+}
+
+void ScopedReportBase::AddDeadMutex(u64 id) {
+ for (uptr i = 0; i < rep_->mutexes.Size(); i++) {
+ if (rep_->mutexes[i]->id == id)
+ return;
+ }
+ void *mem = internal_alloc(MBlockReportMutex, sizeof(ReportMutex));
+ ReportMutex *rm = new(mem) ReportMutex;
+ rep_->mutexes.PushBack(rm);
+ rm->id = id;
+ rm->addr = 0;
+ rm->destroyed = true;
+ rm->stack = 0;
+}
+
+void ScopedReportBase::AddLocation(uptr addr, uptr size) {
+ if (addr == 0)
+ return;
+#if !SANITIZER_GO
+ int fd = -1;
+ int creat_tid = kInvalidTid;
+ u32 creat_stack = 0;
+ if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) {
+ ReportLocation *loc = ReportLocation::New(ReportLocationFD);
+ loc->fd = fd;
+ loc->tid = creat_tid;
+ loc->stack = SymbolizeStackId(creat_stack);
+ rep_->locs.PushBack(loc);
+ ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
+ if (tctx)
+ AddThread(tctx);
+ return;
+ }
+ MBlock *b = 0;
+ Allocator *a = allocator();
+ if (a->PointerIsMine((void*)addr)) {
+ void *block_begin = a->GetBlockBegin((void*)addr);
+ if (block_begin)
+ b = ctx->metamap.GetBlock((uptr)block_begin);
+ }
+ if (b != 0) {
+ ThreadContext *tctx = FindThreadByTidLocked(b->tid);
+ ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
+ loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
+ loc->heap_chunk_size = b->siz;
+ loc->external_tag = b->tag;
+ loc->tid = tctx ? tctx->tid : b->tid;
+ loc->stack = SymbolizeStackId(b->stk);
+ rep_->locs.PushBack(loc);
+ if (tctx)
+ AddThread(tctx);
+ return;
+ }
+ bool is_stack = false;
+ if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
+ ReportLocation *loc =
+ ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS);
+ loc->tid = tctx->tid;
+ rep_->locs.PushBack(loc);
+ AddThread(tctx);
+ }
+#endif
+ if (ReportLocation *loc = SymbolizeData(addr)) {
+ loc->suppressable = true;
+ rep_->locs.PushBack(loc);
+ return;
+ }
+}
+
+#if !SANITIZER_GO
+void ScopedReportBase::AddSleep(u32 stack_id) {
+ rep_->sleep = SymbolizeStackId(stack_id);
+}
+#endif
+
+void ScopedReportBase::SetCount(int count) { rep_->count = count; }
+
+const ReportDesc *ScopedReportBase::GetReport() const { return rep_; }
+
+ScopedReport::ScopedReport(ReportType typ, uptr tag)
+ : ScopedReportBase(typ, tag) {}
+
+ScopedReport::~ScopedReport() {}
+
+void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
+ MutexSet *mset, uptr *tag) {
+ // This function restores stack trace and mutex set for the thread/epoch.
+ // It does so by getting stack trace and mutex set at the beginning of
+ // trace part, and then replaying the trace till the given epoch.
+ Trace* trace = ThreadTrace(tid);
+ ReadLock l(&trace->mtx);
+ const int partidx = (epoch / kTracePartSize) % TraceParts();
+ TraceHeader* hdr = &trace->headers[partidx];
+ if (epoch < hdr->epoch0 || epoch >= hdr->epoch0 + kTracePartSize)
+ return;
+ CHECK_EQ(RoundDown(epoch, kTracePartSize), hdr->epoch0);
+ const u64 epoch0 = RoundDown(epoch, TraceSize());
+ const u64 eend = epoch % TraceSize();
+ const u64 ebegin = RoundDown(eend, kTracePartSize);
+ DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
+ tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
+ Vector<uptr> stack;
+ stack.Resize(hdr->stack0.size + 64);
+ for (uptr i = 0; i < hdr->stack0.size; i++) {
+ stack[i] = hdr->stack0.trace[i];
+ DPrintf2(" #%02zu: pc=%zx\n", i, stack[i]);
+ }
+ if (mset)
+ *mset = hdr->mset0;
+ uptr pos = hdr->stack0.size;
+ Event *events = (Event*)GetThreadTrace(tid);
+ for (uptr i = ebegin; i <= eend; i++) {
+ Event ev = events[i];
+ EventType typ = (EventType)(ev >> kEventPCBits);
+ uptr pc = (uptr)(ev & ((1ull << kEventPCBits) - 1));
+ DPrintf2(" %zu typ=%d pc=%zx\n", i, typ, pc);
+ if (typ == EventTypeMop) {
+ stack[pos] = pc;
+ } else if (typ == EventTypeFuncEnter) {
+ if (stack.Size() < pos + 2)
+ stack.Resize(pos + 2);
+ stack[pos++] = pc;
+ } else if (typ == EventTypeFuncExit) {
+ if (pos > 0)
+ pos--;
+ }
+ if (mset) {
+ if (typ == EventTypeLock) {
+ mset->Add(pc, true, epoch0 + i);
+ } else if (typ == EventTypeUnlock) {
+ mset->Del(pc, true);
+ } else if (typ == EventTypeRLock) {
+ mset->Add(pc, false, epoch0 + i);
+ } else if (typ == EventTypeRUnlock) {
+ mset->Del(pc, false);
+ }
+ }
+ for (uptr j = 0; j <= pos; j++)
+ DPrintf2(" #%zu: %zx\n", j, stack[j]);
+ }
+ if (pos == 0 && stack[0] == 0)
+ return;
+ pos++;
+ stk->Init(&stack[0], pos);
+ ExtractTagFromStack(stk, tag);
+}
+
+static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
+ uptr addr_min, uptr addr_max) {
+ bool equal_stack = false;
+ RacyStacks hash;
+ bool equal_address = false;
+ RacyAddress ra0 = {addr_min, addr_max};
+ {
+ ReadLock lock(&ctx->racy_mtx);
+ if (flags()->suppress_equal_stacks) {
+ hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
+ for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
+ if (hash == ctx->racy_stacks[i]) {
+ VPrintf(2,
+ "ThreadSanitizer: suppressing report as doubled (stack)\n");
+ equal_stack = true;
+ break;
+ }
+ }
+ }
+ if (flags()->suppress_equal_addresses) {
+ for (uptr i = 0; i < ctx->racy_addresses.Size(); i++) {
+ RacyAddress ra2 = ctx->racy_addresses[i];
+ uptr maxbeg = max(ra0.addr_min, ra2.addr_min);
+ uptr minend = min(ra0.addr_max, ra2.addr_max);
+ if (maxbeg < minend) {
+ VPrintf(2, "ThreadSanitizer: suppressing report as doubled (addr)\n");
+ equal_address = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!equal_stack && !equal_address)
+ return false;
+ if (!equal_stack) {
+ Lock lock(&ctx->racy_mtx);
+ ctx->racy_stacks.PushBack(hash);
+ }
+ if (!equal_address) {
+ Lock lock(&ctx->racy_mtx);
+ ctx->racy_addresses.PushBack(ra0);
+ }
+ return true;
+}
+
+static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
+ uptr addr_min, uptr addr_max) {
+ Lock lock(&ctx->racy_mtx);
+ if (flags()->suppress_equal_stacks) {
+ RacyStacks hash;
+ hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
+ hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
+ ctx->racy_stacks.PushBack(hash);
+ }
+ if (flags()->suppress_equal_addresses) {
+ RacyAddress ra0 = {addr_min, addr_max};
+ ctx->racy_addresses.PushBack(ra0);
+ }
+}
+
+bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
+ if (!flags()->report_bugs || thr->suppress_reports)
+ return false;
+ atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime());
+ const ReportDesc *rep = srep.GetReport();
+ CHECK_EQ(thr->current_report, nullptr);
+ thr->current_report = rep;
+ Suppression *supp = 0;
+ uptr pc_or_addr = 0;
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->mops.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->mops[i]->stack, &supp);
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->stacks.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->stacks[i], &supp);
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->threads.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->threads[i]->stack, &supp);
+ for (uptr i = 0; pc_or_addr == 0 && i < rep->locs.Size(); i++)
+ pc_or_addr = IsSuppressed(rep->typ, rep->locs[i], &supp);
+ if (pc_or_addr != 0) {
+ Lock lock(&ctx->fired_suppressions_mtx);
+ FiredSuppression s = {srep.GetReport()->typ, pc_or_addr, supp};
+ ctx->fired_suppressions.push_back(s);
+ }
+ {
+ bool old_is_freeing = thr->is_freeing;
+ thr->is_freeing = false;
+ bool suppressed = OnReport(rep, pc_or_addr != 0);
+ thr->is_freeing = old_is_freeing;
+ if (suppressed) {
+ thr->current_report = nullptr;
+ return false;
+ }
+ }
+ PrintReport(rep);
+ __tsan_on_report(rep);
+ ctx->nreported++;
+ if (flags()->halt_on_error)
+ Die();
+ thr->current_report = nullptr;
+ return true;
+}
+
+bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace) {
+ ReadLock lock(&ctx->fired_suppressions_mtx);
+ for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
+ if (ctx->fired_suppressions[k].type != type)
+ continue;
+ for (uptr j = 0; j < trace.size; j++) {
+ FiredSuppression *s = &ctx->fired_suppressions[k];
+ if (trace.trace[j] == s->pc_or_addr) {
+ if (s->supp)
+ atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool IsFiredSuppression(Context *ctx, ReportType type, uptr addr) {
+ ReadLock lock(&ctx->fired_suppressions_mtx);
+ for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
+ if (ctx->fired_suppressions[k].type != type)
+ continue;
+ FiredSuppression *s = &ctx->fired_suppressions[k];
+ if (addr == s->pc_or_addr) {
+ if (s->supp)
+ atomic_fetch_add(&s->supp->hit_count, 1, memory_order_relaxed);
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
+ Shadow s0(thr->racy_state[0]);
+ Shadow s1(thr->racy_state[1]);
+ CHECK(!(s0.IsAtomic() && s1.IsAtomic()));
+ if (!s0.IsAtomic() && !s1.IsAtomic())
+ return true;
+ if (s0.IsAtomic() && s1.IsFreed())
+ return true;
+ if (s1.IsAtomic() && thr->is_freeing)
+ return true;
+ return false;
+}
+
+void ReportRace(ThreadState *thr) {
+ CheckNoLocks(thr);
+
+ // Symbolizer makes lots of intercepted calls. If we try to process them,
+ // at best it will cause deadlocks on internal mutexes.
+ ScopedIgnoreInterceptors ignore;
+
+ if (!flags()->report_bugs)
+ return;
+ if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr))
+ return;
+
+ bool freed = false;
+ {
+ Shadow s(thr->racy_state[1]);
+ freed = s.GetFreedAndReset();
+ thr->racy_state[1] = s.raw();
+ }
+
+ uptr addr = ShadowToMem((uptr)thr->racy_shadow_addr);
+ uptr addr_min = 0;
+ uptr addr_max = 0;
+ {
+ uptr a0 = addr + Shadow(thr->racy_state[0]).addr0();
+ uptr a1 = addr + Shadow(thr->racy_state[1]).addr0();
+ uptr e0 = a0 + Shadow(thr->racy_state[0]).size();
+ uptr e1 = a1 + Shadow(thr->racy_state[1]).size();
+ addr_min = min(a0, a1);
+ addr_max = max(e0, e1);
+ if (IsExpectedReport(addr_min, addr_max - addr_min))
+ return;
+ }
+
+ ReportType typ = ReportTypeRace;
+ if (thr->is_vptr_access && freed)
+ typ = ReportTypeVptrUseAfterFree;
+ else if (thr->is_vptr_access)
+ typ = ReportTypeVptrRace;
+ else if (freed)
+ typ = ReportTypeUseAfterFree;
+
+ if (IsFiredSuppression(ctx, typ, addr))
+ return;
+
+ const uptr kMop = 2;
+ VarSizeStackTrace traces[kMop];
+ uptr tags[kMop] = {kExternalTagNone};
+ uptr toppc = TraceTopPC(thr);
+ if (toppc >> kEventPCBits) {
+ // This is a work-around for a known issue.
+ // The scenario where this happens is rather elaborate and requires
+ // an instrumented __sanitizer_report_error_summary callback and
+ // a __tsan_symbolize_external callback and a race during a range memory
+ // access larger than 8 bytes. MemoryAccessRange adds the current PC to
+ // the trace and starts processing memory accesses. A first memory access
+ // triggers a race, we report it and call the instrumented
+ // __sanitizer_report_error_summary, which adds more stuff to the trace
+ // since it is intrumented. Then a second memory access in MemoryAccessRange
+ // also triggers a race and we get here and call TraceTopPC to get the
+ // current PC, however now it contains some unrelated events from the
+ // callback. Most likely, TraceTopPC will now return a EventTypeFuncExit
+ // event. Later we subtract -1 from it (in GetPreviousInstructionPc)
+ // and the resulting PC has kExternalPCBit set, so we pass it to
+ // __tsan_symbolize_external_ex. __tsan_symbolize_external_ex is within its
+ // rights to crash since the PC is completely bogus.
+ // test/tsan/double_race.cpp contains a test case for this.
+ toppc = 0;
+ }
+ ObtainCurrentStack(thr, toppc, &traces[0], &tags[0]);
+ if (IsFiredSuppression(ctx, typ, traces[0]))
+ return;
+
+ // MutexSet is too large to live on stack.
+ Vector<u64> mset_buffer;
+ mset_buffer.Resize(sizeof(MutexSet) / sizeof(u64) + 1);
+ MutexSet *mset2 = new(&mset_buffer[0]) MutexSet();
+
+ Shadow s2(thr->racy_state[1]);
+ RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2, &tags[1]);
+ if (IsFiredSuppression(ctx, typ, traces[1]))
+ return;
+
+ if (HandleRacyStacks(thr, traces, addr_min, addr_max))
+ return;
+
+ // If any of the accesses has a tag, treat this as an "external" race.
+ uptr tag = kExternalTagNone;
+ for (uptr i = 0; i < kMop; i++) {
+ if (tags[i] != kExternalTagNone) {
+ typ = ReportTypeExternalRace;
+ tag = tags[i];
+ break;
+ }
+ }
+
+ ThreadRegistryLock l0(ctx->thread_registry);
+ ScopedReport rep(typ, tag);
+ for (uptr i = 0; i < kMop; i++) {
+ Shadow s(thr->racy_state[i]);
+ rep.AddMemoryAccess(addr, tags[i], s, traces[i],
+ i == 0 ? &thr->mset : mset2);
+ }
+
+ for (uptr i = 0; i < kMop; i++) {
+ FastState s(thr->racy_state[i]);
+ ThreadContext *tctx = static_cast<ThreadContext*>(
+ ctx->thread_registry->GetThreadLocked(s.tid()));
+ if (s.epoch() < tctx->epoch0 || s.epoch() > tctx->epoch1)
+ continue;
+ rep.AddThread(tctx);
+ }
+
+ rep.AddLocation(addr_min, addr_max - addr_min);
+
+#if !SANITIZER_GO
+ { // NOLINT
+ Shadow s(thr->racy_state[1]);
+ if (s.epoch() <= thr->last_sleep_clock.get(s.tid()))
+ rep.AddSleep(thr->last_sleep_stack_id);
+ }
+#endif
+
+ if (!OutputReport(thr, rep))
+ return;
+
+ AddRacyStacks(thr, traces, addr_min, addr_max);
+}
+
+void PrintCurrentStack(ThreadState *thr, uptr pc) {
+ VarSizeStackTrace trace;
+ ObtainCurrentStack(thr, pc, &trace);
+ PrintStack(SymbolizeStack(trace));
+}
+
+// Always inlining PrintCurrentStackSlow, because LocatePcInTrace assumes
+// __sanitizer_print_stack_trace exists in the actual unwinded stack, but
+// tail-call to PrintCurrentStackSlow breaks this assumption because
+// __sanitizer_print_stack_trace disappears after tail-call.
+// However, this solution is not reliable enough, please see dvyukov's comment
+// http://reviews.llvm.org/D19148#406208
+// Also see PR27280 comment 2 and 3 for breaking examples and analysis.
+ALWAYS_INLINE
+void PrintCurrentStackSlow(uptr pc) {
+#if !SANITIZER_GO
+ uptr bp = GET_CURRENT_FRAME();
+ BufferedStackTrace *ptrace =
+ new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace)))
+ BufferedStackTrace();
+ ptrace->Unwind(pc, bp, nullptr, false);
+
+ for (uptr i = 0; i < ptrace->size / 2; i++) {
+ uptr tmp = ptrace->trace_buffer[i];
+ ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
+ ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
+ }
+ PrintStack(SymbolizeStack(*ptrace));
+#endif
+}
+
+} // namespace __tsan
+
+using namespace __tsan;
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ PrintCurrentStackSlow(StackTrace::GetCurrentPc());
+}
+} // extern "C"
+++ /dev/null
-//===-- tsan_rtl_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 ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-#include "tsan_platform.h"
-#include "tsan_report.h"
-#include "tsan_sync.h"
-
-namespace __tsan {
-
-// ThreadContext implementation.
-
-ThreadContext::ThreadContext(int tid)
- : ThreadContextBase(tid)
- , thr()
- , sync()
- , epoch0()
- , epoch1() {
-}
-
-#if !SANITIZER_GO
-ThreadContext::~ThreadContext() {
-}
-#endif
-
-void ThreadContext::OnDead() {
- CHECK_EQ(sync.size(), 0);
-}
-
-void ThreadContext::OnJoined(void *arg) {
- ThreadState *caller_thr = static_cast<ThreadState *>(arg);
- AcquireImpl(caller_thr, 0, &sync);
- sync.Reset(&caller_thr->proc()->clock_cache);
-}
-
-struct OnCreatedArgs {
- ThreadState *thr;
- uptr pc;
-};
-
-void ThreadContext::OnCreated(void *arg) {
- thr = 0;
- if (tid == 0)
- return;
- OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
- if (!args->thr) // GCD workers don't have a parent thread.
- return;
- args->thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
- ReleaseImpl(args->thr, 0, &sync);
- creation_stack_id = CurrentStackId(args->thr, args->pc);
- if (reuse_count == 0)
- StatInc(args->thr, StatThreadMaxTid);
-}
-
-void ThreadContext::OnReset() {
- CHECK_EQ(sync.size(), 0);
- uptr trace_p = GetThreadTrace(tid);
- ReleaseMemoryPagesToOS(trace_p, trace_p + TraceSize() * sizeof(Event));
- //!!! ReleaseMemoryToOS(GetThreadTraceHeader(tid), sizeof(Trace));
-}
-
-void ThreadContext::OnDetached(void *arg) {
- ThreadState *thr1 = static_cast<ThreadState*>(arg);
- sync.Reset(&thr1->proc()->clock_cache);
-}
-
-struct OnStartedArgs {
- ThreadState *thr;
- uptr stk_addr;
- uptr stk_size;
- uptr tls_addr;
- uptr tls_size;
-};
-
-void ThreadContext::OnStarted(void *arg) {
- OnStartedArgs *args = static_cast<OnStartedArgs*>(arg);
- thr = args->thr;
- // RoundUp so that one trace part does not contain events
- // from different threads.
- epoch0 = RoundUp(epoch1 + 1, kTracePartSize);
- epoch1 = (u64)-1;
- new(thr) ThreadState(ctx, tid, unique_id, epoch0, reuse_count,
- args->stk_addr, args->stk_size, args->tls_addr, args->tls_size);
-#if !SANITIZER_GO
- thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0];
- thr->shadow_stack_pos = thr->shadow_stack;
- thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize;
-#else
- // Setup dynamic shadow stack.
- const int kInitStackSize = 8;
- thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack,
- kInitStackSize * sizeof(uptr));
- thr->shadow_stack_pos = thr->shadow_stack;
- thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
-#endif
- if (common_flags()->detect_deadlocks)
- thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id);
- thr->fast_state.SetHistorySize(flags()->history_size);
- // Commit switch to the new part of the trace.
- // TraceAddEvent will reset stack0/mset0 in the new part for us.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
-
- thr->fast_synch_epoch = epoch0;
- AcquireImpl(thr, 0, &sync);
- StatInc(thr, StatSyncAcquire);
- sync.Reset(&thr->proc()->clock_cache);
- thr->is_inited = true;
- DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
- "tls_addr=%zx tls_size=%zx\n",
- tid, (uptr)epoch0, args->stk_addr, args->stk_size,
- args->tls_addr, args->tls_size);
-}
-
-void ThreadContext::OnFinished() {
-#if SANITIZER_GO
- internal_free(thr->shadow_stack);
- thr->shadow_stack = nullptr;
- thr->shadow_stack_pos = nullptr;
- thr->shadow_stack_end = nullptr;
-#endif
- if (!detached) {
- thr->fast_state.IncrementEpoch();
- // Can't increment epoch w/o writing to the trace as well.
- TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
- ReleaseImpl(thr, 0, &sync);
- }
- epoch1 = thr->fast_state.epoch();
-
- if (common_flags()->detect_deadlocks)
- ctx->dd->DestroyLogicalThread(thr->dd_lt);
- thr->clock.ResetCached(&thr->proc()->clock_cache);
-#if !SANITIZER_GO
- thr->last_sleep_clock.ResetCached(&thr->proc()->clock_cache);
-#endif
- thr->~ThreadState();
-#if TSAN_COLLECT_STATS
- StatAggregate(ctx->stat, thr->stat);
-#endif
- thr = 0;
-}
-
-#if !SANITIZER_GO
-struct ThreadLeak {
- ThreadContext *tctx;
- int count;
-};
-
-static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) {
- Vector<ThreadLeak> &leaks = *(Vector<ThreadLeak>*)arg;
- ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
- if (tctx->detached || tctx->status != ThreadStatusFinished)
- return;
- for (uptr i = 0; i < leaks.Size(); i++) {
- if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) {
- leaks[i].count++;
- return;
- }
- }
- ThreadLeak leak = {tctx, 1};
- leaks.PushBack(leak);
-}
-#endif
-
-#if !SANITIZER_GO
-static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) {
- if (tctx->tid == 0) {
- Printf("ThreadSanitizer: main thread finished with ignores enabled\n");
- } else {
- Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled,"
- " created at:\n", tctx->tid, tctx->name);
- PrintStack(SymbolizeStackId(tctx->creation_stack_id));
- }
- Printf(" One of the following ignores was not ended"
- " (in order of probability)\n");
- for (uptr i = 0; i < set->Size(); i++) {
- Printf(" Ignore was enabled at:\n");
- PrintStack(SymbolizeStackId(set->At(i)));
- }
- Die();
-}
-
-static void ThreadCheckIgnore(ThreadState *thr) {
- if (ctx->after_multithreaded_fork)
- return;
- if (thr->ignore_reads_and_writes)
- ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set);
- if (thr->ignore_sync)
- ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set);
-}
-#else
-static void ThreadCheckIgnore(ThreadState *thr) {}
-#endif
-
-void ThreadFinalize(ThreadState *thr) {
- ThreadCheckIgnore(thr);
-#if !SANITIZER_GO
- if (!flags()->report_thread_leaks)
- return;
- ThreadRegistryLock l(ctx->thread_registry);
- Vector<ThreadLeak> leaks;
- ctx->thread_registry->RunCallbackForEachThreadLocked(
- MaybeReportThreadLeak, &leaks);
- for (uptr i = 0; i < leaks.Size(); i++) {
- ScopedReport rep(ReportTypeThreadLeak);
- rep.AddThread(leaks[i].tctx, true);
- rep.SetCount(leaks[i].count);
- OutputReport(thr, rep);
- }
-#endif
-}
-
-int ThreadCount(ThreadState *thr) {
- uptr result;
- ctx->thread_registry->GetNumberOfThreads(0, 0, &result);
- return (int)result;
-}
-
-int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
- StatInc(thr, StatThreadCreate);
- OnCreatedArgs args = { thr, pc };
- u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers.
- int tid =
- ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args);
- DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid);
- StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads());
- return tid;
-}
-
-void ThreadStart(ThreadState *thr, int tid, tid_t os_id, bool workerthread) {
- uptr stk_addr = 0;
- uptr stk_size = 0;
- uptr tls_addr = 0;
- uptr tls_size = 0;
-#if !SANITIZER_GO
- GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
-
- if (tid) {
- if (stk_addr && stk_size)
- MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size);
-
- if (tls_addr && tls_size) ImitateTlsWrite(thr, tls_addr, tls_size);
- }
-#endif
-
- ThreadRegistry *tr = ctx->thread_registry;
- OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size };
- tr->StartThread(tid, os_id, workerthread, &args);
-
- tr->Lock();
- thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid);
- tr->Unlock();
-
-#if !SANITIZER_GO
- if (ctx->after_multithreaded_fork) {
- thr->ignore_interceptors++;
- ThreadIgnoreBegin(thr, 0);
- ThreadIgnoreSyncBegin(thr, 0);
- }
-#endif
-}
-
-void ThreadFinish(ThreadState *thr) {
- ThreadCheckIgnore(thr);
- StatInc(thr, StatThreadFinish);
- if (thr->stk_addr && thr->stk_size)
- DontNeedShadowFor(thr->stk_addr, thr->stk_size);
- if (thr->tls_addr && thr->tls_size)
- DontNeedShadowFor(thr->tls_addr, thr->tls_size);
- thr->is_dead = true;
- ctx->thread_registry->FinishThread(thr->tid);
-}
-
-static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
- uptr uid = (uptr)arg;
- if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
- tctx->user_id = 0;
- return true;
- }
- return false;
-}
-
-int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
- int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
- DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
- return res;
-}
-
-void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
- CHECK_GT(tid, 0);
- CHECK_LT(tid, kMaxTid);
- DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
- ctx->thread_registry->JoinThread(tid, thr);
-}
-
-void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
- CHECK_GT(tid, 0);
- CHECK_LT(tid, kMaxTid);
- ctx->thread_registry->DetachThread(tid, thr);
-}
-
-void ThreadSetName(ThreadState *thr, const char *name) {
- ctx->thread_registry->SetThreadName(thr->tid, name);
-}
-
-void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
- uptr size, bool is_write) {
- if (size == 0)
- return;
-
- u64 *shadow_mem = (u64*)MemToShadow(addr);
- DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n",
- thr->tid, (void*)pc, (void*)addr,
- (int)size, is_write);
-
-#if SANITIZER_DEBUG
- if (!IsAppMem(addr)) {
- Printf("Access to non app mem %zx\n", addr);
- DCHECK(IsAppMem(addr));
- }
- if (!IsAppMem(addr + size - 1)) {
- Printf("Access to non app mem %zx\n", addr + size - 1);
- DCHECK(IsAppMem(addr + size - 1));
- }
- if (!IsShadowMem((uptr)shadow_mem)) {
- Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
- DCHECK(IsShadowMem((uptr)shadow_mem));
- }
- if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) {
- Printf("Bad shadow addr %p (%zx)\n",
- shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1);
- DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1)));
- }
-#endif
-
- StatInc(thr, StatMopRange);
-
- if (*shadow_mem == kShadowRodata) {
- DCHECK(!is_write);
- // Access to .rodata section, no races here.
- // Measurements show that it can be 10-20% of all memory accesses.
- StatInc(thr, StatMopRangeRodata);
- return;
- }
-
- FastState fast_state = thr->fast_state;
- if (fast_state.GetIgnoreBit())
- return;
-
- fast_state.IncrementEpoch();
- thr->fast_state = fast_state;
- TraceAddEvent(thr, fast_state, EventTypeMop, pc);
-
- bool unaligned = (addr % kShadowCell) != 0;
-
- // Handle unaligned beginning, if any.
- for (; addr % kShadowCell && size; addr++, size--) {
- int const kAccessSizeLog = 0;
- Shadow cur(fast_state);
- cur.SetWrite(is_write);
- cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false,
- shadow_mem, cur);
- }
- if (unaligned)
- shadow_mem += kShadowCnt;
- // Handle middle part, if any.
- for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) {
- int const kAccessSizeLog = 3;
- Shadow cur(fast_state);
- cur.SetWrite(is_write);
- cur.SetAddr0AndSizeLog(0, kAccessSizeLog);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false,
- shadow_mem, cur);
- shadow_mem += kShadowCnt;
- }
- // Handle ending, if any.
- for (; size; addr++, size--) {
- int const kAccessSizeLog = 0;
- Shadow cur(fast_state);
- cur.SetWrite(is_write);
- cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
- MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false,
- shadow_mem, cur);
- }
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_rtl_thread.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+#include "tsan_platform.h"
+#include "tsan_report.h"
+#include "tsan_sync.h"
+
+namespace __tsan {
+
+// ThreadContext implementation.
+
+ThreadContext::ThreadContext(int tid)
+ : ThreadContextBase(tid)
+ , thr()
+ , sync()
+ , epoch0()
+ , epoch1() {
+}
+
+#if !SANITIZER_GO
+ThreadContext::~ThreadContext() {
+}
+#endif
+
+void ThreadContext::OnDead() {
+ CHECK_EQ(sync.size(), 0);
+}
+
+void ThreadContext::OnJoined(void *arg) {
+ ThreadState *caller_thr = static_cast<ThreadState *>(arg);
+ AcquireImpl(caller_thr, 0, &sync);
+ sync.Reset(&caller_thr->proc()->clock_cache);
+}
+
+struct OnCreatedArgs {
+ ThreadState *thr;
+ uptr pc;
+};
+
+void ThreadContext::OnCreated(void *arg) {
+ thr = 0;
+ if (tid == 0)
+ return;
+ OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg);
+ if (!args->thr) // GCD workers don't have a parent thread.
+ return;
+ args->thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0);
+ ReleaseImpl(args->thr, 0, &sync);
+ creation_stack_id = CurrentStackId(args->thr, args->pc);
+ if (reuse_count == 0)
+ StatInc(args->thr, StatThreadMaxTid);
+}
+
+void ThreadContext::OnReset() {
+ CHECK_EQ(sync.size(), 0);
+ uptr trace_p = GetThreadTrace(tid);
+ ReleaseMemoryPagesToOS(trace_p, trace_p + TraceSize() * sizeof(Event));
+ //!!! ReleaseMemoryToOS(GetThreadTraceHeader(tid), sizeof(Trace));
+}
+
+void ThreadContext::OnDetached(void *arg) {
+ ThreadState *thr1 = static_cast<ThreadState*>(arg);
+ sync.Reset(&thr1->proc()->clock_cache);
+}
+
+struct OnStartedArgs {
+ ThreadState *thr;
+ uptr stk_addr;
+ uptr stk_size;
+ uptr tls_addr;
+ uptr tls_size;
+};
+
+void ThreadContext::OnStarted(void *arg) {
+ OnStartedArgs *args = static_cast<OnStartedArgs*>(arg);
+ thr = args->thr;
+ // RoundUp so that one trace part does not contain events
+ // from different threads.
+ epoch0 = RoundUp(epoch1 + 1, kTracePartSize);
+ epoch1 = (u64)-1;
+ new(thr) ThreadState(ctx, tid, unique_id, epoch0, reuse_count,
+ args->stk_addr, args->stk_size, args->tls_addr, args->tls_size);
+#if !SANITIZER_GO
+ thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0];
+ thr->shadow_stack_pos = thr->shadow_stack;
+ thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize;
+#else
+ // Setup dynamic shadow stack.
+ const int kInitStackSize = 8;
+ thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack,
+ kInitStackSize * sizeof(uptr));
+ thr->shadow_stack_pos = thr->shadow_stack;
+ thr->shadow_stack_end = thr->shadow_stack + kInitStackSize;
+#endif
+ if (common_flags()->detect_deadlocks)
+ thr->dd_lt = ctx->dd->CreateLogicalThread(unique_id);
+ thr->fast_state.SetHistorySize(flags()->history_size);
+ // Commit switch to the new part of the trace.
+ // TraceAddEvent will reset stack0/mset0 in the new part for us.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+
+ thr->fast_synch_epoch = epoch0;
+ AcquireImpl(thr, 0, &sync);
+ StatInc(thr, StatSyncAcquire);
+ sync.Reset(&thr->proc()->clock_cache);
+ thr->is_inited = true;
+ DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx "
+ "tls_addr=%zx tls_size=%zx\n",
+ tid, (uptr)epoch0, args->stk_addr, args->stk_size,
+ args->tls_addr, args->tls_size);
+}
+
+void ThreadContext::OnFinished() {
+#if SANITIZER_GO
+ internal_free(thr->shadow_stack);
+ thr->shadow_stack = nullptr;
+ thr->shadow_stack_pos = nullptr;
+ thr->shadow_stack_end = nullptr;
+#endif
+ if (!detached) {
+ thr->fast_state.IncrementEpoch();
+ // Can't increment epoch w/o writing to the trace as well.
+ TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0);
+ ReleaseImpl(thr, 0, &sync);
+ }
+ epoch1 = thr->fast_state.epoch();
+
+ if (common_flags()->detect_deadlocks)
+ ctx->dd->DestroyLogicalThread(thr->dd_lt);
+ thr->clock.ResetCached(&thr->proc()->clock_cache);
+#if !SANITIZER_GO
+ thr->last_sleep_clock.ResetCached(&thr->proc()->clock_cache);
+#endif
+ thr->~ThreadState();
+#if TSAN_COLLECT_STATS
+ StatAggregate(ctx->stat, thr->stat);
+#endif
+ thr = 0;
+}
+
+#if !SANITIZER_GO
+struct ThreadLeak {
+ ThreadContext *tctx;
+ int count;
+};
+
+static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) {
+ Vector<ThreadLeak> &leaks = *(Vector<ThreadLeak>*)arg;
+ ThreadContext *tctx = static_cast<ThreadContext*>(tctx_base);
+ if (tctx->detached || tctx->status != ThreadStatusFinished)
+ return;
+ for (uptr i = 0; i < leaks.Size(); i++) {
+ if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) {
+ leaks[i].count++;
+ return;
+ }
+ }
+ ThreadLeak leak = {tctx, 1};
+ leaks.PushBack(leak);
+}
+#endif
+
+#if !SANITIZER_GO
+static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) {
+ if (tctx->tid == 0) {
+ Printf("ThreadSanitizer: main thread finished with ignores enabled\n");
+ } else {
+ Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled,"
+ " created at:\n", tctx->tid, tctx->name);
+ PrintStack(SymbolizeStackId(tctx->creation_stack_id));
+ }
+ Printf(" One of the following ignores was not ended"
+ " (in order of probability)\n");
+ for (uptr i = 0; i < set->Size(); i++) {
+ Printf(" Ignore was enabled at:\n");
+ PrintStack(SymbolizeStackId(set->At(i)));
+ }
+ Die();
+}
+
+static void ThreadCheckIgnore(ThreadState *thr) {
+ if (ctx->after_multithreaded_fork)
+ return;
+ if (thr->ignore_reads_and_writes)
+ ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set);
+ if (thr->ignore_sync)
+ ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set);
+}
+#else
+static void ThreadCheckIgnore(ThreadState *thr) {}
+#endif
+
+void ThreadFinalize(ThreadState *thr) {
+ ThreadCheckIgnore(thr);
+#if !SANITIZER_GO
+ if (!flags()->report_thread_leaks)
+ return;
+ ThreadRegistryLock l(ctx->thread_registry);
+ Vector<ThreadLeak> leaks;
+ ctx->thread_registry->RunCallbackForEachThreadLocked(
+ MaybeReportThreadLeak, &leaks);
+ for (uptr i = 0; i < leaks.Size(); i++) {
+ ScopedReport rep(ReportTypeThreadLeak);
+ rep.AddThread(leaks[i].tctx, true);
+ rep.SetCount(leaks[i].count);
+ OutputReport(thr, rep);
+ }
+#endif
+}
+
+int ThreadCount(ThreadState *thr) {
+ uptr result;
+ ctx->thread_registry->GetNumberOfThreads(0, 0, &result);
+ return (int)result;
+}
+
+int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) {
+ StatInc(thr, StatThreadCreate);
+ OnCreatedArgs args = { thr, pc };
+ u32 parent_tid = thr ? thr->tid : kInvalidTid; // No parent for GCD workers.
+ int tid =
+ ctx->thread_registry->CreateThread(uid, detached, parent_tid, &args);
+ DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent_tid, tid, uid);
+ StatSet(thr, StatThreadMaxAlive, ctx->thread_registry->GetMaxAliveThreads());
+ return tid;
+}
+
+void ThreadStart(ThreadState *thr, int tid, tid_t os_id,
+ ThreadType thread_type) {
+ uptr stk_addr = 0;
+ uptr stk_size = 0;
+ uptr tls_addr = 0;
+ uptr tls_size = 0;
+#if !SANITIZER_GO
+ if (thread_type != ThreadType::Fiber)
+ GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size);
+
+ if (tid) {
+ if (stk_addr && stk_size)
+ MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size);
+
+ if (tls_addr && tls_size) ImitateTlsWrite(thr, tls_addr, tls_size);
+ }
+#endif
+
+ ThreadRegistry *tr = ctx->thread_registry;
+ OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size };
+ tr->StartThread(tid, os_id, thread_type, &args);
+
+ tr->Lock();
+ thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid);
+ tr->Unlock();
+
+#if !SANITIZER_GO
+ if (ctx->after_multithreaded_fork) {
+ thr->ignore_interceptors++;
+ ThreadIgnoreBegin(thr, 0);
+ ThreadIgnoreSyncBegin(thr, 0);
+ }
+#endif
+}
+
+void ThreadFinish(ThreadState *thr) {
+ ThreadCheckIgnore(thr);
+ StatInc(thr, StatThreadFinish);
+ if (thr->stk_addr && thr->stk_size)
+ DontNeedShadowFor(thr->stk_addr, thr->stk_size);
+ if (thr->tls_addr && thr->tls_size)
+ DontNeedShadowFor(thr->tls_addr, thr->tls_size);
+ thr->is_dead = true;
+ ctx->thread_registry->FinishThread(thr->tid);
+}
+
+static bool FindThreadByUid(ThreadContextBase *tctx, void *arg) {
+ uptr uid = (uptr)arg;
+ if (tctx->user_id == uid && tctx->status != ThreadStatusInvalid) {
+ tctx->user_id = 0;
+ return true;
+ }
+ return false;
+}
+
+int ThreadTid(ThreadState *thr, uptr pc, uptr uid) {
+ int res = ctx->thread_registry->FindThread(FindThreadByUid, (void*)uid);
+ DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, res);
+ return res;
+}
+
+void ThreadJoin(ThreadState *thr, uptr pc, int tid) {
+ CHECK_GT(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid);
+ ctx->thread_registry->JoinThread(tid, thr);
+}
+
+void ThreadDetach(ThreadState *thr, uptr pc, int tid) {
+ CHECK_GT(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ ctx->thread_registry->DetachThread(tid, thr);
+}
+
+void ThreadNotJoined(ThreadState *thr, uptr pc, int tid, uptr uid) {
+ CHECK_GT(tid, 0);
+ CHECK_LT(tid, kMaxTid);
+ ctx->thread_registry->SetThreadUserId(tid, uid);
+}
+
+void ThreadSetName(ThreadState *thr, const char *name) {
+ ctx->thread_registry->SetThreadName(thr->tid, name);
+}
+
+void MemoryAccessRange(ThreadState *thr, uptr pc, uptr addr,
+ uptr size, bool is_write) {
+ if (size == 0)
+ return;
+
+ u64 *shadow_mem = (u64*)MemToShadow(addr);
+ DPrintf2("#%d: MemoryAccessRange: @%p %p size=%d is_write=%d\n",
+ thr->tid, (void*)pc, (void*)addr,
+ (int)size, is_write);
+
+#if SANITIZER_DEBUG
+ if (!IsAppMem(addr)) {
+ Printf("Access to non app mem %zx\n", addr);
+ DCHECK(IsAppMem(addr));
+ }
+ if (!IsAppMem(addr + size - 1)) {
+ Printf("Access to non app mem %zx\n", addr + size - 1);
+ DCHECK(IsAppMem(addr + size - 1));
+ }
+ if (!IsShadowMem((uptr)shadow_mem)) {
+ Printf("Bad shadow addr %p (%zx)\n", shadow_mem, addr);
+ DCHECK(IsShadowMem((uptr)shadow_mem));
+ }
+ if (!IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1))) {
+ Printf("Bad shadow addr %p (%zx)\n",
+ shadow_mem + size * kShadowCnt / 8 - 1, addr + size - 1);
+ DCHECK(IsShadowMem((uptr)(shadow_mem + size * kShadowCnt / 8 - 1)));
+ }
+#endif
+
+ StatInc(thr, StatMopRange);
+
+ if (*shadow_mem == kShadowRodata) {
+ DCHECK(!is_write);
+ // Access to .rodata section, no races here.
+ // Measurements show that it can be 10-20% of all memory accesses.
+ StatInc(thr, StatMopRangeRodata);
+ return;
+ }
+
+ FastState fast_state = thr->fast_state;
+ if (fast_state.GetIgnoreBit())
+ return;
+
+ fast_state.IncrementEpoch();
+ thr->fast_state = fast_state;
+ TraceAddEvent(thr, fast_state, EventTypeMop, pc);
+
+ bool unaligned = (addr % kShadowCell) != 0;
+
+ // Handle unaligned beginning, if any.
+ for (; addr % kShadowCell && size; addr++, size--) {
+ int const kAccessSizeLog = 0;
+ Shadow cur(fast_state);
+ cur.SetWrite(is_write);
+ cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false,
+ shadow_mem, cur);
+ }
+ if (unaligned)
+ shadow_mem += kShadowCnt;
+ // Handle middle part, if any.
+ for (; size >= kShadowCell; addr += kShadowCell, size -= kShadowCell) {
+ int const kAccessSizeLog = 3;
+ Shadow cur(fast_state);
+ cur.SetWrite(is_write);
+ cur.SetAddr0AndSizeLog(0, kAccessSizeLog);
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false,
+ shadow_mem, cur);
+ shadow_mem += kShadowCnt;
+ }
+ // Handle ending, if any.
+ for (; size; addr++, size--) {
+ int const kAccessSizeLog = 0;
+ Shadow cur(fast_state);
+ cur.SetWrite(is_write);
+ cur.SetAddr0AndSizeLog(addr & (kShadowCell - 1), kAccessSizeLog);
+ MemoryAccessImpl(thr, addr, kAccessSizeLog, is_write, false,
+ shadow_mem, cur);
+ }
+}
+
+#if !SANITIZER_GO
+void FiberSwitchImpl(ThreadState *from, ThreadState *to) {
+ Processor *proc = from->proc();
+ ProcUnwire(proc, from);
+ ProcWire(proc, to);
+ set_cur_thread(to);
+}
+
+ThreadState *FiberCreate(ThreadState *thr, uptr pc, unsigned flags) {
+ void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadState));
+ ThreadState *fiber = static_cast<ThreadState *>(mem);
+ internal_memset(fiber, 0, sizeof(*fiber));
+ int tid = ThreadCreate(thr, pc, 0, true);
+ FiberSwitchImpl(thr, fiber);
+ ThreadStart(fiber, tid, 0, ThreadType::Fiber);
+ FiberSwitchImpl(fiber, thr);
+ return fiber;
+}
+
+void FiberDestroy(ThreadState *thr, uptr pc, ThreadState *fiber) {
+ FiberSwitchImpl(thr, fiber);
+ ThreadFinish(fiber);
+ FiberSwitchImpl(fiber, thr);
+ internal_free(fiber);
+}
+
+void FiberSwitch(ThreadState *thr, uptr pc,
+ ThreadState *fiber, unsigned flags) {
+ if (!(flags & FiberSwitchFlagNoSync))
+ Release(thr, pc, (uptr)fiber);
+ FiberSwitchImpl(thr, fiber);
+ if (!(flags & FiberSwitchFlagNoSync))
+ Acquire(fiber, pc, (uptr)fiber);
+}
+#endif
+
+} // namespace __tsan
+++ /dev/null
-//===-- tsan_stack_trace.cc -----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_stack_trace.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-
-namespace __tsan {
-
-VarSizeStackTrace::VarSizeStackTrace()
- : StackTrace(nullptr, 0), trace_buffer(nullptr) {}
-
-VarSizeStackTrace::~VarSizeStackTrace() {
- ResizeBuffer(0);
-}
-
-void VarSizeStackTrace::ResizeBuffer(uptr new_size) {
- if (trace_buffer) {
- internal_free(trace_buffer);
- }
- trace_buffer =
- (new_size > 0)
- ? (uptr *)internal_alloc(MBlockStackTrace,
- new_size * sizeof(trace_buffer[0]))
- : nullptr;
- trace = trace_buffer;
- size = new_size;
-}
-
-void VarSizeStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
- ResizeBuffer(cnt + !!extra_top_pc);
- internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
- if (extra_top_pc)
- trace_buffer[cnt] = extra_top_pc;
-}
-
-void VarSizeStackTrace::ReverseOrder() {
- for (u32 i = 0; i < (size >> 1); i++)
- Swap(trace_buffer[i], trace_buffer[size - 1 - i]);
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_stack_trace.cpp ----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_stack_trace.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+
+namespace __tsan {
+
+VarSizeStackTrace::VarSizeStackTrace()
+ : StackTrace(nullptr, 0), trace_buffer(nullptr) {}
+
+VarSizeStackTrace::~VarSizeStackTrace() {
+ ResizeBuffer(0);
+}
+
+void VarSizeStackTrace::ResizeBuffer(uptr new_size) {
+ if (trace_buffer) {
+ internal_free(trace_buffer);
+ }
+ trace_buffer =
+ (new_size > 0)
+ ? (uptr *)internal_alloc(MBlockStackTrace,
+ new_size * sizeof(trace_buffer[0]))
+ : nullptr;
+ trace = trace_buffer;
+ size = new_size;
+}
+
+void VarSizeStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
+ ResizeBuffer(cnt + !!extra_top_pc);
+ internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
+ if (extra_top_pc)
+ trace_buffer[cnt] = extra_top_pc;
+}
+
+void VarSizeStackTrace::ReverseOrder() {
+ for (u32 i = 0; i < (size >> 1); i++)
+ Swap(trace_buffer[i], trace_buffer[size - 1 - i]);
+}
+
+} // namespace __tsan
+
+#if !SANITIZER_GO
+void __sanitizer::BufferedStackTrace::UnwindImpl(
+ uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+ uptr top = 0;
+ uptr bottom = 0;
+ if (StackTrace::WillUseFastUnwind(request_fast)) {
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ Unwind(max_depth, pc, bp, nullptr, top, bottom, true);
+ } else
+ Unwind(max_depth, pc, 0, context, 0, 0, false);
+}
+#endif // SANITIZER_GO
//===-- tsan_stack_trace.h --------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_stat.cc ------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "tsan_stat.h"
-#include "tsan_rtl.h"
-
-namespace __tsan {
-
-#if TSAN_COLLECT_STATS
-
-void StatAggregate(u64 *dst, u64 *src) {
- for (int i = 0; i < StatCnt; i++)
- dst[i] += src[i];
-}
-
-void StatOutput(u64 *stat) {
- stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero];
-
- static const char *name[StatCnt] = {};
- name[StatMop] = "Memory accesses ";
- name[StatMopRead] = " Including reads ";
- name[StatMopWrite] = " writes ";
- name[StatMop1] = " Including size 1 ";
- name[StatMop2] = " size 2 ";
- name[StatMop4] = " size 4 ";
- name[StatMop8] = " size 8 ";
- name[StatMopSame] = " Including same ";
- name[StatMopIgnored] = " Including ignored ";
- name[StatMopRange] = " Including range ";
- name[StatMopRodata] = " Including .rodata ";
- name[StatMopRangeRodata] = " Including .rodata range ";
- name[StatShadowProcessed] = "Shadow processed ";
- name[StatShadowZero] = " Including empty ";
- name[StatShadowNonZero] = " Including non empty ";
- name[StatShadowSameSize] = " Including same size ";
- name[StatShadowIntersect] = " intersect ";
- name[StatShadowNotIntersect] = " not intersect ";
- name[StatShadowSameThread] = " Including same thread ";
- name[StatShadowAnotherThread] = " another thread ";
- name[StatShadowReplace] = " Including evicted ";
-
- name[StatFuncEnter] = "Function entries ";
- name[StatFuncExit] = "Function exits ";
- name[StatEvents] = "Events collected ";
-
- name[StatThreadCreate] = "Total threads created ";
- name[StatThreadFinish] = " threads finished ";
- name[StatThreadReuse] = " threads reused ";
- name[StatThreadMaxTid] = " max tid ";
- name[StatThreadMaxAlive] = " max alive threads ";
-
- name[StatMutexCreate] = "Mutexes created ";
- name[StatMutexDestroy] = " destroyed ";
- name[StatMutexLock] = " lock ";
- name[StatMutexUnlock] = " unlock ";
- name[StatMutexRecLock] = " recursive lock ";
- name[StatMutexRecUnlock] = " recursive unlock ";
- name[StatMutexReadLock] = " read lock ";
- name[StatMutexReadUnlock] = " read unlock ";
-
- name[StatSyncCreated] = "Sync objects created ";
- name[StatSyncDestroyed] = " destroyed ";
- name[StatSyncAcquire] = " acquired ";
- name[StatSyncRelease] = " released ";
-
- name[StatClockAcquire] = "Clock acquire ";
- name[StatClockAcquireEmpty] = " empty clock ";
- name[StatClockAcquireFastRelease] = " fast from release-store ";
- name[StatClockAcquireFull] = " full (slow) ";
- name[StatClockAcquiredSomething] = " acquired something ";
- name[StatClockRelease] = "Clock release ";
- name[StatClockReleaseResize] = " resize ";
- name[StatClockReleaseFast] = " fast ";
- name[StatClockReleaseSlow] = " dirty overflow (slow) ";
- name[StatClockReleaseFull] = " full (slow) ";
- name[StatClockReleaseAcquired] = " was acquired ";
- name[StatClockReleaseClearTail] = " clear tail ";
- name[StatClockStore] = "Clock release store ";
- name[StatClockStoreResize] = " resize ";
- name[StatClockStoreFast] = " fast ";
- name[StatClockStoreFull] = " slow ";
- name[StatClockStoreTail] = " clear tail ";
- name[StatClockAcquireRelease] = "Clock acquire-release ";
-
- name[StatAtomic] = "Atomic operations ";
- name[StatAtomicLoad] = " Including load ";
- name[StatAtomicStore] = " store ";
- name[StatAtomicExchange] = " exchange ";
- name[StatAtomicFetchAdd] = " fetch_add ";
- name[StatAtomicFetchSub] = " fetch_sub ";
- name[StatAtomicFetchAnd] = " fetch_and ";
- name[StatAtomicFetchOr] = " fetch_or ";
- name[StatAtomicFetchXor] = " fetch_xor ";
- name[StatAtomicFetchNand] = " fetch_nand ";
- name[StatAtomicCAS] = " compare_exchange ";
- name[StatAtomicFence] = " fence ";
- name[StatAtomicRelaxed] = " Including relaxed ";
- name[StatAtomicConsume] = " consume ";
- name[StatAtomicAcquire] = " acquire ";
- name[StatAtomicRelease] = " release ";
- name[StatAtomicAcq_Rel] = " acq_rel ";
- name[StatAtomicSeq_Cst] = " seq_cst ";
- name[StatAtomic1] = " Including size 1 ";
- name[StatAtomic2] = " size 2 ";
- name[StatAtomic4] = " size 4 ";
- name[StatAtomic8] = " size 8 ";
- name[StatAtomic16] = " size 16 ";
-
- name[StatAnnotation] = "Dynamic annotations ";
- name[StatAnnotateHappensBefore] = " HappensBefore ";
- name[StatAnnotateHappensAfter] = " HappensAfter ";
- name[StatAnnotateCondVarSignal] = " CondVarSignal ";
- name[StatAnnotateCondVarSignalAll] = " CondVarSignalAll ";
- name[StatAnnotateMutexIsNotPHB] = " MutexIsNotPHB ";
- name[StatAnnotateCondVarWait] = " CondVarWait ";
- name[StatAnnotateRWLockCreate] = " RWLockCreate ";
- name[StatAnnotateRWLockCreateStatic] = " StatAnnotateRWLockCreateStatic ";
- name[StatAnnotateRWLockDestroy] = " RWLockDestroy ";
- name[StatAnnotateRWLockAcquired] = " RWLockAcquired ";
- name[StatAnnotateRWLockReleased] = " RWLockReleased ";
- name[StatAnnotateTraceMemory] = " TraceMemory ";
- name[StatAnnotateFlushState] = " FlushState ";
- name[StatAnnotateNewMemory] = " NewMemory ";
- name[StatAnnotateNoOp] = " NoOp ";
- name[StatAnnotateFlushExpectedRaces] = " FlushExpectedRaces ";
- name[StatAnnotateEnableRaceDetection] = " EnableRaceDetection ";
- name[StatAnnotateMutexIsUsedAsCondVar] = " MutexIsUsedAsCondVar ";
- name[StatAnnotatePCQGet] = " PCQGet ";
- name[StatAnnotatePCQPut] = " PCQPut ";
- name[StatAnnotatePCQDestroy] = " PCQDestroy ";
- name[StatAnnotatePCQCreate] = " PCQCreate ";
- name[StatAnnotateExpectRace] = " ExpectRace ";
- name[StatAnnotateBenignRaceSized] = " BenignRaceSized ";
- name[StatAnnotateBenignRace] = " BenignRace ";
- name[StatAnnotateIgnoreReadsBegin] = " IgnoreReadsBegin ";
- name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd ";
- name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin ";
- name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd ";
- name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin ";
- name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd ";
- name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange ";
- name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange ";
- name[StatAnnotateThreadName] = " ThreadName ";
- name[Stat__tsan_mutex_create] = " __tsan_mutex_create ";
- name[Stat__tsan_mutex_destroy] = " __tsan_mutex_destroy ";
- name[Stat__tsan_mutex_pre_lock] = " __tsan_mutex_pre_lock ";
- name[Stat__tsan_mutex_post_lock] = " __tsan_mutex_post_lock ";
- name[Stat__tsan_mutex_pre_unlock] = " __tsan_mutex_pre_unlock ";
- name[Stat__tsan_mutex_post_unlock] = " __tsan_mutex_post_unlock ";
- name[Stat__tsan_mutex_pre_signal] = " __tsan_mutex_pre_signal ";
- name[Stat__tsan_mutex_post_signal] = " __tsan_mutex_post_signal ";
- name[Stat__tsan_mutex_pre_divert] = " __tsan_mutex_pre_divert ";
- name[Stat__tsan_mutex_post_divert] = " __tsan_mutex_post_divert ";
-
- name[StatMtxTotal] = "Contentionz ";
- name[StatMtxTrace] = " Trace ";
- name[StatMtxThreads] = " Threads ";
- name[StatMtxReport] = " Report ";
- name[StatMtxSyncVar] = " SyncVar ";
- name[StatMtxSyncTab] = " SyncTab ";
- name[StatMtxSlab] = " Slab ";
- name[StatMtxAtExit] = " Atexit ";
- name[StatMtxAnnotations] = " Annotations ";
- name[StatMtxMBlock] = " MBlock ";
- name[StatMtxDeadlockDetector] = " DeadlockDetector ";
- name[StatMtxFired] = " FiredSuppressions ";
- name[StatMtxRacy] = " RacyStacks ";
- name[StatMtxFD] = " FD ";
- name[StatMtxGlobalProc] = " GlobalProc ";
-
- Printf("Statistics:\n");
- for (int i = 0; i < StatCnt; i++)
- Printf("%s: %16zu\n", name[i], (uptr)stat[i]);
-}
-
-#endif
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_stat.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "tsan_stat.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+#if TSAN_COLLECT_STATS
+
+void StatAggregate(u64 *dst, u64 *src) {
+ for (int i = 0; i < StatCnt; i++)
+ dst[i] += src[i];
+}
+
+void StatOutput(u64 *stat) {
+ stat[StatShadowNonZero] = stat[StatShadowProcessed] - stat[StatShadowZero];
+
+ static const char *name[StatCnt] = {};
+ name[StatMop] = "Memory accesses ";
+ name[StatMopRead] = " Including reads ";
+ name[StatMopWrite] = " writes ";
+ name[StatMop1] = " Including size 1 ";
+ name[StatMop2] = " size 2 ";
+ name[StatMop4] = " size 4 ";
+ name[StatMop8] = " size 8 ";
+ name[StatMopSame] = " Including same ";
+ name[StatMopIgnored] = " Including ignored ";
+ name[StatMopRange] = " Including range ";
+ name[StatMopRodata] = " Including .rodata ";
+ name[StatMopRangeRodata] = " Including .rodata range ";
+ name[StatShadowProcessed] = "Shadow processed ";
+ name[StatShadowZero] = " Including empty ";
+ name[StatShadowNonZero] = " Including non empty ";
+ name[StatShadowSameSize] = " Including same size ";
+ name[StatShadowIntersect] = " intersect ";
+ name[StatShadowNotIntersect] = " not intersect ";
+ name[StatShadowSameThread] = " Including same thread ";
+ name[StatShadowAnotherThread] = " another thread ";
+ name[StatShadowReplace] = " Including evicted ";
+
+ name[StatFuncEnter] = "Function entries ";
+ name[StatFuncExit] = "Function exits ";
+ name[StatEvents] = "Events collected ";
+
+ name[StatThreadCreate] = "Total threads created ";
+ name[StatThreadFinish] = " threads finished ";
+ name[StatThreadReuse] = " threads reused ";
+ name[StatThreadMaxTid] = " max tid ";
+ name[StatThreadMaxAlive] = " max alive threads ";
+
+ name[StatMutexCreate] = "Mutexes created ";
+ name[StatMutexDestroy] = " destroyed ";
+ name[StatMutexLock] = " lock ";
+ name[StatMutexUnlock] = " unlock ";
+ name[StatMutexRecLock] = " recursive lock ";
+ name[StatMutexRecUnlock] = " recursive unlock ";
+ name[StatMutexReadLock] = " read lock ";
+ name[StatMutexReadUnlock] = " read unlock ";
+
+ name[StatSyncCreated] = "Sync objects created ";
+ name[StatSyncDestroyed] = " destroyed ";
+ name[StatSyncAcquire] = " acquired ";
+ name[StatSyncRelease] = " released ";
+
+ name[StatClockAcquire] = "Clock acquire ";
+ name[StatClockAcquireEmpty] = " empty clock ";
+ name[StatClockAcquireFastRelease] = " fast from release-store ";
+ name[StatClockAcquireFull] = " full (slow) ";
+ name[StatClockAcquiredSomething] = " acquired something ";
+ name[StatClockRelease] = "Clock release ";
+ name[StatClockReleaseResize] = " resize ";
+ name[StatClockReleaseFast] = " fast ";
+ name[StatClockReleaseSlow] = " dirty overflow (slow) ";
+ name[StatClockReleaseFull] = " full (slow) ";
+ name[StatClockReleaseAcquired] = " was acquired ";
+ name[StatClockReleaseClearTail] = " clear tail ";
+ name[StatClockStore] = "Clock release store ";
+ name[StatClockStoreResize] = " resize ";
+ name[StatClockStoreFast] = " fast ";
+ name[StatClockStoreFull] = " slow ";
+ name[StatClockStoreTail] = " clear tail ";
+ name[StatClockAcquireRelease] = "Clock acquire-release ";
+
+ name[StatAtomic] = "Atomic operations ";
+ name[StatAtomicLoad] = " Including load ";
+ name[StatAtomicStore] = " store ";
+ name[StatAtomicExchange] = " exchange ";
+ name[StatAtomicFetchAdd] = " fetch_add ";
+ name[StatAtomicFetchSub] = " fetch_sub ";
+ name[StatAtomicFetchAnd] = " fetch_and ";
+ name[StatAtomicFetchOr] = " fetch_or ";
+ name[StatAtomicFetchXor] = " fetch_xor ";
+ name[StatAtomicFetchNand] = " fetch_nand ";
+ name[StatAtomicCAS] = " compare_exchange ";
+ name[StatAtomicFence] = " fence ";
+ name[StatAtomicRelaxed] = " Including relaxed ";
+ name[StatAtomicConsume] = " consume ";
+ name[StatAtomicAcquire] = " acquire ";
+ name[StatAtomicRelease] = " release ";
+ name[StatAtomicAcq_Rel] = " acq_rel ";
+ name[StatAtomicSeq_Cst] = " seq_cst ";
+ name[StatAtomic1] = " Including size 1 ";
+ name[StatAtomic2] = " size 2 ";
+ name[StatAtomic4] = " size 4 ";
+ name[StatAtomic8] = " size 8 ";
+ name[StatAtomic16] = " size 16 ";
+
+ name[StatAnnotation] = "Dynamic annotations ";
+ name[StatAnnotateHappensBefore] = " HappensBefore ";
+ name[StatAnnotateHappensAfter] = " HappensAfter ";
+ name[StatAnnotateCondVarSignal] = " CondVarSignal ";
+ name[StatAnnotateCondVarSignalAll] = " CondVarSignalAll ";
+ name[StatAnnotateMutexIsNotPHB] = " MutexIsNotPHB ";
+ name[StatAnnotateCondVarWait] = " CondVarWait ";
+ name[StatAnnotateRWLockCreate] = " RWLockCreate ";
+ name[StatAnnotateRWLockCreateStatic] = " StatAnnotateRWLockCreateStatic ";
+ name[StatAnnotateRWLockDestroy] = " RWLockDestroy ";
+ name[StatAnnotateRWLockAcquired] = " RWLockAcquired ";
+ name[StatAnnotateRWLockReleased] = " RWLockReleased ";
+ name[StatAnnotateTraceMemory] = " TraceMemory ";
+ name[StatAnnotateFlushState] = " FlushState ";
+ name[StatAnnotateNewMemory] = " NewMemory ";
+ name[StatAnnotateNoOp] = " NoOp ";
+ name[StatAnnotateFlushExpectedRaces] = " FlushExpectedRaces ";
+ name[StatAnnotateEnableRaceDetection] = " EnableRaceDetection ";
+ name[StatAnnotateMutexIsUsedAsCondVar] = " MutexIsUsedAsCondVar ";
+ name[StatAnnotatePCQGet] = " PCQGet ";
+ name[StatAnnotatePCQPut] = " PCQPut ";
+ name[StatAnnotatePCQDestroy] = " PCQDestroy ";
+ name[StatAnnotatePCQCreate] = " PCQCreate ";
+ name[StatAnnotateExpectRace] = " ExpectRace ";
+ name[StatAnnotateBenignRaceSized] = " BenignRaceSized ";
+ name[StatAnnotateBenignRace] = " BenignRace ";
+ name[StatAnnotateIgnoreReadsBegin] = " IgnoreReadsBegin ";
+ name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd ";
+ name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin ";
+ name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd ";
+ name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin ";
+ name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd ";
+ name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange ";
+ name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange ";
+ name[StatAnnotateThreadName] = " ThreadName ";
+ name[Stat__tsan_mutex_create] = " __tsan_mutex_create ";
+ name[Stat__tsan_mutex_destroy] = " __tsan_mutex_destroy ";
+ name[Stat__tsan_mutex_pre_lock] = " __tsan_mutex_pre_lock ";
+ name[Stat__tsan_mutex_post_lock] = " __tsan_mutex_post_lock ";
+ name[Stat__tsan_mutex_pre_unlock] = " __tsan_mutex_pre_unlock ";
+ name[Stat__tsan_mutex_post_unlock] = " __tsan_mutex_post_unlock ";
+ name[Stat__tsan_mutex_pre_signal] = " __tsan_mutex_pre_signal ";
+ name[Stat__tsan_mutex_post_signal] = " __tsan_mutex_post_signal ";
+ name[Stat__tsan_mutex_pre_divert] = " __tsan_mutex_pre_divert ";
+ name[Stat__tsan_mutex_post_divert] = " __tsan_mutex_post_divert ";
+
+ name[StatMtxTotal] = "Contentionz ";
+ name[StatMtxTrace] = " Trace ";
+ name[StatMtxThreads] = " Threads ";
+ name[StatMtxReport] = " Report ";
+ name[StatMtxSyncVar] = " SyncVar ";
+ name[StatMtxSyncTab] = " SyncTab ";
+ name[StatMtxSlab] = " Slab ";
+ name[StatMtxAtExit] = " Atexit ";
+ name[StatMtxAnnotations] = " Annotations ";
+ name[StatMtxMBlock] = " MBlock ";
+ name[StatMtxDeadlockDetector] = " DeadlockDetector ";
+ name[StatMtxFired] = " FiredSuppressions ";
+ name[StatMtxRacy] = " RacyStacks ";
+ name[StatMtxFD] = " FD ";
+ name[StatMtxGlobalProc] = " GlobalProc ";
+
+ Printf("Statistics:\n");
+ for (int i = 0; i < StatCnt; i++)
+ Printf("%s: %16zu\n", name[i], (uptr)stat[i]);
+}
+
+#endif
+
+} // namespace __tsan
//===-- tsan_stat.h ---------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_suppressions.cc ----------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
-#include "tsan_suppressions.h"
-#include "tsan_rtl.h"
-#include "tsan_flags.h"
-#include "tsan_mman.h"
-#include "tsan_platform.h"
-
-#if !SANITIZER_GO
-// Suppressions for true/false positives in standard libraries.
-static const char *const std_suppressions =
-// Libstdc++ 4.4 has data races in std::string.
-// See http://crbug.com/181502 for an example.
-"race:^_M_rep$\n"
-"race:^_M_is_leaked$\n"
-// False positive when using std <thread>.
-// Happens because we miss atomic synchronization in libstdc++.
-// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details.
-"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n";
-
-// Can be overriden in frontend.
-SANITIZER_WEAK_DEFAULT_IMPL
-const char *__tsan_default_suppressions() {
- return 0;
-}
-#endif
-
-namespace __tsan {
-
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
-static SuppressionContext *suppression_ctx = nullptr;
-static const char *kSuppressionTypes[] = {
- kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex,
- kSuppressionThread, kSuppressionSignal, kSuppressionLib,
- kSuppressionDeadlock};
-
-void InitializeSuppressions() {
- CHECK_EQ(nullptr, suppression_ctx);
- suppression_ctx = new (suppression_placeholder) // NOLINT
- SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
- suppression_ctx->ParseFromFile(flags()->suppressions);
-#if !SANITIZER_GO
- suppression_ctx->Parse(__tsan_default_suppressions());
- suppression_ctx->Parse(std_suppressions);
-#endif
-}
-
-SuppressionContext *Suppressions() {
- CHECK(suppression_ctx);
- return suppression_ctx;
-}
-
-static const char *conv(ReportType typ) {
- if (typ == ReportTypeRace)
- return kSuppressionRace;
- else if (typ == ReportTypeVptrRace)
- return kSuppressionRace;
- else if (typ == ReportTypeUseAfterFree)
- return kSuppressionRace;
- else if (typ == ReportTypeVptrUseAfterFree)
- return kSuppressionRace;
- else if (typ == ReportTypeExternalRace)
- return kSuppressionRace;
- else if (typ == ReportTypeThreadLeak)
- return kSuppressionThread;
- else if (typ == ReportTypeMutexDestroyLocked)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexDoubleLock)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexInvalidAccess)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexBadUnlock)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexBadReadLock)
- return kSuppressionMutex;
- else if (typ == ReportTypeMutexBadReadUnlock)
- return kSuppressionMutex;
- else if (typ == ReportTypeSignalUnsafe)
- return kSuppressionSignal;
- else if (typ == ReportTypeErrnoInSignal)
- return kSuppressionNone;
- else if (typ == ReportTypeDeadlock)
- return kSuppressionDeadlock;
- Printf("ThreadSanitizer: unknown report type %d\n", typ);
- Die();
-}
-
-static uptr IsSuppressed(const char *stype, const AddressInfo &info,
- Suppression **sp) {
- if (suppression_ctx->Match(info.function, stype, sp) ||
- suppression_ctx->Match(info.file, stype, sp) ||
- suppression_ctx->Match(info.module, stype, sp)) {
- VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ);
- atomic_fetch_add(&(*sp)->hit_count, 1, memory_order_relaxed);
- return info.address;
- }
- return 0;
-}
-
-uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
- CHECK(suppression_ctx);
- if (!suppression_ctx->SuppressionCount() || stack == 0 ||
- !stack->suppressable)
- return 0;
- const char *stype = conv(typ);
- if (0 == internal_strcmp(stype, kSuppressionNone))
- return 0;
- for (const SymbolizedStack *frame = stack->frames; frame;
- frame = frame->next) {
- uptr pc = IsSuppressed(stype, frame->info, sp);
- if (pc != 0)
- return pc;
- }
- if (0 == internal_strcmp(stype, kSuppressionRace) && stack->frames != nullptr)
- return IsSuppressed(kSuppressionRaceTop, stack->frames->info, sp);
- return 0;
-}
-
-uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
- CHECK(suppression_ctx);
- if (!suppression_ctx->SuppressionCount() || loc == 0 ||
- loc->type != ReportLocationGlobal || !loc->suppressable)
- return 0;
- const char *stype = conv(typ);
- if (0 == internal_strcmp(stype, kSuppressionNone))
- return 0;
- Suppression *s;
- const DataInfo &global = loc->global;
- if (suppression_ctx->Match(global.name, stype, &s) ||
- suppression_ctx->Match(global.module, stype, &s)) {
- VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", s->templ);
- atomic_fetch_add(&s->hit_count, 1, memory_order_relaxed);
- *sp = s;
- return global.start;
- }
- return 0;
-}
-
-void PrintMatchedSuppressions() {
- InternalMmapVector<Suppression *> matched;
- CHECK(suppression_ctx);
- suppression_ctx->GetMatched(&matched);
- if (!matched.size())
- return;
- int hit_count = 0;
- for (uptr i = 0; i < matched.size(); i++)
- hit_count += atomic_load_relaxed(&matched[i]->hit_count);
- Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count,
- (int)internal_getpid());
- for (uptr i = 0; i < matched.size(); i++) {
- Printf("%d %s:%s\n", atomic_load_relaxed(&matched[i]->hit_count),
- matched[i]->type, matched[i]->templ);
- }
-}
-} // namespace __tsan
--- /dev/null
+//===-- tsan_suppressions.cpp ---------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "tsan_suppressions.h"
+#include "tsan_rtl.h"
+#include "tsan_flags.h"
+#include "tsan_mman.h"
+#include "tsan_platform.h"
+
+#if !SANITIZER_GO
+// Suppressions for true/false positives in standard libraries.
+static const char *const std_suppressions =
+// Libstdc++ 4.4 has data races in std::string.
+// See http://crbug.com/181502 for an example.
+"race:^_M_rep$\n"
+"race:^_M_is_leaked$\n"
+// False positive when using std <thread>.
+// Happens because we miss atomic synchronization in libstdc++.
+// See http://llvm.org/bugs/show_bug.cgi?id=17066 for details.
+"race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n";
+
+// Can be overriden in frontend.
+SANITIZER_WEAK_DEFAULT_IMPL
+const char *__tsan_default_suppressions() {
+ return 0;
+}
+#endif
+
+namespace __tsan {
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char *kSuppressionTypes[] = {
+ kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex,
+ kSuppressionThread, kSuppressionSignal, kSuppressionLib,
+ kSuppressionDeadlock};
+
+void InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+#if !SANITIZER_GO
+ suppression_ctx->Parse(__tsan_default_suppressions());
+ suppression_ctx->Parse(std_suppressions);
+#endif
+}
+
+SuppressionContext *Suppressions() {
+ CHECK(suppression_ctx);
+ return suppression_ctx;
+}
+
+static const char *conv(ReportType typ) {
+ switch (typ) {
+ case ReportTypeRace:
+ case ReportTypeVptrRace:
+ case ReportTypeUseAfterFree:
+ case ReportTypeVptrUseAfterFree:
+ case ReportTypeExternalRace:
+ return kSuppressionRace;
+ case ReportTypeThreadLeak:
+ return kSuppressionThread;
+ case ReportTypeMutexDestroyLocked:
+ case ReportTypeMutexDoubleLock:
+ case ReportTypeMutexInvalidAccess:
+ case ReportTypeMutexBadUnlock:
+ case ReportTypeMutexBadReadLock:
+ case ReportTypeMutexBadReadUnlock:
+ return kSuppressionMutex;
+ case ReportTypeSignalUnsafe:
+ case ReportTypeErrnoInSignal:
+ return kSuppressionSignal;
+ case ReportTypeDeadlock:
+ return kSuppressionDeadlock;
+ // No default case so compiler warns us if we miss one
+ }
+ UNREACHABLE("missing case");
+}
+
+static uptr IsSuppressed(const char *stype, const AddressInfo &info,
+ Suppression **sp) {
+ if (suppression_ctx->Match(info.function, stype, sp) ||
+ suppression_ctx->Match(info.file, stype, sp) ||
+ suppression_ctx->Match(info.module, stype, sp)) {
+ VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ);
+ atomic_fetch_add(&(*sp)->hit_count, 1, memory_order_relaxed);
+ return info.address;
+ }
+ return 0;
+}
+
+uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
+ CHECK(suppression_ctx);
+ if (!suppression_ctx->SuppressionCount() || stack == 0 ||
+ !stack->suppressable)
+ return 0;
+ const char *stype = conv(typ);
+ if (0 == internal_strcmp(stype, kSuppressionNone))
+ return 0;
+ for (const SymbolizedStack *frame = stack->frames; frame;
+ frame = frame->next) {
+ uptr pc = IsSuppressed(stype, frame->info, sp);
+ if (pc != 0)
+ return pc;
+ }
+ if (0 == internal_strcmp(stype, kSuppressionRace) && stack->frames != nullptr)
+ return IsSuppressed(kSuppressionRaceTop, stack->frames->info, sp);
+ return 0;
+}
+
+uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
+ CHECK(suppression_ctx);
+ if (!suppression_ctx->SuppressionCount() || loc == 0 ||
+ loc->type != ReportLocationGlobal || !loc->suppressable)
+ return 0;
+ const char *stype = conv(typ);
+ if (0 == internal_strcmp(stype, kSuppressionNone))
+ return 0;
+ Suppression *s;
+ const DataInfo &global = loc->global;
+ if (suppression_ctx->Match(global.name, stype, &s) ||
+ suppression_ctx->Match(global.module, stype, &s)) {
+ VPrintf(2, "ThreadSanitizer: matched suppression '%s'\n", s->templ);
+ atomic_fetch_add(&s->hit_count, 1, memory_order_relaxed);
+ *sp = s;
+ return global.start;
+ }
+ return 0;
+}
+
+void PrintMatchedSuppressions() {
+ InternalMmapVector<Suppression *> matched;
+ CHECK(suppression_ctx);
+ suppression_ctx->GetMatched(&matched);
+ if (!matched.size())
+ return;
+ int hit_count = 0;
+ for (uptr i = 0; i < matched.size(); i++)
+ hit_count += atomic_load_relaxed(&matched[i]->hit_count);
+ Printf("ThreadSanitizer: Matched %d suppressions (pid=%d):\n", hit_count,
+ (int)internal_getpid());
+ for (uptr i = 0; i < matched.size(); i++) {
+ Printf("%d %s:%s\n", atomic_load_relaxed(&matched[i]->hit_count),
+ matched[i]->type, matched[i]->templ);
+ }
+}
+} // namespace __tsan
//===-- tsan_suppressions.h -------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_symbolize.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-
-#include "tsan_symbolize.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-#include "tsan_flags.h"
-#include "tsan_report.h"
-#include "tsan_rtl.h"
-
-namespace __tsan {
-
-void EnterSymbolizer() {
- ThreadState *thr = cur_thread();
- CHECK(!thr->in_symbolizer);
- thr->in_symbolizer = true;
- thr->ignore_interceptors++;
-}
-
-void ExitSymbolizer() {
- ThreadState *thr = cur_thread();
- CHECK(thr->in_symbolizer);
- thr->in_symbolizer = false;
- thr->ignore_interceptors--;
-}
-
-// Legacy API.
-// May be overriden by JIT/JAVA/etc,
-// whatever produces PCs marked with kExternalPCBit.
-SANITIZER_WEAK_DEFAULT_IMPL
-bool __tsan_symbolize_external(uptr pc, char *func_buf, uptr func_siz,
- char *file_buf, uptr file_siz, int *line,
- int *col) {
- return false;
-}
-
-// New API: call __tsan_symbolize_external_ex only when it exists.
-// Once old clients are gone, provide dummy implementation.
-SANITIZER_WEAK_DEFAULT_IMPL
-void __tsan_symbolize_external_ex(uptr pc,
- void (*add_frame)(void *, const char *,
- const char *, int, int),
- void *ctx) {}
-
-struct SymbolizedStackBuilder {
- SymbolizedStack *head;
- SymbolizedStack *tail;
- uptr addr;
-};
-
-static void AddFrame(void *ctx, const char *function_name, const char *file,
- int line, int column) {
- SymbolizedStackBuilder *ssb = (struct SymbolizedStackBuilder *)ctx;
- if (ssb->tail) {
- ssb->tail->next = SymbolizedStack::New(ssb->addr);
- ssb->tail = ssb->tail->next;
- } else {
- ssb->head = ssb->tail = SymbolizedStack::New(ssb->addr);
- }
- AddressInfo *info = &ssb->tail->info;
- if (function_name) {
- info->function = internal_strdup(function_name);
- }
- if (file) {
- info->file = internal_strdup(file);
- }
- info->line = line;
- info->column = column;
-}
-
-SymbolizedStack *SymbolizeCode(uptr addr) {
- // Check if PC comes from non-native land.
- if (addr & kExternalPCBit) {
- SymbolizedStackBuilder ssb = {nullptr, nullptr, addr};
- __tsan_symbolize_external_ex(addr, AddFrame, &ssb);
- if (ssb.head)
- return ssb.head;
- // Legacy code: remove along with the declaration above
- // once all clients using this API are gone.
- // Declare static to not consume too much stack space.
- // We symbolize reports in a single thread, so this is fine.
- static char func_buf[1024];
- static char file_buf[1024];
- int line, col;
- SymbolizedStack *frame = SymbolizedStack::New(addr);
- if (__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), file_buf,
- sizeof(file_buf), &line, &col)) {
- frame->info.function = internal_strdup(func_buf);
- frame->info.file = internal_strdup(file_buf);
- frame->info.line = line;
- frame->info.column = col;
- }
- return frame;
- }
- return Symbolizer::GetOrInit()->SymbolizePC(addr);
-}
-
-ReportLocation *SymbolizeData(uptr addr) {
- DataInfo info;
- if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info))
- return 0;
- ReportLocation *ent = ReportLocation::New(ReportLocationGlobal);
- internal_memcpy(&ent->global, &info, sizeof(info));
- return ent;
-}
-
-void SymbolizeFlush() {
- Symbolizer::GetOrInit()->Flush();
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_symbolize.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+
+#include "tsan_symbolize.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include "tsan_flags.h"
+#include "tsan_report.h"
+#include "tsan_rtl.h"
+
+namespace __tsan {
+
+void EnterSymbolizer() {
+ ThreadState *thr = cur_thread();
+ CHECK(!thr->in_symbolizer);
+ thr->in_symbolizer = true;
+ thr->ignore_interceptors++;
+}
+
+void ExitSymbolizer() {
+ ThreadState *thr = cur_thread();
+ CHECK(thr->in_symbolizer);
+ thr->in_symbolizer = false;
+ thr->ignore_interceptors--;
+}
+
+// Legacy API.
+// May be overriden by JIT/JAVA/etc,
+// whatever produces PCs marked with kExternalPCBit.
+SANITIZER_WEAK_DEFAULT_IMPL
+bool __tsan_symbolize_external(uptr pc, char *func_buf, uptr func_siz,
+ char *file_buf, uptr file_siz, int *line,
+ int *col) {
+ return false;
+}
+
+// New API: call __tsan_symbolize_external_ex only when it exists.
+// Once old clients are gone, provide dummy implementation.
+SANITIZER_WEAK_DEFAULT_IMPL
+void __tsan_symbolize_external_ex(uptr pc,
+ void (*add_frame)(void *, const char *,
+ const char *, int, int),
+ void *ctx) {}
+
+struct SymbolizedStackBuilder {
+ SymbolizedStack *head;
+ SymbolizedStack *tail;
+ uptr addr;
+};
+
+static void AddFrame(void *ctx, const char *function_name, const char *file,
+ int line, int column) {
+ SymbolizedStackBuilder *ssb = (struct SymbolizedStackBuilder *)ctx;
+ if (ssb->tail) {
+ ssb->tail->next = SymbolizedStack::New(ssb->addr);
+ ssb->tail = ssb->tail->next;
+ } else {
+ ssb->head = ssb->tail = SymbolizedStack::New(ssb->addr);
+ }
+ AddressInfo *info = &ssb->tail->info;
+ if (function_name) {
+ info->function = internal_strdup(function_name);
+ }
+ if (file) {
+ info->file = internal_strdup(file);
+ }
+ info->line = line;
+ info->column = column;
+}
+
+SymbolizedStack *SymbolizeCode(uptr addr) {
+ // Check if PC comes from non-native land.
+ if (addr & kExternalPCBit) {
+ SymbolizedStackBuilder ssb = {nullptr, nullptr, addr};
+ __tsan_symbolize_external_ex(addr, AddFrame, &ssb);
+ if (ssb.head)
+ return ssb.head;
+ // Legacy code: remove along with the declaration above
+ // once all clients using this API are gone.
+ // Declare static to not consume too much stack space.
+ // We symbolize reports in a single thread, so this is fine.
+ static char func_buf[1024];
+ static char file_buf[1024];
+ int line, col;
+ SymbolizedStack *frame = SymbolizedStack::New(addr);
+ if (__tsan_symbolize_external(addr, func_buf, sizeof(func_buf), file_buf,
+ sizeof(file_buf), &line, &col)) {
+ frame->info.function = internal_strdup(func_buf);
+ frame->info.file = internal_strdup(file_buf);
+ frame->info.line = line;
+ frame->info.column = col;
+ }
+ return frame;
+ }
+ return Symbolizer::GetOrInit()->SymbolizePC(addr);
+}
+
+ReportLocation *SymbolizeData(uptr addr) {
+ DataInfo info;
+ if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info))
+ return 0;
+ ReportLocation *ent = ReportLocation::New(ReportLocationGlobal);
+ internal_memcpy(&ent->global, &info, sizeof(info));
+ return ent;
+}
+
+void SymbolizeFlush() {
+ Symbolizer::GetOrInit()->Flush();
+}
+
+} // namespace __tsan
//===-- tsan_symbolize.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- tsan_sync.cc ------------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file is a part of ThreadSanitizer (TSan), a race detector.
-//
-//===----------------------------------------------------------------------===//
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "tsan_sync.h"
-#include "tsan_rtl.h"
-#include "tsan_mman.h"
-
-namespace __tsan {
-
-void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
-
-SyncVar::SyncVar()
- : mtx(MutexTypeSyncVar, StatMtxSyncVar) {
- Reset(0);
-}
-
-void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
- this->addr = addr;
- this->uid = uid;
- this->next = 0;
-
- creation_stack_id = 0;
- if (!SANITIZER_GO) // Go does not use them
- creation_stack_id = CurrentStackId(thr, pc);
- if (common_flags()->detect_deadlocks)
- DDMutexInit(thr, pc, this);
-}
-
-void SyncVar::Reset(Processor *proc) {
- uid = 0;
- creation_stack_id = 0;
- owner_tid = kInvalidTid;
- last_lock = 0;
- recursion = 0;
- atomic_store_relaxed(&flags, 0);
-
- if (proc == 0) {
- CHECK_EQ(clock.size(), 0);
- CHECK_EQ(read_clock.size(), 0);
- } else {
- clock.Reset(&proc->clock_cache);
- read_clock.Reset(&proc->clock_cache);
- }
-}
-
-MetaMap::MetaMap()
- : block_alloc_("heap block allocator")
- , sync_alloc_("sync allocator") {
- atomic_store(&uid_gen_, 0, memory_order_relaxed);
-}
-
-void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
- u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
- MBlock *b = block_alloc_.Map(idx);
- b->siz = sz;
- b->tag = 0;
- b->tid = thr->tid;
- b->stk = CurrentStackId(thr, pc);
- u32 *meta = MemToMeta(p);
- DCHECK_EQ(*meta, 0);
- *meta = idx | kFlagBlock;
-}
-
-uptr MetaMap::FreeBlock(Processor *proc, uptr p) {
- MBlock* b = GetBlock(p);
- if (b == 0)
- return 0;
- uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
- FreeRange(proc, p, sz);
- return sz;
-}
-
-bool MetaMap::FreeRange(Processor *proc, uptr p, uptr sz) {
- bool has_something = false;
- u32 *meta = MemToMeta(p);
- u32 *end = MemToMeta(p + sz);
- if (end == meta)
- end++;
- for (; meta < end; meta++) {
- u32 idx = *meta;
- if (idx == 0) {
- // Note: don't write to meta in this case -- the block can be huge.
- continue;
- }
- *meta = 0;
- has_something = true;
- while (idx != 0) {
- if (idx & kFlagBlock) {
- block_alloc_.Free(&proc->block_cache, idx & ~kFlagMask);
- break;
- } else if (idx & kFlagSync) {
- DCHECK(idx & kFlagSync);
- SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
- u32 next = s->next;
- s->Reset(proc);
- sync_alloc_.Free(&proc->sync_cache, idx & ~kFlagMask);
- idx = next;
- } else {
- CHECK(0);
- }
- }
- }
- return has_something;
-}
-
-// ResetRange removes all meta objects from the range.
-// It is called for large mmap-ed regions. The function is best-effort wrt
-// freeing of meta objects, because we don't want to page in the whole range
-// which can be huge. The function probes pages one-by-one until it finds a page
-// without meta objects, at this point it stops freeing meta objects. Because
-// thread stacks grow top-down, we do the same starting from end as well.
-void MetaMap::ResetRange(Processor *proc, uptr p, uptr sz) {
- if (SANITIZER_GO) {
- // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
- // so we do the optimization only for C/C++.
- FreeRange(proc, p, sz);
- return;
- }
- const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
- const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
- if (sz <= 4 * kPageSize) {
- // If the range is small, just do the normal free procedure.
- FreeRange(proc, p, sz);
- return;
- }
- // First, round both ends of the range to page size.
- uptr diff = RoundUp(p, kPageSize) - p;
- if (diff != 0) {
- FreeRange(proc, p, diff);
- p += diff;
- sz -= diff;
- }
- diff = p + sz - RoundDown(p + sz, kPageSize);
- if (diff != 0) {
- FreeRange(proc, p + sz - diff, diff);
- sz -= diff;
- }
- // Now we must have a non-empty page-aligned range.
- CHECK_GT(sz, 0);
- CHECK_EQ(p, RoundUp(p, kPageSize));
- CHECK_EQ(sz, RoundUp(sz, kPageSize));
- const uptr p0 = p;
- const uptr sz0 = sz;
- // Probe start of the range.
- for (uptr checked = 0; sz > 0; checked += kPageSize) {
- bool has_something = FreeRange(proc, p, kPageSize);
- p += kPageSize;
- sz -= kPageSize;
- if (!has_something && checked > (128 << 10))
- break;
- }
- // Probe end of the range.
- for (uptr checked = 0; sz > 0; checked += kPageSize) {
- bool has_something = FreeRange(proc, p + sz - kPageSize, kPageSize);
- sz -= kPageSize;
- // Stacks grow down, so sync object are most likely at the end of the region
- // (if it is a stack). The very end of the stack is TLS and tsan increases
- // TLS by at least 256K, so check at least 512K.
- if (!has_something && checked > (512 << 10))
- break;
- }
- // Finally, page out the whole range (including the parts that we've just
- // freed). Note: we can't simply madvise, because we need to leave a zeroed
- // range (otherwise __tsan_java_move can crash if it encounters a left-over
- // meta objects in java heap).
- uptr metap = (uptr)MemToMeta(p0);
- uptr metasz = sz0 / kMetaRatio;
- UnmapOrDie((void*)metap, metasz);
- if (!MmapFixedNoReserve(metap, metasz))
- Die();
-}
-
-MBlock* MetaMap::GetBlock(uptr p) {
- u32 *meta = MemToMeta(p);
- u32 idx = *meta;
- for (;;) {
- if (idx == 0)
- return 0;
- if (idx & kFlagBlock)
- return block_alloc_.Map(idx & ~kFlagMask);
- DCHECK(idx & kFlagSync);
- SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
- idx = s->next;
- }
-}
-
-SyncVar* MetaMap::GetOrCreateAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock) {
- return GetAndLock(thr, pc, addr, write_lock, true);
-}
-
-SyncVar* MetaMap::GetIfExistsAndLock(uptr addr, bool write_lock) {
- return GetAndLock(0, 0, addr, write_lock, false);
-}
-
-SyncVar* MetaMap::GetAndLock(ThreadState *thr, uptr pc,
- uptr addr, bool write_lock, bool create) {
- u32 *meta = MemToMeta(addr);
- u32 idx0 = *meta;
- u32 myidx = 0;
- SyncVar *mys = 0;
- for (;;) {
- u32 idx = idx0;
- for (;;) {
- if (idx == 0)
- break;
- if (idx & kFlagBlock)
- break;
- DCHECK(idx & kFlagSync);
- SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
- if (s->addr == addr) {
- if (myidx != 0) {
- mys->Reset(thr->proc());
- sync_alloc_.Free(&thr->proc()->sync_cache, myidx);
- }
- if (write_lock)
- s->mtx.Lock();
- else
- s->mtx.ReadLock();
- return s;
- }
- idx = s->next;
- }
- if (!create)
- return 0;
- if (*meta != idx0) {
- idx0 = *meta;
- continue;
- }
-
- if (myidx == 0) {
- const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
- myidx = sync_alloc_.Alloc(&thr->proc()->sync_cache);
- mys = sync_alloc_.Map(myidx);
- mys->Init(thr, pc, addr, uid);
- }
- mys->next = idx0;
- if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
- myidx | kFlagSync, memory_order_release)) {
- if (write_lock)
- mys->mtx.Lock();
- else
- mys->mtx.ReadLock();
- return mys;
- }
- }
-}
-
-void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
- // src and dst can overlap,
- // there are no concurrent accesses to the regions (e.g. stop-the-world).
- CHECK_NE(src, dst);
- CHECK_NE(sz, 0);
- uptr diff = dst - src;
- u32 *src_meta = MemToMeta(src);
- u32 *dst_meta = MemToMeta(dst);
- u32 *src_meta_end = MemToMeta(src + sz);
- uptr inc = 1;
- if (dst > src) {
- src_meta = MemToMeta(src + sz) - 1;
- dst_meta = MemToMeta(dst + sz) - 1;
- src_meta_end = MemToMeta(src) - 1;
- inc = -1;
- }
- for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
- CHECK_EQ(*dst_meta, 0);
- u32 idx = *src_meta;
- *src_meta = 0;
- *dst_meta = idx;
- // Patch the addresses in sync objects.
- while (idx != 0) {
- if (idx & kFlagBlock)
- break;
- CHECK(idx & kFlagSync);
- SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
- s->addr += diff;
- idx = s->next;
- }
- }
-}
-
-void MetaMap::OnProcIdle(Processor *proc) {
- block_alloc_.FlushCache(&proc->block_cache);
- sync_alloc_.FlushCache(&proc->sync_cache);
-}
-
-} // namespace __tsan
--- /dev/null
+//===-- tsan_sync.cpp -----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of ThreadSanitizer (TSan), a race detector.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "tsan_sync.h"
+#include "tsan_rtl.h"
+#include "tsan_mman.h"
+
+namespace __tsan {
+
+void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s);
+
+SyncVar::SyncVar()
+ : mtx(MutexTypeSyncVar, StatMtxSyncVar) {
+ Reset(0);
+}
+
+void SyncVar::Init(ThreadState *thr, uptr pc, uptr addr, u64 uid) {
+ this->addr = addr;
+ this->uid = uid;
+ this->next = 0;
+
+ creation_stack_id = 0;
+ if (!SANITIZER_GO) // Go does not use them
+ creation_stack_id = CurrentStackId(thr, pc);
+ if (common_flags()->detect_deadlocks)
+ DDMutexInit(thr, pc, this);
+}
+
+void SyncVar::Reset(Processor *proc) {
+ uid = 0;
+ creation_stack_id = 0;
+ owner_tid = kInvalidTid;
+ last_lock = 0;
+ recursion = 0;
+ atomic_store_relaxed(&flags, 0);
+
+ if (proc == 0) {
+ CHECK_EQ(clock.size(), 0);
+ CHECK_EQ(read_clock.size(), 0);
+ } else {
+ clock.Reset(&proc->clock_cache);
+ read_clock.Reset(&proc->clock_cache);
+ }
+}
+
+MetaMap::MetaMap()
+ : block_alloc_("heap block allocator")
+ , sync_alloc_("sync allocator") {
+ atomic_store(&uid_gen_, 0, memory_order_relaxed);
+}
+
+void MetaMap::AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz) {
+ u32 idx = block_alloc_.Alloc(&thr->proc()->block_cache);
+ MBlock *b = block_alloc_.Map(idx);
+ b->siz = sz;
+ b->tag = 0;
+ b->tid = thr->tid;
+ b->stk = CurrentStackId(thr, pc);
+ u32 *meta = MemToMeta(p);
+ DCHECK_EQ(*meta, 0);
+ *meta = idx | kFlagBlock;
+}
+
+uptr MetaMap::FreeBlock(Processor *proc, uptr p) {
+ MBlock* b = GetBlock(p);
+ if (b == 0)
+ return 0;
+ uptr sz = RoundUpTo(b->siz, kMetaShadowCell);
+ FreeRange(proc, p, sz);
+ return sz;
+}
+
+bool MetaMap::FreeRange(Processor *proc, uptr p, uptr sz) {
+ bool has_something = false;
+ u32 *meta = MemToMeta(p);
+ u32 *end = MemToMeta(p + sz);
+ if (end == meta)
+ end++;
+ for (; meta < end; meta++) {
+ u32 idx = *meta;
+ if (idx == 0) {
+ // Note: don't write to meta in this case -- the block can be huge.
+ continue;
+ }
+ *meta = 0;
+ has_something = true;
+ while (idx != 0) {
+ if (idx & kFlagBlock) {
+ block_alloc_.Free(&proc->block_cache, idx & ~kFlagMask);
+ break;
+ } else if (idx & kFlagSync) {
+ DCHECK(idx & kFlagSync);
+ SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
+ u32 next = s->next;
+ s->Reset(proc);
+ sync_alloc_.Free(&proc->sync_cache, idx & ~kFlagMask);
+ idx = next;
+ } else {
+ CHECK(0);
+ }
+ }
+ }
+ return has_something;
+}
+
+// ResetRange removes all meta objects from the range.
+// It is called for large mmap-ed regions. The function is best-effort wrt
+// freeing of meta objects, because we don't want to page in the whole range
+// which can be huge. The function probes pages one-by-one until it finds a page
+// without meta objects, at this point it stops freeing meta objects. Because
+// thread stacks grow top-down, we do the same starting from end as well.
+void MetaMap::ResetRange(Processor *proc, uptr p, uptr sz) {
+ if (SANITIZER_GO) {
+ // UnmapOrDie/MmapFixedNoReserve does not work on Windows,
+ // so we do the optimization only for C/C++.
+ FreeRange(proc, p, sz);
+ return;
+ }
+ const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
+ const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
+ if (sz <= 4 * kPageSize) {
+ // If the range is small, just do the normal free procedure.
+ FreeRange(proc, p, sz);
+ return;
+ }
+ // First, round both ends of the range to page size.
+ uptr diff = RoundUp(p, kPageSize) - p;
+ if (diff != 0) {
+ FreeRange(proc, p, diff);
+ p += diff;
+ sz -= diff;
+ }
+ diff = p + sz - RoundDown(p + sz, kPageSize);
+ if (diff != 0) {
+ FreeRange(proc, p + sz - diff, diff);
+ sz -= diff;
+ }
+ // Now we must have a non-empty page-aligned range.
+ CHECK_GT(sz, 0);
+ CHECK_EQ(p, RoundUp(p, kPageSize));
+ CHECK_EQ(sz, RoundUp(sz, kPageSize));
+ const uptr p0 = p;
+ const uptr sz0 = sz;
+ // Probe start of the range.
+ for (uptr checked = 0; sz > 0; checked += kPageSize) {
+ bool has_something = FreeRange(proc, p, kPageSize);
+ p += kPageSize;
+ sz -= kPageSize;
+ if (!has_something && checked > (128 << 10))
+ break;
+ }
+ // Probe end of the range.
+ for (uptr checked = 0; sz > 0; checked += kPageSize) {
+ bool has_something = FreeRange(proc, p + sz - kPageSize, kPageSize);
+ sz -= kPageSize;
+ // Stacks grow down, so sync object are most likely at the end of the region
+ // (if it is a stack). The very end of the stack is TLS and tsan increases
+ // TLS by at least 256K, so check at least 512K.
+ if (!has_something && checked > (512 << 10))
+ break;
+ }
+ // Finally, page out the whole range (including the parts that we've just
+ // freed). Note: we can't simply madvise, because we need to leave a zeroed
+ // range (otherwise __tsan_java_move can crash if it encounters a left-over
+ // meta objects in java heap).
+ uptr metap = (uptr)MemToMeta(p0);
+ uptr metasz = sz0 / kMetaRatio;
+ UnmapOrDie((void*)metap, metasz);
+ if (!MmapFixedNoReserve(metap, metasz))
+ Die();
+}
+
+MBlock* MetaMap::GetBlock(uptr p) {
+ u32 *meta = MemToMeta(p);
+ u32 idx = *meta;
+ for (;;) {
+ if (idx == 0)
+ return 0;
+ if (idx & kFlagBlock)
+ return block_alloc_.Map(idx & ~kFlagMask);
+ DCHECK(idx & kFlagSync);
+ SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
+ idx = s->next;
+ }
+}
+
+SyncVar* MetaMap::GetOrCreateAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock) {
+ return GetAndLock(thr, pc, addr, write_lock, true);
+}
+
+SyncVar* MetaMap::GetIfExistsAndLock(uptr addr, bool write_lock) {
+ return GetAndLock(0, 0, addr, write_lock, false);
+}
+
+SyncVar* MetaMap::GetAndLock(ThreadState *thr, uptr pc,
+ uptr addr, bool write_lock, bool create) {
+ u32 *meta = MemToMeta(addr);
+ u32 idx0 = *meta;
+ u32 myidx = 0;
+ SyncVar *mys = 0;
+ for (;;) {
+ u32 idx = idx0;
+ for (;;) {
+ if (idx == 0)
+ break;
+ if (idx & kFlagBlock)
+ break;
+ DCHECK(idx & kFlagSync);
+ SyncVar * s = sync_alloc_.Map(idx & ~kFlagMask);
+ if (s->addr == addr) {
+ if (myidx != 0) {
+ mys->Reset(thr->proc());
+ sync_alloc_.Free(&thr->proc()->sync_cache, myidx);
+ }
+ if (write_lock)
+ s->mtx.Lock();
+ else
+ s->mtx.ReadLock();
+ return s;
+ }
+ idx = s->next;
+ }
+ if (!create)
+ return 0;
+ if (*meta != idx0) {
+ idx0 = *meta;
+ continue;
+ }
+
+ if (myidx == 0) {
+ const u64 uid = atomic_fetch_add(&uid_gen_, 1, memory_order_relaxed);
+ myidx = sync_alloc_.Alloc(&thr->proc()->sync_cache);
+ mys = sync_alloc_.Map(myidx);
+ mys->Init(thr, pc, addr, uid);
+ }
+ mys->next = idx0;
+ if (atomic_compare_exchange_strong((atomic_uint32_t*)meta, &idx0,
+ myidx | kFlagSync, memory_order_release)) {
+ if (write_lock)
+ mys->mtx.Lock();
+ else
+ mys->mtx.ReadLock();
+ return mys;
+ }
+ }
+}
+
+void MetaMap::MoveMemory(uptr src, uptr dst, uptr sz) {
+ // src and dst can overlap,
+ // there are no concurrent accesses to the regions (e.g. stop-the-world).
+ CHECK_NE(src, dst);
+ CHECK_NE(sz, 0);
+ uptr diff = dst - src;
+ u32 *src_meta = MemToMeta(src);
+ u32 *dst_meta = MemToMeta(dst);
+ u32 *src_meta_end = MemToMeta(src + sz);
+ uptr inc = 1;
+ if (dst > src) {
+ src_meta = MemToMeta(src + sz) - 1;
+ dst_meta = MemToMeta(dst + sz) - 1;
+ src_meta_end = MemToMeta(src) - 1;
+ inc = -1;
+ }
+ for (; src_meta != src_meta_end; src_meta += inc, dst_meta += inc) {
+ CHECK_EQ(*dst_meta, 0);
+ u32 idx = *src_meta;
+ *src_meta = 0;
+ *dst_meta = idx;
+ // Patch the addresses in sync objects.
+ while (idx != 0) {
+ if (idx & kFlagBlock)
+ break;
+ CHECK(idx & kFlagSync);
+ SyncVar *s = sync_alloc_.Map(idx & ~kFlagMask);
+ s->addr += diff;
+ idx = s->next;
+ }
+ }
+}
+
+void MetaMap::OnProcIdle(Processor *proc) {
+ block_alloc_.FlushCache(&proc->block_cache);
+ sync_alloc_.FlushCache(&proc->sync_cache);
+}
+
+} // namespace __tsan
//===-- tsan_sync.h ---------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- tsan_trace.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- tsan_update_shadow_word_inl.h ---------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
const unsigned kAccessSize = 1 << kAccessSizeLog;
u64 *sp = &shadow_mem[idx];
old = LoadShadow(sp);
- if (old.IsZero()) {
+ if (LIKELY(old.IsZero())) {
StatInc(thr, StatShadowZero);
- if (store_word)
+ if (!stored) {
StoreIfNotYetStored(sp, &store_word);
- // The above StoreIfNotYetStored could be done unconditionally
- // and it even shows 4% gain on synthetic benchmarks (r4307).
+ stored = true;
+ }
break;
}
// is the memory access equal to the previous?
- if (Shadow::Addr0AndSizeAreEqual(cur, old)) {
+ if (LIKELY(Shadow::Addr0AndSizeAreEqual(cur, old))) {
StatInc(thr, StatShadowSameSize);
// same thread?
- if (Shadow::TidsAreEqual(old, cur)) {
+ if (LIKELY(Shadow::TidsAreEqual(old, cur))) {
StatInc(thr, StatShadowSameThread);
- if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))
+ if (LIKELY(old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))) {
StoreIfNotYetStored(sp, &store_word);
+ stored = true;
+ }
break;
}
StatInc(thr, StatShadowAnotherThread);
if (HappensBefore(old, thr)) {
- if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic))
+ if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) {
StoreIfNotYetStored(sp, &store_word);
+ stored = true;
+ }
break;
}
- if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))
+ if (LIKELY(old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)))
break;
goto RACE;
}
StatInc(thr, StatShadowAnotherThread);
if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic))
break;
- if (HappensBefore(old, thr))
+ if (LIKELY(HappensBefore(old, thr)))
break;
goto RACE;
}
toolexeclib_LTLIBRARIES = libubsan.la
ubsan_plugin_files = \
- ubsan_diag.cc \
- ubsan_flags.cc \
- ubsan_handlers.cc \
- ubsan_handlers_cxx.cc \
- ubsan_init.cc \
- ubsan_monitor.cc \
- ubsan_type_hash.cc \
- ubsan_type_hash_itanium.cc \
- ubsan_type_hash_win.cc \
- ubsan_value.cc
+ ubsan_diag.cpp \
+ ubsan_flags.cpp \
+ ubsan_handlers.cpp \
+ ubsan_handlers_cxx.cpp \
+ ubsan_init.cpp \
+ ubsan_monitor.cpp \
+ ubsan_type_hash.cpp \
+ ubsan_type_hash_itanium.cpp \
+ ubsan_type_hash_win.cpp \
+ ubsan_value.cpp
ubsan_files = $(ubsan_plugin_files)
-# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/../depcomp
-am__depfiles_maybe = depfiles
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/ubsan_diag.Plo \
+ ./$(DEPDIR)/ubsan_flags.Plo ./$(DEPDIR)/ubsan_handlers.Plo \
+ ./$(DEPDIR)/ubsan_handlers_cxx.Plo ./$(DEPDIR)/ubsan_init.Plo \
+ ./$(DEPDIR)/ubsan_monitor.Plo ./$(DEPDIR)/ubsan_type_hash.Plo \
+ ./$(DEPDIR)/ubsan_type_hash_itanium.Plo \
+ ./$(DEPDIR)/ubsan_type_hash_win.Plo \
+ ./$(DEPDIR)/ubsan_value.Plo
am__mv = mv -f
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libubsan.la
ubsan_plugin_files = \
- ubsan_diag.cc \
- ubsan_flags.cc \
- ubsan_handlers.cc \
- ubsan_handlers_cxx.cc \
- ubsan_init.cc \
- ubsan_monitor.cc \
- ubsan_type_hash.cc \
- ubsan_type_hash_itanium.cc \
- ubsan_type_hash_win.cc \
- ubsan_value.cc
+ ubsan_diag.cpp \
+ ubsan_flags.cpp \
+ ubsan_handlers.cpp \
+ ubsan_handlers_cxx.cpp \
+ ubsan_init.cpp \
+ ubsan_monitor.cpp \
+ ubsan_type_hash.cpp \
+ ubsan_type_hash_itanium.cpp \
+ ubsan_type_hash_win.cpp \
+ ubsan_value.cpp
ubsan_files = $(ubsan_plugin_files)
libubsan_la_SOURCES = $(ubsan_files)
all: all-am
.SUFFIXES:
-.SUFFIXES: .cc .lo .o .obj
+.SUFFIXES: .cpp .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_diag.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_flags.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers_cxx.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_init.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_monitor.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_itanium.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_win.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_value.Plo@am__quote@
-
-.cc.o:
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_diag.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_flags.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_handlers_cxx.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_init.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_monitor.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_itanium.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_type_hash_win.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ubsan_value.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cpp.o:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
-.cc.obj:
+.cpp.obj:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-.cc.lo:
+.cpp.lo:
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/ubsan_diag.Plo
+ -rm -f ./$(DEPDIR)/ubsan_flags.Plo
+ -rm -f ./$(DEPDIR)/ubsan_handlers.Plo
+ -rm -f ./$(DEPDIR)/ubsan_handlers_cxx.Plo
+ -rm -f ./$(DEPDIR)/ubsan_init.Plo
+ -rm -f ./$(DEPDIR)/ubsan_monitor.Plo
+ -rm -f ./$(DEPDIR)/ubsan_type_hash.Plo
+ -rm -f ./$(DEPDIR)/ubsan_type_hash_itanium.Plo
+ -rm -f ./$(DEPDIR)/ubsan_type_hash_win.Plo
+ -rm -f ./$(DEPDIR)/ubsan_value.Plo
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -f ./$(DEPDIR)/ubsan_diag.Plo
+ -rm -f ./$(DEPDIR)/ubsan_flags.Plo
+ -rm -f ./$(DEPDIR)/ubsan_handlers.Plo
+ -rm -f ./$(DEPDIR)/ubsan_handlers_cxx.Plo
+ -rm -f ./$(DEPDIR)/ubsan_init.Plo
+ -rm -f ./$(DEPDIR)/ubsan_monitor.Plo
+ -rm -f ./$(DEPDIR)/ubsan_type_hash.Plo
+ -rm -f ./$(DEPDIR)/ubsan_type_hash_itanium.Plo
+ -rm -f ./$(DEPDIR)/ubsan_type_hash_win.Plo
+ -rm -f ./$(DEPDIR)/ubsan_value.Plo
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
- clean-libtool clean-toolexeclibLTLIBRARIES cscopelist-am ctags \
- ctags-am distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags dvi dvi-am html html-am info \
- info-am install install-am install-data install-data-am \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-pdf install-pdf-am install-ps \
- install-ps-am install-strip install-toolexeclibLTLIBRARIES \
- installcheck installcheck-am installdirs maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags tags-am uninstall uninstall-am \
- uninstall-toolexeclibLTLIBRARIES
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-toolexeclibLTLIBRARIES \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags dvi dvi-am \
+ html html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip \
+ install-toolexeclibLTLIBRARIES installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-toolexeclibLTLIBRARIES
.PRECIOUS: Makefile
//===-- ubsan_checks.inc ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
UBSAN_CHECK(NullPointerUse, "null-pointer-use", "null")
UBSAN_CHECK(PointerOverflow, "pointer-overflow", "pointer-overflow")
UBSAN_CHECK(MisalignedPointerUse, "misaligned-pointer-use", "alignment")
+UBSAN_CHECK(AlignmentAssumption, "alignment-assumption", "alignment")
UBSAN_CHECK(InsufficientObjectSize, "insufficient-object-size", "object-size")
UBSAN_CHECK(SignedIntegerOverflow, "signed-integer-overflow",
"signed-integer-overflow")
UBSAN_CHECK(ImplicitSignedIntegerTruncation,
"implicit-signed-integer-truncation",
"implicit-signed-integer-truncation")
+UBSAN_CHECK(ImplicitIntegerSignChange,
+ "implicit-integer-sign-change",
+ "implicit-integer-sign-change")
+UBSAN_CHECK(ImplicitSignedIntegerTruncationOrSignChange,
+ "implicit-signed-integer-truncation-or-sign-change",
+ "implicit-signed-integer-truncation,implicit-integer-sign-change")
UBSAN_CHECK(InvalidShiftBase, "invalid-shift-base", "shift-base")
UBSAN_CHECK(InvalidShiftExponent, "invalid-shift-exponent", "shift-exponent")
UBSAN_CHECK(OutOfBoundsIndex, "out-of-bounds-index", "bounds")
+++ /dev/null
-//===-- ubsan_diag.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Diagnostic reporting for the UBSan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_diag.h"
-#include "ubsan_init.h"
-#include "ubsan_flags.h"
-#include "ubsan_monitor.h"
-#include "sanitizer_common/sanitizer_placement_new.h"
-#include "sanitizer_common/sanitizer_report_decorator.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "sanitizer_common/sanitizer_stacktrace_printer.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-#include <stdio.h>
-
-using namespace __ubsan;
-
-void __ubsan::GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc,
- uptr bp, void *context, bool fast) {
- uptr top = 0;
- uptr bottom = 0;
- if (fast)
- GetThreadStackTopAndBottom(false, &top, &bottom);
- stack->Unwind(max_depth, pc, bp, context, top, bottom, fast);
-}
-
-static void MaybePrintStackTrace(uptr pc, uptr bp) {
- // We assume that flags are already parsed, as UBSan runtime
- // will definitely be called when we print the first diagnostics message.
- if (!flags()->print_stacktrace)
- return;
-
- BufferedStackTrace stack;
- GetStackTrace(&stack, kStackTraceMax, pc, bp, nullptr,
- common_flags()->fast_unwind_on_fatal);
- stack.Print();
-}
-
-static const char *ConvertTypeToString(ErrorType Type) {
- switch (Type) {
-#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
- case ErrorType::Name: \
- return SummaryKind;
-#include "ubsan_checks.inc"
-#undef UBSAN_CHECK
- }
- UNREACHABLE("unknown ErrorType!");
-}
-
-static const char *ConvertTypeToFlagName(ErrorType Type) {
- switch (Type) {
-#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
- case ErrorType::Name: \
- return FSanitizeFlagName;
-#include "ubsan_checks.inc"
-#undef UBSAN_CHECK
- }
- UNREACHABLE("unknown ErrorType!");
-}
-
-static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
- if (!common_flags()->print_summary)
- return;
- if (!flags()->report_error_type)
- Type = ErrorType::GenericUB;
- const char *ErrorKind = ConvertTypeToString(Type);
- if (Loc.isSourceLocation()) {
- SourceLocation SLoc = Loc.getSourceLocation();
- if (!SLoc.isInvalid()) {
- AddressInfo AI;
- AI.file = internal_strdup(SLoc.getFilename());
- AI.line = SLoc.getLine();
- AI.column = SLoc.getColumn();
- AI.function = internal_strdup(""); // Avoid printing ?? as function name.
- ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
- AI.Clear();
- return;
- }
- } else if (Loc.isSymbolizedStack()) {
- const AddressInfo &AI = Loc.getSymbolizedStack()->info;
- ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
- return;
- }
- ReportErrorSummary(ErrorKind, GetSanititizerToolName());
-}
-
-namespace {
-class Decorator : public SanitizerCommonDecorator {
- public:
- Decorator() : SanitizerCommonDecorator() {}
- const char *Highlight() const { return Green(); }
- const char *Note() const { return Black(); }
-};
-}
-
-SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
- InitAsStandaloneIfNecessary();
- return Symbolizer::GetOrInit()->SymbolizePC(PC);
-}
-
-Diag &Diag::operator<<(const TypeDescriptor &V) {
- return AddArg(V.getTypeName());
-}
-
-Diag &Diag::operator<<(const Value &V) {
- if (V.getType().isSignedIntegerTy())
- AddArg(V.getSIntValue());
- else if (V.getType().isUnsignedIntegerTy())
- AddArg(V.getUIntValue());
- else if (V.getType().isFloatTy())
- AddArg(V.getFloatValue());
- else
- AddArg("<unknown>");
- return *this;
-}
-
-/// Hexadecimal printing for numbers too large for Printf to handle directly.
-static void RenderHex(InternalScopedString *Buffer, UIntMax Val) {
-#if HAVE_INT128_T
- Buffer->append("0x%08x%08x%08x%08x", (unsigned int)(Val >> 96),
- (unsigned int)(Val >> 64), (unsigned int)(Val >> 32),
- (unsigned int)(Val));
-#else
- UNREACHABLE("long long smaller than 64 bits?");
-#endif
-}
-
-static void RenderLocation(InternalScopedString *Buffer, Location Loc) {
- switch (Loc.getKind()) {
- case Location::LK_Source: {
- SourceLocation SLoc = Loc.getSourceLocation();
- if (SLoc.isInvalid())
- Buffer->append("<unknown>");
- else
- RenderSourceLocation(Buffer, SLoc.getFilename(), SLoc.getLine(),
- SLoc.getColumn(), common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- return;
- }
- case Location::LK_Memory:
- Buffer->append("%p", Loc.getMemoryLocation());
- return;
- case Location::LK_Symbolized: {
- const AddressInfo &Info = Loc.getSymbolizedStack()->info;
- if (Info.file)
- RenderSourceLocation(Buffer, Info.file, Info.line, Info.column,
- common_flags()->symbolize_vs_style,
- common_flags()->strip_path_prefix);
- else if (Info.module)
- RenderModuleLocation(Buffer, Info.module, Info.module_offset,
- Info.module_arch, common_flags()->strip_path_prefix);
- else
- Buffer->append("%p", Info.address);
- return;
- }
- case Location::LK_Null:
- Buffer->append("<unknown>");
- return;
- }
-}
-
-static void RenderText(InternalScopedString *Buffer, const char *Message,
- const Diag::Arg *Args) {
- for (const char *Msg = Message; *Msg; ++Msg) {
- if (*Msg != '%') {
- Buffer->append("%c", *Msg);
- continue;
- }
- const Diag::Arg &A = Args[*++Msg - '0'];
- switch (A.Kind) {
- case Diag::AK_String:
- Buffer->append("%s", A.String);
- break;
- case Diag::AK_TypeName: {
- if (SANITIZER_WINDOWS)
- // The Windows implementation demangles names early.
- Buffer->append("'%s'", A.String);
- else
- Buffer->append("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
- break;
- }
- case Diag::AK_SInt:
- // 'long long' is guaranteed to be at least 64 bits wide.
- if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
- Buffer->append("%lld", (long long)A.SInt);
- else
- RenderHex(Buffer, A.SInt);
- break;
- case Diag::AK_UInt:
- if (A.UInt <= UINT64_MAX)
- Buffer->append("%llu", (unsigned long long)A.UInt);
- else
- RenderHex(Buffer, A.UInt);
- break;
- case Diag::AK_Float: {
- // FIXME: Support floating-point formatting in sanitizer_common's
- // printf, and stop using snprintf here.
- char FloatBuffer[32];
-#if SANITIZER_WINDOWS
- sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
-#else
- snprintf(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
-#endif
- Buffer->append("%s", FloatBuffer);
- break;
- }
- case Diag::AK_Pointer:
- Buffer->append("%p", A.Pointer);
- break;
- }
- }
-}
-
-/// Find the earliest-starting range in Ranges which ends after Loc.
-static Range *upperBound(MemoryLocation Loc, Range *Ranges,
- unsigned NumRanges) {
- Range *Best = 0;
- for (unsigned I = 0; I != NumRanges; ++I)
- if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
- (!Best ||
- Best->getStart().getMemoryLocation() >
- Ranges[I].getStart().getMemoryLocation()))
- Best = &Ranges[I];
- return Best;
-}
-
-static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
- return (LHS < RHS) ? 0 : LHS - RHS;
-}
-
-static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
- const uptr Limit = (uptr)-1;
- return (LHS > Limit - RHS) ? Limit : LHS + RHS;
-}
-
-/// Render a snippet of the address space near a location.
-static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
- Range *Ranges, unsigned NumRanges,
- const Diag::Arg *Args) {
- // Show at least the 8 bytes surrounding Loc.
- const unsigned MinBytesNearLoc = 4;
- MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
- MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
- MemoryLocation OrigMin = Min;
- for (unsigned I = 0; I < NumRanges; ++I) {
- Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
- Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
- }
-
- // If we have too many interesting bytes, prefer to show bytes after Loc.
- const unsigned BytesToShow = 32;
- if (Max - Min > BytesToShow)
- Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
- Max = addNoOverflow(Min, BytesToShow);
-
- if (!IsAccessibleMemoryRange(Min, Max - Min)) {
- Printf("<memory cannot be printed>\n");
- return;
- }
-
- // Emit data.
- InternalScopedString Buffer(1024);
- for (uptr P = Min; P != Max; ++P) {
- unsigned char C = *reinterpret_cast<const unsigned char*>(P);
- Buffer.append("%s%02x", (P % 8 == 0) ? " " : " ", C);
- }
- Buffer.append("\n");
-
- // Emit highlights.
- Buffer.append(Decor.Highlight());
- Range *InRange = upperBound(Min, Ranges, NumRanges);
- for (uptr P = Min; P != Max; ++P) {
- char Pad = ' ', Byte = ' ';
- if (InRange && InRange->getEnd().getMemoryLocation() == P)
- InRange = upperBound(P, Ranges, NumRanges);
- if (!InRange && P > Loc)
- break;
- if (InRange && InRange->getStart().getMemoryLocation() < P)
- Pad = '~';
- if (InRange && InRange->getStart().getMemoryLocation() <= P)
- Byte = '~';
- if (P % 8 == 0)
- Buffer.append("%c", Pad);
- Buffer.append("%c", Pad);
- Buffer.append("%c", P == Loc ? '^' : Byte);
- Buffer.append("%c", Byte);
- }
- Buffer.append("%s\n", Decor.Default());
-
- // Go over the line again, and print names for the ranges.
- InRange = 0;
- unsigned Spaces = 0;
- for (uptr P = Min; P != Max; ++P) {
- if (!InRange || InRange->getEnd().getMemoryLocation() == P)
- InRange = upperBound(P, Ranges, NumRanges);
- if (!InRange)
- break;
-
- Spaces += (P % 8) == 0 ? 2 : 1;
-
- if (InRange && InRange->getStart().getMemoryLocation() == P) {
- while (Spaces--)
- Buffer.append(" ");
- RenderText(&Buffer, InRange->getText(), Args);
- Buffer.append("\n");
- // FIXME: We only support naming one range for now!
- break;
- }
-
- Spaces += 2;
- }
-
- Printf("%s", Buffer.data());
- // FIXME: Print names for anything we can identify within the line:
- //
- // * If we can identify the memory itself as belonging to a particular
- // global, stack variable, or dynamic allocation, then do so.
- //
- // * If we have a pointer-size, pointer-aligned range highlighted,
- // determine whether the value of that range is a pointer to an
- // entity which we can name, and if so, print that name.
- //
- // This needs an external symbolizer, or (preferably) ASan instrumentation.
-}
-
-Diag::~Diag() {
- // All diagnostics should be printed under report mutex.
- ScopedReport::CheckLocked();
- Decorator Decor;
- InternalScopedString Buffer(1024);
-
- // Prepare a report that a monitor process can inspect.
- if (Level == DL_Error) {
- RenderText(&Buffer, Message, Args);
- UndefinedBehaviorReport UBR{ConvertTypeToString(ET), Loc, Buffer};
- Buffer.clear();
- }
-
- Buffer.append(Decor.Bold());
- RenderLocation(&Buffer, Loc);
- Buffer.append(":");
-
- switch (Level) {
- case DL_Error:
- Buffer.append("%s runtime error: %s%s", Decor.Warning(), Decor.Default(),
- Decor.Bold());
- break;
-
- case DL_Note:
- Buffer.append("%s note: %s", Decor.Note(), Decor.Default());
- break;
- }
-
- RenderText(&Buffer, Message, Args);
-
- Buffer.append("%s\n", Decor.Default());
- Printf("%s", Buffer.data());
-
- if (Loc.isMemoryLocation())
- PrintMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args);
-}
-
-ScopedReport::Initializer::Initializer() { InitAsStandaloneIfNecessary(); }
-
-ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
- ErrorType Type)
- : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {}
-
-ScopedReport::~ScopedReport() {
- MaybePrintStackTrace(Opts.pc, Opts.bp);
- MaybeReportErrorSummary(SummaryLoc, Type);
- if (flags()->halt_on_error)
- Die();
-}
-
-ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
-static SuppressionContext *suppression_ctx = nullptr;
-static const char kVptrCheck[] = "vptr_check";
-static const char *kSuppressionTypes[] = {
-#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
-#include "ubsan_checks.inc"
-#undef UBSAN_CHECK
- kVptrCheck,
-};
-
-void __ubsan::InitializeSuppressions() {
- CHECK_EQ(nullptr, suppression_ctx);
- suppression_ctx = new (suppression_placeholder) // NOLINT
- SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
- suppression_ctx->ParseFromFile(flags()->suppressions);
-}
-
-bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
- InitAsStandaloneIfNecessary();
- CHECK(suppression_ctx);
- Suppression *s;
- return suppression_ctx->Match(TypeName, kVptrCheck, &s);
-}
-
-bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
- InitAsStandaloneIfNecessary();
- CHECK(suppression_ctx);
- const char *SuppType = ConvertTypeToFlagName(ET);
- // Fast path: don't symbolize PC if there is no suppressions for given UB
- // type.
- if (!suppression_ctx->HasSuppressionType(SuppType))
- return false;
- Suppression *s = nullptr;
- // Suppress by file name known to runtime.
- if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
- return true;
- // Suppress by module name.
- if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
- if (suppression_ctx->Match(Module, SuppType, &s))
- return true;
- }
- // Suppress by function or source file name from debug info.
- SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
- const AddressInfo &AI = Stack.get()->info;
- return suppression_ctx->Match(AI.function, SuppType, &s) ||
- suppression_ctx->Match(AI.file, SuppType, &s);
-}
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_diag.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnostic reporting for the UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_diag.h"
+#include "ubsan_init.h"
+#include "ubsan_flags.h"
+#include "ubsan_monitor.h"
+#include "sanitizer_common/sanitizer_placement_new.h"
+#include "sanitizer_common/sanitizer_report_decorator.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "sanitizer_common/sanitizer_stacktrace_printer.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+#include <stdio.h>
+
+using namespace __ubsan;
+
+// UBSan is combined with runtimes that already provide this functionality
+// (e.g., ASan) as well as runtimes that lack it (e.g., scudo). Tried to use
+// weak linkage to resolve this issue which is not portable and breaks on
+// Windows.
+// TODO(yln): This is a temporary workaround. GetStackTrace functions will be
+// removed in the future.
+void ubsan_GetStackTrace(BufferedStackTrace *stack, uptr max_depth,
+ uptr pc, uptr bp, void *context, bool fast) {
+ uptr top = 0;
+ uptr bottom = 0;
+ if (StackTrace::WillUseFastUnwind(fast)) {
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ stack->Unwind(max_depth, pc, bp, nullptr, top, bottom, true);
+ } else
+ stack->Unwind(max_depth, pc, bp, context, 0, 0, false);
+}
+
+static void MaybePrintStackTrace(uptr pc, uptr bp) {
+ // We assume that flags are already parsed, as UBSan runtime
+ // will definitely be called when we print the first diagnostics message.
+ if (!flags()->print_stacktrace)
+ return;
+
+ BufferedStackTrace stack;
+ ubsan_GetStackTrace(&stack, kStackTraceMax, pc, bp, nullptr,
+ common_flags()->fast_unwind_on_fatal);
+ stack.Print();
+}
+
+static const char *ConvertTypeToString(ErrorType Type) {
+ switch (Type) {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
+ case ErrorType::Name: \
+ return SummaryKind;
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+ }
+ UNREACHABLE("unknown ErrorType!");
+}
+
+static const char *ConvertTypeToFlagName(ErrorType Type) {
+ switch (Type) {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) \
+ case ErrorType::Name: \
+ return FSanitizeFlagName;
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+ }
+ UNREACHABLE("unknown ErrorType!");
+}
+
+static void MaybeReportErrorSummary(Location Loc, ErrorType Type) {
+ if (!common_flags()->print_summary)
+ return;
+ if (!flags()->report_error_type)
+ Type = ErrorType::GenericUB;
+ const char *ErrorKind = ConvertTypeToString(Type);
+ if (Loc.isSourceLocation()) {
+ SourceLocation SLoc = Loc.getSourceLocation();
+ if (!SLoc.isInvalid()) {
+ AddressInfo AI;
+ AI.file = internal_strdup(SLoc.getFilename());
+ AI.line = SLoc.getLine();
+ AI.column = SLoc.getColumn();
+ AI.function = internal_strdup(""); // Avoid printing ?? as function name.
+ ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
+ AI.Clear();
+ return;
+ }
+ } else if (Loc.isSymbolizedStack()) {
+ const AddressInfo &AI = Loc.getSymbolizedStack()->info;
+ ReportErrorSummary(ErrorKind, AI, GetSanititizerToolName());
+ return;
+ }
+ ReportErrorSummary(ErrorKind, GetSanititizerToolName());
+}
+
+namespace {
+class Decorator : public SanitizerCommonDecorator {
+ public:
+ Decorator() : SanitizerCommonDecorator() {}
+ const char *Highlight() const { return Green(); }
+ const char *Note() const { return Black(); }
+};
+}
+
+SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) {
+ InitAsStandaloneIfNecessary();
+ return Symbolizer::GetOrInit()->SymbolizePC(PC);
+}
+
+Diag &Diag::operator<<(const TypeDescriptor &V) {
+ return AddArg(V.getTypeName());
+}
+
+Diag &Diag::operator<<(const Value &V) {
+ if (V.getType().isSignedIntegerTy())
+ AddArg(V.getSIntValue());
+ else if (V.getType().isUnsignedIntegerTy())
+ AddArg(V.getUIntValue());
+ else if (V.getType().isFloatTy())
+ AddArg(V.getFloatValue());
+ else
+ AddArg("<unknown>");
+ return *this;
+}
+
+/// Hexadecimal printing for numbers too large for Printf to handle directly.
+static void RenderHex(InternalScopedString *Buffer, UIntMax Val) {
+#if HAVE_INT128_T
+ Buffer->append("0x%08x%08x%08x%08x", (unsigned int)(Val >> 96),
+ (unsigned int)(Val >> 64), (unsigned int)(Val >> 32),
+ (unsigned int)(Val));
+#else
+ UNREACHABLE("long long smaller than 64 bits?");
+#endif
+}
+
+static void RenderLocation(InternalScopedString *Buffer, Location Loc) {
+ switch (Loc.getKind()) {
+ case Location::LK_Source: {
+ SourceLocation SLoc = Loc.getSourceLocation();
+ if (SLoc.isInvalid())
+ Buffer->append("<unknown>");
+ else
+ RenderSourceLocation(Buffer, SLoc.getFilename(), SLoc.getLine(),
+ SLoc.getColumn(), common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ return;
+ }
+ case Location::LK_Memory:
+ Buffer->append("%p", Loc.getMemoryLocation());
+ return;
+ case Location::LK_Symbolized: {
+ const AddressInfo &Info = Loc.getSymbolizedStack()->info;
+ if (Info.file)
+ RenderSourceLocation(Buffer, Info.file, Info.line, Info.column,
+ common_flags()->symbolize_vs_style,
+ common_flags()->strip_path_prefix);
+ else if (Info.module)
+ RenderModuleLocation(Buffer, Info.module, Info.module_offset,
+ Info.module_arch, common_flags()->strip_path_prefix);
+ else
+ Buffer->append("%p", Info.address);
+ return;
+ }
+ case Location::LK_Null:
+ Buffer->append("<unknown>");
+ return;
+ }
+}
+
+static void RenderText(InternalScopedString *Buffer, const char *Message,
+ const Diag::Arg *Args) {
+ for (const char *Msg = Message; *Msg; ++Msg) {
+ if (*Msg != '%') {
+ Buffer->append("%c", *Msg);
+ continue;
+ }
+ const Diag::Arg &A = Args[*++Msg - '0'];
+ switch (A.Kind) {
+ case Diag::AK_String:
+ Buffer->append("%s", A.String);
+ break;
+ case Diag::AK_TypeName: {
+ if (SANITIZER_WINDOWS)
+ // The Windows implementation demangles names early.
+ Buffer->append("'%s'", A.String);
+ else
+ Buffer->append("'%s'", Symbolizer::GetOrInit()->Demangle(A.String));
+ break;
+ }
+ case Diag::AK_SInt:
+ // 'long long' is guaranteed to be at least 64 bits wide.
+ if (A.SInt >= INT64_MIN && A.SInt <= INT64_MAX)
+ Buffer->append("%lld", (long long)A.SInt);
+ else
+ RenderHex(Buffer, A.SInt);
+ break;
+ case Diag::AK_UInt:
+ if (A.UInt <= UINT64_MAX)
+ Buffer->append("%llu", (unsigned long long)A.UInt);
+ else
+ RenderHex(Buffer, A.UInt);
+ break;
+ case Diag::AK_Float: {
+ // FIXME: Support floating-point formatting in sanitizer_common's
+ // printf, and stop using snprintf here.
+ char FloatBuffer[32];
+#if SANITIZER_WINDOWS
+ sprintf_s(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
+#else
+ snprintf(FloatBuffer, sizeof(FloatBuffer), "%Lg", (long double)A.Float);
+#endif
+ Buffer->append("%s", FloatBuffer);
+ break;
+ }
+ case Diag::AK_Pointer:
+ Buffer->append("%p", A.Pointer);
+ break;
+ }
+ }
+}
+
+/// Find the earliest-starting range in Ranges which ends after Loc.
+static Range *upperBound(MemoryLocation Loc, Range *Ranges,
+ unsigned NumRanges) {
+ Range *Best = 0;
+ for (unsigned I = 0; I != NumRanges; ++I)
+ if (Ranges[I].getEnd().getMemoryLocation() > Loc &&
+ (!Best ||
+ Best->getStart().getMemoryLocation() >
+ Ranges[I].getStart().getMemoryLocation()))
+ Best = &Ranges[I];
+ return Best;
+}
+
+static inline uptr subtractNoOverflow(uptr LHS, uptr RHS) {
+ return (LHS < RHS) ? 0 : LHS - RHS;
+}
+
+static inline uptr addNoOverflow(uptr LHS, uptr RHS) {
+ const uptr Limit = (uptr)-1;
+ return (LHS > Limit - RHS) ? Limit : LHS + RHS;
+}
+
+/// Render a snippet of the address space near a location.
+static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc,
+ Range *Ranges, unsigned NumRanges,
+ const Diag::Arg *Args) {
+ // Show at least the 8 bytes surrounding Loc.
+ const unsigned MinBytesNearLoc = 4;
+ MemoryLocation Min = subtractNoOverflow(Loc, MinBytesNearLoc);
+ MemoryLocation Max = addNoOverflow(Loc, MinBytesNearLoc);
+ MemoryLocation OrigMin = Min;
+ for (unsigned I = 0; I < NumRanges; ++I) {
+ Min = __sanitizer::Min(Ranges[I].getStart().getMemoryLocation(), Min);
+ Max = __sanitizer::Max(Ranges[I].getEnd().getMemoryLocation(), Max);
+ }
+
+ // If we have too many interesting bytes, prefer to show bytes after Loc.
+ const unsigned BytesToShow = 32;
+ if (Max - Min > BytesToShow)
+ Min = __sanitizer::Min(Max - BytesToShow, OrigMin);
+ Max = addNoOverflow(Min, BytesToShow);
+
+ if (!IsAccessibleMemoryRange(Min, Max - Min)) {
+ Printf("<memory cannot be printed>\n");
+ return;
+ }
+
+ // Emit data.
+ InternalScopedString Buffer(1024);
+ for (uptr P = Min; P != Max; ++P) {
+ unsigned char C = *reinterpret_cast<const unsigned char*>(P);
+ Buffer.append("%s%02x", (P % 8 == 0) ? " " : " ", C);
+ }
+ Buffer.append("\n");
+
+ // Emit highlights.
+ Buffer.append(Decor.Highlight());
+ Range *InRange = upperBound(Min, Ranges, NumRanges);
+ for (uptr P = Min; P != Max; ++P) {
+ char Pad = ' ', Byte = ' ';
+ if (InRange && InRange->getEnd().getMemoryLocation() == P)
+ InRange = upperBound(P, Ranges, NumRanges);
+ if (!InRange && P > Loc)
+ break;
+ if (InRange && InRange->getStart().getMemoryLocation() < P)
+ Pad = '~';
+ if (InRange && InRange->getStart().getMemoryLocation() <= P)
+ Byte = '~';
+ if (P % 8 == 0)
+ Buffer.append("%c", Pad);
+ Buffer.append("%c", Pad);
+ Buffer.append("%c", P == Loc ? '^' : Byte);
+ Buffer.append("%c", Byte);
+ }
+ Buffer.append("%s\n", Decor.Default());
+
+ // Go over the line again, and print names for the ranges.
+ InRange = 0;
+ unsigned Spaces = 0;
+ for (uptr P = Min; P != Max; ++P) {
+ if (!InRange || InRange->getEnd().getMemoryLocation() == P)
+ InRange = upperBound(P, Ranges, NumRanges);
+ if (!InRange)
+ break;
+
+ Spaces += (P % 8) == 0 ? 2 : 1;
+
+ if (InRange && InRange->getStart().getMemoryLocation() == P) {
+ while (Spaces--)
+ Buffer.append(" ");
+ RenderText(&Buffer, InRange->getText(), Args);
+ Buffer.append("\n");
+ // FIXME: We only support naming one range for now!
+ break;
+ }
+
+ Spaces += 2;
+ }
+
+ Printf("%s", Buffer.data());
+ // FIXME: Print names for anything we can identify within the line:
+ //
+ // * If we can identify the memory itself as belonging to a particular
+ // global, stack variable, or dynamic allocation, then do so.
+ //
+ // * If we have a pointer-size, pointer-aligned range highlighted,
+ // determine whether the value of that range is a pointer to an
+ // entity which we can name, and if so, print that name.
+ //
+ // This needs an external symbolizer, or (preferably) ASan instrumentation.
+}
+
+Diag::~Diag() {
+ // All diagnostics should be printed under report mutex.
+ ScopedReport::CheckLocked();
+ Decorator Decor;
+ InternalScopedString Buffer(1024);
+
+ // Prepare a report that a monitor process can inspect.
+ if (Level == DL_Error) {
+ RenderText(&Buffer, Message, Args);
+ UndefinedBehaviorReport UBR{ConvertTypeToString(ET), Loc, Buffer};
+ Buffer.clear();
+ }
+
+ Buffer.append(Decor.Bold());
+ RenderLocation(&Buffer, Loc);
+ Buffer.append(":");
+
+ switch (Level) {
+ case DL_Error:
+ Buffer.append("%s runtime error: %s%s", Decor.Warning(), Decor.Default(),
+ Decor.Bold());
+ break;
+
+ case DL_Note:
+ Buffer.append("%s note: %s", Decor.Note(), Decor.Default());
+ break;
+ }
+
+ RenderText(&Buffer, Message, Args);
+
+ Buffer.append("%s\n", Decor.Default());
+ Printf("%s", Buffer.data());
+
+ if (Loc.isMemoryLocation())
+ PrintMemorySnippet(Decor, Loc.getMemoryLocation(), Ranges, NumRanges, Args);
+}
+
+ScopedReport::Initializer::Initializer() { InitAsStandaloneIfNecessary(); }
+
+ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc,
+ ErrorType Type)
+ : Opts(Opts), SummaryLoc(SummaryLoc), Type(Type) {}
+
+ScopedReport::~ScopedReport() {
+ MaybePrintStackTrace(Opts.pc, Opts.bp);
+ MaybeReportErrorSummary(SummaryLoc, Type);
+ if (flags()->halt_on_error)
+ Die();
+}
+
+ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)];
+static SuppressionContext *suppression_ctx = nullptr;
+static const char kVptrCheck[] = "vptr_check";
+static const char *kSuppressionTypes[] = {
+#define UBSAN_CHECK(Name, SummaryKind, FSanitizeFlagName) FSanitizeFlagName,
+#include "ubsan_checks.inc"
+#undef UBSAN_CHECK
+ kVptrCheck,
+};
+
+void __ubsan::InitializeSuppressions() {
+ CHECK_EQ(nullptr, suppression_ctx);
+ suppression_ctx = new (suppression_placeholder) // NOLINT
+ SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes));
+ suppression_ctx->ParseFromFile(flags()->suppressions);
+}
+
+bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) {
+ InitAsStandaloneIfNecessary();
+ CHECK(suppression_ctx);
+ Suppression *s;
+ return suppression_ctx->Match(TypeName, kVptrCheck, &s);
+}
+
+bool __ubsan::IsPCSuppressed(ErrorType ET, uptr PC, const char *Filename) {
+ InitAsStandaloneIfNecessary();
+ CHECK(suppression_ctx);
+ const char *SuppType = ConvertTypeToFlagName(ET);
+ // Fast path: don't symbolize PC if there is no suppressions for given UB
+ // type.
+ if (!suppression_ctx->HasSuppressionType(SuppType))
+ return false;
+ Suppression *s = nullptr;
+ // Suppress by file name known to runtime.
+ if (Filename != nullptr && suppression_ctx->Match(Filename, SuppType, &s))
+ return true;
+ // Suppress by module name.
+ if (const char *Module = Symbolizer::GetOrInit()->GetModuleNameForPc(PC)) {
+ if (suppression_ctx->Match(Module, SuppType, &s))
+ return true;
+ }
+ // Suppress by function or source file name from debug info.
+ SymbolizedStackHolder Stack(Symbolizer::GetOrInit()->SymbolizePC(PC));
+ const AddressInfo &AI = Stack.get()->info;
+ return suppression_ctx->Match(AI.function, SuppType, &s) ||
+ suppression_ctx->Match(AI.file, SuppType, &s);
+}
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_diag.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
GET_CALLER_PC_BP; \
ReportOptions Opts = {unrecoverable_handler, pc, bp}
-void GetStackTrace(BufferedStackTrace *stack, uptr max_depth, uptr pc, uptr bp,
- void *context, bool fast);
-
/// \brief Instantiate this class before printing diagnostics in the error
/// report. This class ensures that reports from different threads and from
/// different sanitizers won't be mixed.
+++ /dev/null
-//===-- ubsan_diag_standalone.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Diagnostic reporting for the standalone UBSan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_diag.h"
-
-using namespace __ubsan;
-
-extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE
-void __sanitizer_print_stack_trace() {
- uptr top = 0;
- uptr bottom = 0;
- bool request_fast_unwind = common_flags()->fast_unwind_on_fatal;
- if (request_fast_unwind)
- __sanitizer::GetThreadStackTopAndBottom(false, &top, &bottom);
-
- GET_CURRENT_PC_BP_SP;
- (void)sp;
- BufferedStackTrace stack;
- stack.Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom,
- request_fast_unwind);
- stack.Print();
-}
-} // extern "C"
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_diag_standalone.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Diagnostic reporting for the standalone UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_diag.h"
+
+using namespace __ubsan;
+
+void __sanitizer::BufferedStackTrace::UnwindImpl(
+ uptr pc, uptr bp, void *context, bool request_fast, u32 max_depth) {
+ uptr top = 0;
+ uptr bottom = 0;
+ if (StackTrace::WillUseFastUnwind(request_fast)) {
+ GetThreadStackTopAndBottom(false, &top, &bottom);
+ Unwind(max_depth, pc, bp, nullptr, top, bottom, true);
+ } else
+ Unwind(max_depth, pc, bp, context, 0, 0, false);
+}
+
+extern "C" {
+SANITIZER_INTERFACE_ATTRIBUTE
+void __sanitizer_print_stack_trace() {
+ GET_CURRENT_PC_BP;
+ BufferedStackTrace stack;
+ stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal);
+ stack.Print();
+}
+} // extern "C"
+
+#endif // CAN_SANITIZE_UB
+++ /dev/null
-//===-- ubsan_flags.cc ----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Runtime flags for UndefinedBehaviorSanitizer.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_flags.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
-#include "sanitizer_common/sanitizer_flag_parser.h"
-
-#include <stdlib.h>
-
-namespace __ubsan {
-
-const char *MaybeCallUbsanDefaultOptions() {
- return (&__ubsan_default_options) ? __ubsan_default_options() : "";
-}
-
-static const char *GetFlag(const char *flag) {
- // We cannot call getenv() from inside a preinit array initializer
- if (SANITIZER_CAN_USE_PREINIT_ARRAY) {
- return GetEnv(flag);
- } else {
- return getenv(flag);
- }
-}
-
-Flags ubsan_flags;
-
-void Flags::SetDefaults() {
-#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
-#include "ubsan_flags.inc"
-#undef UBSAN_FLAG
-}
-
-void RegisterUbsanFlags(FlagParser *parser, Flags *f) {
-#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &f->Name);
-#include "ubsan_flags.inc"
-#undef UBSAN_FLAG
-}
-
-void InitializeFlags() {
- SetCommonFlagsDefaults();
- {
- CommonFlags cf;
- cf.CopyFrom(*common_flags());
- cf.print_summary = false;
- cf.external_symbolizer_path = GetFlag("UBSAN_SYMBOLIZER_PATH");
- OverrideCommonFlags(cf);
- }
-
- Flags *f = flags();
- f->SetDefaults();
-
- FlagParser parser;
- RegisterCommonFlags(&parser);
- RegisterUbsanFlags(&parser, f);
-
- // Override from user-specified string.
- parser.ParseString(MaybeCallUbsanDefaultOptions());
- // Override from environment variable.
- parser.ParseString(GetFlag("UBSAN_OPTIONS"));
- InitializeCommonFlags();
- if (Verbosity()) ReportUnrecognizedFlags();
-
- if (common_flags()->help) parser.PrintFlagDescriptions();
-}
-
-} // namespace __ubsan
-
-SANITIZER_INTERFACE_WEAK_DEF(const char *, __ubsan_default_options, void) {
- return "";
-}
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_flags.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Runtime flags for UndefinedBehaviorSanitizer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_flag_parser.h"
+
+#include <stdlib.h>
+
+namespace __ubsan {
+
+const char *MaybeCallUbsanDefaultOptions() {
+ return (&__ubsan_default_options) ? __ubsan_default_options() : "";
+}
+
+static const char *GetFlag(const char *flag) {
+ // We cannot call getenv() from inside a preinit array initializer
+ if (SANITIZER_CAN_USE_PREINIT_ARRAY) {
+ return GetEnv(flag);
+ } else {
+ return getenv(flag);
+ }
+}
+
+Flags ubsan_flags;
+
+void Flags::SetDefaults() {
+#define UBSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
+#include "ubsan_flags.inc"
+#undef UBSAN_FLAG
+}
+
+void RegisterUbsanFlags(FlagParser *parser, Flags *f) {
+#define UBSAN_FLAG(Type, Name, DefaultValue, Description) \
+ RegisterFlag(parser, #Name, Description, &f->Name);
+#include "ubsan_flags.inc"
+#undef UBSAN_FLAG
+}
+
+void InitializeFlags() {
+ SetCommonFlagsDefaults();
+ {
+ CommonFlags cf;
+ cf.CopyFrom(*common_flags());
+ cf.print_summary = false;
+ cf.external_symbolizer_path = GetFlag("UBSAN_SYMBOLIZER_PATH");
+ OverrideCommonFlags(cf);
+ }
+
+ Flags *f = flags();
+ f->SetDefaults();
+
+ FlagParser parser;
+ RegisterCommonFlags(&parser);
+ RegisterUbsanFlags(&parser, f);
+
+ // Override from user-specified string.
+ parser.ParseString(MaybeCallUbsanDefaultOptions());
+ // Override from environment variable.
+ parser.ParseStringFromEnv("UBSAN_OPTIONS");
+ InitializeCommonFlags();
+ if (Verbosity()) ReportUnrecognizedFlags();
+
+ if (common_flags()->help) parser.PrintFlagDescriptions();
+}
+
+} // namespace __ubsan
+
+SANITIZER_INTERFACE_WEAK_DEF(const char *, __ubsan_default_options, void) {
+ return "";
+}
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_flags.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- ubsan_flags.inc -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
UBSAN_FLAG(bool, report_error_type, false,
"Print specific error type instead of 'undefined-behavior' in summary.")
UBSAN_FLAG(bool, silence_unsigned_overflow, false,
- "Do not print error reports for unsigned integer overflow. "
- "Used to provide fuzzing signal without blowing up logs.")
+ "Do not print non-fatal error reports for unsigned integer overflow. "
+ "Used to provide fuzzing signal without blowing up logs.")
+++ /dev/null
-//===-- ubsan_handlers.cc -------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Error logging entry points for the UBSan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_handlers.h"
-#include "ubsan_diag.h"
-#include "ubsan_flags.h"
-#include "ubsan_monitor.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-
-using namespace __sanitizer;
-using namespace __ubsan;
-
-namespace __ubsan {
-bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) {
- // We are not allowed to skip error report: if we are in unrecoverable
- // handler, we have to terminate the program right now, and therefore
- // have to print some diagnostic.
- //
- // Even if source location is disabled, it doesn't mean that we have
- // already report an error to the user: some concurrently running
- // thread could have acquired it, but not yet printed the report.
- if (Opts.FromUnrecoverableHandler)
- return false;
- return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename());
-}
-
-const char *TypeCheckKinds[] = {
- "load of", "store to", "reference binding to", "member access within",
- "member call on", "constructor call on", "downcast of", "downcast of",
- "upcast of", "cast to virtual base of", "_Nonnull binding to",
- "dynamic operation on"};
-}
-
-static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
- ReportOptions Opts) {
- Location Loc = Data->Loc.acquire();
-
- uptr Alignment = (uptr)1 << Data->LogAlignment;
- ErrorType ET;
- if (!Pointer)
- ET = ErrorType::NullPointerUse;
- else if (Pointer & (Alignment - 1))
- ET = ErrorType::MisalignedPointerUse;
- else
- ET = ErrorType::InsufficientObjectSize;
-
- // Use the SourceLocation from Data to track deduplication, even if it's
- // invalid.
- if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
- return;
-
- SymbolizedStackHolder FallbackLoc;
- if (Data->Loc.isInvalid()) {
- FallbackLoc.reset(getCallerLocation(Opts.pc));
- Loc = FallbackLoc;
- }
-
- ScopedReport R(Opts, Loc, ET);
-
- switch (ET) {
- case ErrorType::NullPointerUse:
- Diag(Loc, DL_Error, ET, "%0 null pointer of type %1")
- << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
- break;
- case ErrorType::MisalignedPointerUse:
- Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %3, "
- "which requires %2 byte alignment")
- << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Alignment
- << Data->Type;
- break;
- case ErrorType::InsufficientObjectSize:
- Diag(Loc, DL_Error, ET, "%0 address %1 with insufficient space "
- "for an object of type %2")
- << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type;
- break;
- default:
- UNREACHABLE("unexpected error type!");
- }
-
- if (Pointer)
- Diag(Pointer, DL_Note, ET, "pointer points here");
-}
-
-void __ubsan::__ubsan_handle_type_mismatch_v1(TypeMismatchData *Data,
- ValueHandle Pointer) {
- GET_REPORT_OPTIONS(false);
- handleTypeMismatchImpl(Data, Pointer, Opts);
-}
-void __ubsan::__ubsan_handle_type_mismatch_v1_abort(TypeMismatchData *Data,
- ValueHandle Pointer) {
- GET_REPORT_OPTIONS(true);
- handleTypeMismatchImpl(Data, Pointer, Opts);
- Die();
-}
-
-/// \brief Common diagnostic emission for various forms of integer overflow.
-template <typename T>
-static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
- const char *Operator, T RHS,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- bool IsSigned = Data->Type.isSignedIntegerTy();
- ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
- : ErrorType::UnsignedIntegerOverflow;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- if (!IsSigned && flags()->silence_unsigned_overflow)
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET, "%0 integer overflow: "
- "%1 %2 %3 cannot be represented in type %4")
- << (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS)
- << Operator << RHS << Data->Type;
-}
-
-#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \
- void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
- ValueHandle RHS) { \
- GET_REPORT_OPTIONS(unrecoverable); \
- handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
- if (unrecoverable) \
- Die(); \
- }
-
-UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false)
-UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort, "+", true)
-UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow, "-", false)
-UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort, "-", true)
-UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow, "*", false)
-UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
-
-static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- bool IsSigned = Data->Type.isSignedIntegerTy();
- ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
- : ErrorType::UnsignedIntegerOverflow;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- if (!IsSigned && flags()->silence_unsigned_overflow)
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- if (IsSigned)
- Diag(Loc, DL_Error, ET,
- "negation of %0 cannot be represented in type %1; "
- "cast to an unsigned type to negate this value to itself")
- << Value(Data->Type, OldVal) << Data->Type;
- else
- Diag(Loc, DL_Error, ET, "negation of %0 cannot be represented in type %1")
- << Value(Data->Type, OldVal) << Data->Type;
-}
-
-void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
- ValueHandle OldVal) {
- GET_REPORT_OPTIONS(false);
- handleNegateOverflowImpl(Data, OldVal, Opts);
-}
-void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
- ValueHandle OldVal) {
- GET_REPORT_OPTIONS(true);
- handleNegateOverflowImpl(Data, OldVal, Opts);
- Die();
-}
-
-static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
- ValueHandle RHS, ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- Value LHSVal(Data->Type, LHS);
- Value RHSVal(Data->Type, RHS);
-
- ErrorType ET;
- if (RHSVal.isMinusOne())
- ET = ErrorType::SignedIntegerOverflow;
- else if (Data->Type.isIntegerTy())
- ET = ErrorType::IntegerDivideByZero;
- else
- ET = ErrorType::FloatDivideByZero;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- switch (ET) {
- case ErrorType::SignedIntegerOverflow:
- Diag(Loc, DL_Error, ET,
- "division of %0 by -1 cannot be represented in type %1")
- << LHSVal << Data->Type;
- break;
- default:
- Diag(Loc, DL_Error, ET, "division by zero");
- break;
- }
-}
-
-void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
- ValueHandle LHS, ValueHandle RHS) {
- GET_REPORT_OPTIONS(false);
- handleDivremOverflowImpl(Data, LHS, RHS, Opts);
-}
-void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- GET_REPORT_OPTIONS(true);
- handleDivremOverflowImpl(Data, LHS, RHS, Opts);
- Die();
-}
-
-static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
- ValueHandle LHS, ValueHandle RHS,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- Value LHSVal(Data->LHSType, LHS);
- Value RHSVal(Data->RHSType, RHS);
-
- ErrorType ET;
- if (RHSVal.isNegative() ||
- RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
- ET = ErrorType::InvalidShiftExponent;
- else
- ET = ErrorType::InvalidShiftBase;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- if (ET == ErrorType::InvalidShiftExponent) {
- if (RHSVal.isNegative())
- Diag(Loc, DL_Error, ET, "shift exponent %0 is negative") << RHSVal;
- else
- Diag(Loc, DL_Error, ET,
- "shift exponent %0 is too large for %1-bit type %2")
- << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
- } else {
- if (LHSVal.isNegative())
- Diag(Loc, DL_Error, ET, "left shift of negative value %0") << LHSVal;
- else
- Diag(Loc, DL_Error, ET,
- "left shift of %0 by %1 places cannot be represented in type %2")
- << LHSVal << RHSVal << Data->LHSType;
- }
-}
-
-void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- GET_REPORT_OPTIONS(false);
- handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
-}
-void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
- ShiftOutOfBoundsData *Data,
- ValueHandle LHS,
- ValueHandle RHS) {
- GET_REPORT_OPTIONS(true);
- handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
- Die();
-}
-
-static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::OutOfBoundsIndex;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Value IndexVal(Data->IndexType, Index);
- Diag(Loc, DL_Error, ET, "index %0 out of bounds for type %1")
- << IndexVal << Data->ArrayType;
-}
-
-void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
- ValueHandle Index) {
- GET_REPORT_OPTIONS(false);
- handleOutOfBoundsImpl(Data, Index, Opts);
-}
-void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
- ValueHandle Index) {
- GET_REPORT_OPTIONS(true);
- handleOutOfBoundsImpl(Data, Index, Opts);
- Die();
-}
-
-static void handleBuiltinUnreachableImpl(UnreachableData *Data,
- ReportOptions Opts) {
- ErrorType ET = ErrorType::UnreachableCall;
- ScopedReport R(Opts, Data->Loc, ET);
- Diag(Data->Loc, DL_Error, ET,
- "execution reached an unreachable program point");
-}
-
-void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
- GET_REPORT_OPTIONS(true);
- handleBuiltinUnreachableImpl(Data, Opts);
- Die();
-}
-
-static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) {
- ErrorType ET = ErrorType::MissingReturn;
- ScopedReport R(Opts, Data->Loc, ET);
- Diag(Data->Loc, DL_Error, ET,
- "execution reached the end of a value-returning function "
- "without returning a value");
-}
-
-void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
- GET_REPORT_OPTIONS(true);
- handleMissingReturnImpl(Data, Opts);
- Die();
-}
-
-static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::NonPositiveVLAIndex;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET, "variable length array bound evaluates to "
- "non-positive value %0")
- << Value(Data->Type, Bound);
-}
-
-void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
- ValueHandle Bound) {
- GET_REPORT_OPTIONS(false);
- handleVLABoundNotPositive(Data, Bound, Opts);
-}
-void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
- ValueHandle Bound) {
- GET_REPORT_OPTIONS(true);
- handleVLABoundNotPositive(Data, Bound, Opts);
- Die();
-}
-
-static bool looksLikeFloatCastOverflowDataV1(void *Data) {
- // First field is either a pointer to filename or a pointer to a
- // TypeDescriptor.
- u8 *FilenameOrTypeDescriptor;
- internal_memcpy(&FilenameOrTypeDescriptor, Data,
- sizeof(FilenameOrTypeDescriptor));
-
- // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer
- // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known,
- // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename,
- // adding two printable characters will not yield such a value. Otherwise,
- // if one of them is 0xff, this is most likely TK_Unknown type descriptor.
- u16 MaybeFromTypeKind =
- FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1];
- return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff ||
- FilenameOrTypeDescriptor[1] == 0xff;
-}
-
-static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
- ReportOptions Opts) {
- SymbolizedStackHolder CallerLoc;
- Location Loc;
- const TypeDescriptor *FromType, *ToType;
- ErrorType ET = ErrorType::FloatCastOverflow;
-
- if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
- auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
- CallerLoc.reset(getCallerLocation(Opts.pc));
- Loc = CallerLoc;
- FromType = &Data->FromType;
- ToType = &Data->ToType;
- } else {
- auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
- SourceLocation SLoc = Data->Loc.acquire();
- if (ignoreReport(SLoc, Opts, ET))
- return;
- Loc = SLoc;
- FromType = &Data->FromType;
- ToType = &Data->ToType;
- }
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET,
- "%0 is outside the range of representable values of type %2")
- << Value(*FromType, From) << *FromType << *ToType;
-}
-
-void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) {
- GET_REPORT_OPTIONS(false);
- handleFloatCastOverflow(Data, From, Opts);
-}
-void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data,
- ValueHandle From) {
- GET_REPORT_OPTIONS(true);
- handleFloatCastOverflow(Data, From, Opts);
- Die();
-}
-
-static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- // This check could be more precise if we used different handlers for
- // -fsanitize=bool and -fsanitize=enum.
- bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'")) ||
- (0 == internal_strncmp(Data->Type.getTypeName(), "'BOOL'", 6));
- ErrorType ET =
- IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET,
- "load of value %0, which is not a valid value for type %1")
- << Value(Data->Type, Val) << Data->Type;
-}
-
-void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
- ValueHandle Val) {
- GET_REPORT_OPTIONS(false);
- handleLoadInvalidValue(Data, Val, Opts);
-}
-void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
- ValueHandle Val) {
- GET_REPORT_OPTIONS(true);
- handleLoadInvalidValue(Data, Val, Opts);
- Die();
-}
-
-static void handleImplicitConversion(ImplicitConversionData *Data,
- ReportOptions Opts, ValueHandle Src,
- ValueHandle Dst) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::GenericUB;
-
- const TypeDescriptor &SrcTy = Data->FromType;
- const TypeDescriptor &DstTy = Data->ToType;
-
- bool SrcSigned = SrcTy.isSignedIntegerTy();
- bool DstSigned = DstTy.isSignedIntegerTy();
-
- switch (Data->Kind) {
- case ICCK_IntegerTruncation: { // Legacy, no longer used.
- // Let's figure out what it should be as per the new types, and upgrade.
- // If both types are unsigned, then it's an unsigned truncation.
- // Else, it is a signed truncation.
- if (!SrcSigned && !DstSigned) {
- ET = ErrorType::ImplicitUnsignedIntegerTruncation;
- } else {
- ET = ErrorType::ImplicitSignedIntegerTruncation;
- }
- break;
- }
- case ICCK_UnsignedIntegerTruncation:
- ET = ErrorType::ImplicitUnsignedIntegerTruncation;
- break;
- case ICCK_SignedIntegerTruncation:
- ET = ErrorType::ImplicitSignedIntegerTruncation;
- break;
- }
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- // FIXME: is it possible to dump the values as hex with fixed width?
-
- Diag(Loc, DL_Error, ET,
- "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
- "type %4 changed the value to %5 (%6-bit, %7signed)")
- << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
- << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
- << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
-}
-
-void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data,
- ValueHandle Src,
- ValueHandle Dst) {
- GET_REPORT_OPTIONS(false);
- handleImplicitConversion(Data, Opts, Src, Dst);
-}
-void __ubsan::__ubsan_handle_implicit_conversion_abort(
- ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) {
- GET_REPORT_OPTIONS(true);
- handleImplicitConversion(Data, Opts, Src, Dst);
- Die();
-}
-
-static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::InvalidBuiltin;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET,
- "passing zero to %0, which is not a valid argument")
- << ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()");
-}
-
-void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) {
- GET_REPORT_OPTIONS(true);
- handleInvalidBuiltin(Data, Opts);
-}
-void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) {
- GET_REPORT_OPTIONS(true);
- handleInvalidBuiltin(Data, Opts);
- Die();
-}
-
-static void handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
- ValueHandle Function,
- ReportOptions Opts) {
- SourceLocation CallLoc = Data->Loc.acquire();
- ErrorType ET = ErrorType::FunctionTypeMismatch;
-
- if (ignoreReport(CallLoc, Opts, ET))
- return;
-
- ScopedReport R(Opts, CallLoc, ET);
-
- SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
- const char *FName = FLoc.get()->info.function;
- if (!FName)
- FName = "(unknown)";
-
- Diag(CallLoc, DL_Error, ET,
- "call to function %0 through pointer to incorrect function type %1")
- << FName << Data->Type;
- Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
-}
-
-void
-__ubsan::__ubsan_handle_function_type_mismatch(FunctionTypeMismatchData *Data,
- ValueHandle Function) {
- GET_REPORT_OPTIONS(false);
- handleFunctionTypeMismatch(Data, Function, Opts);
-}
-
-void __ubsan::__ubsan_handle_function_type_mismatch_abort(
- FunctionTypeMismatchData *Data, ValueHandle Function) {
- GET_REPORT_OPTIONS(true);
- handleFunctionTypeMismatch(Data, Function, Opts);
- Die();
-}
-
-static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr,
- ReportOptions Opts, bool IsAttr) {
- if (!LocPtr)
- UNREACHABLE("source location pointer is null!");
-
- SourceLocation Loc = LocPtr->acquire();
- ErrorType ET = ErrorType::InvalidNullReturn;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET,
- "null pointer returned from function declared to never return null");
- if (!Data->AttrLoc.isInvalid())
- Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here")
- << (IsAttr ? "returns_nonnull attribute"
- : "_Nonnull return type annotation");
-}
-
-void __ubsan::__ubsan_handle_nonnull_return_v1(NonNullReturnData *Data,
- SourceLocation *LocPtr) {
- GET_REPORT_OPTIONS(false);
- handleNonNullReturn(Data, LocPtr, Opts, true);
-}
-
-void __ubsan::__ubsan_handle_nonnull_return_v1_abort(NonNullReturnData *Data,
- SourceLocation *LocPtr) {
- GET_REPORT_OPTIONS(true);
- handleNonNullReturn(Data, LocPtr, Opts, true);
- Die();
-}
-
-void __ubsan::__ubsan_handle_nullability_return_v1(NonNullReturnData *Data,
- SourceLocation *LocPtr) {
- GET_REPORT_OPTIONS(false);
- handleNonNullReturn(Data, LocPtr, Opts, false);
-}
-
-void __ubsan::__ubsan_handle_nullability_return_v1_abort(
- NonNullReturnData *Data, SourceLocation *LocPtr) {
- GET_REPORT_OPTIONS(true);
- handleNonNullReturn(Data, LocPtr, Opts, false);
- Die();
-}
-
-static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts,
- bool IsAttr) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::InvalidNullArgument;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET,
- "null pointer passed as argument %0, which is declared to "
- "never be null")
- << Data->ArgIndex;
- if (!Data->AttrLoc.isInvalid())
- Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here")
- << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation");
-}
-
-void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
- GET_REPORT_OPTIONS(false);
- handleNonNullArg(Data, Opts, true);
-}
-
-void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
- GET_REPORT_OPTIONS(true);
- handleNonNullArg(Data, Opts, true);
- Die();
-}
-
-void __ubsan::__ubsan_handle_nullability_arg(NonNullArgData *Data) {
- GET_REPORT_OPTIONS(false);
- handleNonNullArg(Data, Opts, false);
-}
-
-void __ubsan::__ubsan_handle_nullability_arg_abort(NonNullArgData *Data) {
- GET_REPORT_OPTIONS(true);
- handleNonNullArg(Data, Opts, false);
- Die();
-}
-
-static void handlePointerOverflowImpl(PointerOverflowData *Data,
- ValueHandle Base,
- ValueHandle Result,
- ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::PointerOverflow;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) {
- if (Base > Result)
- Diag(Loc, DL_Error, ET,
- "addition of unsigned offset to %0 overflowed to %1")
- << (void *)Base << (void *)Result;
- else
- Diag(Loc, DL_Error, ET,
- "subtraction of unsigned offset from %0 overflowed to %1")
- << (void *)Base << (void *)Result;
- } else {
- Diag(Loc, DL_Error, ET,
- "pointer index expression with base %0 overflowed to %1")
- << (void *)Base << (void *)Result;
- }
-}
-
-void __ubsan::__ubsan_handle_pointer_overflow(PointerOverflowData *Data,
- ValueHandle Base,
- ValueHandle Result) {
- GET_REPORT_OPTIONS(false);
- handlePointerOverflowImpl(Data, Base, Result, Opts);
-}
-
-void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data,
- ValueHandle Base,
- ValueHandle Result) {
- GET_REPORT_OPTIONS(true);
- handlePointerOverflowImpl(Data, Base, Result, Opts);
- Die();
-}
-
-static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
- ReportOptions Opts) {
- if (Data->CheckKind != CFITCK_ICall && Data->CheckKind != CFITCK_NVMFCall)
- Die();
-
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::CFIBadType;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
-
- const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall
- ? "non-virtual pointer to member function call"
- : "indirect function call";
- Diag(Loc, DL_Error, ET,
- "control flow integrity check for type %0 failed during %1")
- << Data->Type << CheckKindStr;
-
- SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
- const char *FName = FLoc.get()->info.function;
- if (!FName)
- FName = "(unknown)";
- Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
-
- // If the failure involved different DSOs for the check location and icall
- // target, report the DSO names.
- const char *DstModule = FLoc.get()->info.module;
- if (!DstModule)
- DstModule = "(unknown)";
-
- const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(Opts.pc);
- if (!SrcModule)
- SrcModule = "(unknown)";
-
- if (internal_strcmp(SrcModule, DstModule))
- Diag(Loc, DL_Note, ET,
- "check failed in %0, destination function located in %1")
- << SrcModule << DstModule;
-}
-
-namespace __ubsan {
-
-#ifdef UBSAN_CAN_USE_CXXABI
-
-#ifdef _WIN32
-
-extern "C" void __ubsan_handle_cfi_bad_type_default(CFICheckFailData *Data,
- ValueHandle Vtable,
- bool ValidVtable,
- ReportOptions Opts) {
- Die();
-}
-
-WIN_WEAK_ALIAS(__ubsan_handle_cfi_bad_type, __ubsan_handle_cfi_bad_type_default)
-#else
-SANITIZER_WEAK_ATTRIBUTE
-#endif
-void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
- bool ValidVtable, ReportOptions Opts);
-
-#else
-void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
- bool ValidVtable, ReportOptions Opts) {
- Die();
-}
-#endif
-
-} // namespace __ubsan
-
-void __ubsan::__ubsan_handle_cfi_bad_icall(CFIBadIcallData *CallData,
- ValueHandle Function) {
- GET_REPORT_OPTIONS(false);
- CFICheckFailData Data = {CFITCK_ICall, CallData->Loc, CallData->Type};
- handleCFIBadIcall(&Data, Function, Opts);
-}
-
-void __ubsan::__ubsan_handle_cfi_bad_icall_abort(CFIBadIcallData *CallData,
- ValueHandle Function) {
- GET_REPORT_OPTIONS(true);
- CFICheckFailData Data = {CFITCK_ICall, CallData->Loc, CallData->Type};
- handleCFIBadIcall(&Data, Function, Opts);
- Die();
-}
-
-void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data,
- ValueHandle Value,
- uptr ValidVtable) {
- GET_REPORT_OPTIONS(false);
- if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall)
- handleCFIBadIcall(Data, Value, Opts);
- else
- __ubsan_handle_cfi_bad_type(Data, Value, ValidVtable, Opts);
-}
-
-void __ubsan::__ubsan_handle_cfi_check_fail_abort(CFICheckFailData *Data,
- ValueHandle Value,
- uptr ValidVtable) {
- GET_REPORT_OPTIONS(true);
- if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall)
- handleCFIBadIcall(Data, Value, Opts);
- else
- __ubsan_handle_cfi_bad_type(Data, Value, ValidVtable, Opts);
- Die();
-}
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_handlers.cpp ------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_handlers.h"
+#include "ubsan_diag.h"
+#include "ubsan_flags.h"
+#include "ubsan_monitor.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+bool ignoreReport(SourceLocation SLoc, ReportOptions Opts, ErrorType ET) {
+ // We are not allowed to skip error report: if we are in unrecoverable
+ // handler, we have to terminate the program right now, and therefore
+ // have to print some diagnostic.
+ //
+ // Even if source location is disabled, it doesn't mean that we have
+ // already report an error to the user: some concurrently running
+ // thread could have acquired it, but not yet printed the report.
+ if (Opts.FromUnrecoverableHandler)
+ return false;
+ return SLoc.isDisabled() || IsPCSuppressed(ET, Opts.pc, SLoc.getFilename());
+}
+
+const char *TypeCheckKinds[] = {
+ "load of", "store to", "reference binding to", "member access within",
+ "member call on", "constructor call on", "downcast of", "downcast of",
+ "upcast of", "cast to virtual base of", "_Nonnull binding to",
+ "dynamic operation on"};
+}
+
+static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,
+ ReportOptions Opts) {
+ Location Loc = Data->Loc.acquire();
+
+ uptr Alignment = (uptr)1 << Data->LogAlignment;
+ ErrorType ET;
+ if (!Pointer)
+ ET = ErrorType::NullPointerUse;
+ else if (Pointer & (Alignment - 1))
+ ET = ErrorType::MisalignedPointerUse;
+ else
+ ET = ErrorType::InsufficientObjectSize;
+
+ // Use the SourceLocation from Data to track deduplication, even if it's
+ // invalid.
+ if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
+ return;
+
+ SymbolizedStackHolder FallbackLoc;
+ if (Data->Loc.isInvalid()) {
+ FallbackLoc.reset(getCallerLocation(Opts.pc));
+ Loc = FallbackLoc;
+ }
+
+ ScopedReport R(Opts, Loc, ET);
+
+ switch (ET) {
+ case ErrorType::NullPointerUse:
+ Diag(Loc, DL_Error, ET, "%0 null pointer of type %1")
+ << TypeCheckKinds[Data->TypeCheckKind] << Data->Type;
+ break;
+ case ErrorType::MisalignedPointerUse:
+ Diag(Loc, DL_Error, ET, "%0 misaligned address %1 for type %3, "
+ "which requires %2 byte alignment")
+ << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Alignment
+ << Data->Type;
+ break;
+ case ErrorType::InsufficientObjectSize:
+ Diag(Loc, DL_Error, ET, "%0 address %1 with insufficient space "
+ "for an object of type %2")
+ << TypeCheckKinds[Data->TypeCheckKind] << (void *)Pointer << Data->Type;
+ break;
+ default:
+ UNREACHABLE("unexpected error type!");
+ }
+
+ if (Pointer)
+ Diag(Pointer, DL_Note, ET, "pointer points here");
+}
+
+void __ubsan::__ubsan_handle_type_mismatch_v1(TypeMismatchData *Data,
+ ValueHandle Pointer) {
+ GET_REPORT_OPTIONS(false);
+ handleTypeMismatchImpl(Data, Pointer, Opts);
+}
+void __ubsan::__ubsan_handle_type_mismatch_v1_abort(TypeMismatchData *Data,
+ ValueHandle Pointer) {
+ GET_REPORT_OPTIONS(true);
+ handleTypeMismatchImpl(Data, Pointer, Opts);
+ Die();
+}
+
+static void handleAlignmentAssumptionImpl(AlignmentAssumptionData *Data,
+ ValueHandle Pointer,
+ ValueHandle Alignment,
+ ValueHandle Offset,
+ ReportOptions Opts) {
+ Location Loc = Data->Loc.acquire();
+ SourceLocation AssumptionLoc = Data->AssumptionLoc.acquire();
+
+ ErrorType ET = ErrorType::AlignmentAssumption;
+
+ if (ignoreReport(Loc.getSourceLocation(), Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ uptr RealPointer = Pointer - Offset;
+ uptr LSB = LeastSignificantSetBitIndex(RealPointer);
+ uptr ActualAlignment = uptr(1) << LSB;
+
+ uptr Mask = Alignment - 1;
+ uptr MisAlignmentOffset = RealPointer & Mask;
+
+ if (!Offset) {
+ Diag(Loc, DL_Error, ET,
+ "assumption of %0 byte alignment for pointer of type %1 failed")
+ << Alignment << Data->Type;
+ } else {
+ Diag(Loc, DL_Error, ET,
+ "assumption of %0 byte alignment (with offset of %1 byte) for pointer "
+ "of type %2 failed")
+ << Alignment << Offset << Data->Type;
+ }
+
+ if (!AssumptionLoc.isInvalid())
+ Diag(AssumptionLoc, DL_Note, ET, "alignment assumption was specified here");
+
+ Diag(RealPointer, DL_Note, ET,
+ "%0address is %1 aligned, misalignment offset is %2 bytes")
+ << (Offset ? "offset " : "") << ActualAlignment << MisAlignmentOffset;
+}
+
+void __ubsan::__ubsan_handle_alignment_assumption(AlignmentAssumptionData *Data,
+ ValueHandle Pointer,
+ ValueHandle Alignment,
+ ValueHandle Offset) {
+ GET_REPORT_OPTIONS(false);
+ handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset, Opts);
+}
+void __ubsan::__ubsan_handle_alignment_assumption_abort(
+ AlignmentAssumptionData *Data, ValueHandle Pointer, ValueHandle Alignment,
+ ValueHandle Offset) {
+ GET_REPORT_OPTIONS(true);
+ handleAlignmentAssumptionImpl(Data, Pointer, Alignment, Offset, Opts);
+ Die();
+}
+
+/// \brief Common diagnostic emission for various forms of integer overflow.
+template <typename T>
+static void handleIntegerOverflowImpl(OverflowData *Data, ValueHandle LHS,
+ const char *Operator, T RHS,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ bool IsSigned = Data->Type.isSignedIntegerTy();
+ ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+ : ErrorType::UnsignedIntegerOverflow;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ // If this is an unsigned overflow in non-fatal mode, potentially ignore it.
+ if (!IsSigned && !Opts.FromUnrecoverableHandler &&
+ flags()->silence_unsigned_overflow)
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET, "%0 integer overflow: "
+ "%1 %2 %3 cannot be represented in type %4")
+ << (IsSigned ? "signed" : "unsigned") << Value(Data->Type, LHS)
+ << Operator << RHS << Data->Type;
+}
+
+#define UBSAN_OVERFLOW_HANDLER(handler_name, op, unrecoverable) \
+ void __ubsan::handler_name(OverflowData *Data, ValueHandle LHS, \
+ ValueHandle RHS) { \
+ GET_REPORT_OPTIONS(unrecoverable); \
+ handleIntegerOverflowImpl(Data, LHS, op, Value(Data->Type, RHS), Opts); \
+ if (unrecoverable) \
+ Die(); \
+ }
+
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow, "+", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_add_overflow_abort, "+", true)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow, "-", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_sub_overflow_abort, "-", true)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow, "*", false)
+UBSAN_OVERFLOW_HANDLER(__ubsan_handle_mul_overflow_abort, "*", true)
+
+static void handleNegateOverflowImpl(OverflowData *Data, ValueHandle OldVal,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ bool IsSigned = Data->Type.isSignedIntegerTy();
+ ErrorType ET = IsSigned ? ErrorType::SignedIntegerOverflow
+ : ErrorType::UnsignedIntegerOverflow;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ if (!IsSigned && flags()->silence_unsigned_overflow)
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ if (IsSigned)
+ Diag(Loc, DL_Error, ET,
+ "negation of %0 cannot be represented in type %1; "
+ "cast to an unsigned type to negate this value to itself")
+ << Value(Data->Type, OldVal) << Data->Type;
+ else
+ Diag(Loc, DL_Error, ET, "negation of %0 cannot be represented in type %1")
+ << Value(Data->Type, OldVal) << Data->Type;
+}
+
+void __ubsan::__ubsan_handle_negate_overflow(OverflowData *Data,
+ ValueHandle OldVal) {
+ GET_REPORT_OPTIONS(false);
+ handleNegateOverflowImpl(Data, OldVal, Opts);
+}
+void __ubsan::__ubsan_handle_negate_overflow_abort(OverflowData *Data,
+ ValueHandle OldVal) {
+ GET_REPORT_OPTIONS(true);
+ handleNegateOverflowImpl(Data, OldVal, Opts);
+ Die();
+}
+
+static void handleDivremOverflowImpl(OverflowData *Data, ValueHandle LHS,
+ ValueHandle RHS, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ Value LHSVal(Data->Type, LHS);
+ Value RHSVal(Data->Type, RHS);
+
+ ErrorType ET;
+ if (RHSVal.isMinusOne())
+ ET = ErrorType::SignedIntegerOverflow;
+ else if (Data->Type.isIntegerTy())
+ ET = ErrorType::IntegerDivideByZero;
+ else
+ ET = ErrorType::FloatDivideByZero;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ switch (ET) {
+ case ErrorType::SignedIntegerOverflow:
+ Diag(Loc, DL_Error, ET,
+ "division of %0 by -1 cannot be represented in type %1")
+ << LHSVal << Data->Type;
+ break;
+ default:
+ Diag(Loc, DL_Error, ET, "division by zero");
+ break;
+ }
+}
+
+void __ubsan::__ubsan_handle_divrem_overflow(OverflowData *Data,
+ ValueHandle LHS, ValueHandle RHS) {
+ GET_REPORT_OPTIONS(false);
+ handleDivremOverflowImpl(Data, LHS, RHS, Opts);
+}
+void __ubsan::__ubsan_handle_divrem_overflow_abort(OverflowData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ GET_REPORT_OPTIONS(true);
+ handleDivremOverflowImpl(Data, LHS, RHS, Opts);
+ Die();
+}
+
+static void handleShiftOutOfBoundsImpl(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS, ValueHandle RHS,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ Value LHSVal(Data->LHSType, LHS);
+ Value RHSVal(Data->RHSType, RHS);
+
+ ErrorType ET;
+ if (RHSVal.isNegative() ||
+ RHSVal.getPositiveIntValue() >= Data->LHSType.getIntegerBitWidth())
+ ET = ErrorType::InvalidShiftExponent;
+ else
+ ET = ErrorType::InvalidShiftBase;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ if (ET == ErrorType::InvalidShiftExponent) {
+ if (RHSVal.isNegative())
+ Diag(Loc, DL_Error, ET, "shift exponent %0 is negative") << RHSVal;
+ else
+ Diag(Loc, DL_Error, ET,
+ "shift exponent %0 is too large for %1-bit type %2")
+ << RHSVal << Data->LHSType.getIntegerBitWidth() << Data->LHSType;
+ } else {
+ if (LHSVal.isNegative())
+ Diag(Loc, DL_Error, ET, "left shift of negative value %0") << LHSVal;
+ else
+ Diag(Loc, DL_Error, ET,
+ "left shift of %0 by %1 places cannot be represented in type %2")
+ << LHSVal << RHSVal << Data->LHSType;
+ }
+}
+
+void __ubsan::__ubsan_handle_shift_out_of_bounds(ShiftOutOfBoundsData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ GET_REPORT_OPTIONS(false);
+ handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
+}
+void __ubsan::__ubsan_handle_shift_out_of_bounds_abort(
+ ShiftOutOfBoundsData *Data,
+ ValueHandle LHS,
+ ValueHandle RHS) {
+ GET_REPORT_OPTIONS(true);
+ handleShiftOutOfBoundsImpl(Data, LHS, RHS, Opts);
+ Die();
+}
+
+static void handleOutOfBoundsImpl(OutOfBoundsData *Data, ValueHandle Index,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::OutOfBoundsIndex;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Value IndexVal(Data->IndexType, Index);
+ Diag(Loc, DL_Error, ET, "index %0 out of bounds for type %1")
+ << IndexVal << Data->ArrayType;
+}
+
+void __ubsan::__ubsan_handle_out_of_bounds(OutOfBoundsData *Data,
+ ValueHandle Index) {
+ GET_REPORT_OPTIONS(false);
+ handleOutOfBoundsImpl(Data, Index, Opts);
+}
+void __ubsan::__ubsan_handle_out_of_bounds_abort(OutOfBoundsData *Data,
+ ValueHandle Index) {
+ GET_REPORT_OPTIONS(true);
+ handleOutOfBoundsImpl(Data, Index, Opts);
+ Die();
+}
+
+static void handleBuiltinUnreachableImpl(UnreachableData *Data,
+ ReportOptions Opts) {
+ ErrorType ET = ErrorType::UnreachableCall;
+ ScopedReport R(Opts, Data->Loc, ET);
+ Diag(Data->Loc, DL_Error, ET,
+ "execution reached an unreachable program point");
+}
+
+void __ubsan::__ubsan_handle_builtin_unreachable(UnreachableData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleBuiltinUnreachableImpl(Data, Opts);
+ Die();
+}
+
+static void handleMissingReturnImpl(UnreachableData *Data, ReportOptions Opts) {
+ ErrorType ET = ErrorType::MissingReturn;
+ ScopedReport R(Opts, Data->Loc, ET);
+ Diag(Data->Loc, DL_Error, ET,
+ "execution reached the end of a value-returning function "
+ "without returning a value");
+}
+
+void __ubsan::__ubsan_handle_missing_return(UnreachableData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleMissingReturnImpl(Data, Opts);
+ Die();
+}
+
+static void handleVLABoundNotPositive(VLABoundData *Data, ValueHandle Bound,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::NonPositiveVLAIndex;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET, "variable length array bound evaluates to "
+ "non-positive value %0")
+ << Value(Data->Type, Bound);
+}
+
+void __ubsan::__ubsan_handle_vla_bound_not_positive(VLABoundData *Data,
+ ValueHandle Bound) {
+ GET_REPORT_OPTIONS(false);
+ handleVLABoundNotPositive(Data, Bound, Opts);
+}
+void __ubsan::__ubsan_handle_vla_bound_not_positive_abort(VLABoundData *Data,
+ ValueHandle Bound) {
+ GET_REPORT_OPTIONS(true);
+ handleVLABoundNotPositive(Data, Bound, Opts);
+ Die();
+}
+
+static bool looksLikeFloatCastOverflowDataV1(void *Data) {
+ // First field is either a pointer to filename or a pointer to a
+ // TypeDescriptor.
+ u8 *FilenameOrTypeDescriptor;
+ internal_memcpy(&FilenameOrTypeDescriptor, Data,
+ sizeof(FilenameOrTypeDescriptor));
+
+ // Heuristic: For float_cast_overflow, the TypeKind will be either TK_Integer
+ // (0x0), TK_Float (0x1) or TK_Unknown (0xff). If both types are known,
+ // adding both bytes will be 0 or 1 (for BE or LE). If it were a filename,
+ // adding two printable characters will not yield such a value. Otherwise,
+ // if one of them is 0xff, this is most likely TK_Unknown type descriptor.
+ u16 MaybeFromTypeKind =
+ FilenameOrTypeDescriptor[0] + FilenameOrTypeDescriptor[1];
+ return MaybeFromTypeKind < 2 || FilenameOrTypeDescriptor[0] == 0xff ||
+ FilenameOrTypeDescriptor[1] == 0xff;
+}
+
+static void handleFloatCastOverflow(void *DataPtr, ValueHandle From,
+ ReportOptions Opts) {
+ SymbolizedStackHolder CallerLoc;
+ Location Loc;
+ const TypeDescriptor *FromType, *ToType;
+ ErrorType ET = ErrorType::FloatCastOverflow;
+
+ if (looksLikeFloatCastOverflowDataV1(DataPtr)) {
+ auto Data = reinterpret_cast<FloatCastOverflowData *>(DataPtr);
+ CallerLoc.reset(getCallerLocation(Opts.pc));
+ Loc = CallerLoc;
+ FromType = &Data->FromType;
+ ToType = &Data->ToType;
+ } else {
+ auto Data = reinterpret_cast<FloatCastOverflowDataV2 *>(DataPtr);
+ SourceLocation SLoc = Data->Loc.acquire();
+ if (ignoreReport(SLoc, Opts, ET))
+ return;
+ Loc = SLoc;
+ FromType = &Data->FromType;
+ ToType = &Data->ToType;
+ }
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET,
+ "%0 is outside the range of representable values of type %2")
+ << Value(*FromType, From) << *FromType << *ToType;
+}
+
+void __ubsan::__ubsan_handle_float_cast_overflow(void *Data, ValueHandle From) {
+ GET_REPORT_OPTIONS(false);
+ handleFloatCastOverflow(Data, From, Opts);
+}
+void __ubsan::__ubsan_handle_float_cast_overflow_abort(void *Data,
+ ValueHandle From) {
+ GET_REPORT_OPTIONS(true);
+ handleFloatCastOverflow(Data, From, Opts);
+ Die();
+}
+
+static void handleLoadInvalidValue(InvalidValueData *Data, ValueHandle Val,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ // This check could be more precise if we used different handlers for
+ // -fsanitize=bool and -fsanitize=enum.
+ bool IsBool = (0 == internal_strcmp(Data->Type.getTypeName(), "'bool'")) ||
+ (0 == internal_strncmp(Data->Type.getTypeName(), "'BOOL'", 6));
+ ErrorType ET =
+ IsBool ? ErrorType::InvalidBoolLoad : ErrorType::InvalidEnumLoad;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET,
+ "load of value %0, which is not a valid value for type %1")
+ << Value(Data->Type, Val) << Data->Type;
+}
+
+void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data,
+ ValueHandle Val) {
+ GET_REPORT_OPTIONS(false);
+ handleLoadInvalidValue(Data, Val, Opts);
+}
+void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data,
+ ValueHandle Val) {
+ GET_REPORT_OPTIONS(true);
+ handleLoadInvalidValue(Data, Val, Opts);
+ Die();
+}
+
+static void handleImplicitConversion(ImplicitConversionData *Data,
+ ReportOptions Opts, ValueHandle Src,
+ ValueHandle Dst) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::GenericUB;
+
+ const TypeDescriptor &SrcTy = Data->FromType;
+ const TypeDescriptor &DstTy = Data->ToType;
+
+ bool SrcSigned = SrcTy.isSignedIntegerTy();
+ bool DstSigned = DstTy.isSignedIntegerTy();
+
+ switch (Data->Kind) {
+ case ICCK_IntegerTruncation: { // Legacy, no longer used.
+ // Let's figure out what it should be as per the new types, and upgrade.
+ // If both types are unsigned, then it's an unsigned truncation.
+ // Else, it is a signed truncation.
+ if (!SrcSigned && !DstSigned) {
+ ET = ErrorType::ImplicitUnsignedIntegerTruncation;
+ } else {
+ ET = ErrorType::ImplicitSignedIntegerTruncation;
+ }
+ break;
+ }
+ case ICCK_UnsignedIntegerTruncation:
+ ET = ErrorType::ImplicitUnsignedIntegerTruncation;
+ break;
+ case ICCK_SignedIntegerTruncation:
+ ET = ErrorType::ImplicitSignedIntegerTruncation;
+ break;
+ case ICCK_IntegerSignChange:
+ ET = ErrorType::ImplicitIntegerSignChange;
+ break;
+ case ICCK_SignedIntegerTruncationOrSignChange:
+ ET = ErrorType::ImplicitSignedIntegerTruncationOrSignChange;
+ break;
+ }
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ // FIXME: is it possible to dump the values as hex with fixed width?
+
+ Diag(Loc, DL_Error, ET,
+ "implicit conversion from type %0 of value %1 (%2-bit, %3signed) to "
+ "type %4 changed the value to %5 (%6-bit, %7signed)")
+ << SrcTy << Value(SrcTy, Src) << SrcTy.getIntegerBitWidth()
+ << (SrcSigned ? "" : "un") << DstTy << Value(DstTy, Dst)
+ << DstTy.getIntegerBitWidth() << (DstSigned ? "" : "un");
+}
+
+void __ubsan::__ubsan_handle_implicit_conversion(ImplicitConversionData *Data,
+ ValueHandle Src,
+ ValueHandle Dst) {
+ GET_REPORT_OPTIONS(false);
+ handleImplicitConversion(Data, Opts, Src, Dst);
+}
+void __ubsan::__ubsan_handle_implicit_conversion_abort(
+ ImplicitConversionData *Data, ValueHandle Src, ValueHandle Dst) {
+ GET_REPORT_OPTIONS(true);
+ handleImplicitConversion(Data, Opts, Src, Dst);
+ Die();
+}
+
+static void handleInvalidBuiltin(InvalidBuiltinData *Data, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::InvalidBuiltin;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET,
+ "passing zero to %0, which is not a valid argument")
+ << ((Data->Kind == BCK_CTZPassedZero) ? "ctz()" : "clz()");
+}
+
+void __ubsan::__ubsan_handle_invalid_builtin(InvalidBuiltinData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleInvalidBuiltin(Data, Opts);
+}
+void __ubsan::__ubsan_handle_invalid_builtin_abort(InvalidBuiltinData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleInvalidBuiltin(Data, Opts);
+ Die();
+}
+
+static void handleNonNullReturn(NonNullReturnData *Data, SourceLocation *LocPtr,
+ ReportOptions Opts, bool IsAttr) {
+ if (!LocPtr)
+ UNREACHABLE("source location pointer is null!");
+
+ SourceLocation Loc = LocPtr->acquire();
+ ErrorType ET = ErrorType::InvalidNullReturn;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET,
+ "null pointer returned from function declared to never return null");
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here")
+ << (IsAttr ? "returns_nonnull attribute"
+ : "_Nonnull return type annotation");
+}
+
+void __ubsan::__ubsan_handle_nonnull_return_v1(NonNullReturnData *Data,
+ SourceLocation *LocPtr) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullReturn(Data, LocPtr, Opts, true);
+}
+
+void __ubsan::__ubsan_handle_nonnull_return_v1_abort(NonNullReturnData *Data,
+ SourceLocation *LocPtr) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullReturn(Data, LocPtr, Opts, true);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_nullability_return_v1(NonNullReturnData *Data,
+ SourceLocation *LocPtr) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullReturn(Data, LocPtr, Opts, false);
+}
+
+void __ubsan::__ubsan_handle_nullability_return_v1_abort(
+ NonNullReturnData *Data, SourceLocation *LocPtr) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullReturn(Data, LocPtr, Opts, false);
+ Die();
+}
+
+static void handleNonNullArg(NonNullArgData *Data, ReportOptions Opts,
+ bool IsAttr) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::InvalidNullArgument;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET,
+ "null pointer passed as argument %0, which is declared to "
+ "never be null")
+ << Data->ArgIndex;
+ if (!Data->AttrLoc.isInvalid())
+ Diag(Data->AttrLoc, DL_Note, ET, "%0 specified here")
+ << (IsAttr ? "nonnull attribute" : "_Nonnull type annotation");
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullArg(Data, Opts, true);
+}
+
+void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullArg(Data, Opts, true);
+ Die();
+}
+
+void __ubsan::__ubsan_handle_nullability_arg(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(false);
+ handleNonNullArg(Data, Opts, false);
+}
+
+void __ubsan::__ubsan_handle_nullability_arg_abort(NonNullArgData *Data) {
+ GET_REPORT_OPTIONS(true);
+ handleNonNullArg(Data, Opts, false);
+ Die();
+}
+
+static void handlePointerOverflowImpl(PointerOverflowData *Data,
+ ValueHandle Base,
+ ValueHandle Result,
+ ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::PointerOverflow;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ if ((sptr(Base) >= 0) == (sptr(Result) >= 0)) {
+ if (Base > Result)
+ Diag(Loc, DL_Error, ET,
+ "addition of unsigned offset to %0 overflowed to %1")
+ << (void *)Base << (void *)Result;
+ else
+ Diag(Loc, DL_Error, ET,
+ "subtraction of unsigned offset from %0 overflowed to %1")
+ << (void *)Base << (void *)Result;
+ } else {
+ Diag(Loc, DL_Error, ET,
+ "pointer index expression with base %0 overflowed to %1")
+ << (void *)Base << (void *)Result;
+ }
+}
+
+void __ubsan::__ubsan_handle_pointer_overflow(PointerOverflowData *Data,
+ ValueHandle Base,
+ ValueHandle Result) {
+ GET_REPORT_OPTIONS(false);
+ handlePointerOverflowImpl(Data, Base, Result, Opts);
+}
+
+void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data,
+ ValueHandle Base,
+ ValueHandle Result) {
+ GET_REPORT_OPTIONS(true);
+ handlePointerOverflowImpl(Data, Base, Result, Opts);
+ Die();
+}
+
+static void handleCFIBadIcall(CFICheckFailData *Data, ValueHandle Function,
+ ReportOptions Opts) {
+ if (Data->CheckKind != CFITCK_ICall && Data->CheckKind != CFITCK_NVMFCall)
+ Die();
+
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::CFIBadType;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ const char *CheckKindStr = Data->CheckKind == CFITCK_NVMFCall
+ ? "non-virtual pointer to member function call"
+ : "indirect function call";
+ Diag(Loc, DL_Error, ET,
+ "control flow integrity check for type %0 failed during %1")
+ << Data->Type << CheckKindStr;
+
+ SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
+ const char *FName = FLoc.get()->info.function;
+ if (!FName)
+ FName = "(unknown)";
+ Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
+
+ // If the failure involved different DSOs for the check location and icall
+ // target, report the DSO names.
+ const char *DstModule = FLoc.get()->info.module;
+ if (!DstModule)
+ DstModule = "(unknown)";
+
+ const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(Opts.pc);
+ if (!SrcModule)
+ SrcModule = "(unknown)";
+
+ if (internal_strcmp(SrcModule, DstModule))
+ Diag(Loc, DL_Note, ET,
+ "check failed in %0, destination function located in %1")
+ << SrcModule << DstModule;
+}
+
+namespace __ubsan {
+
+#ifdef UBSAN_CAN_USE_CXXABI
+
+#ifdef _WIN32
+
+extern "C" void __ubsan_handle_cfi_bad_type_default(CFICheckFailData *Data,
+ ValueHandle Vtable,
+ bool ValidVtable,
+ ReportOptions Opts) {
+ Die();
+}
+
+WIN_WEAK_ALIAS(__ubsan_handle_cfi_bad_type, __ubsan_handle_cfi_bad_type_default)
+#else
+SANITIZER_WEAK_ATTRIBUTE
+#endif
+void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
+ bool ValidVtable, ReportOptions Opts);
+
+#else
+void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
+ bool ValidVtable, ReportOptions Opts) {
+ Die();
+}
+#endif
+
+} // namespace __ubsan
+
+void __ubsan::__ubsan_handle_cfi_check_fail(CFICheckFailData *Data,
+ ValueHandle Value,
+ uptr ValidVtable) {
+ GET_REPORT_OPTIONS(false);
+ if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall)
+ handleCFIBadIcall(Data, Value, Opts);
+ else
+ __ubsan_handle_cfi_bad_type(Data, Value, ValidVtable, Opts);
+}
+
+void __ubsan::__ubsan_handle_cfi_check_fail_abort(CFICheckFailData *Data,
+ ValueHandle Value,
+ uptr ValidVtable) {
+ GET_REPORT_OPTIONS(true);
+ if (Data->CheckKind == CFITCK_ICall || Data->CheckKind == CFITCK_NVMFCall)
+ handleCFIBadIcall(Data, Value, Opts);
+ else
+ __ubsan_handle_cfi_bad_type(Data, Value, ValidVtable, Opts);
+ Die();
+}
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_handlers.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
/// type.
RECOVERABLE(type_mismatch_v1, TypeMismatchData *Data, ValueHandle Pointer)
+struct AlignmentAssumptionData {
+ SourceLocation Loc;
+ SourceLocation AssumptionLoc;
+ const TypeDescriptor &Type;
+};
+
+/// \brief Handle a runtime alignment assumption check failure,
+/// caused by a misaligned pointer.
+RECOVERABLE(alignment_assumption, AlignmentAssumptionData *Data,
+ ValueHandle Pointer, ValueHandle Alignment, ValueHandle Offset)
+
struct OverflowData {
SourceLocation Loc;
const TypeDescriptor &Type;
ICCK_IntegerTruncation = 0, // Legacy, was only used by clang 7.
ICCK_UnsignedIntegerTruncation = 1,
ICCK_SignedIntegerTruncation = 2,
+ ICCK_IntegerSignChange = 3,
+ ICCK_SignedIntegerTruncationOrSignChange = 4,
};
struct ImplicitConversionData {
/// Handle a builtin called in an invalid way.
RECOVERABLE(invalid_builtin, InvalidBuiltinData *Data)
-struct FunctionTypeMismatchData {
- SourceLocation Loc;
- const TypeDescriptor &Type;
-};
-
-RECOVERABLE(function_type_mismatch,
- FunctionTypeMismatchData *Data,
- ValueHandle Val)
-
struct NonNullReturnData {
SourceLocation AttrLoc;
};
CFITCK_VMFCall,
};
-struct CFIBadIcallData {
- SourceLocation Loc;
- const TypeDescriptor &Type;
-};
-
struct CFICheckFailData {
CFITypeCheckKind CheckKind;
SourceLocation Loc;
const TypeDescriptor &Type;
};
-/// \brief Handle control flow integrity failure for indirect function calls.
-RECOVERABLE(cfi_bad_icall, CFIBadIcallData *Data, ValueHandle Function)
-
/// \brief Handle control flow integrity failures.
RECOVERABLE(cfi_check_fail, CFICheckFailData *Data, ValueHandle Function,
uptr VtableIsValid)
+++ /dev/null
-//===-- ubsan_handlers_cxx.cc ---------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Error logging entry points for the UBSan runtime, which are only used for C++
-// compilations. This file is permitted to use language features which require
-// linking against a C++ ABI library.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_handlers.h"
-#include "ubsan_handlers_cxx.h"
-#include "ubsan_diag.h"
-#include "ubsan_type_hash.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_suppressions.h"
-
-using namespace __sanitizer;
-using namespace __ubsan;
-
-namespace __ubsan {
- extern const char *TypeCheckKinds[];
-}
-
-// Returns true if UBSan has printed an error report.
-static bool HandleDynamicTypeCacheMiss(
- DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
- ReportOptions Opts) {
- if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
- // Just a cache miss. The type matches after all.
- return false;
-
- // Check if error report should be suppressed.
- DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer);
- if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
- return false;
-
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::DynamicTypeMismatch;
- if (ignoreReport(Loc, Opts, ET))
- return false;
-
- ScopedReport R(Opts, Loc, ET);
-
- Diag(Loc, DL_Error, ET,
- "%0 address %1 which does not point to an object of type %2")
- << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
-
- // If possible, say what type it actually points to.
- if (!DTI.isValid()) {
- if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) {
- Diag(Pointer, DL_Note, ET,
- "object has a possibly invalid vptr: abs(offset to top) too big")
- << TypeName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr");
- } else {
- Diag(Pointer, DL_Note, ET, "object has invalid vptr")
- << TypeName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
- }
- } else if (!DTI.getOffset())
- Diag(Pointer, DL_Note, ET, "object is of type %0")
- << TypeName(DTI.getMostDerivedTypeName())
- << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
- else
- // FIXME: Find the type at the specified offset, and include that
- // in the note.
- Diag(Pointer - DTI.getOffset(), DL_Note, ET,
- "object is base class subobject at offset %0 within object of type %1")
- << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
- << TypeName(DTI.getSubobjectTypeName())
- << Range(Pointer, Pointer + sizeof(uptr),
- "vptr for %2 base class of %1");
- return true;
-}
-
-void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
- DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- GET_REPORT_OPTIONS(false);
- HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
-}
-void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
- DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
- // Note: -fsanitize=vptr is always recoverable.
- GET_REPORT_OPTIONS(false);
- if (HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts))
- Die();
-}
-
-namespace __ubsan {
-void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
- bool ValidVtable, ReportOptions Opts) {
- SourceLocation Loc = Data->Loc.acquire();
- ErrorType ET = ErrorType::CFIBadType;
-
- if (ignoreReport(Loc, Opts, ET))
- return;
-
- ScopedReport R(Opts, Loc, ET);
- DynamicTypeInfo DTI = ValidVtable
- ? getDynamicTypeInfoFromVtable((void *)Vtable)
- : DynamicTypeInfo(0, 0, 0);
-
- const char *CheckKindStr;
- switch (Data->CheckKind) {
- case CFITCK_VCall:
- CheckKindStr = "virtual call";
- break;
- case CFITCK_NVCall:
- CheckKindStr = "non-virtual call";
- break;
- case CFITCK_DerivedCast:
- CheckKindStr = "base-to-derived cast";
- break;
- case CFITCK_UnrelatedCast:
- CheckKindStr = "cast to unrelated type";
- break;
- case CFITCK_VMFCall:
- CheckKindStr = "virtual pointer to member function call";
- break;
- case CFITCK_ICall:
- case CFITCK_NVMFCall:
- Die();
- }
-
- Diag(Loc, DL_Error, ET,
- "control flow integrity check for type %0 failed during "
- "%1 (vtable address %2)")
- << Data->Type << CheckKindStr << (void *)Vtable;
-
- // If possible, say what type it actually points to.
- if (!DTI.isValid())
- Diag(Vtable, DL_Note, ET, "invalid vtable");
- else
- Diag(Vtable, DL_Note, ET, "vtable is of type %0")
- << TypeName(DTI.getMostDerivedTypeName());
-
- // If the failure involved different DSOs for the check location and vtable,
- // report the DSO names.
- const char *DstModule = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable);
- if (!DstModule)
- DstModule = "(unknown)";
-
- const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(Opts.pc);
- if (!SrcModule)
- SrcModule = "(unknown)";
-
- if (internal_strcmp(SrcModule, DstModule))
- Diag(Loc, DL_Note, ET, "check failed in %0, vtable located in %1")
- << SrcModule << DstModule;
-}
-} // namespace __ubsan
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_handlers_cxx.cpp --------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Error logging entry points for the UBSan runtime, which are only used for C++
+// compilations. This file is permitted to use language features which require
+// linking against a C++ ABI library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_handlers.h"
+#include "ubsan_handlers_cxx.h"
+#include "ubsan_diag.h"
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_suppressions.h"
+
+using namespace __sanitizer;
+using namespace __ubsan;
+
+namespace __ubsan {
+ extern const char *TypeCheckKinds[];
+}
+
+// Returns true if UBSan has printed an error report.
+static bool HandleDynamicTypeCacheMiss(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash,
+ ReportOptions Opts) {
+ if (checkDynamicType((void*)Pointer, Data->TypeInfo, Hash))
+ // Just a cache miss. The type matches after all.
+ return false;
+
+ // Check if error report should be suppressed.
+ DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer);
+ if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName()))
+ return false;
+
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::DynamicTypeMismatch;
+ if (ignoreReport(Loc, Opts, ET))
+ return false;
+
+ ScopedReport R(Opts, Loc, ET);
+
+ Diag(Loc, DL_Error, ET,
+ "%0 address %1 which does not point to an object of type %2")
+ << TypeCheckKinds[Data->TypeCheckKind] << (void*)Pointer << Data->Type;
+
+ // If possible, say what type it actually points to.
+ if (!DTI.isValid()) {
+ if (DTI.getOffset() < -VptrMaxOffsetToTop || DTI.getOffset() > VptrMaxOffsetToTop) {
+ Diag(Pointer, DL_Note, ET,
+ "object has a possibly invalid vptr: abs(offset to top) too big")
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "possibly invalid vptr");
+ } else {
+ Diag(Pointer, DL_Note, ET, "object has invalid vptr")
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr");
+ }
+ } else if (!DTI.getOffset())
+ Diag(Pointer, DL_Note, ET, "object is of type %0")
+ << TypeName(DTI.getMostDerivedTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0");
+ else
+ // FIXME: Find the type at the specified offset, and include that
+ // in the note.
+ Diag(Pointer - DTI.getOffset(), DL_Note, ET,
+ "object is base class subobject at offset %0 within object of type %1")
+ << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName())
+ << TypeName(DTI.getSubobjectTypeName())
+ << Range(Pointer, Pointer + sizeof(uptr),
+ "vptr for %2 base class of %1");
+ return true;
+}
+
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+ GET_REPORT_OPTIONS(false);
+ HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts);
+}
+void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort(
+ DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash) {
+ // Note: -fsanitize=vptr is always recoverable.
+ GET_REPORT_OPTIONS(false);
+ if (HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts))
+ Die();
+}
+
+namespace __ubsan {
+void __ubsan_handle_cfi_bad_type(CFICheckFailData *Data, ValueHandle Vtable,
+ bool ValidVtable, ReportOptions Opts) {
+ SourceLocation Loc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::CFIBadType;
+
+ if (ignoreReport(Loc, Opts, ET))
+ return;
+
+ ScopedReport R(Opts, Loc, ET);
+ DynamicTypeInfo DTI = ValidVtable
+ ? getDynamicTypeInfoFromVtable((void *)Vtable)
+ : DynamicTypeInfo(0, 0, 0);
+
+ const char *CheckKindStr;
+ switch (Data->CheckKind) {
+ case CFITCK_VCall:
+ CheckKindStr = "virtual call";
+ break;
+ case CFITCK_NVCall:
+ CheckKindStr = "non-virtual call";
+ break;
+ case CFITCK_DerivedCast:
+ CheckKindStr = "base-to-derived cast";
+ break;
+ case CFITCK_UnrelatedCast:
+ CheckKindStr = "cast to unrelated type";
+ break;
+ case CFITCK_VMFCall:
+ CheckKindStr = "virtual pointer to member function call";
+ break;
+ case CFITCK_ICall:
+ case CFITCK_NVMFCall:
+ Die();
+ }
+
+ Diag(Loc, DL_Error, ET,
+ "control flow integrity check for type %0 failed during "
+ "%1 (vtable address %2)")
+ << Data->Type << CheckKindStr << (void *)Vtable;
+
+ // If possible, say what type it actually points to.
+ if (!DTI.isValid())
+ Diag(Vtable, DL_Note, ET, "invalid vtable");
+ else
+ Diag(Vtable, DL_Note, ET, "vtable is of type %0")
+ << TypeName(DTI.getMostDerivedTypeName());
+
+ // If the failure involved different DSOs for the check location and vtable,
+ // report the DSO names.
+ const char *DstModule = Symbolizer::GetOrInit()->GetModuleNameForPc(Vtable);
+ if (!DstModule)
+ DstModule = "(unknown)";
+
+ const char *SrcModule = Symbolizer::GetOrInit()->GetModuleNameForPc(Opts.pc);
+ if (!SrcModule)
+ SrcModule = "(unknown)";
+
+ if (internal_strcmp(SrcModule, DstModule))
+ Diag(Loc, DL_Note, ET, "check failed in %0, vtable located in %1")
+ << SrcModule << DstModule;
+}
+
+static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
+ ValueHandle Function,
+ ValueHandle calleeRTTI,
+ ValueHandle fnRTTI, ReportOptions Opts) {
+ if (checkTypeInfoEquality(reinterpret_cast<void *>(calleeRTTI),
+ reinterpret_cast<void *>(fnRTTI)))
+ return false;
+
+ SourceLocation CallLoc = Data->Loc.acquire();
+ ErrorType ET = ErrorType::FunctionTypeMismatch;
+
+ if (ignoreReport(CallLoc, Opts, ET))
+ return true;
+
+ ScopedReport R(Opts, CallLoc, ET);
+
+ SymbolizedStackHolder FLoc(getSymbolizedLocation(Function));
+ const char *FName = FLoc.get()->info.function;
+ if (!FName)
+ FName = "(unknown)";
+
+ Diag(CallLoc, DL_Error, ET,
+ "call to function %0 through pointer to incorrect function type %1")
+ << FName << Data->Type;
+ Diag(FLoc, DL_Note, ET, "%0 defined here") << FName;
+ return true;
+}
+
+void __ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data,
+ ValueHandle Function,
+ ValueHandle calleeRTTI,
+ ValueHandle fnRTTI) {
+ GET_REPORT_OPTIONS(false);
+ handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts);
+}
+
+void __ubsan_handle_function_type_mismatch_v1_abort(
+ FunctionTypeMismatchData *Data, ValueHandle Function,
+ ValueHandle calleeRTTI, ValueHandle fnRTTI) {
+ GET_REPORT_OPTIONS(true);
+ if (handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts))
+ Die();
+}
+} // namespace __ubsan
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_handlers_cxx.h ------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __ubsan_handle_dynamic_type_cache_miss_abort(
DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash);
+
+struct FunctionTypeMismatchData {
+ SourceLocation Loc;
+ const TypeDescriptor &Type;
+};
+
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data,
+ ValueHandle Val,
+ ValueHandle calleeRTTI,
+ ValueHandle fnRTTI);
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
+__ubsan_handle_function_type_mismatch_v1_abort(FunctionTypeMismatchData *Data,
+ ValueHandle Val,
+ ValueHandle calleeRTTI,
+ ValueHandle fnRTTI);
}
#endif // UBSAN_HANDLERS_H
+++ /dev/null
-//===-- ubsan_init.cc -----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Initialization of UBSan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_diag.h"
-#include "ubsan_init.h"
-#include "ubsan_flags.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-#include "sanitizer_common/sanitizer_mutex.h"
-#include "sanitizer_common/sanitizer_symbolizer.h"
-
-using namespace __ubsan;
-
-const char *__ubsan::GetSanititizerToolName() {
- return "UndefinedBehaviorSanitizer";
-}
-
-static bool ubsan_initialized;
-static StaticSpinMutex ubsan_init_mu;
-
-static void CommonInit() {
- InitializeSuppressions();
-}
-
-static void CommonStandaloneInit() {
- SanitizerToolName = GetSanititizerToolName();
- CacheBinaryName();
- InitializeFlags();
- __sanitizer_set_report_path(common_flags()->log_path);
- AndroidLogInit();
- InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
- CommonInit();
-}
-
-void __ubsan::InitAsStandalone() {
- SpinMutexLock l(&ubsan_init_mu);
- if (!ubsan_initialized) {
- CommonStandaloneInit();
- ubsan_initialized = true;
- }
-}
-
-void __ubsan::InitAsStandaloneIfNecessary() { return InitAsStandalone(); }
-
-void __ubsan::InitAsPlugin() {
- SpinMutexLock l(&ubsan_init_mu);
- if (!ubsan_initialized) {
- CommonInit();
- ubsan_initialized = true;
- }
-}
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_init.cpp ----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_diag.h"
+#include "ubsan_init.h"
+#include "ubsan_flags.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+#include "sanitizer_common/sanitizer_mutex.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
+
+using namespace __ubsan;
+
+const char *__ubsan::GetSanititizerToolName() {
+ return "UndefinedBehaviorSanitizer";
+}
+
+static bool ubsan_initialized;
+static StaticSpinMutex ubsan_init_mu;
+
+static void CommonInit() {
+ InitializeSuppressions();
+}
+
+static void CommonStandaloneInit() {
+ SanitizerToolName = GetSanititizerToolName();
+ CacheBinaryName();
+ InitializeFlags();
+ __sanitizer_set_report_path(common_flags()->log_path);
+ AndroidLogInit();
+ InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
+ CommonInit();
+}
+
+void __ubsan::InitAsStandalone() {
+ SpinMutexLock l(&ubsan_init_mu);
+ if (!ubsan_initialized) {
+ CommonStandaloneInit();
+ ubsan_initialized = true;
+ }
+}
+
+void __ubsan::InitAsStandaloneIfNecessary() { return InitAsStandalone(); }
+
+void __ubsan::InitAsPlugin() {
+ SpinMutexLock l(&ubsan_init_mu);
+ if (!ubsan_initialized) {
+ CommonInit();
+ ubsan_initialized = true;
+ }
+}
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_init.h --------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- ubsan_init_standalone.cc ------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Initialization of standalone UBSan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if !CAN_SANITIZE_UB
-# error "UBSan is not supported on this platform!"
-#endif
-
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "ubsan_init.h"
-#include "ubsan_signals_standalone.h"
-
-namespace __ubsan {
-
-class UbsanStandaloneInitializer {
- public:
- UbsanStandaloneInitializer() {
- InitAsStandalone();
- InitializeDeadlySignals();
- }
-};
-static UbsanStandaloneInitializer ubsan_standalone_initializer;
-
-} // namespace __ubsan
--- /dev/null
+//===-- ubsan_init_standalone.cpp -----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of standalone UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if !CAN_SANITIZE_UB
+# error "UBSan is not supported on this platform!"
+#endif
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "ubsan_init.h"
+#include "ubsan_signals_standalone.h"
+
+namespace __ubsan {
+
+class UbsanStandaloneInitializer {
+ public:
+ UbsanStandaloneInitializer() {
+ InitAsStandalone();
+ InitializeDeadlySignals();
+ }
+};
+static UbsanStandaloneInitializer ubsan_standalone_initializer;
+
+} // namespace __ubsan
+++ /dev/null
-//===-- ubsan_init_standalone_preinit.cc ---------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Initialization of standalone UBSan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if !CAN_SANITIZE_UB
-#error "UBSan is not supported on this platform!"
-#endif
-
-#include "sanitizer_common/sanitizer_internal_defs.h"
-#include "ubsan_init.h"
-#include "ubsan_signals_standalone.h"
-
-#if SANITIZER_CAN_USE_PREINIT_ARRAY
-
-namespace __ubsan {
-
-static void PreInitAsStandalone() {
- InitAsStandalone();
- InitializeDeadlySignals();
-}
-
-} // namespace __ubsan
-
-__attribute__((section(".preinit_array"), used)) void (*__local_ubsan_preinit)(
- void) = __ubsan::PreInitAsStandalone;
-#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
--- /dev/null
+//===-- ubsan_init_standalone_preinit.cpp --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Initialization of standalone UBSan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if !CAN_SANITIZE_UB
+#error "UBSan is not supported on this platform!"
+#endif
+
+#include "sanitizer_common/sanitizer_internal_defs.h"
+#include "ubsan_init.h"
+#include "ubsan_signals_standalone.h"
+
+#if SANITIZER_CAN_USE_PREINIT_ARRAY
+
+namespace __ubsan {
+
+static void PreInitAsStandalone() {
+ InitAsStandalone();
+ InitializeDeadlySignals();
+}
+
+} // namespace __ubsan
+
+__attribute__((section(".preinit_array"), used)) void (*__local_ubsan_preinit)(
+ void) = __ubsan::PreInitAsStandalone;
+#endif // SANITIZER_CAN_USE_PREINIT_ARRAY
//===-- ubsan_interface.inc -----------------------------------------------===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Ubsan interface list.
//===----------------------------------------------------------------------===//
INTERFACE_FUNCTION(__ubsan_handle_add_overflow)
INTERFACE_FUNCTION(__ubsan_handle_add_overflow_abort)
+INTERFACE_FUNCTION(__ubsan_handle_alignment_assumption)
+INTERFACE_FUNCTION(__ubsan_handle_alignment_assumption_abort)
INTERFACE_FUNCTION(__ubsan_handle_builtin_unreachable)
INTERFACE_FUNCTION(__ubsan_handle_cfi_bad_type)
INTERFACE_FUNCTION(__ubsan_handle_cfi_check_fail)
INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss_abort)
INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow)
INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow_abort)
-INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch)
-INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_abort)
+INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1)
+INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1_abort)
INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion)
INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion_abort)
INTERFACE_FUNCTION(__ubsan_handle_invalid_builtin)
+++ /dev/null
-//===-- ubsan_monitor.cc ----------------------------------------*- C++ -*-===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Hooks which allow a monitor process to inspect UBSan's diagnostics.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_monitor.h"
-
-using namespace __ubsan;
-
-UndefinedBehaviorReport::UndefinedBehaviorReport(const char *IssueKind,
- Location &Loc,
- InternalScopedString &Msg)
- : IssueKind(IssueKind), Loc(Loc), Buffer(Msg.length() + 1) {
- // We have the common sanitizer reporting lock, so it's safe to register a
- // new UB report.
- RegisterUndefinedBehaviorReport(this);
-
- // Make a copy of the diagnostic.
- Buffer.append("%s", Msg.data());
-
- // Let the monitor know that a report is available.
- __ubsan_on_report();
-}
-
-static UndefinedBehaviorReport *CurrentUBR;
-
-void __ubsan::RegisterUndefinedBehaviorReport(UndefinedBehaviorReport *UBR) {
- CurrentUBR = UBR;
-}
-
-SANITIZER_WEAK_DEFAULT_IMPL
-void __ubsan::__ubsan_on_report(void) {}
-
-void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind,
- const char **OutMessage,
- const char **OutFilename,
- unsigned *OutLine,
- unsigned *OutCol,
- char **OutMemoryAddr) {
- if (!OutIssueKind || !OutMessage || !OutFilename || !OutLine || !OutCol ||
- !OutMemoryAddr)
- UNREACHABLE("Invalid arguments passed to __ubsan_get_current_report_data");
-
- InternalScopedString &Buf = CurrentUBR->Buffer;
-
- // Ensure that the first character of the diagnostic text can't start with a
- // lowercase letter.
- char FirstChar = Buf.data()[0];
- if (FirstChar >= 'a' && FirstChar <= 'z')
- Buf.data()[0] = FirstChar - 'a' + 'A';
-
- *OutIssueKind = CurrentUBR->IssueKind;
- *OutMessage = Buf.data();
- if (!CurrentUBR->Loc.isSourceLocation()) {
- *OutFilename = "<unknown>";
- *OutLine = *OutCol = 0;
- } else {
- SourceLocation SL = CurrentUBR->Loc.getSourceLocation();
- *OutFilename = SL.getFilename();
- *OutLine = SL.getLine();
- *OutCol = SL.getColumn();
- }
-
- if (CurrentUBR->Loc.isMemoryLocation())
- *OutMemoryAddr = (char *)CurrentUBR->Loc.getMemoryLocation();
- else
- *OutMemoryAddr = nullptr;
-}
--- /dev/null
+//===-- ubsan_monitor.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Hooks which allow a monitor process to inspect UBSan's diagnostics.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_monitor.h"
+
+using namespace __ubsan;
+
+UndefinedBehaviorReport::UndefinedBehaviorReport(const char *IssueKind,
+ Location &Loc,
+ InternalScopedString &Msg)
+ : IssueKind(IssueKind), Loc(Loc), Buffer(Msg.length() + 1) {
+ // We have the common sanitizer reporting lock, so it's safe to register a
+ // new UB report.
+ RegisterUndefinedBehaviorReport(this);
+
+ // Make a copy of the diagnostic.
+ Buffer.append("%s", Msg.data());
+
+ // Let the monitor know that a report is available.
+ __ubsan_on_report();
+}
+
+static UndefinedBehaviorReport *CurrentUBR;
+
+void __ubsan::RegisterUndefinedBehaviorReport(UndefinedBehaviorReport *UBR) {
+ CurrentUBR = UBR;
+}
+
+SANITIZER_WEAK_DEFAULT_IMPL
+void __ubsan::__ubsan_on_report(void) {}
+
+void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind,
+ const char **OutMessage,
+ const char **OutFilename,
+ unsigned *OutLine,
+ unsigned *OutCol,
+ char **OutMemoryAddr) {
+ if (!OutIssueKind || !OutMessage || !OutFilename || !OutLine || !OutCol ||
+ !OutMemoryAddr)
+ UNREACHABLE("Invalid arguments passed to __ubsan_get_current_report_data");
+
+ InternalScopedString &Buf = CurrentUBR->Buffer;
+
+ // Ensure that the first character of the diagnostic text can't start with a
+ // lowercase letter.
+ char FirstChar = Buf.data()[0];
+ if (FirstChar >= 'a' && FirstChar <= 'z')
+ Buf.data()[0] = FirstChar - 'a' + 'A';
+
+ *OutIssueKind = CurrentUBR->IssueKind;
+ *OutMessage = Buf.data();
+ if (!CurrentUBR->Loc.isSourceLocation()) {
+ *OutFilename = "<unknown>";
+ *OutLine = *OutCol = 0;
+ } else {
+ SourceLocation SL = CurrentUBR->Loc.getSourceLocation();
+ *OutFilename = SL.getFilename();
+ *OutLine = SL.getLine();
+ *OutCol = SL.getColumn();
+ }
+
+ if (CurrentUBR->Loc.isMemoryLocation())
+ *OutMemoryAddr = (char *)CurrentUBR->Loc.getMemoryLocation();
+ else
+ *OutMemoryAddr = nullptr;
+}
//===-- ubsan_monitor.h -----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
//===-- ubsan_platform.h ----------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
#ifndef UBSAN_PLATFORM_H
#define UBSAN_PLATFORM_H
-#ifndef CAN_SANITIZE_UB
// Other platforms should be easy to add, and probably work as-is.
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \
defined(__NetBSD__) || defined(__OpenBSD__) || \
#else
# define CAN_SANITIZE_UB 0
#endif
-#endif //CAN_SANITIZE_UB
#endif
+++ /dev/null
-//=-- ubsan_signals_standalone.cc
-//------------------------------------------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Installs signal handlers and related interceptors for UBSan standalone.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#include "sanitizer_common/sanitizer_platform.h"
-#if CAN_SANITIZE_UB
-#include "interception/interception.h"
-#include "sanitizer_common/sanitizer_stacktrace.h"
-#include "ubsan_diag.h"
-#include "ubsan_init.h"
-
-// Interception of signals breaks too many things on Android.
-// * It requires that ubsan is the first dependency of the main executable for
-// the interceptors to work correctly. This complicates deployment, as it
-// prevents us from enabling ubsan on random platform modules independently.
-// * For this to work with ART VM, ubsan signal handler has to be set after the
-// debuggerd handler, but before the ART handler.
-// * Interceptors don't work at all when ubsan runtime is loaded late, ex. when
-// it is part of an APK that does not use wrap.sh method.
-#if SANITIZER_FUCHSIA || SANITIZER_ANDROID
-
-namespace __ubsan {
-void InitializeDeadlySignals() {}
-}
-
-#else
-
-#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
-#include "sanitizer_common/sanitizer_signal_interceptors.inc"
-
-namespace __ubsan {
-
-static void OnStackUnwind(const SignalContext &sig, const void *,
- BufferedStackTrace *stack) {
- GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context,
- common_flags()->fast_unwind_on_fatal);
-}
-
-static void UBsanOnDeadlySignal(int signo, void *siginfo, void *context) {
- HandleDeadlySignal(siginfo, context, GetTid(), &OnStackUnwind, nullptr);
-}
-
-static bool is_initialized = false;
-
-void InitializeDeadlySignals() {
- if (is_initialized)
- return;
- is_initialized = true;
- InitializeSignalInterceptors();
- InstallDeadlySignalHandlers(&UBsanOnDeadlySignal);
-}
-
-} // namespace __ubsan
-
-#endif
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//=-- ubsan_signals_standalone.cpp ----------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Installs signal handlers and related interceptors for UBSan standalone.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#include "sanitizer_common/sanitizer_platform.h"
+#if CAN_SANITIZE_UB
+#include "interception/interception.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
+#include "ubsan_diag.h"
+#include "ubsan_init.h"
+
+// Interception of signals breaks too many things on Android.
+// * It requires that ubsan is the first dependency of the main executable for
+// the interceptors to work correctly. This complicates deployment, as it
+// prevents us from enabling ubsan on random platform modules independently.
+// * For this to work with ART VM, ubsan signal handler has to be set after the
+// debuggerd handler, but before the ART handler.
+// * Interceptors don't work at all when ubsan runtime is loaded late, ex. when
+// it is part of an APK that does not use wrap.sh method.
+#if SANITIZER_FUCHSIA || SANITIZER_ANDROID
+
+namespace __ubsan {
+void InitializeDeadlySignals() {}
+}
+
+#else
+
+#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name)
+#include "sanitizer_common/sanitizer_signal_interceptors.inc"
+
+// TODO(yln): Temporary workaround. Will be removed.
+void ubsan_GetStackTrace(BufferedStackTrace *stack, uptr max_depth,
+ uptr pc, uptr bp, void *context, bool fast);
+
+namespace __ubsan {
+
+static void OnStackUnwind(const SignalContext &sig, const void *,
+ BufferedStackTrace *stack) {
+ ubsan_GetStackTrace(stack, kStackTraceMax, sig.pc, sig.bp, sig.context,
+ common_flags()->fast_unwind_on_fatal);
+}
+
+static void UBsanOnDeadlySignal(int signo, void *siginfo, void *context) {
+ HandleDeadlySignal(siginfo, context, GetTid(), &OnStackUnwind, nullptr);
+}
+
+static bool is_initialized = false;
+
+void InitializeDeadlySignals() {
+ if (is_initialized)
+ return;
+ is_initialized = true;
+ InitializeSignalInterceptors();
+ InstallDeadlySignalHandlers(&UBsanOnDeadlySignal);
+}
+
+} // namespace __ubsan
+
+#endif
+
+#endif // CAN_SANITIZE_UB
//=-- ubsan_signals_standalone.h
//------------------------------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- ubsan_type_hash.cc ------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Implementation of a hash table for fast checking of inheritance
-// relationships. This file is only linked into C++ compilations, and is
-// permitted to use language features which require a C++ ABI library.
-//
-// Most of the implementation lives in an ABI-specific source file
-// (ubsan_type_hash_{itanium,win}.cc).
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_type_hash.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-
-/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
-__ubsan::HashValue
-__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize];
-
-__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) {
- void *VtablePtr = *reinterpret_cast<void **>(Object);
- return getDynamicTypeInfoFromVtable(VtablePtr);
-}
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_type_hash.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of a hash table for fast checking of inheritance
+// relationships. This file is only linked into C++ compilations, and is
+// permitted to use language features which require a C++ ABI library.
+//
+// Most of the implementation lives in an ABI-specific source file
+// (ubsan_type_hash_{itanium,win}.cpp).
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+/// A cache of recently-checked hashes. Mini hash table with "random" evictions.
+__ubsan::HashValue
+__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize];
+
+__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) {
+ void *VtablePtr = *reinterpret_cast<void **>(Object);
+ return getDynamicTypeInfoFromVtable(VtablePtr);
+}
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_type_hash.h ---------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
HashValue __ubsan_vptr_type_cache[VptrTypeCacheSize];
+/// \brief Do whatever is required by the ABI to check for std::type_info
+/// equivalence beyond simple pointer comparison.
+bool checkTypeInfoEquality(const void *TypeInfo1, const void *TypeInfo2);
+
} // namespace __ubsan
#endif // UBSAN_TYPE_HASH_H
+++ /dev/null
-//===-- ubsan_type_hash_itanium.cc ----------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Implementation of type hashing/lookup for Itanium C++ ABI.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS
-#include "ubsan_type_hash.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-
-// The following are intended to be binary compatible with the definitions
-// given in the Itanium ABI. We make no attempt to be ODR-compatible with
-// those definitions, since existing ABI implementations aren't.
-
-namespace std {
- class type_info {
- public:
- virtual ~type_info();
-
- const char *__type_name;
- };
-}
-
-namespace __cxxabiv1 {
-
-/// Type info for classes with no bases, and base class for type info for
-/// classes with bases.
-class __class_type_info : public std::type_info {
- ~__class_type_info() override;
-};
-
-/// Type info for classes with simple single public inheritance.
-class __si_class_type_info : public __class_type_info {
-public:
- ~__si_class_type_info() override;
-
- const __class_type_info *__base_type;
-};
-
-class __base_class_type_info {
-public:
- const __class_type_info *__base_type;
- long __offset_flags;
-
- enum __offset_flags_masks {
- __virtual_mask = 0x1,
- __public_mask = 0x2,
- __offset_shift = 8
- };
-};
-
-/// Type info for classes with multiple, virtual, or non-public inheritance.
-class __vmi_class_type_info : public __class_type_info {
-public:
- ~__vmi_class_type_info() override;
-
- unsigned int flags;
- unsigned int base_count;
- __base_class_type_info base_info[1];
-};
-
-}
-
-namespace abi = __cxxabiv1;
-
-using namespace __sanitizer;
-
-// We implement a simple two-level cache for type-checking results. For each
-// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
-// unique; if it collides, we will get false negatives, but:
-// * such a collision would have to occur on the *first* bad access,
-// * the probability of such a collision is low (and for a 64-bit target, is
-// negligible), and
-// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
-// give better coverage.
-//
-// The first caching layer is a small hash table with no chaining; buckets are
-// reused as needed. The second caching layer is a large hash table with open
-// chaining. We can freely evict from either layer since this is just a cache.
-//
-// FIXME: Make these hash table accesses thread-safe. The races here are benign:
-// assuming the unsequenced loads and stores don't misbehave too badly,
-// the worst case is false negatives or poor cache behavior, not false
-// positives or crashes.
-
-/// Find a bucket to store the given hash value in.
-static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
- static const unsigned HashTableSize = 65537;
- static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
-
- unsigned First = (V & 65535) ^ 1;
- unsigned Probe = First;
- for (int Tries = 5; Tries; --Tries) {
- if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
- return &__ubsan_vptr_hash_set[Probe];
- Probe += ((V >> 16) & 65535) + 1;
- if (Probe >= HashTableSize)
- Probe -= HashTableSize;
- }
- // FIXME: Pick a random entry from the probe sequence to evict rather than
- // just taking the first.
- return &__ubsan_vptr_hash_set[First];
-}
-
-/// \brief Determine whether \p Derived has a \p Base base class subobject at
-/// offset \p Offset.
-static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
- const abi::__class_type_info *Base,
- sptr Offset) {
- if (Derived->__type_name == Base->__type_name ||
- (SANITIZER_NON_UNIQUE_TYPEINFO &&
- Derived->__type_name[0] != '*' &&
- !internal_strcmp(Derived->__type_name, Base->__type_name)))
- return Offset == 0;
-
- if (const abi::__si_class_type_info *SI =
- dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
-
- const abi::__vmi_class_type_info *VTI =
- dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
- if (!VTI)
- // No base class subobjects.
- return false;
-
- // Look for a base class which is derived from \p Base at the right offset.
- for (unsigned int base = 0; base != VTI->base_count; ++base) {
- // FIXME: Curtail the recursion if this base can't possibly contain the
- // given offset.
- sptr OffsetHere = VTI->base_info[base].__offset_flags >>
- abi::__base_class_type_info::__offset_shift;
- if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // For now, just punt on virtual bases and say 'yes'.
- // FIXME: OffsetHere is the offset in the vtable of the virtual base
- // offset. Read the vbase offset out of the vtable and use it.
- return true;
- if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
- Base, Offset - OffsetHere))
- return true;
- }
-
- return false;
-}
-
-/// \brief Find the derived-most dynamic base class of \p Derived at offset
-/// \p Offset.
-static const abi::__class_type_info *findBaseAtOffset(
- const abi::__class_type_info *Derived, sptr Offset) {
- if (!Offset)
- return Derived;
-
- if (const abi::__si_class_type_info *SI =
- dynamic_cast<const abi::__si_class_type_info*>(Derived))
- return findBaseAtOffset(SI->__base_type, Offset);
-
- const abi::__vmi_class_type_info *VTI =
- dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
- if (!VTI)
- // No base class subobjects.
- return nullptr;
-
- for (unsigned int base = 0; base != VTI->base_count; ++base) {
- sptr OffsetHere = VTI->base_info[base].__offset_flags >>
- abi::__base_class_type_info::__offset_shift;
- if (VTI->base_info[base].__offset_flags &
- abi::__base_class_type_info::__virtual_mask)
- // FIXME: Can't handle virtual bases yet.
- continue;
- if (const abi::__class_type_info *Base =
- findBaseAtOffset(VTI->base_info[base].__base_type,
- Offset - OffsetHere))
- return Base;
- }
-
- return nullptr;
-}
-
-namespace {
-
-struct VtablePrefix {
- /// The offset from the vptr to the start of the most-derived object.
- /// This will only be greater than zero in some virtual base class vtables
- /// used during object con-/destruction, and will usually be exactly zero.
- sptr Offset;
- /// The type_info object describing the most-derived class type.
- std::type_info *TypeInfo;
-};
-VtablePrefix *getVtablePrefix(void *Vtable) {
- VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
- VtablePrefix *Prefix = Vptr - 1;
- if (!IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix)))
- return nullptr;
- if (!Prefix->TypeInfo)
- // This can't possibly be a valid vtable.
- return nullptr;
- return Prefix;
-}
-
-}
-
-bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
- // A crash anywhere within this function probably means the vptr is corrupted.
- // FIXME: Perform these checks more cautiously.
-
- // Check whether this is something we've evicted from the cache.
- HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
- if (*Bucket == Hash) {
- __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
- return true;
- }
-
- void *VtablePtr = *reinterpret_cast<void **>(Object);
- VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
- if (!Vtable)
- return false;
- if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop) {
- // Too large or too small offset are signs of Vtable corruption.
- return false;
- }
-
- // Check that this is actually a type_info object for a class type.
- abi::__class_type_info *Derived =
- dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
- if (!Derived)
- return false;
-
- abi::__class_type_info *Base = (abi::__class_type_info*)Type;
- if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
- return false;
-
- // Success. Cache this result.
- __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
- *Bucket = Hash;
- return true;
-}
-
-__ubsan::DynamicTypeInfo
-__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
- VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
- if (!Vtable)
- return DynamicTypeInfo(nullptr, 0, nullptr);
- if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop)
- return DynamicTypeInfo(nullptr, Vtable->Offset, nullptr);
- const abi::__class_type_info *ObjectType = findBaseAtOffset(
- static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
- -Vtable->Offset);
- return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
- ObjectType ? ObjectType->__type_name : "<unknown>");
-}
-
-#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS
--- /dev/null
+//===-- ubsan_type_hash_itanium.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of type hashing/lookup for Itanium C++ ABI.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+// The following are intended to be binary compatible with the definitions
+// given in the Itanium ABI. We make no attempt to be ODR-compatible with
+// those definitions, since existing ABI implementations aren't.
+
+namespace std {
+ class type_info {
+ public:
+ virtual ~type_info();
+
+ const char *__type_name;
+ };
+}
+
+namespace __cxxabiv1 {
+
+/// Type info for classes with no bases, and base class for type info for
+/// classes with bases.
+class __class_type_info : public std::type_info {
+ ~__class_type_info() override;
+};
+
+/// Type info for classes with simple single public inheritance.
+class __si_class_type_info : public __class_type_info {
+public:
+ ~__si_class_type_info() override;
+
+ const __class_type_info *__base_type;
+};
+
+class __base_class_type_info {
+public:
+ const __class_type_info *__base_type;
+ long __offset_flags;
+
+ enum __offset_flags_masks {
+ __virtual_mask = 0x1,
+ __public_mask = 0x2,
+ __offset_shift = 8
+ };
+};
+
+/// Type info for classes with multiple, virtual, or non-public inheritance.
+class __vmi_class_type_info : public __class_type_info {
+public:
+ ~__vmi_class_type_info() override;
+
+ unsigned int flags;
+ unsigned int base_count;
+ __base_class_type_info base_info[1];
+};
+
+}
+
+namespace abi = __cxxabiv1;
+
+using namespace __sanitizer;
+
+// We implement a simple two-level cache for type-checking results. For each
+// (vptr,type) pair, a hash is computed. This hash is assumed to be globally
+// unique; if it collides, we will get false negatives, but:
+// * such a collision would have to occur on the *first* bad access,
+// * the probability of such a collision is low (and for a 64-bit target, is
+// negligible), and
+// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs
+// give better coverage.
+//
+// The first caching layer is a small hash table with no chaining; buckets are
+// reused as needed. The second caching layer is a large hash table with open
+// chaining. We can freely evict from either layer since this is just a cache.
+//
+// FIXME: Make these hash table accesses thread-safe. The races here are benign:
+// assuming the unsequenced loads and stores don't misbehave too badly,
+// the worst case is false negatives or poor cache behavior, not false
+// positives or crashes.
+
+/// Find a bucket to store the given hash value in.
+static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) {
+ static const unsigned HashTableSize = 65537;
+ static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize];
+
+ unsigned First = (V & 65535) ^ 1;
+ unsigned Probe = First;
+ for (int Tries = 5; Tries; --Tries) {
+ if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V)
+ return &__ubsan_vptr_hash_set[Probe];
+ Probe += ((V >> 16) & 65535) + 1;
+ if (Probe >= HashTableSize)
+ Probe -= HashTableSize;
+ }
+ // FIXME: Pick a random entry from the probe sequence to evict rather than
+ // just taking the first.
+ return &__ubsan_vptr_hash_set[First];
+}
+
+/// \brief Determine whether \p Derived has a \p Base base class subobject at
+/// offset \p Offset.
+static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
+ const abi::__class_type_info *Base,
+ sptr Offset) {
+ if (Derived->__type_name == Base->__type_name ||
+ __ubsan::checkTypeInfoEquality(Derived, Base))
+ return Offset == 0;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return false;
+
+ // Look for a base class which is derived from \p Base at the right offset.
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ // FIXME: Curtail the recursion if this base can't possibly contain the
+ // given offset.
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // For now, just punt on virtual bases and say 'yes'.
+ // FIXME: OffsetHere is the offset in the vtable of the virtual base
+ // offset. Read the vbase offset out of the vtable and use it.
+ return true;
+ if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
+ Base, Offset - OffsetHere))
+ return true;
+ }
+
+ return false;
+}
+
+/// \brief Find the derived-most dynamic base class of \p Derived at offset
+/// \p Offset.
+static const abi::__class_type_info *findBaseAtOffset(
+ const abi::__class_type_info *Derived, sptr Offset) {
+ if (!Offset)
+ return Derived;
+
+ if (const abi::__si_class_type_info *SI =
+ dynamic_cast<const abi::__si_class_type_info*>(Derived))
+ return findBaseAtOffset(SI->__base_type, Offset);
+
+ const abi::__vmi_class_type_info *VTI =
+ dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
+ if (!VTI)
+ // No base class subobjects.
+ return nullptr;
+
+ for (unsigned int base = 0; base != VTI->base_count; ++base) {
+ sptr OffsetHere = VTI->base_info[base].__offset_flags >>
+ abi::__base_class_type_info::__offset_shift;
+ if (VTI->base_info[base].__offset_flags &
+ abi::__base_class_type_info::__virtual_mask)
+ // FIXME: Can't handle virtual bases yet.
+ continue;
+ if (const abi::__class_type_info *Base =
+ findBaseAtOffset(VTI->base_info[base].__base_type,
+ Offset - OffsetHere))
+ return Base;
+ }
+
+ return nullptr;
+}
+
+namespace {
+
+struct VtablePrefix {
+ /// The offset from the vptr to the start of the most-derived object.
+ /// This will only be greater than zero in some virtual base class vtables
+ /// used during object con-/destruction, and will usually be exactly zero.
+ sptr Offset;
+ /// The type_info object describing the most-derived class type.
+ std::type_info *TypeInfo;
+};
+VtablePrefix *getVtablePrefix(void *Vtable) {
+ VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable);
+ VtablePrefix *Prefix = Vptr - 1;
+ if (!IsAccessibleMemoryRange((uptr)Prefix, sizeof(VtablePrefix)))
+ return nullptr;
+ if (!Prefix->TypeInfo)
+ // This can't possibly be a valid vtable.
+ return nullptr;
+ return Prefix;
+}
+
+}
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // A crash anywhere within this function probably means the vptr is corrupted.
+ // FIXME: Perform these checks more cautiously.
+
+ // Check whether this is something we've evicted from the cache.
+ HashValue *Bucket = getTypeCacheHashTableBucket(Hash);
+ if (*Bucket == Hash) {
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ return true;
+ }
+
+ void *VtablePtr = *reinterpret_cast<void **>(Object);
+ VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
+ if (!Vtable)
+ return false;
+ if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop) {
+ // Too large or too small offset are signs of Vtable corruption.
+ return false;
+ }
+
+ // Check that this is actually a type_info object for a class type.
+ abi::__class_type_info *Derived =
+ dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo);
+ if (!Derived)
+ return false;
+
+ abi::__class_type_info *Base = (abi::__class_type_info*)Type;
+ if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
+ return false;
+
+ // Success. Cache this result.
+ __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash;
+ *Bucket = Hash;
+ return true;
+}
+
+__ubsan::DynamicTypeInfo
+__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
+ VtablePrefix *Vtable = getVtablePrefix(VtablePtr);
+ if (!Vtable)
+ return DynamicTypeInfo(nullptr, 0, nullptr);
+ if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop)
+ return DynamicTypeInfo(nullptr, Vtable->Offset, nullptr);
+ const abi::__class_type_info *ObjectType = findBaseAtOffset(
+ static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
+ -Vtable->Offset);
+ return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
+ ObjectType ? ObjectType->__type_name : "<unknown>");
+}
+
+bool __ubsan::checkTypeInfoEquality(const void *TypeInfo1,
+ const void *TypeInfo2) {
+ auto TI1 = static_cast<const std::type_info *>(TypeInfo1);
+ auto TI2 = static_cast<const std::type_info *>(TypeInfo2);
+ return SANITIZER_NON_UNIQUE_TYPEINFO && TI1->__type_name[0] != '*' &&
+ TI2->__type_name[0] != '*' &&
+ !internal_strcmp(TI1->__type_name, TI2->__type_name);
+}
+
+#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS
+++ /dev/null
-//===-- ubsan_type_hash_win.cc --------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Implementation of type hashing/lookup for Microsoft C++ ABI.
-//
-//===----------------------------------------------------------------------===//
-
-#include "sanitizer_common/sanitizer_platform.h"
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB && SANITIZER_WINDOWS
-#include "ubsan_type_hash.h"
-
-#include "sanitizer_common/sanitizer_common.h"
-
-#include <typeinfo>
-
-struct CompleteObjectLocator {
- int is_image_relative;
- int offset_to_top;
- int vfptr_offset;
- int rtti_addr;
- int chd_addr;
- int obj_locator_addr;
-};
-
-struct CompleteObjectLocatorAbs {
- int is_image_relative;
- int offset_to_top;
- int vfptr_offset;
- std::type_info *rtti_addr;
- void *chd_addr;
- CompleteObjectLocator *obj_locator_addr;
-};
-
-bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
- // FIXME: Implement.
- return false;
-}
-
-__ubsan::DynamicTypeInfo
-__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
- // The virtual table may not have a complete object locator if the object
- // was compiled without RTTI (i.e. we might be reading from some other global
- // laid out before the virtual table), so we need to carefully validate each
- // pointer dereference and perform sanity checks.
- CompleteObjectLocator **obj_locator_ptr =
- ((CompleteObjectLocator**)VtablePtr)-1;
- if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*)))
- return DynamicTypeInfo(0, 0, 0);
-
- CompleteObjectLocator *obj_locator = *obj_locator_ptr;
- if (!IsAccessibleMemoryRange((uptr)obj_locator,
- sizeof(CompleteObjectLocator)))
- return DynamicTypeInfo(0, 0, 0);
-
- std::type_info *tinfo;
- if (obj_locator->is_image_relative == 1) {
- char *image_base = ((char *)obj_locator) - obj_locator->obj_locator_addr;
- tinfo = (std::type_info *)(image_base + obj_locator->rtti_addr);
- } else if (obj_locator->is_image_relative == 0)
- tinfo = ((CompleteObjectLocatorAbs *)obj_locator)->rtti_addr;
- else
- // Probably not a complete object locator.
- return DynamicTypeInfo(0, 0, 0);
-
- if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info)))
- return DynamicTypeInfo(0, 0, 0);
-
- // Okay, this is probably a std::type_info. Request its name.
- // FIXME: Implement a base class search like we do for Itanium.
- return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top,
- "<unknown>");
-}
-
-#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS
--- /dev/null
+//===-- ubsan_type_hash_win.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Implementation of type hashing/lookup for Microsoft C++ ABI.
+//
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_common/sanitizer_platform.h"
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB && SANITIZER_WINDOWS
+#include "ubsan_type_hash.h"
+
+#include "sanitizer_common/sanitizer_common.h"
+
+#include <typeinfo>
+
+struct CompleteObjectLocator {
+ int is_image_relative;
+ int offset_to_top;
+ int vfptr_offset;
+ int rtti_addr;
+ int chd_addr;
+ int obj_locator_addr;
+};
+
+struct CompleteObjectLocatorAbs {
+ int is_image_relative;
+ int offset_to_top;
+ int vfptr_offset;
+ std::type_info *rtti_addr;
+ void *chd_addr;
+ CompleteObjectLocator *obj_locator_addr;
+};
+
+bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
+ // FIXME: Implement.
+ return false;
+}
+
+__ubsan::DynamicTypeInfo
+__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) {
+ // The virtual table may not have a complete object locator if the object
+ // was compiled without RTTI (i.e. we might be reading from some other global
+ // laid out before the virtual table), so we need to carefully validate each
+ // pointer dereference and perform sanity checks.
+ CompleteObjectLocator **obj_locator_ptr =
+ ((CompleteObjectLocator**)VtablePtr)-1;
+ if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ CompleteObjectLocator *obj_locator = *obj_locator_ptr;
+ if (!IsAccessibleMemoryRange((uptr)obj_locator,
+ sizeof(CompleteObjectLocator)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ std::type_info *tinfo;
+ if (obj_locator->is_image_relative == 1) {
+ char *image_base = ((char *)obj_locator) - obj_locator->obj_locator_addr;
+ tinfo = (std::type_info *)(image_base + obj_locator->rtti_addr);
+ } else if (obj_locator->is_image_relative == 0)
+ tinfo = ((CompleteObjectLocatorAbs *)obj_locator)->rtti_addr;
+ else
+ // Probably not a complete object locator.
+ return DynamicTypeInfo(0, 0, 0);
+
+ if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info)))
+ return DynamicTypeInfo(0, 0, 0);
+
+ // Okay, this is probably a std::type_info. Request its name.
+ // FIXME: Implement a base class search like we do for Itanium.
+ return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top,
+ "<unknown>");
+}
+
+bool __ubsan::checkTypeInfoEquality(const void *, const void *) {
+ return false;
+}
+
+#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS
+++ /dev/null
-//===-- ubsan_value.cc ----------------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Representation of a runtime value, as marshaled from the generated code to
-// the ubsan runtime.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ubsan_platform.h"
-#if CAN_SANITIZE_UB
-#include "ubsan_value.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_libc.h"
-
-using namespace __ubsan;
-
-SIntMax Value::getSIntValue() const {
- CHECK(getType().isSignedIntegerTy());
- if (isInlineInt()) {
- // Val was zero-extended to ValueHandle. Sign-extend from original width
- // to SIntMax.
- const unsigned ExtraBits =
- sizeof(SIntMax) * 8 - getType().getIntegerBitWidth();
- return SIntMax(Val) << ExtraBits >> ExtraBits;
- }
- if (getType().getIntegerBitWidth() == 64)
- return *reinterpret_cast<s64*>(Val);
-#if HAVE_INT128_T
- if (getType().getIntegerBitWidth() == 128)
- return *reinterpret_cast<s128*>(Val);
-#else
- if (getType().getIntegerBitWidth() == 128)
- UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
-#endif
- UNREACHABLE("unexpected bit width");
-}
-
-UIntMax Value::getUIntValue() const {
- CHECK(getType().isUnsignedIntegerTy());
- if (isInlineInt())
- return Val;
- if (getType().getIntegerBitWidth() == 64)
- return *reinterpret_cast<u64*>(Val);
-#if HAVE_INT128_T
- if (getType().getIntegerBitWidth() == 128)
- return *reinterpret_cast<u128*>(Val);
-#else
- if (getType().getIntegerBitWidth() == 128)
- UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
-#endif
- UNREACHABLE("unexpected bit width");
-}
-
-UIntMax Value::getPositiveIntValue() const {
- if (getType().isUnsignedIntegerTy())
- return getUIntValue();
- SIntMax Val = getSIntValue();
- CHECK(Val >= 0);
- return Val;
-}
-
-/// Get the floating-point value of this object, extended to a long double.
-/// These are always passed by address (our calling convention doesn't allow
-/// them to be passed in floating-point registers, so this has little cost).
-FloatMax Value::getFloatValue() const {
- CHECK(getType().isFloatTy());
- if (isInlineFloat()) {
- switch (getType().getFloatBitWidth()) {
-#if 0
- // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion
- // from '__fp16' to 'long double'.
- case 16: {
- __fp16 Value;
- internal_memcpy(&Value, &Val, 4);
- return Value;
- }
-#endif
- case 32: {
- float Value;
-#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
- // For big endian the float value is in the last 4 bytes.
- // On some targets we may only have 4 bytes so we count backwards from
- // the end of Val to account for both the 32-bit and 64-bit cases.
- internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4);
-#else
- internal_memcpy(&Value, &Val, 4);
-#endif
- return Value;
- }
- case 64: {
- double Value;
- internal_memcpy(&Value, &Val, 8);
- return Value;
- }
- }
- } else {
- switch (getType().getFloatBitWidth()) {
- case 64: return *reinterpret_cast<double*>(Val);
- case 80: return *reinterpret_cast<long double*>(Val);
- case 96: return *reinterpret_cast<long double*>(Val);
- case 128: return *reinterpret_cast<long double*>(Val);
- }
- }
- UNREACHABLE("unexpected floating point bit width");
-}
-
-#endif // CAN_SANITIZE_UB
--- /dev/null
+//===-- ubsan_value.cpp ---------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of a runtime value, as marshaled from the generated code to
+// the ubsan runtime.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ubsan_platform.h"
+#if CAN_SANITIZE_UB
+#include "ubsan_value.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_libc.h"
+
+using namespace __ubsan;
+
+SIntMax Value::getSIntValue() const {
+ CHECK(getType().isSignedIntegerTy());
+ if (isInlineInt()) {
+ // Val was zero-extended to ValueHandle. Sign-extend from original width
+ // to SIntMax.
+ const unsigned ExtraBits =
+ sizeof(SIntMax) * 8 - getType().getIntegerBitWidth();
+ return SIntMax(Val) << ExtraBits >> ExtraBits;
+ }
+ if (getType().getIntegerBitWidth() == 64)
+ return *reinterpret_cast<s64*>(Val);
+#if HAVE_INT128_T
+ if (getType().getIntegerBitWidth() == 128)
+ return *reinterpret_cast<s128*>(Val);
+#else
+ if (getType().getIntegerBitWidth() == 128)
+ UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
+#endif
+ UNREACHABLE("unexpected bit width");
+}
+
+UIntMax Value::getUIntValue() const {
+ CHECK(getType().isUnsignedIntegerTy());
+ if (isInlineInt())
+ return Val;
+ if (getType().getIntegerBitWidth() == 64)
+ return *reinterpret_cast<u64*>(Val);
+#if HAVE_INT128_T
+ if (getType().getIntegerBitWidth() == 128)
+ return *reinterpret_cast<u128*>(Val);
+#else
+ if (getType().getIntegerBitWidth() == 128)
+ UNREACHABLE("libclang_rt.ubsan was built without __int128 support");
+#endif
+ UNREACHABLE("unexpected bit width");
+}
+
+UIntMax Value::getPositiveIntValue() const {
+ if (getType().isUnsignedIntegerTy())
+ return getUIntValue();
+ SIntMax Val = getSIntValue();
+ CHECK(Val >= 0);
+ return Val;
+}
+
+/// Get the floating-point value of this object, extended to a long double.
+/// These are always passed by address (our calling convention doesn't allow
+/// them to be passed in floating-point registers, so this has little cost).
+FloatMax Value::getFloatValue() const {
+ CHECK(getType().isFloatTy());
+ if (isInlineFloat()) {
+ switch (getType().getFloatBitWidth()) {
+#if 0
+ // FIXME: OpenCL / NEON 'half' type. LLVM can't lower the conversion
+ // from '__fp16' to 'long double'.
+ case 16: {
+ __fp16 Value;
+ internal_memcpy(&Value, &Val, 4);
+ return Value;
+ }
+#endif
+ case 32: {
+ float Value;
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+ // For big endian the float value is in the last 4 bytes.
+ // On some targets we may only have 4 bytes so we count backwards from
+ // the end of Val to account for both the 32-bit and 64-bit cases.
+ internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4);
+#else
+ internal_memcpy(&Value, &Val, 4);
+#endif
+ return Value;
+ }
+ case 64: {
+ double Value;
+ internal_memcpy(&Value, &Val, 8);
+ return Value;
+ }
+ }
+ } else {
+ switch (getType().getFloatBitWidth()) {
+ case 64: return *reinterpret_cast<double*>(Val);
+ case 80: return *reinterpret_cast<long double*>(Val);
+ case 96: return *reinterpret_cast<long double*>(Val);
+ case 128: return *reinterpret_cast<long double*>(Val);
+ }
+ }
+ UNREACHABLE("unexpected floating point bit width");
+}
+
+#endif // CAN_SANITIZE_UB
//===-- ubsan_value.h -------------------------------------------*- C++ -*-===//
//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
+++ /dev/null
-//===-- ubsan_win_dll_thunk.cc --------------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines a family of thunks that should be statically linked into
-// the DLLs that have instrumentation in order to delegate the calls to the
-// shared runtime that lives in the main binary.
-// See https://github.com/google/sanitizers/issues/209 for the details.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DLL_THUNK
-#include "sanitizer_common/sanitizer_win_dll_thunk.h"
-// Ubsan interface functions.
-#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "ubsan_interface.inc"
-#endif // SANITIZER_DLL_THUNK
--- /dev/null
+//===-- ubsan_win_dll_thunk.cpp -------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a family of thunks that should be statically linked into
+// the DLLs that have instrumentation in order to delegate the calls to the
+// shared runtime that lives in the main binary.
+// See https://github.com/google/sanitizers/issues/209 for the details.
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DLL_THUNK
+#include "sanitizer_common/sanitizer_win_dll_thunk.h"
+// Ubsan interface functions.
+#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "ubsan_interface.inc"
+#endif // SANITIZER_DLL_THUNK
+++ /dev/null
-//===-- ubsan_win_dynamic_runtime_thunk.cc --------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines things that need to be present in the application modules
-// to interact with Ubsan, when it is included in a dll.
-//
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
-#define SANITIZER_IMPORT_INTERFACE 1
-#include "sanitizer_common/sanitizer_win_defs.h"
-// Define weak alias for all weak functions imported from ubsan.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
-#include "ubsan_interface.inc"
-#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
--- /dev/null
+//===-- ubsan_win_dynamic_runtime_thunk.cpp -------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines things that need to be present in the application modules
+// to interact with Ubsan, when it is included in a dll.
+//
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DYNAMIC_RUNTIME_THUNK
+#define SANITIZER_IMPORT_INTERFACE 1
+#include "sanitizer_common/sanitizer_win_defs.h"
+// Define weak alias for all weak functions imported from ubsan.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) WIN_WEAK_IMPORT_DEF(Name)
+#include "ubsan_interface.inc"
+#endif // SANITIZER_DYNAMIC_RUNTIME_THUNK
+++ /dev/null
-//===-- ubsan_win_weak_interception.cc ------------------------------------===//
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-// This module should be included in Ubsan when it is implemented as a shared
-// library on Windows (dll), in order to delegate the calls of weak functions to
-// the implementation in the main executable when a strong definition is
-// provided.
-//===----------------------------------------------------------------------===//
-#ifdef SANITIZER_DYNAMIC
-#include "sanitizer_common/sanitizer_win_weak_interception.h"
-#include "ubsan_flags.h"
-#include "ubsan_monitor.h"
-// Check if strong definitions for weak functions are present in the main
-// executable. If that is the case, override dll functions to point to strong
-// implementations.
-#define INTERFACE_FUNCTION(Name)
-#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
-#include "ubsan_interface.inc"
-#endif // SANITIZER_DYNAMIC
--- /dev/null
+//===-- ubsan_win_weak_interception.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// This module should be included in Ubsan when it is implemented as a shared
+// library on Windows (dll), in order to delegate the calls of weak functions to
+// the implementation in the main executable when a strong definition is
+// provided.
+//===----------------------------------------------------------------------===//
+#ifdef SANITIZER_DYNAMIC
+#include "sanitizer_common/sanitizer_win_weak_interception.h"
+#include "ubsan_flags.h"
+#include "ubsan_monitor.h"
+// Check if strong definitions for weak functions are present in the main
+// executable. If that is the case, override dll functions to point to strong
+// implementations.
+#define INTERFACE_FUNCTION(Name)
+#define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name)
+#include "ubsan_interface.inc"
+#endif // SANITIZER_DYNAMIC