MFH
Sponsored by: The FreeBSD Foundation
This commit is contained in:
commit
b655ec9752
@ -982,6 +982,7 @@ distributeworld installworld stageworld: _installcheck_world
|
||||
cp $$libs $$progs ${INSTALLTMP}
|
||||
cp -R $${PATH_LOCALE:-"/usr/share/locale"} ${INSTALLTMP}/locale
|
||||
.if defined(NO_ROOT)
|
||||
-mkdir -p ${METALOG:H}
|
||||
echo "#${MTREE_MAGIC}" > ${METALOG}
|
||||
.endif
|
||||
.if make(distributeworld)
|
||||
@ -1644,6 +1645,11 @@ ${_bt}-usr.sbin/nmtree: ${_bt}-lib/libnetbsd
|
||||
_cat= bin/cat
|
||||
.endif
|
||||
|
||||
# r264059 support for status=
|
||||
.if ${BOOTSTRAPPING} < 1100017
|
||||
_dd= bin/dd
|
||||
.endif
|
||||
|
||||
# r277259 crunchide: Correct 64-bit section header offset
|
||||
# r281674 crunchide: always include both 32- and 64-bit ELF support
|
||||
# r285986 crunchen: use STRIPBIN rather than STRIP
|
||||
@ -1670,11 +1676,11 @@ _gensnmptree= usr.sbin/bsnmpd/gensnmptree
|
||||
_clang_tblgen= \
|
||||
lib/clang/libllvmsupport \
|
||||
lib/clang/libllvmtablegen \
|
||||
usr.bin/clang/tblgen \
|
||||
usr.bin/clang/llvm-tblgen \
|
||||
usr.bin/clang/clang-tblgen
|
||||
|
||||
${_bt}-usr.bin/clang/clang-tblgen: ${_bt}-lib/clang/libllvmtablegen ${_bt}-lib/clang/libllvmsupport
|
||||
${_bt}-usr.bin/clang/tblgen: ${_bt}-lib/clang/libllvmtablegen ${_bt}-lib/clang/libllvmsupport
|
||||
${_bt}-usr.bin/clang/llvm-tblgen: ${_bt}-lib/clang/libllvmtablegen ${_bt}-lib/clang/libllvmsupport
|
||||
.endif
|
||||
|
||||
# Default to building the GPL DTC, but build the BSDL one if users explicitly
|
||||
@ -1696,13 +1702,12 @@ _kerberos5_bootstrap_tools= \
|
||||
.ORDER: ${_kerberos5_bootstrap_tools:C/^/${_bt}-/g}
|
||||
.endif
|
||||
|
||||
.if ${MK_MANDOCDB} != "no"
|
||||
# r283777 makewhatis(1) replaced with mandoc version which builds a database.
|
||||
.if ${MK_MANDOCDB} != "no" && ${BOOTSTRAPPING} < 1100075
|
||||
_libopenbsd?= lib/libopenbsd
|
||||
_makewhatis= lib/libsqlite3 \
|
||||
usr.bin/mandoc
|
||||
${_bt}-usr.bin/mandoc: ${_bt}-lib/libopenbsd ${_bt}-lib/libsqlite3
|
||||
.else
|
||||
_makewhatis=usr.bin/makewhatis
|
||||
.endif
|
||||
|
||||
bootstrap-tools: .PHONY
|
||||
@ -1719,6 +1724,7 @@ bootstrap-tools: .PHONY
|
||||
${_dtc} \
|
||||
${_awk} \
|
||||
${_cat} \
|
||||
${_dd} \
|
||||
usr.bin/lorder \
|
||||
${_libopenbsd} \
|
||||
${_makewhatis} \
|
||||
@ -1881,7 +1887,7 @@ NXBENV= MAKEOBJDIRPREFIX=${OBJTREE}/nxb \
|
||||
INSTALL="sh ${.CURDIR}/tools/install.sh" \
|
||||
PATH=${PATH}:${OBJTREE}/gperf_for_gcc/usr/bin
|
||||
NXBMAKE= ${NXBENV} ${MAKE} \
|
||||
TBLGEN=${NXBDESTDIR}/usr/bin/tblgen \
|
||||
LLVM_TBLGEN=${NXBDESTDIR}/usr/bin/llvm-tblgen \
|
||||
CLANG_TBLGEN=${NXBDESTDIR}/usr/bin/clang-tblgen \
|
||||
MACHINE=${TARGET} MACHINE_ARCH=${TARGET_ARCH} \
|
||||
MK_GDB=no MK_TESTS=no \
|
||||
@ -2079,7 +2085,16 @@ _lib_libradius= lib/libradius
|
||||
.endif
|
||||
|
||||
.if ${MK_OFED} != "no"
|
||||
_ofed_lib= contrib/ofed/usr.lib/
|
||||
_ofed_lib= contrib/ofed/usr.lib
|
||||
_prebuild_libs+= contrib/ofed/usr.lib/libosmcomp
|
||||
_prebuild_libs+= contrib/ofed/usr.lib/libopensm
|
||||
_prebuild_libs+= contrib/ofed/usr.lib/libibcommon
|
||||
_prebuild_libs+= contrib/ofed/usr.lib/libibverbs
|
||||
_prebuild_libs+= contrib/ofed/usr.lib/libibumad
|
||||
|
||||
contrib/ofed/usr.lib/libopensm__L: lib/libthr__L
|
||||
contrib/ofed/usr.lib/libosmcomp__L: lib/libthr__L
|
||||
contrib/ofed/usr.lib/libibumad__L: contrib/ofed/usr.lib/libibcommon__L
|
||||
.endif
|
||||
|
||||
.if ${MK_CASPER} != "no"
|
||||
@ -2092,7 +2107,7 @@ lib/liblzma__L: lib/libthr__L
|
||||
|
||||
_generic_libs= ${_cddl_lib} gnu/lib ${_kerberos5_lib} lib ${_secure_lib} usr.bin/lex/lib ${_ofed_lib}
|
||||
.for _DIR in ${LOCAL_LIB_DIRS}
|
||||
.if exists(${.CURDIR}/${_DIR}/Makefile)
|
||||
.if exists(${.CURDIR}/${_DIR}/Makefile) && empty(_generic_libs:M${_DIR})
|
||||
_generic_libs+= ${_DIR}
|
||||
.endif
|
||||
.endfor
|
||||
|
@ -38,6 +38,89 @@
|
||||
# xargs -n1 | sort | uniq -d;
|
||||
# done
|
||||
|
||||
# 20160305: new clang import which bumps version from 3.7.1 to 3.8.0.
|
||||
OLD_FILES+=usr/bin/macho-dump
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/allocator_interface.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/asan_interface.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/common_interface_defs.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/coverage_interface.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/dfsan_interface.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/linux_syscall_hooks.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/lsan_interface.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/msan_interface.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/sanitizer/tsan_interface_atomic.h
|
||||
OLD_DIRS+=usr/lib/clang/3.7.1/include/sanitizer
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/__stddef_max_align_t.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/__wmmintrin_aes.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/__wmmintrin_pclmul.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/adxintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/altivec.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/ammintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/arm_acle.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/arm_neon.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx2intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512bwintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512cdintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512dqintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512erintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512fintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512vlbwintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512vldqintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avx512vlintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/avxintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/bmi2intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/bmiintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/cpuid.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/cuda_builtin_vars.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/emmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/f16cintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/fma4intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/fmaintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/fxsrintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/htmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/htmxlintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/ia32intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/immintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/lzcntintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/mm3dnow.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/mm_malloc.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/mmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/module.modulemap
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/nmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/pmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/popcntintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/prfchwintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/rdseedintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/rtmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/s390intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/shaintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/smmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/tbmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/tmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/vadefs.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/vecintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/wmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/x86intrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/xmmintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/xopintrin.h
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/include/xtestintrin.h
|
||||
OLD_DIRS+=usr/lib/clang/3.7.1/include
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.asan-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.asan-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.asan_cxx-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.asan_cxx-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.profile-arm.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.profile-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.profile-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.safestack-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.safestack-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.ubsan_standalone-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.ubsan_standalone-x86_64.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.ubsan_standalone_cxx-i386.a
|
||||
OLD_FILES+=usr/lib/clang/3.7.1/lib/freebsd/libclang_rt.ubsan_standalone_cxx-x86_64.a
|
||||
OLD_DIRS+=usr/lib/clang/3.7.1/lib/freebsd
|
||||
OLD_DIRS+=usr/lib/clang/3.7.1/lib
|
||||
OLD_DIRS+=usr/lib/clang/3.7.1
|
||||
# 20160301: Remove taskqueue_enqueue_fast
|
||||
OLD_FILES+=usr/share/man/man9/taskqueue_enqueue_fast.9.gz
|
||||
# 20160225: Remove casperd and libcapsicum.
|
||||
@ -59,6 +142,15 @@ OLD_FILES+=libexec/casper/grp
|
||||
OLD_FILES+=libexec/casper/pwd
|
||||
OLD_FILES+=libexec/casper/random
|
||||
OLD_FILES+=libexec/casper/sysctl
|
||||
OLD_DIRS+=libexec/casper
|
||||
OLD_FILES+=usr/lib/libcapsicum.a
|
||||
OLD_FILES+=usr/lib/libcapsicum.so
|
||||
OLD_LIBS+=lib/libcapsicum.so.0
|
||||
OLD_FILES+=usr/lib/libcapsicum_p.a
|
||||
OLD_FILES+=usr/lib32/libcapsicum.a
|
||||
OLD_FILES+=usr/lib32/libcapsicum.so
|
||||
OLD_LIBS+=usr/lib32/libcapsicum.so.0
|
||||
OLD_FILES+=usr/lib32/libcapsicum_p.a
|
||||
# 20160223: functionality from mkulzma(1) merged into mkuzip(1)
|
||||
OLD_FILES+=usr/bin/mkulzma
|
||||
# 20160211: Remove obsolete unbound-control-setup
|
||||
|
13
UPDATING
13
UPDATING
@ -31,6 +31,19 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 11.x IS SLOW:
|
||||
disable the most expensive debugging functionality run
|
||||
"ln -s 'abort:false,junk:false' /etc/malloc.conf".)
|
||||
|
||||
20160305:
|
||||
Clang, llvm, lldb and compiler-rt have been upgraded to 3.8.0. Please
|
||||
see the 20141231 entry below for information about prerequisites and
|
||||
upgrading, if you are not already using clang 3.5.0 or higher.
|
||||
|
||||
20160301:
|
||||
The AIO subsystem is now a standard part of the kernel. The
|
||||
VFS_AIO kernel option and aio.ko kernel module have been removed.
|
||||
Due to stability concerns, asynchronous I/O requests are only
|
||||
permitted on sockets and raw disks by default. To enable
|
||||
asynchronous I/O requests on all file types, set the
|
||||
vfs.aio.enable_unsafe sysctl to a non-zero value.
|
||||
|
||||
20160226:
|
||||
The ELF object manipulation tool objcopy is now provided by the
|
||||
ELF Tool Chain project rather than by GNU binutils. It should be a
|
||||
|
@ -80,6 +80,7 @@
|
||||
/****************** local defines *********************/
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#define SAVESIGVEC
|
||||
#define NLS_BUGS
|
||||
#define BSD_STYLE_COLORLS
|
||||
/* Use LC_MESSAGES locale category to open the message catalog */
|
||||
|
@ -102,6 +102,7 @@ typedef struct tcpsinfo {
|
||||
string tcps_raddr; /* remote address, as a string */
|
||||
int32_t tcps_state; /* TCP state */
|
||||
uint32_t tcps_iss; /* Initial sequence # sent */
|
||||
uint32_t tcps_irs; /* Initial sequence # received */
|
||||
uint32_t tcps_suna; /* sequence # sent but unacked */
|
||||
uint32_t tcps_smax; /* highest sequence number sent */
|
||||
uint32_t tcps_snxt; /* next sequence # to send */
|
||||
@ -112,10 +113,12 @@ typedef struct tcpsinfo {
|
||||
uint32_t tcps_swl1; /* window update seg seq number */
|
||||
uint32_t tcps_swl2; /* window update seg ack number */
|
||||
uint32_t tcps_rup; /* receive urgent pointer */
|
||||
uint32_t tcps_radv; /* advertised window */
|
||||
uint32_t tcps_rwnd; /* receive window size */
|
||||
int32_t tcps_rcv_ws; /* receive window scaling */
|
||||
uint32_t tcps_cwnd; /* congestion window */
|
||||
uint32_t tcps_cwnd_ssthresh; /* threshold for congestion avoidance */
|
||||
uint32_t tcps_srecover; /* for use in NewReno Fast Recovery */
|
||||
uint32_t tcps_sack_fack; /* SACK sequence # we have acked */
|
||||
uint32_t tcps_sack_snxt; /* next SACK seq # for retransmission */
|
||||
uint32_t tcps_rto; /* round-trip timeout, msec */
|
||||
@ -123,6 +126,10 @@ typedef struct tcpsinfo {
|
||||
int tcps_retransmit; /* retransmit send event, boolean */
|
||||
int tcps_srtt; /* smoothed RTT in units of (TCP_RTT_SCALE*hz) */
|
||||
int tcps_debug; /* socket has SO_DEBUG set */
|
||||
int32_t tcps_dupacks; /* consecutive dup acks received */
|
||||
uint32_t tcps_rtttime; /* RTT measurement start time */
|
||||
uint32_t tcps_rtseq; /* sequence # being timed */
|
||||
uint32_t tcps_ts_recent; /* timestamp echo data */
|
||||
} tcpsinfo_t;
|
||||
|
||||
/*
|
||||
@ -192,6 +199,7 @@ translator tcpsinfo_t < struct tcpcb *p > {
|
||||
inet_ntoa6(&p->t_inpcb->inp_inc.inc_ie.ie_dependfaddr.ie6_foreign);
|
||||
tcps_state = p == NULL ? -1 : p->t_state;
|
||||
tcps_iss = p == NULL ? 0 : p->iss;
|
||||
tcps_irs = p == NULL ? 0 : p->irs;
|
||||
tcps_suna = p == NULL ? 0 : p->snd_una;
|
||||
tcps_smax = p == NULL ? 0 : p->snd_max;
|
||||
tcps_snxt = p == NULL ? 0 : p->snd_nxt;
|
||||
@ -201,11 +209,13 @@ translator tcpsinfo_t < struct tcpcb *p > {
|
||||
tcps_snd_ws = p == NULL ? -1 : p->snd_scale;
|
||||
tcps_swl1 = p == NULL ? -1 : p->snd_wl1;
|
||||
tcps_swl2 = p == NULL ? -1 : p->snd_wl2;
|
||||
tcps_radv = p == NULL ? -1 : p->rcv_adv;
|
||||
tcps_rwnd = p == NULL ? -1 : p->rcv_wnd;
|
||||
tcps_rup = p == NULL ? -1 : p->rcv_up;
|
||||
tcps_rcv_ws = p == NULL ? -1 : p->rcv_scale;
|
||||
tcps_cwnd = p == NULL ? -1 : p->snd_cwnd;
|
||||
tcps_cwnd_ssthresh = p == NULL ? -1 : p->snd_ssthresh;
|
||||
tcps_srecover = p == NULL ? -1 : p->snd_recover;
|
||||
tcps_sack_fack = p == NULL ? 0 : p->snd_fack;
|
||||
tcps_sack_snxt = p == NULL ? 0 : p->sack_newdata;
|
||||
tcps_rto = p == NULL ? -1 : (p->t_rxtcur * 1000) / `hz;
|
||||
@ -214,6 +224,10 @@ translator tcpsinfo_t < struct tcpcb *p > {
|
||||
tcps_srtt = p == NULL ? -1 : p->t_srtt; /* smoothed RTT in units of (TCP_RTT_SCALE*hz) */
|
||||
tcps_debug = p == NULL ? 0 :
|
||||
p->t_inpcb->inp_socket->so_options & 1;
|
||||
tcps_dupacks = p == NULL ? -1 : p->t_dupacks;
|
||||
tcps_rtttime = p == NULL ? -1 : p->t_rtttime;
|
||||
tcps_rtseq = p == NULL ? -1 : p->t_rtseq;
|
||||
tcps_ts_recent = p == NULL ? -1 : p->ts_recent;
|
||||
};
|
||||
|
||||
#pragma D binding "1.6.3" translator
|
||||
|
@ -110,10 +110,6 @@ extern "C" {
|
||||
void __asan_report_error(void *pc, void *bp, void *sp,
|
||||
void *addr, int is_write, size_t access_size);
|
||||
|
||||
// Sets the exit code to use when reporting an error.
|
||||
// Returns the old value.
|
||||
int __asan_set_error_exit_code(int exit_code);
|
||||
|
||||
// Deprecated. Call __sanitizer_set_death_callback instead.
|
||||
void __asan_set_death_callback(void (*callback)(void));
|
||||
|
||||
|
@ -105,12 +105,31 @@ extern "C" {
|
||||
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();
|
||||
|
||||
// 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_strcmp(void *called_pc, const char *s1,
|
||||
const char *s2, int result);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -27,9 +27,11 @@ extern "C" {
|
||||
// descriptor. Returns -1 on failure, or if coverage dumping is disabled.
|
||||
// This is intended for use by sandboxing code.
|
||||
intptr_t __sanitizer_maybe_open_cov_file(const char *name);
|
||||
// Get the number of total unique covered entities (blocks, edges, calls).
|
||||
// Get the number of unique covered blocks (or edges).
|
||||
// This can be useful for coverage-directed in-process fuzzers.
|
||||
uintptr_t __sanitizer_get_total_unique_coverage();
|
||||
// Get the number of unique indirect caller-callee pairs.
|
||||
uintptr_t __sanitizer_get_total_unique_caller_callee_pairs();
|
||||
|
||||
// Reset the basic-block (edge) coverage to the initial state.
|
||||
// Useful for in-process fuzzing to start collecting coverage from scratch.
|
||||
@ -39,6 +41,13 @@ extern "C" {
|
||||
// Some of the entries in *data will be zero.
|
||||
uintptr_t __sanitizer_get_coverage_guards(uintptr_t **data);
|
||||
|
||||
// Set *data to the growing buffer with covered PCs and return the size
|
||||
// of the buffer. The entries are never zero.
|
||||
// When only unique pcs are collected, the size is equal to
|
||||
// __sanitizer_get_total_unique_coverage.
|
||||
// WARNING: EXPERIMENTAL API.
|
||||
uintptr_t __sanitizer_get_coverage_pc_buffer(uintptr_t **data);
|
||||
|
||||
// The coverage instrumentation may optionally provide imprecise counters.
|
||||
// Rather than exposing the counter values to the user we instead map
|
||||
// the counters to a bitset.
|
||||
|
@ -91,16 +91,18 @@ void dfsan_set_write_callback(dfsan_write_callback_t labeled_write_callback);
|
||||
/// <label> <parent label 1> <parent label 2> <label description if any>
|
||||
void dfsan_dump_labels(int fd);
|
||||
|
||||
/// Interceptor hooks.
|
||||
/// Whenever a dfsan's custom function is called the corresponding
|
||||
/// hook is called it non-zero. The hooks should be defined by the user.
|
||||
/// The primary use case is taint-guided fuzzing, where the fuzzer
|
||||
/// needs to see the parameters of the function and the labels.
|
||||
/// FIXME: implement more hooks.
|
||||
|
||||
/// memcmp hook.
|
||||
void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2,
|
||||
size_t n, dfsan_label s1_label,
|
||||
dfsan_label s2_label, dfsan_label n_label);
|
||||
void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2,
|
||||
size_t n, dfsan_label s1_label,
|
||||
dfsan_label s2_label, dfsan_label n_label);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
|
@ -43,7 +43,7 @@ extern "C" {
|
||||
|
||||
// Check for leaks now. This function behaves identically to the default
|
||||
// end-of-process leak check. In particular, it will terminate the process if
|
||||
// leaks are found and the exit_code flag is non-zero.
|
||||
// leaks are found and the exitcode runtime flag is non-zero.
|
||||
// Subsequent calls to this function will have no effect and end-of-process
|
||||
// leak check will not run. Effectively, end-of-process leak check is moved to
|
||||
// the time of first invocation of this function.
|
||||
|
@ -61,10 +61,6 @@ extern "C" {
|
||||
* is not. */
|
||||
void __msan_check_mem_is_initialized(const volatile void *x, size_t size);
|
||||
|
||||
/* Set exit code when error(s) were detected.
|
||||
Value of 0 means don't change the program exit code. */
|
||||
void __msan_set_exit_code(int exit_code);
|
||||
|
||||
/* For testing:
|
||||
__msan_set_expect_umr(1);
|
||||
... some buggy code ...
|
||||
@ -92,14 +88,22 @@ extern "C" {
|
||||
Memory will be marked uninitialized, with origin at the call site. */
|
||||
void __msan_allocated_memory(const volatile void* data, size_t size);
|
||||
|
||||
/* Tell MSan about newly destroyed memory. Mark memory as uninitialized. */
|
||||
void __sanitizer_dtor_callback(const volatile void* data, size_t size);
|
||||
|
||||
/* This function may be optionally provided by user and should return
|
||||
a string containing Msan runtime options. See msan_flags.h for details. */
|
||||
const char* __msan_default_options();
|
||||
|
||||
/* Sets the callback to be called right before death on error.
|
||||
Passing 0 will unset the callback. */
|
||||
/* Deprecated. Call __sanitizer_set_death_callback instead. */
|
||||
void __msan_set_death_callback(void (*callback)(void));
|
||||
|
||||
/* Update shadow for the application copy of size bytes from src to dst.
|
||||
Src and dst are application addresses. This function does not copy the
|
||||
actual application memory, it only updates shadow and origin for such
|
||||
copy. Source and destination regions can overlap. */
|
||||
void __msan_copy_shadow(const volatile void *dst, const volatile void *src,
|
||||
size_t size);
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -23,4 +23,4 @@ from the root of your CMake build tree:
|
||||
make check-asan
|
||||
|
||||
For more instructions see:
|
||||
http://code.google.com/p/address-sanitizer/wiki/HowToBuild
|
||||
https://github.com/google/sanitizers/wiki/AddressSanitizerHowToBuild
|
||||
|
@ -38,7 +38,7 @@ static struct AsanDeactivatedFlags {
|
||||
#undef ASAN_ACTIVATION_FLAG
|
||||
#undef COMMON_ACTIVATION_FLAG
|
||||
|
||||
RegisterIncludeFlag(parser, cf);
|
||||
RegisterIncludeFlags(parser, cf);
|
||||
}
|
||||
|
||||
void OverrideFromActivationFlags() {
|
||||
@ -61,11 +61,6 @@ static struct AsanDeactivatedFlags {
|
||||
parser.ParseString(env);
|
||||
}
|
||||
|
||||
// Override from getprop asan.options.
|
||||
char buf[100];
|
||||
GetExtraActivationFlags(buf, sizeof(buf));
|
||||
parser.ParseString(buf);
|
||||
|
||||
SetVerbosity(cf.verbosity);
|
||||
|
||||
if (Verbosity()) ReportUnrecognizedFlags();
|
||||
@ -124,6 +119,8 @@ void AsanActivate() {
|
||||
if (!asan_is_deactivated) return;
|
||||
VReport(1, "Activating ASan\n");
|
||||
|
||||
UpdateProcessName();
|
||||
|
||||
asan_deactivated_flags.OverrideFromActivationFlags();
|
||||
|
||||
SetCanPoisonMemory(asan_deactivated_flags.poison_heap);
|
||||
|
@ -14,8 +14,8 @@
|
||||
// with ThreadSanitizer and MemorySanitizer.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_allocator.h"
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_poisoning.h"
|
||||
#include "asan_report.h"
|
||||
@ -541,7 +541,7 @@ struct Allocator {
|
||||
u8 chunk_state = m->chunk_state;
|
||||
if (chunk_state != CHUNK_ALLOCATED)
|
||||
ReportInvalidFree(old_ptr, chunk_state, stack);
|
||||
CHECK_NE(REAL(memcpy), (void*)0);
|
||||
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.
|
||||
@ -579,7 +579,7 @@ struct Allocator {
|
||||
|
||||
// Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg).
|
||||
AsanChunk *GetAsanChunk(void *alloc_beg) {
|
||||
if (!alloc_beg) return 0;
|
||||
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]);
|
||||
@ -619,7 +619,7 @@ struct Allocator {
|
||||
// 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 = 0;
|
||||
AsanChunk *m2 = nullptr;
|
||||
for (uptr l = 1; l < GetPageSizeCached(); l++) {
|
||||
m2 = GetAsanChunkByAddr(addr - l);
|
||||
if (m2 == m1) continue; // Still the same chunk.
|
||||
@ -653,7 +653,7 @@ static AsanAllocator &get_allocator() {
|
||||
}
|
||||
|
||||
bool AsanChunkView::IsValid() {
|
||||
return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE;
|
||||
return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE;
|
||||
}
|
||||
uptr AsanChunkView::Beg() { return chunk_->Beg(); }
|
||||
uptr AsanChunkView::End() { return Beg() + UsedSize(); }
|
||||
@ -723,11 +723,11 @@ void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
|
||||
}
|
||||
|
||||
void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
|
||||
if (p == 0)
|
||||
if (!p)
|
||||
return instance.Allocate(size, 8, stack, FROM_MALLOC, true);
|
||||
if (size == 0) {
|
||||
instance.Deallocate(p, 0, stack, FROM_MALLOC);
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
return instance.Reallocate(p, size, stack);
|
||||
}
|
||||
@ -755,7 +755,7 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
|
||||
}
|
||||
|
||||
uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) {
|
||||
if (ptr == 0) return 0;
|
||||
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);
|
||||
@ -780,7 +780,7 @@ void AsanSoftRssLimitExceededCallback(bool exceeded) {
|
||||
instance.allocator.SetRssLimitIsExceeded(exceeded);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// --- Implementation of LSan-specific functions --- {{{1
|
||||
namespace __lsan {
|
||||
@ -881,7 +881,7 @@ int __sanitizer_get_ownership(const void *p) {
|
||||
}
|
||||
|
||||
uptr __sanitizer_get_allocated_size(const void *p) {
|
||||
if (p == 0) return 0;
|
||||
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.
|
||||
@ -904,5 +904,5 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE
|
||||
void __sanitizer_free_hook(void *ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -114,6 +114,11 @@ struct AsanMapUnmapCallback {
|
||||
# if defined(__powerpc64__)
|
||||
const uptr kAllocatorSpace = 0xa0000000000ULL;
|
||||
const uptr kAllocatorSize = 0x20000000000ULL; // 2T.
|
||||
# elif defined(__aarch64__)
|
||||
// AArch64/SANITIZIER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA
|
||||
// so no need to different values for different VMA.
|
||||
const uptr kAllocatorSpace = 0x10000000000ULL;
|
||||
const uptr kAllocatorSize = 0x10000000000ULL; // 3T.
|
||||
# else
|
||||
const uptr kAllocatorSpace = 0x600000000000ULL;
|
||||
const uptr kAllocatorSize = 0x40000000000ULL; // 4T.
|
||||
|
@ -108,14 +108,14 @@ static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
using namespace __asan;
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
|
||||
uptr *region_address, uptr *region_size) {
|
||||
AddressDescription descr = { name, name_size, 0, 0, 0 };
|
||||
AddressDescription descr = { name, name_size, 0, 0, nullptr };
|
||||
AsanLocateAddress(addr, &descr);
|
||||
if (region_address) *region_address = descr.region_address;
|
||||
if (region_size) *region_size = descr.region_size;
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
// FakeStack is used to detect use-after-return bugs.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_poisoning.h"
|
||||
#include "asan_thread.h"
|
||||
@ -32,7 +33,8 @@ ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {
|
||||
if (class_id <= 6) {
|
||||
for (uptr i = 0; i < (1U << class_id); i++) {
|
||||
shadow[i] = magic;
|
||||
SanitizerBreakOptimization(0); // Make sure this does not become memset.
|
||||
// Make sure this does not become memset.
|
||||
SanitizerBreakOptimization(nullptr);
|
||||
}
|
||||
} else {
|
||||
// The size class is too big, it's cheaper to poison only size bytes.
|
||||
@ -80,7 +82,9 @@ void FakeStack::PoisonAll(u8 magic) {
|
||||
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);
|
||||
@ -106,7 +110,7 @@ FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
|
||||
*SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos];
|
||||
return res;
|
||||
}
|
||||
return 0; // We are out of fake stack.
|
||||
return nullptr; // We are out of fake stack.
|
||||
}
|
||||
|
||||
uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr *frame_beg, uptr *frame_end) {
|
||||
@ -183,7 +187,7 @@ void SetTLSFakeStack(FakeStack *fs) { }
|
||||
|
||||
static FakeStack *GetFakeStack() {
|
||||
AsanThread *t = GetCurrentThread();
|
||||
if (!t) return 0;
|
||||
if (!t) return nullptr;
|
||||
return t->fake_stack();
|
||||
}
|
||||
|
||||
@ -191,7 +195,7 @@ static FakeStack *GetFakeStackFast() {
|
||||
if (FakeStack *fs = GetTLSFakeStack())
|
||||
return fs;
|
||||
if (!__asan_option_detect_stack_use_after_return)
|
||||
return 0;
|
||||
return nullptr;
|
||||
return GetFakeStack();
|
||||
}
|
||||
|
||||
@ -212,7 +216,7 @@ ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {
|
||||
SetShadow(ptr, size, class_id, kMagic8);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan;
|
||||
@ -245,13 +249,13 @@ 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 0;
|
||||
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 0;
|
||||
if (!frame) return nullptr;
|
||||
if (frame->magic != kCurrentStackFrameMagic)
|
||||
return 0;
|
||||
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);
|
||||
@ -276,4 +280,4 @@ void __asan_allocas_unpoison(uptr top, uptr bottom) {
|
||||
REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,
|
||||
(bottom - top) / SHADOW_GRANULARITY);
|
||||
}
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
|
@ -65,6 +65,7 @@ void InitializeFlags() {
|
||||
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();
|
||||
@ -115,14 +116,6 @@ void InitializeFlags() {
|
||||
ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
|
||||
#endif
|
||||
|
||||
// Let activation flags override current settings. On Android they come
|
||||
// from a system property. On other platforms this is no-op.
|
||||
if (!flags()->start_deactivated) {
|
||||
char buf[100];
|
||||
GetExtraActivationFlags(buf, sizeof(buf));
|
||||
asan_parser.ParseString(buf);
|
||||
}
|
||||
|
||||
SetVerbosity(common_flags()->verbosity);
|
||||
|
||||
// TODO(eugenis): dump all flags at verbosity>=2?
|
||||
|
@ -44,9 +44,6 @@ ASAN_FLAG(
|
||||
"to find more errors.")
|
||||
ASAN_FLAG(bool, replace_intrin, true,
|
||||
"If set, uses custom wrappers for memset/memcpy/memmove intinsics.")
|
||||
ASAN_FLAG(bool, mac_ignore_invalid_free, false,
|
||||
"Ignore invalid free() calls to work around some bugs. Used on OS X "
|
||||
"only.")
|
||||
ASAN_FLAG(bool, detect_stack_use_after_return, false,
|
||||
"Enables stack-use-after-return checking at run-time.")
|
||||
ASAN_FLAG(int, min_uar_stack_size_log, 16, // We can't do smaller anyway.
|
||||
@ -62,8 +59,6 @@ ASAN_FLAG(
|
||||
"bytes that will be filled with malloc_fill_byte on malloc.")
|
||||
ASAN_FLAG(int, malloc_fill_byte, 0xbe,
|
||||
"Value used to fill the newly allocated memory.")
|
||||
ASAN_FLAG(int, exitcode, ASAN_DEFAULT_FAILURE_EXITCODE,
|
||||
"Override the program exit status if the tool found an error.")
|
||||
ASAN_FLAG(bool, allow_user_poisoning, true,
|
||||
"If set, user may manually mark memory regions as poisoned or "
|
||||
"unpoisoned.")
|
||||
@ -77,10 +72,7 @@ ASAN_FLAG(bool, check_malloc_usable_size, true,
|
||||
"295.*.")
|
||||
ASAN_FLAG(bool, unmap_shadow_on_exit, false,
|
||||
"If set, explicitly unmaps the (huge) shadow at exit.")
|
||||
ASAN_FLAG(
|
||||
bool, abort_on_error, false,
|
||||
"If set, the tool calls abort() instead of _exit() after printing the "
|
||||
"error report.")
|
||||
ASAN_FLAG(bool, protect_shadow_gap, true, "If set, mprotect the shadow gap")
|
||||
ASAN_FLAG(bool, print_stats, false,
|
||||
"Print various statistics after printing an error message or if "
|
||||
"atexit=1.")
|
||||
@ -104,8 +96,8 @@ ASAN_FLAG(bool, poison_array_cookie, true,
|
||||
"Poison (or not) the array cookie after operator new[].")
|
||||
|
||||
// Turn off alloc/dealloc mismatch checker on Mac and Windows for now.
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=131
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=309
|
||||
// https://github.com/google/sanitizers/issues/131
|
||||
// https://github.com/google/sanitizers/issues/309
|
||||
// TODO(glider,timurrrr): Fix known issues and enable this back.
|
||||
ASAN_FLAG(bool, alloc_dealloc_mismatch,
|
||||
(SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0),
|
||||
@ -113,9 +105,6 @@ ASAN_FLAG(bool, alloc_dealloc_mismatch,
|
||||
|
||||
ASAN_FLAG(bool, new_delete_type_mismatch, true,
|
||||
"Report errors on mismatch betwen size of new and delete.")
|
||||
ASAN_FLAG(bool, strict_memcmp, true,
|
||||
"If true, assume that memcmp(p1, p2, n) always reads n bytes before "
|
||||
"comparing p1 and p2.")
|
||||
ASAN_FLAG(
|
||||
bool, strict_init_order, false,
|
||||
"If true, assume that dynamic initializers can never access globals from "
|
||||
@ -134,8 +123,8 @@ ASAN_FLAG(
|
||||
"The bigger the value the harder we try.")
|
||||
ASAN_FLAG(
|
||||
bool, detect_container_overflow, true,
|
||||
"If true, honor the container overflow annotations. "
|
||||
"See https://code.google.com/p/address-sanitizer/wiki/ContainerOverflow")
|
||||
"If true, honor the container overflow annotations. See "
|
||||
"https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow")
|
||||
ASAN_FLAG(int, detect_odr_violation, 2,
|
||||
"If >=2, detect violation of One-Definition-Rule (ODR); "
|
||||
"If ==1, detect ODR-violation only if the two variables "
|
||||
@ -143,3 +132,6 @@ ASAN_FLAG(int, detect_odr_violation, 2,
|
||||
ASAN_FLAG(bool, dump_instruction_bytes, false,
|
||||
"If true, dump 16 bytes starting at the instruction that caused SEGV")
|
||||
ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.")
|
||||
ASAN_FLAG(bool, halt_on_error, true,
|
||||
"Crash the program after printing the first error report "
|
||||
"(WARNING: USE AT YOUR OWN RISK!)")
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
// Handle globals.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
@ -167,7 +168,7 @@ static void RegisterGlobal(const Global *g) {
|
||||
l->next = list_of_all_globals;
|
||||
list_of_all_globals = l;
|
||||
if (g->has_dynamic_init) {
|
||||
if (dynamic_init_globals == 0) {
|
||||
if (!dynamic_init_globals) {
|
||||
dynamic_init_globals = new(allocator_for_globals)
|
||||
VectorOfGlobals(kDynamicInitGlobalsInitialCapacity);
|
||||
}
|
||||
@ -206,7 +207,7 @@ void StopInitOrderChecking() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
@ -27,8 +27,8 @@ extern "C" {
|
||||
// v3=>v4: added '__asan_global_source_location' to __asan_global.
|
||||
// v4=>v5: changed the semantics and format of __asan_stack_malloc_ and
|
||||
// __asan_stack_free_ functions.
|
||||
#define __asan_init __asan_init_v5
|
||||
#define __asan_init_name "__asan_init_v5"
|
||||
// v5=>v6: changed the name of the version check symbol
|
||||
#define __asan_version_mismatch_check __asan_version_mismatch_check_v6
|
||||
}
|
||||
|
||||
#endif // ASAN_INIT_VERSION_H
|
||||
|
@ -11,8 +11,8 @@
|
||||
//
|
||||
// Intercept various libc functions.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_interceptors.h"
|
||||
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
@ -27,6 +27,12 @@
|
||||
#include "sanitizer_common/sanitizer_posix.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 {
|
||||
|
||||
// Return true if we can quickly decide that the region is unpoisoned.
|
||||
@ -69,7 +75,7 @@ struct AsanInterceptorContext {
|
||||
} \
|
||||
if (!suppressed) { \
|
||||
GET_CURRENT_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \
|
||||
ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
@ -105,7 +111,7 @@ static inline bool RangesOverlap(const char *offset1, uptr length1,
|
||||
|
||||
static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) {
|
||||
#if ASAN_INTERCEPT_STRNLEN
|
||||
if (REAL(strnlen) != 0) {
|
||||
if (REAL(strnlen)) {
|
||||
return REAL(strnlen)(s, maxlen);
|
||||
}
|
||||
#endif
|
||||
@ -123,7 +129,7 @@ int OnExit() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Wrappers ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
@ -172,7 +178,7 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *)
|
||||
} while (false)
|
||||
#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name)
|
||||
// Strict init-order checking is dlopen-hostile:
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=178
|
||||
// https://github.com/google/sanitizers/issues/178
|
||||
#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \
|
||||
if (flags()->strict_init_order) { \
|
||||
StopInitOrderChecking(); \
|
||||
@ -216,7 +222,7 @@ 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))) == 0)
|
||||
atomic_load(¶m->t, memory_order_acquire))) == nullptr)
|
||||
internal_sched_yield();
|
||||
SetCurrentThread(t);
|
||||
return t->ThreadStart(GetTid(), ¶m->is_registered);
|
||||
@ -231,7 +237,7 @@ INTERCEPTOR(int, pthread_create, void *thread,
|
||||
StopInitOrderChecking();
|
||||
GET_STACK_TRACE_THREAD;
|
||||
int detached = 0;
|
||||
if (attr != 0)
|
||||
if (attr)
|
||||
REAL(pthread_attr_getdetachstate)(attr, &detached);
|
||||
ThreadStartParam param;
|
||||
atomic_store(¶m.t, 0, memory_order_relaxed);
|
||||
@ -270,14 +276,14 @@ INTERCEPTOR(void*, bsd_signal, int signum, void *handler) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#endif
|
||||
|
||||
INTERCEPTOR(void*, signal, int signum, void *handler) {
|
||||
if (!IsDeadlySignal(signum) || common_flags()->allow_user_segv_handler) {
|
||||
return REAL(signal)(signum, handler);
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act,
|
||||
struct sigaction *oldact) {
|
||||
@ -292,7 +298,7 @@ int real_sigaction(int signum, const void *act, void *oldact) {
|
||||
return REAL(sigaction)(signum, (const struct sigaction *)act,
|
||||
(struct sigaction *)oldact);
|
||||
}
|
||||
} // namespace __sanitizer
|
||||
} // namespace __sanitizer
|
||||
|
||||
#elif SANITIZER_POSIX
|
||||
// We need to have defined REAL(sigaction) on posix systems.
|
||||
@ -363,40 +369,6 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int CharCmp(unsigned char c1, unsigned char c2) {
|
||||
return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1;
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) {
|
||||
void *ctx;
|
||||
ASAN_INTERCEPTOR_ENTER(ctx, memcmp);
|
||||
if (UNLIKELY(!asan_inited)) return internal_memcmp(a1, a2, size);
|
||||
ENSURE_ASAN_INITED();
|
||||
if (flags()->replace_intrin) {
|
||||
if (flags()->strict_memcmp) {
|
||||
// Check the entire regions even if the first bytes of the buffers are
|
||||
// different.
|
||||
ASAN_READ_RANGE(ctx, a1, size);
|
||||
ASAN_READ_RANGE(ctx, a2, size);
|
||||
// Fallthrough to REAL(memcmp) below.
|
||||
} else {
|
||||
unsigned char c1 = 0, c2 = 0;
|
||||
const unsigned char *s1 = (const unsigned char*)a1;
|
||||
const unsigned char *s2 = (const unsigned char*)a2;
|
||||
uptr i;
|
||||
for (i = 0; i < size; i++) {
|
||||
c1 = s1[i];
|
||||
c2 = s2[i];
|
||||
if (c1 != c2) break;
|
||||
}
|
||||
ASAN_READ_RANGE(ctx, s1, Min(i + 1, size));
|
||||
ASAN_READ_RANGE(ctx, s2, Min(i + 1, size));
|
||||
return CharCmp(c1, c2);
|
||||
}
|
||||
}
|
||||
return REAL(memcmp(a1, a2, size));
|
||||
}
|
||||
|
||||
// memcpy is called during __asan_init() from the internals of printf(...).
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
|
||||
@ -743,7 +715,7 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg,
|
||||
#endif
|
||||
ENSURE_ASAN_INITED();
|
||||
int res = REAL(__cxa_atexit)(func, arg, dso_handle);
|
||||
REAL(__cxa_atexit)(AtCxaAtexit, 0, 0);
|
||||
REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr);
|
||||
return res;
|
||||
}
|
||||
#endif // ASAN_INTERCEPT___CXA_ATEXIT
|
||||
@ -767,7 +739,6 @@ void InitializeAsanInterceptors() {
|
||||
InitializeCommonInterceptors();
|
||||
|
||||
// Intercept mem* functions.
|
||||
ASAN_INTERCEPT_FUNC(memcmp);
|
||||
ASAN_INTERCEPT_FUNC(memmove);
|
||||
ASAN_INTERCEPT_FUNC(memset);
|
||||
if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) {
|
||||
@ -806,9 +777,8 @@ void InitializeAsanInterceptors() {
|
||||
ASAN_INTERCEPT_FUNC(sigaction);
|
||||
#if SANITIZER_ANDROID
|
||||
ASAN_INTERCEPT_FUNC(bsd_signal);
|
||||
#else
|
||||
ASAN_INTERCEPT_FUNC(signal);
|
||||
#endif
|
||||
ASAN_INTERCEPT_FUNC(signal);
|
||||
#endif
|
||||
#if ASAN_INTERCEPT_SWAPCONTEXT
|
||||
ASAN_INTERCEPT_FUNC(swapcontext);
|
||||
@ -827,7 +797,11 @@ void InitializeAsanInterceptors() {
|
||||
|
||||
// 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
|
||||
|
||||
@ -845,4 +819,4 @@ void InitializeAsanInterceptors() {
|
||||
VReport(1, "AddressSanitizer: libc interceptors initialized\n");
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
@ -98,6 +98,12 @@ DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act,
|
||||
if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \
|
||||
VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \
|
||||
} 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"); \
|
||||
} while (0)
|
||||
#else
|
||||
// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
|
||||
#define ASAN_INTERCEPT_FUNC(name)
|
||||
|
@ -27,10 +27,14 @@ using __sanitizer::uptr;
|
||||
extern "C" {
|
||||
// This function should be called at the very beginning of the process,
|
||||
// before any instrumented code is executed and before any call to malloc.
|
||||
// Please note that __asan_init is a macro that is replaced with
|
||||
// __asan_init_vXXX at compile-time.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_init();
|
||||
|
||||
// This function exists purely to get a linker/loader error when using
|
||||
// incompatible versions of instrumentation and runtime library. Please note
|
||||
// that __asan_version_mismatch_check is a macro that is replaced with
|
||||
// __asan_version_mismatch_check_vXXX at compile-time.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_version_mismatch_check();
|
||||
|
||||
// This structure is used to describe the source location of a place where
|
||||
// global was defined.
|
||||
struct __asan_global_source_location {
|
||||
@ -130,8 +134,6 @@ extern "C" {
|
||||
void __asan_report_error(uptr pc, uptr bp, uptr sp,
|
||||
uptr addr, int is_write, uptr access_size, u32 exp);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
int __asan_set_error_exit_code(int exit_code);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_set_death_callback(void (*callback)(void));
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
@ -165,6 +167,19 @@ extern "C" {
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN(uptr p, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN(uptr p, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load1_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load2_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load4_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load8_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_load16_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store1_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store2_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store4_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store8_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_store16_noabort(uptr p);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_loadN_noabort(uptr p, uptr size);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_storeN_noabort(uptr p, uptr size);
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load1(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load2(uptr p, u32 exp);
|
||||
SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load4(uptr p, u32 exp);
|
||||
|
@ -21,8 +21,6 @@
|
||||
#include "sanitizer_common/sanitizer_stacktrace.h"
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
|
||||
#define ASAN_DEFAULT_FAILURE_EXITCODE 1
|
||||
|
||||
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
|
||||
# error "The AddressSanitizer run-time should not be"
|
||||
" instrumented by AddressSanitizer"
|
||||
@ -75,12 +73,9 @@ void *AsanDoesNotSupportStaticLinkage();
|
||||
void AsanCheckDynamicRTPrereqs();
|
||||
void AsanCheckIncompatibleRT();
|
||||
|
||||
void AsanOnSIGSEGV(int, void *siginfo, void *context);
|
||||
void AsanOnDeadlySignal(int, void *siginfo, void *context);
|
||||
|
||||
void DisableReexec();
|
||||
void MaybeReexec();
|
||||
void ReadContextStack(void *context, uptr *stack, uptr *ssize);
|
||||
void AsanPlatformThreadInit();
|
||||
void StopInitOrderChecking();
|
||||
|
||||
// Wrapper for TLS/TSD.
|
||||
|
@ -70,14 +70,6 @@ namespace __asan {
|
||||
|
||||
void InitializePlatformInterceptors() {}
|
||||
|
||||
void DisableReexec() {
|
||||
// No need to re-exec on Linux.
|
||||
}
|
||||
|
||||
void MaybeReexec() {
|
||||
// No need to re-exec on Linux.
|
||||
}
|
||||
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
// This will fail to link with -static.
|
||||
return &_DYNAMIC; // defined in link.h
|
||||
@ -117,7 +109,7 @@ void AsanCheckDynamicRTPrereqs() {
|
||||
return;
|
||||
|
||||
// Ensure that dynamic RT is the first DSO in the list
|
||||
const char *first_dso_name = 0;
|
||||
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; "
|
||||
@ -142,7 +134,8 @@ void AsanCheckIncompatibleRT() {
|
||||
// system libraries, causing crashes later in ASan initialization.
|
||||
MemoryMappingLayout proc_maps(/*cache_enabled*/true);
|
||||
char filename[128];
|
||||
while (proc_maps.Next(0, 0, 0, filename, sizeof(filename), 0)) {
|
||||
while (proc_maps.Next(nullptr, nullptr, nullptr, filename,
|
||||
sizeof(filename), nullptr)) {
|
||||
if (IsDynamicRTName(filename)) {
|
||||
Report("Your application is linked against "
|
||||
"incompatible ASan runtimes.\n");
|
||||
@ -155,11 +148,7 @@ void AsanCheckIncompatibleRT() {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // SANITIZER_ANDROID
|
||||
|
||||
void AsanPlatformThreadInit() {
|
||||
// Nothing here for now.
|
||||
}
|
||||
#endif // SANITIZER_ANDROID
|
||||
|
||||
#if !SANITIZER_ANDROID
|
||||
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
||||
@ -177,6 +166,6 @@ void *AsanDlSymNext(const char *sym) {
|
||||
return dlsym(RTLD_NEXT, sym);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
#endif // SANITIZER_FREEBSD || SANITIZER_LINUX
|
||||
|
@ -24,26 +24,17 @@
|
||||
#include "sanitizer_common/sanitizer_libc.h"
|
||||
#include "sanitizer_common/sanitizer_mac.h"
|
||||
|
||||
#if !SANITIZER_IOS
|
||||
#include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron
|
||||
#else
|
||||
extern "C" {
|
||||
extern char ***_NSGetArgv(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h> // for dladdr()
|
||||
#include <fcntl.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <mach-o/dyld.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 <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h> // for free()
|
||||
#include <unistd.h>
|
||||
#include <libkern/OSAtomic.h>
|
||||
|
||||
namespace __asan {
|
||||
|
||||
@ -52,187 +43,12 @@ void InitializePlatformInterceptors() {}
|
||||
bool PlatformHasDifferentMemcpyAndMemmove() {
|
||||
// On OS X 10.7 memcpy() and memmove() are both resolved
|
||||
// into memmove$VARIANT$sse42.
|
||||
// See also http://code.google.com/p/address-sanitizer/issues/detail?id=34.
|
||||
// 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;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
void __asan_init();
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
static bool reexec_disabled = false;
|
||||
|
||||
void DisableReexec() {
|
||||
reexec_disabled = true;
|
||||
}
|
||||
|
||||
bool DyldNeedsEnvVariable() {
|
||||
// If running on OS X 10.11+ or iOS 9.0+, dyld will interpose even if
|
||||
// DYLD_INSERT_LIBRARIES is not set.
|
||||
|
||||
#if SANITIZER_IOSSIM
|
||||
// GetMacosVersion will not work for the simulator, whose kernel version
|
||||
// is tied to the host. Use a weak linking hack for the simulator.
|
||||
// This API was introduced in the same version of the OS as the dyld
|
||||
// optimization.
|
||||
|
||||
// Check for presence of a symbol that is available on OS X 10.11+, iOS 9.0+.
|
||||
return (dlsym(RTLD_NEXT, "mach_memory_info") == nullptr);
|
||||
#else
|
||||
return (GetMacosVersion() <= MACOS_VERSION_YOSEMITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void MaybeReexec() {
|
||||
if (reexec_disabled) return;
|
||||
|
||||
// Make sure the dynamic ASan runtime library is preloaded so that the
|
||||
// wrappers work. If it is not, set DYLD_INSERT_LIBRARIES and re-exec
|
||||
// ourselves.
|
||||
Dl_info info;
|
||||
CHECK(dladdr((void*)((uptr)__asan_init), &info));
|
||||
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 && REAL(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.
|
||||
char program_name[1024];
|
||||
uint32_t buf_size = sizeof(program_name);
|
||||
_NSGetExecutablePath(program_name, &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 ASan wrappers.\n");
|
||||
execv(program_name, *_NSGetArgv());
|
||||
|
||||
// We get here only if execv() failed.
|
||||
Report("ERROR: The process is launched without DYLD_INSERT_LIBRARIES, "
|
||||
"which is required for ASan to work. ASan 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);
|
||||
CHECK("execv failed" && 0);
|
||||
}
|
||||
|
||||
if (!lib_is_in_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);
|
||||
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 = REAL(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);
|
||||
}
|
||||
|
||||
// No-op. Mac does not support static linkage anyway.
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
return 0;
|
||||
@ -244,9 +60,6 @@ void AsanCheckDynamicRTPrereqs() {}
|
||||
// No-op. Mac does not support static linkage anyway.
|
||||
void AsanCheckIncompatibleRT() {}
|
||||
|
||||
void AsanPlatformThreadInit() {
|
||||
}
|
||||
|
||||
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
@ -26,13 +26,25 @@
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
static const uptr kCallocPoolSize = 1024;
|
||||
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
|
||||
static bool IsInCallocPool(const void *ptr) {
|
||||
sptr off = (sptr)ptr - (sptr)calloc_memory_for_dlsym;
|
||||
return 0 <= off && off < (sptr)kCallocPoolSize;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
GET_STACK_TRACE_FREE;
|
||||
if (UNLIKELY(IsInCallocPool(ptr)))
|
||||
return;
|
||||
asan_free(ptr, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, cfree, void *ptr) {
|
||||
GET_STACK_TRACE_FREE;
|
||||
if (UNLIKELY(IsInCallocPool(ptr)))
|
||||
return;
|
||||
asan_free(ptr, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
@ -44,8 +56,6 @@ INTERCEPTOR(void*, malloc, uptr size) {
|
||||
INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
||||
if (UNLIKELY(!asan_inited)) {
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
const uptr kCallocPoolSize = 1024;
|
||||
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static uptr allocated;
|
||||
uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
|
||||
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
|
||||
@ -59,6 +69,13 @@ INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) {
|
||||
|
||||
INTERCEPTOR(void*, realloc, void *ptr, uptr size) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
if (UNLIKELY(IsInCallocPool(ptr))) {
|
||||
uptr offset = (uptr)ptr - (uptr)calloc_memory_for_dlsym;
|
||||
uptr copy_size = Min(size, kCallocPoolSize - offset);
|
||||
void *new_ptr = asan_malloc(size, &stack);
|
||||
internal_memcpy(new_ptr, ptr, copy_size);
|
||||
return new_ptr;
|
||||
}
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
|
@ -15,348 +15,47 @@
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_MAC
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <CoreFoundation/CFBase.h>
|
||||
#include <dlfcn.h>
|
||||
#include <malloc/malloc.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_report.h"
|
||||
#include "asan_stack.h"
|
||||
#include "asan_stats.h"
|
||||
#include "sanitizer_common/sanitizer_mac.h"
|
||||
|
||||
// Similar code is used in Google Perftools,
|
||||
// http://code.google.com/p/google-perftools.
|
||||
|
||||
// ---------------------- Replacement functions ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// TODO(glider): do we need both zones?
|
||||
static malloc_zone_t *system_malloc_zone = 0;
|
||||
static malloc_zone_t asan_zone;
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_create_zone,
|
||||
vm_size_t start_size, unsigned zone_flags) {
|
||||
ENSURE_ASAN_INITED();
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
uptr page_size = GetPageSizeCached();
|
||||
uptr allocated_size = RoundUpTo(sizeof(asan_zone), page_size);
|
||||
malloc_zone_t *new_zone =
|
||||
(malloc_zone_t*)asan_memalign(page_size, allocated_size,
|
||||
&stack, FROM_MALLOC);
|
||||
internal_memcpy(new_zone, &asan_zone, sizeof(asan_zone));
|
||||
new_zone->zone_name = NULL; // The name will be changed anyway.
|
||||
if (GetMacosVersion() >= MACOS_VERSION_LION) {
|
||||
// Prevent the client app from overwriting the zone contents.
|
||||
// Library functions that need to modify the zone will set PROT_WRITE on it.
|
||||
// This matches the behavior of malloc_create_zone() on OSX 10.7 and higher.
|
||||
mprotect(new_zone, allocated_size, PROT_READ);
|
||||
}
|
||||
return new_zone;
|
||||
}
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_default_zone, void) {
|
||||
ENSURE_ASAN_INITED();
|
||||
return &asan_zone;
|
||||
}
|
||||
|
||||
INTERCEPTOR(malloc_zone_t *, malloc_default_purgeable_zone, void) {
|
||||
// FIXME: ASan should support purgeable allocations.
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=139
|
||||
ENSURE_ASAN_INITED();
|
||||
return &asan_zone;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, malloc_make_purgeable, void *ptr) {
|
||||
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
||||
// for now.
|
||||
ENSURE_ASAN_INITED();
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) {
|
||||
// FIXME: ASan should support purgeable allocations. Ignoring them is fine
|
||||
// for now.
|
||||
ENSURE_ASAN_INITED();
|
||||
// Must return 0 if the contents were not purged since the last call to
|
||||
// malloc_make_purgeable().
|
||||
return 0;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) {
|
||||
ENSURE_ASAN_INITED();
|
||||
// Allocate |strlen("asan-") + 1 + internal_strlen(name)| bytes.
|
||||
size_t buflen = 6 + (name ? internal_strlen(name) : 0);
|
||||
InternalScopedString new_name(buflen);
|
||||
if (name && zone->introspect == asan_zone.introspect) {
|
||||
new_name.append("asan-%s", name);
|
||||
name = new_name.data();
|
||||
}
|
||||
|
||||
// Call the system malloc's implementation for both external and our zones,
|
||||
// since that appropriately changes VM region protections on the zone.
|
||||
REAL(malloc_set_zone_name)(zone, name);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, malloc, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
void *res = asan_malloc(size, &stack);
|
||||
return res;
|
||||
}
|
||||
|
||||
INTERCEPTOR(void, free, void *ptr) {
|
||||
ENSURE_ASAN_INITED();
|
||||
if (!ptr) return;
|
||||
GET_STACK_TRACE_FREE;
|
||||
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_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);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, realloc, void *ptr, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, calloc, size_t nmemb, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
INTERCEPTOR(void *, valloc, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
INTERCEPTOR(size_t, malloc_good_size, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
return asan_zone.introspect->good_size(&asan_zone, size);
|
||||
}
|
||||
|
||||
INTERCEPTOR(int, posix_memalign, void **memptr, size_t alignment, size_t size) {
|
||||
ENSURE_ASAN_INITED();
|
||||
CHECK(memptr);
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
void *result = asan_memalign(alignment, size, &stack, FROM_MALLOC);
|
||||
if (result) {
|
||||
*memptr = result;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// TODO(glider): the __asan_mz_* functions should be united with the Linux
|
||||
// wrappers, as they are basically copied from there.
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
size_t __asan_mz_size(malloc_zone_t* zone, const void* ptr) {
|
||||
return asan_mz_size(ptr);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__asan_mz_malloc(malloc_zone_t *zone, uptr size) {
|
||||
if (UNLIKELY(!asan_inited)) {
|
||||
CHECK(system_malloc_zone);
|
||||
return malloc_zone_malloc(system_malloc_zone, size);
|
||||
}
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__asan_mz_calloc(malloc_zone_t *zone, size_t nmemb, size_t size) {
|
||||
if (UNLIKELY(!asan_inited)) {
|
||||
// Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
|
||||
const size_t kCallocPoolSize = 1024;
|
||||
static uptr calloc_memory_for_dlsym[kCallocPoolSize];
|
||||
static size_t allocated;
|
||||
size_t size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize;
|
||||
void *mem = (void*)&calloc_memory_for_dlsym[allocated];
|
||||
allocated += size_in_words;
|
||||
CHECK(allocated < kCallocPoolSize);
|
||||
return mem;
|
||||
}
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_calloc(nmemb, size, &stack);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__asan_mz_valloc(malloc_zone_t *zone, size_t size) {
|
||||
if (UNLIKELY(!asan_inited)) {
|
||||
CHECK(system_malloc_zone);
|
||||
return malloc_zone_valloc(system_malloc_zone, size);
|
||||
}
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_memalign(GetPageSizeCached(), size, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
#define GET_ZONE_FOR_PTR(ptr) \
|
||||
malloc_zone_t *zone_ptr = malloc_zone_from_ptr(ptr); \
|
||||
const char *zone_name = (zone_ptr == 0) ? 0 : zone_ptr->zone_name
|
||||
|
||||
void ALWAYS_INLINE free_common(void *context, void *ptr) {
|
||||
if (!ptr) return;
|
||||
GET_STACK_TRACE_FREE;
|
||||
// FIXME: need to retire this flag.
|
||||
if (!flags()->mac_ignore_invalid_free) {
|
||||
asan_free(ptr, &stack, FROM_MALLOC);
|
||||
} else {
|
||||
GET_ZONE_FOR_PTR(ptr);
|
||||
WarnMacFreeUnallocated((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(glider): the allocation callbacks need to be refactored.
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_mz_free(malloc_zone_t *zone, void *ptr) {
|
||||
free_common(zone, ptr);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__asan_mz_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
|
||||
if (!ptr) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_malloc(size, &stack);
|
||||
} else {
|
||||
if (asan_mz_size(ptr)) {
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_realloc(ptr, size, &stack);
|
||||
} else {
|
||||
// We can't recover from reallocating an unknown address, because
|
||||
// this would require reading at most |size| bytes from
|
||||
// potentially unaccessible memory.
|
||||
GET_STACK_TRACE_FREE;
|
||||
GET_ZONE_FOR_PTR(ptr);
|
||||
ReportMacMzReallocUnknown((uptr)ptr, (uptr)zone_ptr, zone_name, &stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __asan_mz_destroy(malloc_zone_t* zone) {
|
||||
// A no-op -- we will not be destroyed!
|
||||
Report("__asan_mz_destroy() called -- ignoring\n");
|
||||
}
|
||||
|
||||
extern "C"
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void *__asan_mz_memalign(malloc_zone_t *zone, size_t align, size_t size) {
|
||||
if (UNLIKELY(!asan_inited)) {
|
||||
CHECK(system_malloc_zone);
|
||||
return malloc_zone_memalign(system_malloc_zone, align, size);
|
||||
}
|
||||
GET_STACK_TRACE_MALLOC;
|
||||
return asan_memalign(align, size, &stack, FROM_MALLOC);
|
||||
}
|
||||
|
||||
// This function is currently unused, and we build with -Werror.
|
||||
#if 0
|
||||
void __asan_mz_free_definite_size(
|
||||
malloc_zone_t* zone, void *ptr, size_t size) {
|
||||
// TODO(glider): check that |size| is valid.
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
#endif
|
||||
|
||||
kern_return_t mi_enumerator(task_t task, void *,
|
||||
unsigned type_mask, vm_address_t zone_address,
|
||||
memory_reader_t reader,
|
||||
vm_range_recorder_t recorder) {
|
||||
// Should enumerate all the pointers we have. Seems like a lot of work.
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
size_t mi_good_size(malloc_zone_t *zone, size_t size) {
|
||||
// I think it's always safe to return size, but we maybe could do better.
|
||||
return size;
|
||||
}
|
||||
|
||||
boolean_t mi_check(malloc_zone_t *zone) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void mi_log(malloc_zone_t *zone, void *address) {
|
||||
// I don't think we support anything like this
|
||||
}
|
||||
|
||||
void mi_force_lock(malloc_zone_t *zone) {
|
||||
asan_mz_force_lock();
|
||||
}
|
||||
|
||||
void mi_force_unlock(malloc_zone_t *zone) {
|
||||
asan_mz_force_unlock();
|
||||
}
|
||||
|
||||
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
|
||||
AsanMallocStats malloc_stats;
|
||||
FillMallocStatistics(&malloc_stats);
|
||||
CHECK(sizeof(malloc_statistics_t) == sizeof(AsanMallocStats));
|
||||
#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
|
||||
|
||||
boolean_t mi_zone_locked(malloc_zone_t *zone) {
|
||||
// UNIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
#include "sanitizer_common/sanitizer_malloc_mac.inc"
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void ReplaceSystemMalloc() {
|
||||
static malloc_introspection_t asan_introspection;
|
||||
// Ok to use internal_memset, these places are not performance-critical.
|
||||
internal_memset(&asan_introspection, 0, sizeof(asan_introspection));
|
||||
|
||||
asan_introspection.enumerator = &mi_enumerator;
|
||||
asan_introspection.good_size = &mi_good_size;
|
||||
asan_introspection.check = &mi_check;
|
||||
asan_introspection.print = &mi_print;
|
||||
asan_introspection.log = &mi_log;
|
||||
asan_introspection.force_lock = &mi_force_lock;
|
||||
asan_introspection.force_unlock = &mi_force_unlock;
|
||||
asan_introspection.statistics = &mi_statistics;
|
||||
asan_introspection.zone_locked = &mi_zone_locked;
|
||||
|
||||
internal_memset(&asan_zone, 0, sizeof(malloc_zone_t));
|
||||
|
||||
// Use version 6 for OSX >= 10.6.
|
||||
asan_zone.version = 6;
|
||||
asan_zone.zone_name = "asan";
|
||||
asan_zone.size = &__asan_mz_size;
|
||||
asan_zone.malloc = &__asan_mz_malloc;
|
||||
asan_zone.calloc = &__asan_mz_calloc;
|
||||
asan_zone.valloc = &__asan_mz_valloc;
|
||||
asan_zone.free = &__asan_mz_free;
|
||||
asan_zone.realloc = &__asan_mz_realloc;
|
||||
asan_zone.destroy = &__asan_mz_destroy;
|
||||
asan_zone.batch_malloc = 0;
|
||||
asan_zone.batch_free = 0;
|
||||
asan_zone.free_definite_size = 0;
|
||||
asan_zone.memalign = &__asan_mz_memalign;
|
||||
asan_zone.introspect = &asan_introspection;
|
||||
|
||||
// Register the ASan zone.
|
||||
malloc_zone_register(&asan_zone);
|
||||
}
|
||||
} // namespace __asan
|
||||
|
||||
#endif // SANITIZER_MAC
|
||||
#endif
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "asan_internal.h"
|
||||
|
||||
// The full explanation of the memory mapping could be found here:
|
||||
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm
|
||||
// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm
|
||||
//
|
||||
// Typical shadow mapping on Linux/x86_64 with SHADOW_OFFSET == 0x00007fff8000:
|
||||
// || `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
|
||||
@ -73,6 +73,20 @@
|
||||
// || `[0x2000000000, 0x23ffffffff]` || LowShadow ||
|
||||
// || `[0x0000000000, 0x1fffffffff]` || LowMem ||
|
||||
//
|
||||
// Default Linux/AArch64 (39-bit VMA) mapping:
|
||||
// || `[0x2000000000, 0x7fffffffff]` || highmem ||
|
||||
// || `[0x1400000000, 0x1fffffffff]` || highshadow ||
|
||||
// || `[0x1200000000, 0x13ffffffff]` || shadowgap ||
|
||||
// || `[0x1000000000, 0x11ffffffff]` || lowshadow ||
|
||||
// || `[0x0000000000, 0x0fffffffff]` || lowmem ||
|
||||
//
|
||||
// Default Linux/AArch64 (42-bit VMA) mapping:
|
||||
// || `[0x10000000000, 0x3ffffffffff]` || highmem ||
|
||||
// || `[0x0a000000000, 0x0ffffffffff]` || highshadow ||
|
||||
// || `[0x09000000000, 0x09fffffffff]` || shadowgap ||
|
||||
// || `[0x08000000000, 0x08fffffffff]` || lowshadow ||
|
||||
// || `[0x00000000000, 0x07fffffffff]` || lowmem ||
|
||||
//
|
||||
// Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000:
|
||||
// || `[0x500000000000, 0x7fffffffffff]` || HighMem ||
|
||||
// || `[0x4a0000000000, 0x4fffffffffff]` || HighShadow ||
|
||||
@ -113,11 +127,12 @@ static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
|
||||
static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
|
||||
#define SHADOW_SCALE kDefaultShadowScale
|
||||
#if SANITIZER_ANDROID
|
||||
# define SHADOW_OFFSET (0)
|
||||
#else
|
||||
# if SANITIZER_WORDSIZE == 32
|
||||
# if defined(__mips__)
|
||||
|
||||
|
||||
#if SANITIZER_WORDSIZE == 32
|
||||
# if SANITIZER_ANDROID
|
||||
# define SHADOW_OFFSET (0)
|
||||
# elif defined(__mips__)
|
||||
# define SHADOW_OFFSET kMIPS32_ShadowOffset32
|
||||
# elif SANITIZER_FREEBSD
|
||||
# define SHADOW_OFFSET kFreeBSD_ShadowOffset32
|
||||
@ -130,7 +145,7 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShadowOffset32
|
||||
# endif
|
||||
# else
|
||||
#else
|
||||
# if defined(__aarch64__)
|
||||
# define SHADOW_OFFSET kAArch64_ShadowOffset64
|
||||
# elif defined(__powerpc64__)
|
||||
@ -148,7 +163,6 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
# else
|
||||
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define SHADOW_GRANULARITY (1ULL << SHADOW_SCALE)
|
||||
@ -171,7 +185,8 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000
|
||||
|
||||
// With the zero shadow base we can not actually map pages starting from 0.
|
||||
// This constant is somewhat arbitrary.
|
||||
#define kZeroBaseShadowStart (1 << 18)
|
||||
#define kZeroBaseShadowStart 0
|
||||
#define kZeroBaseMaxShadowStart (1 << 18)
|
||||
|
||||
#define kShadowGapBeg (kLowShadowEnd ? kLowShadowEnd + 1 \
|
||||
: kZeroBaseShadowStart)
|
||||
|
@ -30,7 +30,7 @@
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
// This code has issues on OSX.
|
||||
// See https://code.google.com/p/address-sanitizer/issues/detail?id=131.
|
||||
// See https://github.com/google/sanitizers/issues/131.
|
||||
|
||||
// Fake std::nothrow_t to avoid including <new>.
|
||||
namespace std {
|
||||
@ -90,11 +90,11 @@ INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) {
|
||||
|
||||
#if !SANITIZER_MAC
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete(void *ptr) throw() {
|
||||
void operator delete(void *ptr) NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr) throw() {
|
||||
void operator delete[](void *ptr) NOEXCEPT {
|
||||
OPERATOR_DELETE_BODY(FROM_NEW_BR);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
@ -106,12 +106,12 @@ 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) throw() {
|
||||
void operator delete(void *ptr, size_t size) NOEXCEPT {
|
||||
GET_STACK_TRACE_FREE;
|
||||
asan_sized_free(ptr, size, &stack, FROM_NEW);
|
||||
}
|
||||
CXX_OPERATOR_ATTRIBUTE
|
||||
void operator delete[](void *ptr, size_t size) throw() {
|
||||
void operator delete[](void *ptr, size_t size) NOEXCEPT {
|
||||
GET_STACK_TRACE_FREE;
|
||||
asan_sized_free(ptr, size, &stack, FROM_NEW_BR);
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ using namespace __asan; // NOLINT
|
||||
// that user program (un)poisons the memory it owns. It poisons memory
|
||||
// conservatively, and unpoisons progressively to make sure asan shadow
|
||||
// mapping invariant is preserved (see detailed mapping description here:
|
||||
// http://code.google.com/p/address-sanitizer/wiki/AddressSanitizerAlgorithm).
|
||||
// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm).
|
||||
//
|
||||
// * if user asks to poison region [left, right), the program poisons
|
||||
// at least [left, AlignDown(right)).
|
||||
@ -354,7 +354,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
|
||||
// Make a quick sanity check that we are indeed in this state.
|
||||
//
|
||||
// FIXME: Two of these three checks are disabled until we fix
|
||||
// https://code.google.com/p/address-sanitizer/issues/detail?id=258.
|
||||
// https://github.com/google/sanitizers/issues/258.
|
||||
// if (d1 != d2)
|
||||
// CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1);
|
||||
if (a + granularity <= d1)
|
||||
@ -375,10 +375,10 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p,
|
||||
}
|
||||
}
|
||||
|
||||
int __sanitizer_verify_contiguous_container(const void *beg_p,
|
||||
const void *mid_p,
|
||||
const void *end_p) {
|
||||
if (!flags()->detect_container_overflow) return 1;
|
||||
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);
|
||||
@ -395,17 +395,24 @@ int __sanitizer_verify_contiguous_container(const void *beg_p,
|
||||
uptr r3_end = end;
|
||||
for (uptr i = r1_beg; i < r1_end; i++)
|
||||
if (AddressIsPoisoned(i))
|
||||
return 0;
|
||||
return reinterpret_cast<const void *>(i);
|
||||
for (uptr i = r2_beg; i < mid; i++)
|
||||
if (AddressIsPoisoned(i))
|
||||
return 0;
|
||||
return reinterpret_cast<const void *>(i);
|
||||
for (uptr i = mid; i < r2_end; i++)
|
||||
if (!AddressIsPoisoned(i))
|
||||
return 0;
|
||||
return reinterpret_cast<const void *>(i);
|
||||
for (uptr i = r3_beg; i < r3_end; i++)
|
||||
if (!AddressIsPoisoned(i))
|
||||
return 0;
|
||||
return 1;
|
||||
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
|
||||
|
@ -33,11 +33,11 @@
|
||||
|
||||
namespace __asan {
|
||||
|
||||
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||
void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
|
||||
ScopedDeadlySignal signal_scope(GetCurrentThread());
|
||||
int code = (int)((siginfo_t*)siginfo)->si_code;
|
||||
// Write the first message using the bullet-proof write.
|
||||
if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die();
|
||||
if (18 != internal_write(2, "ASAN:DEADLYSIGNAL\n", 18)) Die();
|
||||
SignalContext sig = SignalContext::Create(siginfo, context);
|
||||
|
||||
// Access at a reasonable offset above SP, or slightly below it (to account
|
||||
@ -75,8 +75,12 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||
// unaligned memory access.
|
||||
if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
|
||||
ReportStackOverflow(sig);
|
||||
else if (signo == SIGFPE)
|
||||
ReportDeadlySignal("FPE", sig);
|
||||
else if (signo == SIGILL)
|
||||
ReportDeadlySignal("ILL", sig);
|
||||
else
|
||||
ReportSIGSEGV("SEGV", sig);
|
||||
ReportDeadlySignal("SEGV", sig);
|
||||
}
|
||||
|
||||
// ---------------------- TSD ---------------- {{{1
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
// This file contains error reporting code.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_flags.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
@ -27,9 +28,11 @@ namespace __asan {
|
||||
|
||||
// -------------------- User-specified callbacks ----------------- {{{1
|
||||
static void (*error_report_callback)(const char*);
|
||||
static char *error_message_buffer = 0;
|
||||
static char *error_message_buffer = nullptr;
|
||||
static uptr error_message_buffer_pos = 0;
|
||||
static uptr error_message_buffer_size = 0;
|
||||
static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED);
|
||||
static const unsigned kAsanBuggyPcPoolSize = 25;
|
||||
static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
|
||||
|
||||
struct ReportData {
|
||||
uptr pc;
|
||||
@ -45,16 +48,20 @@ static bool report_happened = false;
|
||||
static ReportData report_data = {};
|
||||
|
||||
void AppendToErrorMessageBuffer(const char *buffer) {
|
||||
if (error_message_buffer) {
|
||||
uptr length = internal_strlen(buffer);
|
||||
CHECK_GE(error_message_buffer_size, error_message_buffer_pos);
|
||||
uptr remaining = error_message_buffer_size - error_message_buffer_pos;
|
||||
internal_strncpy(error_message_buffer + error_message_buffer_pos,
|
||||
buffer, remaining);
|
||||
error_message_buffer[error_message_buffer_size - 1] = '\0';
|
||||
// FIXME: reallocate the buffer instead of truncating the message.
|
||||
error_message_buffer_pos += Min(remaining, length);
|
||||
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);
|
||||
}
|
||||
|
||||
// ---------------------- Decorator ------------------------------ {{{1
|
||||
@ -373,7 +380,7 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
|
||||
uptr next_var_beg) {
|
||||
uptr var_end = var.beg + var.size;
|
||||
uptr addr_end = addr + access_size;
|
||||
const char *pos_descr = 0;
|
||||
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) {
|
||||
@ -544,7 +551,7 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
|
||||
StackTrace alloc_stack = chunk.GetAllocStack();
|
||||
char tname[128];
|
||||
Decorator d;
|
||||
AsanThreadContext *free_thread = 0;
|
||||
AsanThreadContext *free_thread = nullptr;
|
||||
if (chunk.FreeTid() != kInvalidTid) {
|
||||
free_thread = GetThreadContextByTidLocked(chunk.FreeTid());
|
||||
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
|
||||
@ -621,26 +628,90 @@ void DescribeThread(AsanThreadContext *context) {
|
||||
// immediately after printing error report.
|
||||
class ScopedInErrorReport {
|
||||
public:
|
||||
explicit ScopedInErrorReport(ReportData *report = nullptr) {
|
||||
static atomic_uint32_t num_calls;
|
||||
static u32 reporting_thread_tid;
|
||||
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
|
||||
explicit ScopedInErrorReport(ReportData *report = nullptr,
|
||||
bool fatal = false) {
|
||||
halt_on_error_ = fatal || flags()->halt_on_error;
|
||||
|
||||
if (lock_.TryLock()) {
|
||||
StartReporting(report);
|
||||
return;
|
||||
}
|
||||
|
||||
// ASan found two bugs in different threads simultaneously.
|
||||
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
if (reporting_thread_tid_ == current_tid ||
|
||||
reporting_thread_tid_ == kInvalidTid) {
|
||||
// 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.
|
||||
const char msg[] = "AddressSanitizer: nested bug in the same thread, "
|
||||
"aborting.\n";
|
||||
WriteToFile(kStderrFd, msg, sizeof(msg));
|
||||
|
||||
internal__exit(common_flags()->exitcode);
|
||||
}
|
||||
|
||||
if (halt_on_error_) {
|
||||
// Do not print more than one report, otherwise they will mix up.
|
||||
// Error reporting functions shouldn't return at this situation, as
|
||||
// they are defined as no-return.
|
||||
// they are effectively no-returns.
|
||||
|
||||
Report("AddressSanitizer: while reporting a bug found another one. "
|
||||
"Ignoring.\n");
|
||||
u32 current_tid = GetCurrentTidOrInvalid();
|
||||
if (current_tid != reporting_thread_tid) {
|
||||
// ASan found two bugs in different threads simultaneously. Sleep
|
||||
// long enough to make sure that the thread which started to print
|
||||
// an error report will finish doing it.
|
||||
SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
|
||||
}
|
||||
"Ignoring.\n");
|
||||
|
||||
// Sleep long enough to make sure that the thread which started
|
||||
// to print an error report will finish doing it.
|
||||
SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
|
||||
|
||||
// If we're still not dead for some reason, use raw _exit() instead of
|
||||
// Die() to bypass any additional checks.
|
||||
internal__exit(flags()->exitcode);
|
||||
internal__exit(common_flags()->exitcode);
|
||||
} else {
|
||||
// The other thread will eventually finish reporting
|
||||
// so it's safe to wait
|
||||
lock_.Lock();
|
||||
}
|
||||
|
||||
StartReporting(report);
|
||||
}
|
||||
|
||||
~ScopedInErrorReport() {
|
||||
// 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();
|
||||
|
||||
// Copy the message buffer so that we could start logging without holding a
|
||||
// lock that gets aquired during printing.
|
||||
InternalScopedBuffer<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());
|
||||
}
|
||||
CommonSanitizerReportMutex.Unlock();
|
||||
reporting_thread_tid_ = kInvalidTid;
|
||||
lock_.Unlock();
|
||||
if (halt_on_error_) {
|
||||
Report("ABORTING\n");
|
||||
Die();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void StartReporting(ReportData *report) {
|
||||
if (report) report_data = *report;
|
||||
report_happened = true;
|
||||
ASAN_ON_ERROR();
|
||||
@ -650,27 +721,19 @@ class ScopedInErrorReport {
|
||||
// recursive reports.
|
||||
asanThreadRegistry().Lock();
|
||||
CommonSanitizerReportMutex.Lock();
|
||||
reporting_thread_tid = GetCurrentTidOrInvalid();
|
||||
reporting_thread_tid_ = GetCurrentTidOrInvalid();
|
||||
Printf("===================================================="
|
||||
"=============\n");
|
||||
}
|
||||
// Destructor is NORETURN, as functions that report errors are.
|
||||
NORETURN ~ScopedInErrorReport() {
|
||||
// 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 (error_report_callback) {
|
||||
error_report_callback(error_message_buffer);
|
||||
}
|
||||
Report("ABORTING\n");
|
||||
Die();
|
||||
}
|
||||
|
||||
static StaticSpinMutex lock_;
|
||||
static u32 reporting_thread_tid_;
|
||||
bool halt_on_error_;
|
||||
};
|
||||
|
||||
StaticSpinMutex ScopedInErrorReport::lock_;
|
||||
u32 ScopedInErrorReport::reporting_thread_tid_;
|
||||
|
||||
void ReportStackOverflow(const SignalContext &sig) {
|
||||
ScopedInErrorReport in_report;
|
||||
Decorator d;
|
||||
@ -686,8 +749,8 @@ void ReportStackOverflow(const SignalContext &sig) {
|
||||
ReportErrorSummary("stack-overflow", &stack);
|
||||
}
|
||||
|
||||
void ReportSIGSEGV(const char *description, const SignalContext &sig) {
|
||||
ScopedInErrorReport in_report;
|
||||
void ReportDeadlySignal(const char *description, const SignalContext &sig) {
|
||||
ScopedInErrorReport in_report(/*report*/nullptr, /*fatal*/true);
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
Report(
|
||||
@ -703,7 +766,7 @@ void ReportSIGSEGV(const char *description, const SignalContext &sig) {
|
||||
stack.Print();
|
||||
MaybeDumpInstructionBytes(sig.pc);
|
||||
Printf("AddressSanitizer can not provide additional info.\n");
|
||||
ReportErrorSummary("SEGV", &stack);
|
||||
ReportErrorSummary(description, &stack);
|
||||
}
|
||||
|
||||
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
|
||||
@ -744,7 +807,7 @@ void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
|
||||
stack.Print();
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportErrorSummary("new-delete-type-mismatch", &stack);
|
||||
Report("HINT: if you don't care about these warnings you may set "
|
||||
Report("HINT: if you don't care about these errors you may set "
|
||||
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
|
||||
}
|
||||
|
||||
@ -784,7 +847,7 @@ void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
|
||||
stack.Print();
|
||||
DescribeHeapAddress(addr, 1);
|
||||
ReportErrorSummary("alloc-dealloc-mismatch", &stack);
|
||||
Report("HINT: if you don't care about these warnings you may set "
|
||||
Report("HINT: if you don't care about these errors you may set "
|
||||
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
|
||||
}
|
||||
|
||||
@ -886,7 +949,7 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
||||
Printf(" [2]:\n");
|
||||
StackDepotGet(stack_id2).Print();
|
||||
}
|
||||
Report("HINT: if you don't care about these warnings you may set "
|
||||
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("odr-violation: global '%s' at %s",
|
||||
@ -925,17 +988,6 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
|
||||
}
|
||||
// ----------------------- Mac-specific reports ----------------- {{{1
|
||||
|
||||
void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
|
||||
BufferedStackTrace *stack) {
|
||||
// Just print a warning here.
|
||||
Printf("free_common(%p) -- attempting to free unallocated memory.\n"
|
||||
"AddressSanitizer is ignoring this error on Mac OS now.\n",
|
||||
addr);
|
||||
PrintZoneForPointer(addr, zone_ptr, zone_name);
|
||||
stack->Print();
|
||||
DescribeHeapAddress(addr, 1);
|
||||
}
|
||||
|
||||
void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
|
||||
BufferedStackTrace *stack) {
|
||||
ScopedInErrorReport in_report;
|
||||
@ -947,24 +999,23 @@ void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
|
||||
DescribeHeapAddress(addr, 1);
|
||||
}
|
||||
|
||||
void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
|
||||
BufferedStackTrace *stack) {
|
||||
ScopedInErrorReport in_report;
|
||||
Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
|
||||
"This is an unrecoverable problem, exiting now.\n",
|
||||
addr);
|
||||
PrintZoneForPointer(addr, zone_ptr, zone_name);
|
||||
stack->Print();
|
||||
DescribeHeapAddress(addr, 1);
|
||||
// -------------- 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();
|
||||
}
|
||||
|
||||
} // 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) {
|
||||
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.
|
||||
@ -1033,7 +1084,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||
|
||||
ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
|
||||
bug_descr };
|
||||
ScopedInErrorReport in_report(&report);
|
||||
ScopedInErrorReport in_report(&report, fatal);
|
||||
|
||||
Decorator d;
|
||||
Printf("%s", d.Warning());
|
||||
@ -1059,14 +1110,21 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
||||
PrintShadowMemoryForAddress(addr);
|
||||
}
|
||||
|
||||
} // 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;
|
||||
if (callback) {
|
||||
error_message_buffer_size = 1 << 16;
|
||||
error_message_buffer =
|
||||
(char*)MmapOrDie(error_message_buffer_size, __func__);
|
||||
error_message_buffer_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void __asan_describe_address(uptr addr) {
|
||||
@ -1117,7 +1175,7 @@ SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __sanitizer_ptr_cmp(void *a, void *b) {
|
||||
CheckForInvalidPointerPair(a, b);
|
||||
}
|
||||
} // extern "C"
|
||||
} // extern "C"
|
||||
|
||||
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
|
||||
// Provide default implementation of __asan_on_error that does nothing
|
||||
|
@ -49,44 +49,39 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size);
|
||||
void DescribeThread(AsanThreadContext *context);
|
||||
|
||||
// Different kinds of error reports.
|
||||
void NORETURN ReportStackOverflow(const SignalContext &sig);
|
||||
void NORETURN ReportSIGSEGV(const char *description, const SignalContext &sig);
|
||||
void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
|
||||
BufferedStackTrace *free_stack);
|
||||
void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
|
||||
void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
|
||||
void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
|
||||
AllocType alloc_type,
|
||||
AllocType dealloc_type);
|
||||
void NORETURN
|
||||
ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
|
||||
void NORETURN
|
||||
ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
|
||||
BufferedStackTrace *stack);
|
||||
void NORETURN
|
||||
ReportStringFunctionMemoryRangesOverlap(const char *function,
|
||||
const char *offset1, uptr length1,
|
||||
const char *offset2, uptr length2,
|
||||
BufferedStackTrace *stack);
|
||||
void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size,
|
||||
BufferedStackTrace *stack);
|
||||
void NORETURN
|
||||
ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
|
||||
uptr old_mid, uptr new_mid,
|
||||
BufferedStackTrace *stack);
|
||||
void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
|
||||
uptr access_size, u32 exp, bool fatal);
|
||||
void ReportStackOverflow(const SignalContext &sig);
|
||||
void ReportDeadlySignal(const char *description, const SignalContext &sig);
|
||||
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
|
||||
BufferedStackTrace *free_stack);
|
||||
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
|
||||
void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
|
||||
void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
|
||||
AllocType alloc_type,
|
||||
AllocType dealloc_type);
|
||||
void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
|
||||
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
|
||||
BufferedStackTrace *stack);
|
||||
void ReportStringFunctionMemoryRangesOverlap(const char *function,
|
||||
const char *offset1, uptr length1,
|
||||
const char *offset2, uptr length2,
|
||||
BufferedStackTrace *stack);
|
||||
void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
|
||||
BufferedStackTrace *stack);
|
||||
void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
|
||||
uptr old_mid, uptr new_mid,
|
||||
BufferedStackTrace *stack);
|
||||
|
||||
void NORETURN
|
||||
ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
||||
const __asan_global *g2, u32 stack_id2);
|
||||
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
||||
const __asan_global *g2, u32 stack_id2);
|
||||
|
||||
// Mac-specific errors and warnings.
|
||||
void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
|
||||
BufferedStackTrace *stack);
|
||||
void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
|
||||
const char *zone_name,
|
||||
BufferedStackTrace *stack);
|
||||
void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
|
||||
const char *zone_name,
|
||||
BufferedStackTrace *stack);
|
||||
void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
|
||||
const char *zone_name,
|
||||
BufferedStackTrace *stack);
|
||||
void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
|
||||
const char *zone_name,
|
||||
BufferedStackTrace *stack);
|
||||
|
||||
} // namespace __asan
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
// Main file of the ASan run-time library.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_activation.h"
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_interceptors.h"
|
||||
@ -56,11 +57,6 @@ static void AsanDie() {
|
||||
UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg);
|
||||
}
|
||||
}
|
||||
if (common_flags()->coverage)
|
||||
__sanitizer_cov_dump();
|
||||
if (flags()->abort_on_error)
|
||||
Abort();
|
||||
internal__exit(flags()->exitcode);
|
||||
}
|
||||
|
||||
static void AsanCheckFailed(const char *file, int line, const char *cond,
|
||||
@ -117,13 +113,18 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) {
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
void __asan_report_ ## type ## size(uptr addr) { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, 0); \
|
||||
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; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, exp); \
|
||||
}
|
||||
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)
|
||||
@ -136,22 +137,27 @@ 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; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, 0); \
|
||||
} \
|
||||
extern "C" NOINLINE INTERFACE_ATTRIBUTE \
|
||||
#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; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, exp); \
|
||||
}
|
||||
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) \
|
||||
#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg, fatal) \
|
||||
uptr sp = MEM_TO_SHADOW(addr); \
|
||||
uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \
|
||||
: *reinterpret_cast<u16 *>(sp); \
|
||||
@ -163,7 +169,8 @@ ASAN_REPORT_ERROR_N(store, true)
|
||||
*__asan_test_only_reported_buggy_pointer = addr; \
|
||||
} else { \
|
||||
GET_CALLER_PC_BP_SP; \
|
||||
__asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \
|
||||
ReportGenericError(pc, bp, sp, addr, is_write, size, exp_arg, \
|
||||
fatal); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
@ -171,12 +178,16 @@ ASAN_REPORT_ERROR_N(store, true)
|
||||
#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) \
|
||||
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) \
|
||||
}
|
||||
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)
|
||||
@ -194,7 +205,7 @@ NOINLINE INTERFACE_ATTRIBUTE
|
||||
void __asan_loadN(uptr addr, uptr size) {
|
||||
if (__asan_region_is_poisoned(addr, size)) {
|
||||
GET_CALLER_PC_BP_SP;
|
||||
__asan_report_error(pc, bp, sp, addr, false, size, 0);
|
||||
ReportGenericError(pc, bp, sp, addr, false, size, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +214,16 @@ 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;
|
||||
__asan_report_error(pc, bp, sp, addr, false, size, exp);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +232,7 @@ NOINLINE INTERFACE_ATTRIBUTE
|
||||
void __asan_storeN(uptr addr, uptr size) {
|
||||
if (__asan_region_is_poisoned(addr, size)) {
|
||||
GET_CALLER_PC_BP_SP;
|
||||
__asan_report_error(pc, bp, sp, addr, true, size, 0);
|
||||
ReportGenericError(pc, bp, sp, addr, true, size, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -221,7 +241,16 @@ 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;
|
||||
__asan_report_error(pc, bp, sp, addr, true, size, exp);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,16 +288,15 @@ static NOINLINE void force_interface_symbols() {
|
||||
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(0, 0); break;
|
||||
case 26: __asan_unregister_globals(0, 0); break;
|
||||
case 27: __asan_set_death_callback(0); break;
|
||||
case 28: __asan_set_error_report_callback(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(0); break;
|
||||
case 31: __asan_poison_memory_region(0, 0); break;
|
||||
case 32: __asan_unpoison_memory_region(0, 0); break;
|
||||
case 33: __asan_set_error_exit_code(0); break;
|
||||
case 34: __asan_before_dynamic_init(0); 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;
|
||||
@ -298,9 +326,25 @@ static void InitializeHighMemEnd() {
|
||||
}
|
||||
|
||||
static void ProtectGap(uptr addr, uptr size) {
|
||||
if (!flags()->protect_shadow_gap)
|
||||
return;
|
||||
void *res = MmapNoAccess(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 = GetPageSizeCached();
|
||||
while (size > step && addr < kZeroBaseMaxShadowStart) {
|
||||
addr += step;
|
||||
size -= step;
|
||||
void *res = MmapNoAccess(addr, size, "shadow gap");
|
||||
if (addr == (uptr)res)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Report("ERROR: Failed to protect the shadow gap. "
|
||||
"ASan cannot proceed correctly. ABORTING.\n");
|
||||
DumpProcessMap();
|
||||
@ -363,12 +407,12 @@ static void AsanInitInternal() {
|
||||
CHECK(!asan_init_is_running && "ASan init calls itself!");
|
||||
asan_init_is_running = true;
|
||||
|
||||
CacheBinaryName();
|
||||
|
||||
// Initialize flags. This must be done early, because most of the
|
||||
// initialization steps look at flags().
|
||||
InitializeFlags();
|
||||
|
||||
CacheBinaryName();
|
||||
|
||||
AsanCheckIncompatibleRT();
|
||||
AsanCheckDynamicRTPrereqs();
|
||||
|
||||
@ -381,7 +425,7 @@ static void AsanInitInternal() {
|
||||
AsanDoesNotSupportStaticLinkage();
|
||||
|
||||
// Install tool-specific callbacks in sanitizer_common.
|
||||
SetDieCallback(AsanDie);
|
||||
AddDieCallback(AsanDie);
|
||||
SetCheckFailedCallback(AsanCheckFailed);
|
||||
SetPrintfAndReportCallback(AppendToErrorMessageBuffer);
|
||||
|
||||
@ -457,7 +501,7 @@ static void AsanInitInternal() {
|
||||
}
|
||||
|
||||
AsanTSDInit(PlatformTSDDtor);
|
||||
InstallDeadlySignalHandlers(AsanOnSIGSEGV);
|
||||
InstallDeadlySignalHandlers(AsanOnDeadlySignal);
|
||||
|
||||
AllocatorOptions allocator_options;
|
||||
allocator_options.SetFrom(flags(), common_flags());
|
||||
@ -531,24 +575,26 @@ public: // NOLINT
|
||||
static AsanInitializer asan_initializer;
|
||||
#endif // ASAN_DYNAMIC
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// ---------------------- Interface ---------------- {{{1
|
||||
using namespace __asan; // NOLINT
|
||||
|
||||
int NOINLINE __asan_set_error_exit_code(int exit_code) {
|
||||
int old = flags()->exitcode;
|
||||
flags()->exitcode = exit_code;
|
||||
return old;
|
||||
}
|
||||
|
||||
void NOINLINE __asan_handle_no_return() {
|
||||
int local_stack;
|
||||
AsanThread *curr_thread = GetCurrentThread();
|
||||
CHECK(curr_thread);
|
||||
uptr PageSize = GetPageSizeCached();
|
||||
uptr top = curr_thread->stack_top();
|
||||
uptr bottom = ((uptr)&local_stack - PageSize) & ~(PageSize-1);
|
||||
uptr top, bottom;
|
||||
if (curr_thread) {
|
||||
top = curr_thread->stack_top();
|
||||
bottom = ((uptr)&local_stack - PageSize) & ~(PageSize - 1);
|
||||
} else {
|
||||
// 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;
|
||||
@ -559,12 +605,12 @@ void NOINLINE __asan_handle_no_return() {
|
||||
"stack top: %p; bottom %p; size: %p (%zd)\n"
|
||||
"False positive error reports may follow\n"
|
||||
"For details see "
|
||||
"http://code.google.com/p/address-sanitizer/issues/detail?id=189\n",
|
||||
"https://github.com/google/sanitizers/issues/189\n",
|
||||
top, bottom, top - bottom, top - bottom);
|
||||
return;
|
||||
}
|
||||
PoisonShadow(bottom, top - bottom, 0);
|
||||
if (curr_thread->has_fake_stack())
|
||||
if (curr_thread && curr_thread->has_fake_stack())
|
||||
curr_thread->fake_stack()->HandleNoReturn();
|
||||
}
|
||||
|
||||
@ -578,3 +624,7 @@ void __asan_init() {
|
||||
AsanActivate();
|
||||
AsanInitInternal();
|
||||
}
|
||||
|
||||
void __asan_version_mismatch_check() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
// ASan-private header for asan_stack.cc.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_STACK_H
|
||||
#define ASAN_STACK_H
|
||||
|
||||
@ -48,15 +49,15 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth,
|
||||
uptr stack_bottom = t->stack_bottom();
|
||||
ScopedUnwinding unwind_scope(t);
|
||||
stack->Unwind(max_depth, pc, bp, context, stack_top, stack_bottom, fast);
|
||||
} else if (t == 0 && !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
|
||||
#endif // SANITIZER_WINDOWS
|
||||
}
|
||||
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// NOTE: A Rule of thumb is to retrieve stack trace in the interceptors
|
||||
// as early as possible (in functions exposed to the user), as we generally
|
||||
@ -115,4 +116,4 @@ void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth,
|
||||
stack.Print(); \
|
||||
}
|
||||
|
||||
#endif // ASAN_STACK_H
|
||||
#endif // ASAN_STACK_H
|
||||
|
@ -42,7 +42,7 @@ void AsanThreadContext::OnCreated(void *arg) {
|
||||
|
||||
void AsanThreadContext::OnFinished() {
|
||||
// Drop the link to the AsanThread object.
|
||||
thread = 0;
|
||||
thread = nullptr;
|
||||
}
|
||||
|
||||
// MIPS requires aligned address
|
||||
@ -125,7 +125,7 @@ void AsanThread::Destroy() {
|
||||
FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
|
||||
uptr stack_size = this->stack_size();
|
||||
if (stack_size == 0) // stack_size is not yet available, don't use FakeStack.
|
||||
return 0;
|
||||
return nullptr;
|
||||
uptr old_val = 0;
|
||||
// fake_stack_ has 3 states:
|
||||
// 0 -- not initialized
|
||||
@ -146,11 +146,11 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() {
|
||||
SetTLSFakeStack(fake_stack_);
|
||||
return fake_stack_;
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AsanThread::Init() {
|
||||
fake_stack_ = 0; // Will be initialized lazily if needed.
|
||||
fake_stack_ = nullptr; // Will be initialized lazily if needed.
|
||||
CHECK_EQ(this->stack_size(), 0U);
|
||||
SetThreadStackAndTls();
|
||||
CHECK_GT(this->stack_size(), 0U);
|
||||
@ -161,13 +161,12 @@ void AsanThread::Init() {
|
||||
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);
|
||||
AsanPlatformThreadInit();
|
||||
}
|
||||
|
||||
thread_return_t AsanThread::ThreadStart(
|
||||
uptr os_id, atomic_uintptr_t *signal_thread_is_registered) {
|
||||
Init();
|
||||
asanThreadRegistry().StartThread(tid(), os_id, 0);
|
||||
asanThreadRegistry().StartThread(tid(), os_id, nullptr);
|
||||
if (signal_thread_is_registered)
|
||||
atomic_store(signal_thread_is_registered, 1, memory_order_release);
|
||||
|
||||
@ -277,7 +276,7 @@ AsanThread *GetCurrentThread() {
|
||||
return tctx->thread;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
return context->thread;
|
||||
}
|
||||
@ -302,7 +301,7 @@ AsanThread *FindThreadByStackAddress(uptr addr) {
|
||||
AsanThreadContext *tctx = static_cast<AsanThreadContext *>(
|
||||
asanThreadRegistry().FindThreadContextLocked(ThreadStackContainsAddress,
|
||||
(void *)addr));
|
||||
return tctx ? tctx->thread : 0;
|
||||
return tctx ? tctx->thread : nullptr;
|
||||
}
|
||||
|
||||
void EnsureMainThreadIDIsCorrect() {
|
||||
@ -315,10 +314,10 @@ void EnsureMainThreadIDIsCorrect() {
|
||||
__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
|
||||
__asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
|
||||
__asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
|
||||
if (!context) return 0;
|
||||
if (!context) return nullptr;
|
||||
return context->thread;
|
||||
}
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
// --- Implementation of LSan-specific functions --- {{{1
|
||||
namespace __lsan {
|
||||
@ -355,4 +354,4 @@ void UnlockThreadRegistry() {
|
||||
void EnsureMainThreadIDIsCorrect() {
|
||||
__asan::EnsureMainThreadIDIsCorrect();
|
||||
}
|
||||
} // namespace __lsan
|
||||
} // namespace __lsan
|
||||
|
@ -11,6 +11,7 @@
|
||||
//
|
||||
// ASan-private header for asan_thread.cc.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_THREAD_H
|
||||
#define ASAN_THREAD_H
|
||||
|
||||
@ -36,7 +37,7 @@ class AsanThreadContext : public ThreadContextBase {
|
||||
explicit AsanThreadContext(int tid)
|
||||
: ThreadContextBase(tid), announced(false),
|
||||
destructor_iterations(GetPthreadDestructorIterations()), stack_id(0),
|
||||
thread(0) {}
|
||||
thread(nullptr) {}
|
||||
bool announced;
|
||||
u8 destructor_iterations;
|
||||
u32 stack_id;
|
||||
@ -84,8 +85,8 @@ class AsanThread {
|
||||
void DeleteFakeStack(int tid) {
|
||||
if (!fake_stack_) return;
|
||||
FakeStack *t = fake_stack_;
|
||||
fake_stack_ = 0;
|
||||
SetTLSFakeStack(0);
|
||||
fake_stack_ = nullptr;
|
||||
SetTLSFakeStack(nullptr);
|
||||
t->Destroy(tid);
|
||||
}
|
||||
|
||||
@ -95,7 +96,7 @@ class AsanThread {
|
||||
|
||||
FakeStack *fake_stack() {
|
||||
if (!__asan_option_detect_stack_use_after_return)
|
||||
return 0;
|
||||
return nullptr;
|
||||
if (!has_fake_stack())
|
||||
return AsyncSignalSafeLazyInitFakeStack();
|
||||
return fake_stack_;
|
||||
@ -179,6 +180,6 @@ AsanThread *FindThreadByStackAddress(uptr addr);
|
||||
|
||||
// Used to handle fork().
|
||||
void EnsureMainThreadIDIsCorrect();
|
||||
} // namespace __asan
|
||||
} // namespace __asan
|
||||
|
||||
#endif // ASAN_THREAD_H
|
||||
#endif // ASAN_THREAD_H
|
||||
|
@ -14,9 +14,9 @@
|
||||
|
||||
#include "sanitizer_common/sanitizer_platform.h"
|
||||
#if SANITIZER_WINDOWS
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <dbghelp.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "asan_interceptors.h"
|
||||
@ -175,14 +175,6 @@ void PlatformTSDDtor(void *tsd) {
|
||||
// }}}
|
||||
|
||||
// ---------------------- Various stuff ---------------- {{{
|
||||
void DisableReexec() {
|
||||
// No need to re-exec on Windows.
|
||||
}
|
||||
|
||||
void MaybeReexec() {
|
||||
// No need to re-exec on Windows.
|
||||
}
|
||||
|
||||
void *AsanDoesNotSupportStaticLinkage() {
|
||||
#if defined(_DEBUG)
|
||||
#error Please build the runtime with a non-debug CRT: /MD or /MT
|
||||
@ -194,15 +186,11 @@ void AsanCheckDynamicRTPrereqs() {}
|
||||
|
||||
void AsanCheckIncompatibleRT() {}
|
||||
|
||||
void AsanPlatformThreadInit() {
|
||||
// Nothing here for now.
|
||||
}
|
||||
|
||||
void ReadContextStack(void *context, uptr *stack, uptr *ssize) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
|
||||
void AsanOnDeadlySignal(int, void *siginfo, void *context) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
@ -219,7 +207,7 @@ static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) {
|
||||
? "access-violation"
|
||||
: "in-page-error";
|
||||
SignalContext sig = SignalContext::Create(exception_record, context);
|
||||
ReportSIGSEGV(description, sig);
|
||||
ReportDeadlySignal(description, sig);
|
||||
}
|
||||
|
||||
// FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
|
||||
@ -257,7 +245,7 @@ int __asan_set_seh_filter() {
|
||||
// Put a pointer to __asan_set_seh_filter at the end of the global list
|
||||
// of C initializers, after the default EH is set by the CRT.
|
||||
#pragma section(".CRT$XIZ", long, read) // NOLINT
|
||||
static __declspec(allocate(".CRT$XIZ"))
|
||||
__declspec(allocate(".CRT$XIZ"))
|
||||
int (*__intercept_seh)() = __asan_set_seh_filter;
|
||||
#endif
|
||||
// }}}
|
||||
|
@ -12,8 +12,7 @@
|
||||
// This file defines a family of thunks that should be statically linked into
|
||||
// the DLLs that have ASan instrumentation in order to delegate the calls to the
|
||||
// shared runtime that lives in the main binary.
|
||||
// See https://code.google.com/p/address-sanitizer/issues/detail?id=209 for the
|
||||
// details.
|
||||
// See https://github.com/google/sanitizers/issues/209 for the details.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Only compile this code when buidling asan_dll_thunk.lib
|
||||
@ -30,8 +29,9 @@ void *__stdcall GetProcAddress(void *module, const char *proc_name);
|
||||
void abort();
|
||||
}
|
||||
|
||||
static void *getRealProcAddressOrDie(const char *name) {
|
||||
void *ret = GetProcAddress(GetModuleHandleA(0), name);
|
||||
static uptr getRealProcAddressOrDie(const char *name) {
|
||||
uptr ret =
|
||||
__interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name);
|
||||
if (!ret)
|
||||
abort();
|
||||
return ret;
|
||||
@ -62,13 +62,12 @@ struct FunctionInterceptor<0> {
|
||||
};
|
||||
|
||||
#define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function) \
|
||||
template<> struct FunctionInterceptor<__LINE__> { \
|
||||
template <> struct FunctionInterceptor<__LINE__> { \
|
||||
static void Execute() { \
|
||||
void *wrapper = getRealProcAddressOrDie(main_function); \
|
||||
if (!__interception::OverrideFunction((uptr)dll_function, \
|
||||
(uptr)wrapper, 0)) \
|
||||
uptr wrapper = getRealProcAddressOrDie(main_function); \
|
||||
if (!__interception::OverrideFunction((uptr)dll_function, wrapper, 0)) \
|
||||
abort(); \
|
||||
FunctionInterceptor<__LINE__-1>::Execute(); \
|
||||
FunctionInterceptor<__LINE__ - 1>::Execute(); \
|
||||
} \
|
||||
};
|
||||
|
||||
@ -210,7 +209,7 @@ extern "C" {
|
||||
// __asan_init is expected to be called by only one thread.
|
||||
if (fn) return;
|
||||
|
||||
fn = (fntype)getRealProcAddressOrDie(__asan_init_name);
|
||||
fn = (fntype)getRealProcAddressOrDie("__asan_init");
|
||||
fn();
|
||||
__asan_option_detect_stack_use_after_return =
|
||||
(__asan_should_detect_stack_use_after_return() != 0);
|
||||
@ -219,6 +218,10 @@ extern "C" {
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __asan_version_mismatch_check() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
INTERFACE_FUNCTION(__asan_handle_no_return)
|
||||
|
||||
INTERFACE_FUNCTION(__asan_report_store1)
|
||||
@ -253,6 +256,9 @@ INTERFACE_FUNCTION(__asan_memcpy);
|
||||
INTERFACE_FUNCTION(__asan_memset);
|
||||
INTERFACE_FUNCTION(__asan_memmove);
|
||||
|
||||
INTERFACE_FUNCTION(__asan_alloca_poison);
|
||||
INTERFACE_FUNCTION(__asan_allocas_unpoison);
|
||||
|
||||
INTERFACE_FUNCTION(__asan_register_globals)
|
||||
INTERFACE_FUNCTION(__asan_unregister_globals)
|
||||
|
||||
@ -296,6 +302,7 @@ INTERFACE_FUNCTION(__asan_stack_free_10)
|
||||
|
||||
// FIXME: we might want to have a sanitizer_win_dll_thunk?
|
||||
INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container)
|
||||
INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_dump)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_indir_call16)
|
||||
@ -304,14 +311,17 @@ INTERFACE_FUNCTION(__sanitizer_cov_module_init)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_cmp)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_trace_switch)
|
||||
INTERFACE_FUNCTION(__sanitizer_cov_with_check)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_coverage_guards)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_coverage_pc_buffer)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_free_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_heap_size)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_ownership)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage)
|
||||
INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes)
|
||||
INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file)
|
||||
|
@ -24,6 +24,7 @@
|
||||
// Using #ifdef rather than relying on Makefiles etc.
|
||||
// simplifies the build procedure.
|
||||
#ifdef ASAN_DYNAMIC_RUNTIME_THUNK
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
// First, declare CRT sections we'll be using in this file
|
||||
@ -58,6 +59,7 @@ int __asan_option_detect_stack_use_after_return =
|
||||
// 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 {
|
||||
|
@ -1,344 +0,0 @@
|
||||
#!/bin/bash
|
||||
#===- lib/asan/scripts/asan_device_setup -----------------------------------===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
# Prepare Android device to run ASan applications.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
|
||||
set -e
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
revert=no
|
||||
extra_options=
|
||||
device=
|
||||
lib=
|
||||
use_su=0
|
||||
|
||||
function usage {
|
||||
echo "usage: $0 [--revert] [--device device-id] [--lib path] [--extra-options options]"
|
||||
echo " --revert: Uninstall ASan from the device."
|
||||
echo " --lib: Path to ASan runtime library."
|
||||
echo " --extra-options: Extra ASAN_OPTIONS."
|
||||
echo " --device: Install to the given device. Use 'adb devices' to find"
|
||||
echo " device-id."
|
||||
echo " --use-su: Use 'su -c' prefix for every adb command instead of using"
|
||||
echo " 'adb root' once."
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
function adb_push {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB push "$1" "$2"
|
||||
else
|
||||
local FILENAME=$(basename $1)
|
||||
$ADB push "$1" "/data/local/tmp/$FILENAME"
|
||||
$ADB shell su -c "rm \\\"$2/$FILENAME\\\"" >&/dev/null
|
||||
$ADB shell su -c "cat \\\"/data/local/tmp/$FILENAME\\\" > \\\"$2/$FILENAME\\\""
|
||||
$ADB shell su -c "rm \\\"/data/local/tmp/$FILENAME\\\""
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_remount {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB remount
|
||||
else
|
||||
local STORAGE=`$ADB shell mount | grep /system | cut -d ' ' -f1`
|
||||
if [ "$STORAGE" != "" ]; then
|
||||
echo Remounting $STORAGE at /system
|
||||
$ADB shell su -c "mount -o remount,rw $STORAGE /system"
|
||||
else
|
||||
echo Failed to get storage device name for "/system" mount point
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_shell {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB shell $@
|
||||
else
|
||||
$ADB shell su -c "$*"
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_root {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB root
|
||||
fi
|
||||
}
|
||||
|
||||
function adb_wait_for_device {
|
||||
$ADB wait-for-device
|
||||
}
|
||||
|
||||
function adb_pull {
|
||||
if [ $use_su -eq 0 ]; then
|
||||
$ADB pull "$1" "$2"
|
||||
else
|
||||
local FILENAME=$(basename $1)
|
||||
$ADB shell rm "/data/local/tmp/$FILENAME" >&/dev/null
|
||||
$ADB shell su -c "[ -f \\\"$1\\\" ] && cat \\\"$1\\\" > \\\"/data/local/tmp/$FILENAME\\\" && chown root.shell \\\"/data/local/tmp/$FILENAME\\\" && chmod 755 \\\"/data/local/tmp/$FILENAME\\\"" &&
|
||||
$ADB pull "/data/local/tmp/$FILENAME" "$2" >&/dev/null && $ADB shell "rm \"/data/local/tmp/$FILENAME\""
|
||||
fi
|
||||
}
|
||||
|
||||
function get_device_arch { # OUTVAR
|
||||
local _outvar=$1
|
||||
local _ABI=$(adb_shell getprop ro.product.cpu.abi)
|
||||
local _ARCH=
|
||||
if [[ $_ABI == x86* ]]; then
|
||||
_ARCH=i686
|
||||
elif [[ $_ABI == armeabi* ]]; then
|
||||
_ARCH=arm
|
||||
else
|
||||
echo "Unrecognized device ABI: $_ABI"
|
||||
exit 1
|
||||
fi
|
||||
eval $_outvar=\$_ARCH
|
||||
}
|
||||
|
||||
while [[ $# > 0 ]]; do
|
||||
case $1 in
|
||||
--revert)
|
||||
revert=yes
|
||||
;;
|
||||
--extra-options)
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--extra-options requires an argument."
|
||||
exit 1
|
||||
fi
|
||||
extra_options="$1"
|
||||
;;
|
||||
--lib)
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--lib requires an argument."
|
||||
exit 1
|
||||
fi
|
||||
lib="$1"
|
||||
;;
|
||||
--device)
|
||||
shift
|
||||
if [[ $# == 0 ]]; then
|
||||
echo "--device requires an argument."
|
||||
exit 1
|
||||
fi
|
||||
device="$1"
|
||||
;;
|
||||
--use-su)
|
||||
use_su=1
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
ADB=${ADB:-adb}
|
||||
if [[ x$device != x ]]; then
|
||||
ADB="$ADB -s $device"
|
||||
fi
|
||||
|
||||
if [ $use_su -eq 1 ]; then
|
||||
# Test if 'su' is present on the device
|
||||
SU_TEST_OUT=`$ADB shell su -c "echo foo" 2>&1 | sed 's/\r$//'`
|
||||
if [ $? != 0 -o "$SU_TEST_OUT" != "foo" ]; then
|
||||
echo "ERROR: Cannot use 'su -c':"
|
||||
echo "$ adb shell su -c \"echo foo\""
|
||||
echo $SU_TEST_OUT
|
||||
echo "Check that 'su' binary is correctly installed on the device or omit"
|
||||
echo " --use-su flag"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo '>> Remounting /system rw'
|
||||
adb_wait_for_device
|
||||
adb_root
|
||||
adb_wait_for_device
|
||||
adb_remount
|
||||
adb_wait_for_device
|
||||
|
||||
get_device_arch ARCH
|
||||
echo "Target architecture: $ARCH"
|
||||
ASAN_RT="libclang_rt.asan-$ARCH-android.so"
|
||||
|
||||
if [[ x$revert == xyes ]]; then
|
||||
echo '>> Uninstalling ASan'
|
||||
|
||||
if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
|
||||
echo '>> Pre-L device detected.'
|
||||
adb_shell mv /system/bin/app_process.real /system/bin/app_process
|
||||
adb_shell rm /system/bin/asanwrapper
|
||||
else
|
||||
adb_shell rm /system/bin/app_process.wrap
|
||||
adb_shell rm /system/bin/asanwrapper
|
||||
adb_shell rm /system/bin/app_process
|
||||
adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
|
||||
fi
|
||||
|
||||
echo '>> Restarting shell'
|
||||
adb_shell stop
|
||||
adb_shell start
|
||||
|
||||
# Remove the library on the last step to give a chance to the 'su' binary to
|
||||
# be executed without problem.
|
||||
adb_shell rm /system/lib/$ASAN_RT
|
||||
|
||||
echo '>> Done'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ -d "$lib" ]]; then
|
||||
ASAN_RT_PATH="$lib"
|
||||
elif [[ -f "$lib" && "$lib" == *"$ASAN_RT" ]]; then
|
||||
ASAN_RT_PATH=$(dirname "$lib")
|
||||
elif [[ -f "$HERE/$ASAN_RT" ]]; then
|
||||
ASAN_RT_PATH="$HERE"
|
||||
elif [[ $(basename "$HERE") == "bin" ]]; then
|
||||
# We could be in the toolchain's base directory.
|
||||
# Consider ../lib, ../lib/asan, ../lib/linux and ../lib/clang/$VERSION/lib/linux.
|
||||
P=$(ls "$HERE"/../lib/"$ASAN_RT" "$HERE"/../lib/asan/"$ASAN_RT" "$HERE"/../lib/linux/"$ASAN_RT" "$HERE"/../lib/clang/*/lib/linux/"$ASAN_RT" 2>/dev/null | sort | tail -1)
|
||||
if [[ -n "$P" ]]; then
|
||||
ASAN_RT_PATH="$(dirname "$P")"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "$ASAN_RT_PATH" || ! -f "$ASAN_RT_PATH/$ASAN_RT" ]]; then
|
||||
echo ">> ASan runtime library not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMPDIRBASE=$(mktemp -d)
|
||||
TMPDIROLD="$TMPDIRBASE/old"
|
||||
TMPDIR="$TMPDIRBASE/new"
|
||||
mkdir "$TMPDIROLD"
|
||||
|
||||
RELEASE=$(adb_shell getprop ro.build.version.release)
|
||||
PRE_L=0
|
||||
if echo "$RELEASE" | grep '^4\.' >&/dev/null; then
|
||||
PRE_L=1
|
||||
fi
|
||||
|
||||
if ! adb_shell ls -l /system/bin/app_process | grep -o '\->.*app_process' >&/dev/null; then
|
||||
|
||||
if adb_pull /system/bin/app_process.real /dev/null >&/dev/null; then
|
||||
echo '>> Old-style ASan installation detected. Reverting.'
|
||||
adb_shell mv /system/bin/app_process.real /system/bin/app_process
|
||||
fi
|
||||
|
||||
echo '>> Pre-L device detected. Setting up app_process symlink.'
|
||||
adb_shell mv /system/bin/app_process /system/bin/app_process32
|
||||
adb_shell ln -s /system/bin/app_process32 /system/bin/app_process
|
||||
fi
|
||||
|
||||
echo '>> Copying files from the device'
|
||||
adb_pull /system/bin/app_process.wrap "$TMPDIROLD" || true
|
||||
adb_pull /system/bin/asanwrapper "$TMPDIROLD" || true
|
||||
adb_pull /system/lib/"$ASAN_RT" "$TMPDIROLD" || true
|
||||
cp -r "$TMPDIROLD" "$TMPDIR"
|
||||
|
||||
if [[ -f "$TMPDIR/app_process.wrap" ]]; then
|
||||
echo ">> Previous installation detected"
|
||||
else
|
||||
echo ">> New installation"
|
||||
fi
|
||||
|
||||
echo '>> Generating wrappers'
|
||||
|
||||
cp "$ASAN_RT_PATH/$ASAN_RT" "$TMPDIR/"
|
||||
|
||||
# FIXME: alloc_dealloc_mismatch=0 prevents a failure in libdvm startup,
|
||||
# which may or may not be a real bug (probably not).
|
||||
ASAN_OPTIONS=start_deactivated=1,alloc_dealloc_mismatch=0
|
||||
|
||||
# On Android-L not allowing user segv handler breaks some applications.
|
||||
if [[ PRE_L -eq 0 ]]; then
|
||||
ASAN_OPTIONS="$ASAN_OPTIONS,allow_user_segv_handler=1"
|
||||
fi
|
||||
|
||||
if [[ x$extra_options != x ]] ; then
|
||||
ASAN_OPTIONS="$ASAN_OPTIONS,$extra_options"
|
||||
fi
|
||||
|
||||
# Zygote wrapper.
|
||||
cat <<EOF >"$TMPDIR/app_process.wrap"
|
||||
#!/system/bin/sh-from-zygote
|
||||
ASAN_OPTIONS=$ASAN_OPTIONS \\
|
||||
LD_PRELOAD=\$LD_PRELOAD:$ASAN_RT \\
|
||||
exec /system/bin/app_process32 \$@
|
||||
|
||||
EOF
|
||||
|
||||
# General command-line tool wrapper (use for anything that's not started as
|
||||
# zygote).
|
||||
cat <<EOF >"$TMPDIR/asanwrapper"
|
||||
#!/system/bin/sh
|
||||
LD_PRELOAD=$ASAN_RT \\
|
||||
exec \$@
|
||||
|
||||
EOF
|
||||
|
||||
if ! ( cd "$TMPDIRBASE" && diff -qr old/ new/ ) ; then
|
||||
echo '>> Pushing files to the device'
|
||||
adb_push "$TMPDIR/$ASAN_RT" /system/lib/
|
||||
adb_push "$TMPDIR/app_process.wrap" /system/bin
|
||||
adb_push "$TMPDIR/asanwrapper" /system/bin
|
||||
|
||||
adb_shell rm /system/bin/app_process
|
||||
adb_shell ln -s /system/bin/app_process.wrap /system/bin/app_process
|
||||
|
||||
adb_shell chown root.shell \
|
||||
/system/lib/"$ASAN_RT" \
|
||||
/system/bin/app_process.wrap \
|
||||
/system/bin/asanwrapper
|
||||
adb_shell chmod 644 \
|
||||
/system/lib/"$ASAN_RT"
|
||||
adb_shell chmod 755 \
|
||||
/system/bin/app_process.wrap \
|
||||
/system/bin/asanwrapper
|
||||
|
||||
# Make SELinux happy by keeping app_process wrapper and the shell
|
||||
# it runs on in zygote domain.
|
||||
ENFORCING=0
|
||||
if adb_shell getenforce | grep Enforcing >/dev/null; then
|
||||
# Sometimes shell is not allowed to change file contexts.
|
||||
# Temporarily switch to permissive.
|
||||
ENFORCING=1
|
||||
adb_shell setenforce 0
|
||||
fi
|
||||
|
||||
adb_shell cp /system/bin/sh /system/bin/sh-from-zygote
|
||||
|
||||
if [[ PRE_L -eq 1 ]]; then
|
||||
CTX=u:object_r:system_file:s0
|
||||
else
|
||||
CTX=u:object_r:zygote_exec:s0
|
||||
fi
|
||||
adb_shell chcon $CTX \
|
||||
/system/bin/sh-from-zygote \
|
||||
/system/bin/app_process.wrap \
|
||||
/system/bin/app_process32
|
||||
|
||||
if [ $ENFORCING == 1 ]; then
|
||||
adb_shell setenforce 1
|
||||
fi
|
||||
|
||||
echo '>> Restarting shell (asynchronous)'
|
||||
adb_shell stop
|
||||
adb_shell start
|
||||
|
||||
echo '>> Please wait until the device restarts'
|
||||
else
|
||||
echo '>> Device is up to date'
|
||||
fi
|
||||
|
||||
rm -r "$TMPDIRBASE"
|
@ -1,482 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
|
||||
#
|
||||
# The LLVM Compiler Infrastructure
|
||||
#
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
#
|
||||
#===------------------------------------------------------------------------===#
|
||||
import argparse
|
||||
import bisect
|
||||
import getopt
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
symbolizers = {}
|
||||
DEBUG = False
|
||||
demangle = False
|
||||
binutils_prefix = None
|
||||
sysroot_path = None
|
||||
binary_name_filter = None
|
||||
fix_filename_patterns = None
|
||||
logfile = sys.stdin
|
||||
allow_system_symbolizer = True
|
||||
|
||||
# FIXME: merge the code that calls fix_filename().
|
||||
def fix_filename(file_name):
|
||||
if fix_filename_patterns:
|
||||
for path_to_cut in fix_filename_patterns:
|
||||
file_name = re.sub('.*' + path_to_cut, '', file_name)
|
||||
file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name)
|
||||
file_name = re.sub('.*crtstuff.c:0', '???:0', file_name)
|
||||
return file_name
|
||||
|
||||
def sysroot_path_filter(binary_name):
|
||||
return sysroot_path + binary_name
|
||||
|
||||
def guess_arch(addr):
|
||||
# Guess which arch we're running. 10 = len('0x') + 8 hex digits.
|
||||
if len(addr) > 10:
|
||||
return 'x86_64'
|
||||
else:
|
||||
return 'i386'
|
||||
|
||||
class Symbolizer(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def symbolize(self, addr, binary, offset):
|
||||
"""Symbolize the given address (pair of binary and offset).
|
||||
|
||||
Overriden in subclasses.
|
||||
Args:
|
||||
addr: virtual address of an instruction.
|
||||
binary: path to executable/shared object containing this instruction.
|
||||
offset: instruction offset in the @binary.
|
||||
Returns:
|
||||
list of strings (one string for each inlined frame) describing
|
||||
the code locations for this instruction (that is, function name, file
|
||||
name, line and column numbers).
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
class LLVMSymbolizer(Symbolizer):
|
||||
def __init__(self, symbolizer_path, default_arch, system, dsym_hints=[]):
|
||||
super(LLVMSymbolizer, self).__init__()
|
||||
self.symbolizer_path = symbolizer_path
|
||||
self.default_arch = default_arch
|
||||
self.system = system
|
||||
self.dsym_hints = dsym_hints
|
||||
self.pipe = self.open_llvm_symbolizer()
|
||||
|
||||
def open_llvm_symbolizer(self):
|
||||
cmd = [self.symbolizer_path,
|
||||
'--use-symbol-table=true',
|
||||
'--demangle=%s' % demangle,
|
||||
'--functions=short',
|
||||
'--inlining=true',
|
||||
'--default-arch=%s' % self.default_arch]
|
||||
if self.system == 'Darwin':
|
||||
for hint in self.dsym_hints:
|
||||
cmd.append('--dsym-hint=%s' % hint)
|
||||
if DEBUG:
|
||||
print ' '.join(cmd)
|
||||
try:
|
||||
result = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
except OSError:
|
||||
result = None
|
||||
return result
|
||||
|
||||
def symbolize(self, addr, binary, offset):
|
||||
"""Overrides Symbolizer.symbolize."""
|
||||
if not self.pipe:
|
||||
return None
|
||||
result = []
|
||||
try:
|
||||
symbolizer_input = '"%s" %s' % (binary, offset)
|
||||
if DEBUG:
|
||||
print symbolizer_input
|
||||
print >> self.pipe.stdin, symbolizer_input
|
||||
while True:
|
||||
function_name = self.pipe.stdout.readline().rstrip()
|
||||
if not function_name:
|
||||
break
|
||||
file_name = self.pipe.stdout.readline().rstrip()
|
||||
file_name = fix_filename(file_name)
|
||||
if (not function_name.startswith('??') or
|
||||
not file_name.startswith('??')):
|
||||
# Append only non-trivial frames.
|
||||
result.append('%s in %s %s' % (addr, function_name,
|
||||
file_name))
|
||||
except Exception:
|
||||
result = []
|
||||
if not result:
|
||||
result = None
|
||||
return result
|
||||
|
||||
|
||||
def LLVMSymbolizerFactory(system, default_arch, dsym_hints=[]):
|
||||
symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH')
|
||||
if not symbolizer_path:
|
||||
symbolizer_path = os.getenv('ASAN_SYMBOLIZER_PATH')
|
||||
if not symbolizer_path:
|
||||
# Assume llvm-symbolizer is in PATH.
|
||||
symbolizer_path = 'llvm-symbolizer'
|
||||
return LLVMSymbolizer(symbolizer_path, default_arch, system, dsym_hints)
|
||||
|
||||
|
||||
class Addr2LineSymbolizer(Symbolizer):
|
||||
def __init__(self, binary):
|
||||
super(Addr2LineSymbolizer, self).__init__()
|
||||
self.binary = binary
|
||||
self.pipe = self.open_addr2line()
|
||||
|
||||
def open_addr2line(self):
|
||||
addr2line_tool = 'addr2line'
|
||||
if binutils_prefix:
|
||||
addr2line_tool = binutils_prefix + addr2line_tool
|
||||
cmd = [addr2line_tool, '-f']
|
||||
if demangle:
|
||||
cmd += ['--demangle']
|
||||
cmd += ['-e', self.binary]
|
||||
if DEBUG:
|
||||
print ' '.join(cmd)
|
||||
return subprocess.Popen(cmd,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||
|
||||
def symbolize(self, addr, binary, offset):
|
||||
"""Overrides Symbolizer.symbolize."""
|
||||
if self.binary != binary:
|
||||
return None
|
||||
try:
|
||||
print >> self.pipe.stdin, offset
|
||||
function_name = self.pipe.stdout.readline().rstrip()
|
||||
file_name = self.pipe.stdout.readline().rstrip()
|
||||
except Exception:
|
||||
function_name = ''
|
||||
file_name = ''
|
||||
file_name = fix_filename(file_name)
|
||||
return ['%s in %s %s' % (addr, function_name, file_name)]
|
||||
|
||||
|
||||
class UnbufferedLineConverter(object):
|
||||
"""
|
||||
Wrap a child process that responds to each line of input with one line of
|
||||
output. Uses pty to trick the child into providing unbuffered output.
|
||||
"""
|
||||
def __init__(self, args, close_stderr=False):
|
||||
# Local imports so that the script can start on Windows.
|
||||
import pty
|
||||
import termios
|
||||
pid, fd = pty.fork()
|
||||
if pid == 0:
|
||||
# We're the child. Transfer control to command.
|
||||
if close_stderr:
|
||||
dev_null = os.open('/dev/null', 0)
|
||||
os.dup2(dev_null, 2)
|
||||
os.execvp(args[0], args)
|
||||
else:
|
||||
# Disable echoing.
|
||||
attr = termios.tcgetattr(fd)
|
||||
attr[3] = attr[3] & ~termios.ECHO
|
||||
termios.tcsetattr(fd, termios.TCSANOW, attr)
|
||||
# Set up a file()-like interface to the child process
|
||||
self.r = os.fdopen(fd, "r", 1)
|
||||
self.w = os.fdopen(os.dup(fd), "w", 1)
|
||||
|
||||
def convert(self, line):
|
||||
self.w.write(line + "\n")
|
||||
return self.readline()
|
||||
|
||||
def readline(self):
|
||||
return self.r.readline().rstrip()
|
||||
|
||||
|
||||
class DarwinSymbolizer(Symbolizer):
|
||||
def __init__(self, addr, binary):
|
||||
super(DarwinSymbolizer, self).__init__()
|
||||
self.binary = binary
|
||||
self.arch = guess_arch(addr)
|
||||
self.open_atos()
|
||||
|
||||
def open_atos(self):
|
||||
if DEBUG:
|
||||
print 'atos -o %s -arch %s' % (self.binary, self.arch)
|
||||
cmdline = ['atos', '-o', self.binary, '-arch', self.arch]
|
||||
self.atos = UnbufferedLineConverter(cmdline, close_stderr=True)
|
||||
|
||||
def symbolize(self, addr, binary, offset):
|
||||
"""Overrides Symbolizer.symbolize."""
|
||||
if self.binary != binary:
|
||||
return None
|
||||
atos_line = self.atos.convert('0x%x' % int(offset, 16))
|
||||
while "got symbolicator for" in atos_line:
|
||||
atos_line = self.atos.readline()
|
||||
# A well-formed atos response looks like this:
|
||||
# foo(type1, type2) (in object.name) (filename.cc:80)
|
||||
match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
|
||||
if DEBUG:
|
||||
print 'atos_line: ', atos_line
|
||||
if match:
|
||||
function_name = match.group(1)
|
||||
function_name = re.sub('\(.*?\)', '', function_name)
|
||||
file_name = fix_filename(match.group(3))
|
||||
return ['%s in %s %s' % (addr, function_name, file_name)]
|
||||
else:
|
||||
return ['%s in %s' % (addr, atos_line)]
|
||||
|
||||
|
||||
# Chain several symbolizers so that if one symbolizer fails, we fall back
|
||||
# to the next symbolizer in chain.
|
||||
class ChainSymbolizer(Symbolizer):
|
||||
def __init__(self, symbolizer_list):
|
||||
super(ChainSymbolizer, self).__init__()
|
||||
self.symbolizer_list = symbolizer_list
|
||||
|
||||
def symbolize(self, addr, binary, offset):
|
||||
"""Overrides Symbolizer.symbolize."""
|
||||
for symbolizer in self.symbolizer_list:
|
||||
if symbolizer:
|
||||
result = symbolizer.symbolize(addr, binary, offset)
|
||||
if result:
|
||||
return result
|
||||
return None
|
||||
|
||||
def append_symbolizer(self, symbolizer):
|
||||
self.symbolizer_list.append(symbolizer)
|
||||
|
||||
|
||||
def BreakpadSymbolizerFactory(binary):
|
||||
suffix = os.getenv('BREAKPAD_SUFFIX')
|
||||
if suffix:
|
||||
filename = binary + suffix
|
||||
if os.access(filename, os.F_OK):
|
||||
return BreakpadSymbolizer(filename)
|
||||
return None
|
||||
|
||||
|
||||
def SystemSymbolizerFactory(system, addr, binary):
|
||||
if system == 'Darwin':
|
||||
return DarwinSymbolizer(addr, binary)
|
||||
elif system == 'Linux':
|
||||
return Addr2LineSymbolizer(binary)
|
||||
|
||||
|
||||
class BreakpadSymbolizer(Symbolizer):
|
||||
def __init__(self, filename):
|
||||
super(BreakpadSymbolizer, self).__init__()
|
||||
self.filename = filename
|
||||
lines = file(filename).readlines()
|
||||
self.files = []
|
||||
self.symbols = {}
|
||||
self.address_list = []
|
||||
self.addresses = {}
|
||||
# MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t
|
||||
fragments = lines[0].rstrip().split()
|
||||
self.arch = fragments[2]
|
||||
self.debug_id = fragments[3]
|
||||
self.binary = ' '.join(fragments[4:])
|
||||
self.parse_lines(lines[1:])
|
||||
|
||||
def parse_lines(self, lines):
|
||||
cur_function_addr = ''
|
||||
for line in lines:
|
||||
fragments = line.split()
|
||||
if fragments[0] == 'FILE':
|
||||
assert int(fragments[1]) == len(self.files)
|
||||
self.files.append(' '.join(fragments[2:]))
|
||||
elif fragments[0] == 'PUBLIC':
|
||||
self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:])
|
||||
elif fragments[0] in ['CFI', 'STACK']:
|
||||
pass
|
||||
elif fragments[0] == 'FUNC':
|
||||
cur_function_addr = int(fragments[1], 16)
|
||||
if not cur_function_addr in self.symbols.keys():
|
||||
self.symbols[cur_function_addr] = ' '.join(fragments[4:])
|
||||
else:
|
||||
# Line starting with an address.
|
||||
addr = int(fragments[0], 16)
|
||||
self.address_list.append(addr)
|
||||
# Tuple of symbol address, size, line, file number.
|
||||
self.addresses[addr] = (cur_function_addr,
|
||||
int(fragments[1], 16),
|
||||
int(fragments[2]),
|
||||
int(fragments[3]))
|
||||
self.address_list.sort()
|
||||
|
||||
def get_sym_file_line(self, addr):
|
||||
key = None
|
||||
if addr in self.addresses.keys():
|
||||
key = addr
|
||||
else:
|
||||
index = bisect.bisect_left(self.address_list, addr)
|
||||
if index == 0:
|
||||
return None
|
||||
else:
|
||||
key = self.address_list[index - 1]
|
||||
sym_id, size, line_no, file_no = self.addresses[key]
|
||||
symbol = self.symbols[sym_id]
|
||||
filename = self.files[file_no]
|
||||
if addr < key + size:
|
||||
return symbol, filename, line_no
|
||||
else:
|
||||
return None
|
||||
|
||||
def symbolize(self, addr, binary, offset):
|
||||
if self.binary != binary:
|
||||
return None
|
||||
res = self.get_sym_file_line(int(offset, 16))
|
||||
if res:
|
||||
function_name, file_name, line_no = res
|
||||
result = ['%s in %s %s:%d' % (
|
||||
addr, function_name, file_name, line_no)]
|
||||
print result
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class SymbolizationLoop(object):
|
||||
def __init__(self, binary_name_filter=None, dsym_hint_producer=None):
|
||||
if sys.platform == 'win32':
|
||||
# ASan on Windows uses dbghelp.dll to symbolize in-process, which works
|
||||
# even in sandboxed processes. Nothing needs to be done here.
|
||||
self.process_line = self.process_line_echo
|
||||
else:
|
||||
# Used by clients who may want to supply a different binary name.
|
||||
# E.g. in Chrome several binaries may share a single .dSYM.
|
||||
self.binary_name_filter = binary_name_filter
|
||||
self.dsym_hint_producer = dsym_hint_producer
|
||||
self.system = os.uname()[0]
|
||||
if self.system not in ['Linux', 'Darwin', 'FreeBSD']:
|
||||
raise Exception('Unknown system')
|
||||
self.llvm_symbolizers = {}
|
||||
self.last_llvm_symbolizer = None
|
||||
self.dsym_hints = set([])
|
||||
self.frame_no = 0
|
||||
self.process_line = self.process_line_posix
|
||||
|
||||
def symbolize_address(self, addr, binary, offset):
|
||||
# On non-Darwin (i.e. on platforms without .dSYM debug info) always use
|
||||
# a single symbolizer binary.
|
||||
# On Darwin, if the dsym hint producer is present:
|
||||
# 1. check whether we've seen this binary already; if so,
|
||||
# use |llvm_symbolizers[binary]|, which has already loaded the debug
|
||||
# info for this binary (might not be the case for
|
||||
# |last_llvm_symbolizer|);
|
||||
# 2. otherwise check if we've seen all the hints for this binary already;
|
||||
# if so, reuse |last_llvm_symbolizer| which has the full set of hints;
|
||||
# 3. otherwise create a new symbolizer and pass all currently known
|
||||
# .dSYM hints to it.
|
||||
if not binary in self.llvm_symbolizers:
|
||||
use_new_symbolizer = True
|
||||
if self.system == 'Darwin' and self.dsym_hint_producer:
|
||||
dsym_hints_for_binary = set(self.dsym_hint_producer(binary))
|
||||
use_new_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints)
|
||||
self.dsym_hints |= dsym_hints_for_binary
|
||||
if self.last_llvm_symbolizer and not use_new_symbolizer:
|
||||
self.llvm_symbolizers[binary] = self.last_llvm_symbolizer
|
||||
else:
|
||||
self.last_llvm_symbolizer = LLVMSymbolizerFactory(
|
||||
self.system, guess_arch(addr), self.dsym_hints)
|
||||
self.llvm_symbolizers[binary] = self.last_llvm_symbolizer
|
||||
# Use the chain of symbolizers:
|
||||
# Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos
|
||||
# (fall back to next symbolizer if the previous one fails).
|
||||
if not binary in symbolizers:
|
||||
symbolizers[binary] = ChainSymbolizer(
|
||||
[BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]])
|
||||
result = symbolizers[binary].symbolize(addr, binary, offset)
|
||||
if result is None:
|
||||
if not allow_system_symbolizer:
|
||||
raise Exception('Failed to launch or use llvm-symbolizer.')
|
||||
# Initialize system symbolizer only if other symbolizers failed.
|
||||
symbolizers[binary].append_symbolizer(
|
||||
SystemSymbolizerFactory(self.system, addr, binary))
|
||||
result = symbolizers[binary].symbolize(addr, binary, offset)
|
||||
# The system symbolizer must produce some result.
|
||||
assert result
|
||||
return result
|
||||
|
||||
def get_symbolized_lines(self, symbolized_lines):
|
||||
if not symbolized_lines:
|
||||
return [self.current_line]
|
||||
else:
|
||||
result = []
|
||||
for symbolized_frame in symbolized_lines:
|
||||
result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstrip()))
|
||||
self.frame_no += 1
|
||||
return result
|
||||
|
||||
def process_logfile(self):
|
||||
self.frame_no = 0
|
||||
for line in logfile:
|
||||
processed = self.process_line(line)
|
||||
print '\n'.join(processed)
|
||||
|
||||
def process_line_echo(self, line):
|
||||
return [line.rstrip()]
|
||||
|
||||
def process_line_posix(self, line):
|
||||
self.current_line = line.rstrip()
|
||||
#0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45)
|
||||
stack_trace_line_format = (
|
||||
'^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)')
|
||||
match = re.match(stack_trace_line_format, line)
|
||||
if not match:
|
||||
return [self.current_line]
|
||||
if DEBUG:
|
||||
print line
|
||||
_, frameno_str, addr, binary, offset = match.groups()
|
||||
if frameno_str == '0':
|
||||
# Assume that frame #0 is the first frame of new stack trace.
|
||||
self.frame_no = 0
|
||||
original_binary = binary
|
||||
if self.binary_name_filter:
|
||||
binary = self.binary_name_filter(binary)
|
||||
symbolized_line = self.symbolize_address(addr, binary, offset)
|
||||
if not symbolized_line:
|
||||
if original_binary != binary:
|
||||
symbolized_line = self.symbolize_address(addr, binary, offset)
|
||||
return self.get_symbolized_lines(symbolized_line)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
description='ASan symbolization script',
|
||||
epilog='Example of use:\n'
|
||||
'asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" '
|
||||
'-s "$HOME/SymbolFiles" < asan.log')
|
||||
parser.add_argument('path_to_cut', nargs='*',
|
||||
help='pattern to be cut from the result file path ')
|
||||
parser.add_argument('-d','--demangle', action='store_true',
|
||||
help='demangle function names')
|
||||
parser.add_argument('-s', metavar='SYSROOT',
|
||||
help='set path to sysroot for sanitized binaries')
|
||||
parser.add_argument('-c', metavar='CROSS_COMPILE',
|
||||
help='set prefix for binutils')
|
||||
parser.add_argument('-l','--logfile', default=sys.stdin,
|
||||
type=argparse.FileType('r'),
|
||||
help='set log file name to parse, default is stdin')
|
||||
args = parser.parse_args()
|
||||
if args.path_to_cut:
|
||||
fix_filename_patterns = args.path_to_cut
|
||||
if args.demangle:
|
||||
demangle = True
|
||||
if args.s:
|
||||
binary_name_filter = sysroot_path_filter
|
||||
sysroot_path = args.s
|
||||
if args.c:
|
||||
binutils_prefix = args.c
|
||||
if args.logfile:
|
||||
logfile = args.logfile
|
||||
else:
|
||||
logfile = sys.stdin
|
||||
loop = SymbolizationLoop(binary_name_filter)
|
||||
loop.process_logfile()
|
@ -1,267 +0,0 @@
|
||||
//===-- asan_asm_test.cc --------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#if defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__))
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
namespace {
|
||||
|
||||
template<typename T> void asm_write(T *ptr, T val);
|
||||
template<typename T> T asm_read(T *ptr);
|
||||
template<typename T> void asm_rep_movs(T *dst, T *src, size_t n);
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
#endif // defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__))
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
namespace {
|
||||
|
||||
#define DECLARE_ASM_WRITE(Type, Size, Mov, Reg) \
|
||||
template<> void asm_write<Type>(Type *ptr, Type val) { \
|
||||
__asm__( \
|
||||
Mov " %[val], (%[ptr]) \n\t" \
|
||||
: \
|
||||
: [ptr] "r" (ptr), [val] Reg (val) \
|
||||
: "memory" \
|
||||
); \
|
||||
}
|
||||
|
||||
#define DECLARE_ASM_READ(Type, Size, Mov, Reg) \
|
||||
template<> Type asm_read<Type>(Type *ptr) { \
|
||||
Type res; \
|
||||
__asm__( \
|
||||
Mov " (%[ptr]), %[res] \n\t" \
|
||||
: [res] Reg (res) \
|
||||
: [ptr] "r" (ptr) \
|
||||
: "memory" \
|
||||
); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
|
||||
template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
|
||||
__asm__("rep " Movs " \n\t" \
|
||||
: \
|
||||
: "D"(dst), "S"(src), "c"(size) \
|
||||
: "rsi", "rdi", "rcx", "memory"); \
|
||||
}
|
||||
|
||||
DECLARE_ASM_WRITE(U8, "8", "movq", "r");
|
||||
DECLARE_ASM_READ(U8, "8", "movq", "=r");
|
||||
DECLARE_ASM_REP_MOVS(U8, "movsq");
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
#endif // defined(__x86_64__)
|
||||
|
||||
#if defined(__i386__) && defined(__SSE2__)
|
||||
|
||||
namespace {
|
||||
|
||||
#define DECLARE_ASM_WRITE(Type, Size, Mov, Reg) \
|
||||
template<> void asm_write<Type>(Type *ptr, Type val) { \
|
||||
__asm__( \
|
||||
Mov " %[val], (%[ptr]) \n\t" \
|
||||
: \
|
||||
: [ptr] "r" (ptr), [val] Reg (val) \
|
||||
: "memory" \
|
||||
); \
|
||||
}
|
||||
|
||||
#define DECLARE_ASM_READ(Type, Size, Mov, Reg) \
|
||||
template<> Type asm_read<Type>(Type *ptr) { \
|
||||
Type res; \
|
||||
__asm__( \
|
||||
Mov " (%[ptr]), %[res] \n\t" \
|
||||
: [res] Reg (res) \
|
||||
: [ptr] "r" (ptr) \
|
||||
: "memory" \
|
||||
); \
|
||||
return res; \
|
||||
}
|
||||
|
||||
#define DECLARE_ASM_REP_MOVS(Type, Movs) \
|
||||
template <> void asm_rep_movs<Type>(Type * dst, Type * src, size_t size) { \
|
||||
__asm__("rep " Movs " \n\t" \
|
||||
: \
|
||||
: "D"(dst), "S"(src), "c"(size) \
|
||||
: "esi", "edi", "ecx", "memory"); \
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
#endif // defined(__i386__) && defined(__SSE2__)
|
||||
|
||||
#if defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__))
|
||||
|
||||
namespace {
|
||||
|
||||
DECLARE_ASM_WRITE(U1, "1", "movb", "r");
|
||||
DECLARE_ASM_WRITE(U2, "2", "movw", "r");
|
||||
DECLARE_ASM_WRITE(U4, "4", "movl", "r");
|
||||
DECLARE_ASM_WRITE(__m128i, "16", "movaps", "x");
|
||||
|
||||
DECLARE_ASM_READ(U1, "1", "movb", "=r");
|
||||
DECLARE_ASM_READ(U2, "2", "movw", "=r");
|
||||
DECLARE_ASM_READ(U4, "4", "movl", "=r");
|
||||
DECLARE_ASM_READ(__m128i, "16", "movaps", "=x");
|
||||
|
||||
DECLARE_ASM_REP_MOVS(U1, "movsb");
|
||||
DECLARE_ASM_REP_MOVS(U2, "movsw");
|
||||
DECLARE_ASM_REP_MOVS(U4, "movsl");
|
||||
|
||||
template<typename T> void TestAsmWrite(const char *DeathPattern) {
|
||||
T *buf = new T;
|
||||
EXPECT_DEATH(asm_write(&buf[1], static_cast<T>(0)), DeathPattern);
|
||||
T var = 0x12;
|
||||
asm_write(&var, static_cast<T>(0x21));
|
||||
ASSERT_EQ(static_cast<T>(0x21), var);
|
||||
delete buf;
|
||||
}
|
||||
|
||||
template<> void TestAsmWrite<__m128i>(const char *DeathPattern) {
|
||||
char *buf = new char[16];
|
||||
char *p = buf + 16;
|
||||
if (((uintptr_t) p % 16) != 0)
|
||||
p = buf + 8;
|
||||
assert(((uintptr_t) p % 16) == 0);
|
||||
__m128i val = _mm_set1_epi16(0x1234);
|
||||
EXPECT_DEATH(asm_write<__m128i>((__m128i*) p, val), DeathPattern);
|
||||
__m128i var = _mm_set1_epi16(0x4321);
|
||||
asm_write(&var, val);
|
||||
ASSERT_EQ(0x1234, _mm_extract_epi16(var, 0));
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
template<typename T> void TestAsmRead(const char *DeathPattern) {
|
||||
T *buf = new T;
|
||||
EXPECT_DEATH(asm_read(&buf[1]), DeathPattern);
|
||||
T var = 0x12;
|
||||
ASSERT_EQ(static_cast<T>(0x12), asm_read(&var));
|
||||
delete buf;
|
||||
}
|
||||
|
||||
template<> void TestAsmRead<__m128i>(const char *DeathPattern) {
|
||||
char *buf = new char[16];
|
||||
char *p = buf + 16;
|
||||
if (((uintptr_t) p % 16) != 0)
|
||||
p = buf + 8;
|
||||
assert(((uintptr_t) p % 16) == 0);
|
||||
EXPECT_DEATH(asm_read<__m128i>((__m128i*) p), DeathPattern);
|
||||
__m128i val = _mm_set1_epi16(0x1234);
|
||||
ASSERT_EQ(0x1234, _mm_extract_epi16(asm_read(&val), 0));
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
U4 AsmLoad(U4 *a) {
|
||||
U4 r;
|
||||
__asm__("movl (%[a]), %[r] \n\t" : [r] "=r" (r) : [a] "r" (a) : "memory");
|
||||
return r;
|
||||
}
|
||||
|
||||
void AsmStore(U4 r, U4 *a) {
|
||||
__asm__("movl %[r], (%[a]) \n\t" : : [a] "r" (a), [r] "r" (r) : "memory");
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TestAsmRepMovs(const char *DeathPatternRead,
|
||||
const char *DeathPatternWrite) {
|
||||
T src_good[4] = { 0x0, 0x1, 0x2, 0x3 };
|
||||
T dst_good[4] = {};
|
||||
asm_rep_movs(dst_good, src_good, 4);
|
||||
ASSERT_EQ(static_cast<T>(0x0), dst_good[0]);
|
||||
ASSERT_EQ(static_cast<T>(0x1), dst_good[1]);
|
||||
ASSERT_EQ(static_cast<T>(0x2), dst_good[2]);
|
||||
ASSERT_EQ(static_cast<T>(0x3), dst_good[3]);
|
||||
|
||||
T dst_bad[3];
|
||||
EXPECT_DEATH(asm_rep_movs(dst_bad, src_good, 4), DeathPatternWrite);
|
||||
|
||||
T src_bad[3] = { 0x0, 0x1, 0x2 };
|
||||
EXPECT_DEATH(asm_rep_movs(dst_good, src_bad, 4), DeathPatternRead);
|
||||
|
||||
T* dp = dst_bad + 4;
|
||||
T* sp = src_bad + 4;
|
||||
asm_rep_movs(dp, sp, 0);
|
||||
}
|
||||
|
||||
} // End of anonymous namespace
|
||||
|
||||
TEST(AddressSanitizer, asm_load_store) {
|
||||
U4* buf = new U4[2];
|
||||
EXPECT_DEATH(AsmLoad(&buf[3]), "READ of size 4");
|
||||
EXPECT_DEATH(AsmStore(0x1234, &buf[3]), "WRITE of size 4");
|
||||
delete [] buf;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, asm_rw) {
|
||||
TestAsmWrite<U1>("WRITE of size 1");
|
||||
TestAsmWrite<U2>("WRITE of size 2");
|
||||
TestAsmWrite<U4>("WRITE of size 4");
|
||||
#if defined(__x86_64__)
|
||||
TestAsmWrite<U8>("WRITE of size 8");
|
||||
#endif // defined(__x86_64__)
|
||||
TestAsmWrite<__m128i>("WRITE of size 16");
|
||||
|
||||
TestAsmRead<U1>("READ of size 1");
|
||||
TestAsmRead<U2>("READ of size 2");
|
||||
TestAsmRead<U4>("READ of size 4");
|
||||
#if defined(__x86_64__)
|
||||
TestAsmRead<U8>("READ of size 8");
|
||||
#endif // defined(__x86_64__)
|
||||
TestAsmRead<__m128i>("READ of size 16");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, asm_flags) {
|
||||
long magic = 0x1234;
|
||||
long r = 0x0;
|
||||
|
||||
#if defined(__x86_64__) && !defined(__ILP32__)
|
||||
__asm__("xorq %%rax, %%rax \n\t"
|
||||
"movq (%[p]), %%rax \n\t"
|
||||
"sete %%al \n\t"
|
||||
"movzbq %%al, %[r] \n\t"
|
||||
: [r] "=r"(r)
|
||||
: [p] "r"(&magic)
|
||||
: "rax", "memory");
|
||||
#else
|
||||
__asm__("xorl %%eax, %%eax \n\t"
|
||||
"movl (%[p]), %%eax \n\t"
|
||||
"sete %%al \n\t"
|
||||
"movzbl %%al, %[r] \n\t"
|
||||
: [r] "=r"(r)
|
||||
: [p] "r"(&magic)
|
||||
: "eax", "memory");
|
||||
#endif // defined(__x86_64__) && !defined(__ILP32__)
|
||||
|
||||
ASSERT_EQ(0x1, r);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, asm_rep_movs) {
|
||||
TestAsmRepMovs<U1>("READ of size 1", "WRITE of size 1");
|
||||
TestAsmRepMovs<U2>("READ of size 2", "WRITE of size 2");
|
||||
TestAsmRepMovs<U4>("READ of size 4", "WRITE of size 4");
|
||||
#if defined(__x86_64__)
|
||||
TestAsmRepMovs<U8>("READ of size 8", "WRITE of size 8");
|
||||
#endif // defined(__x86_64__)
|
||||
}
|
||||
|
||||
#endif // defined(__x86_64__) || (defined(__i386__) && defined(__SSE2__))
|
||||
|
||||
#endif // defined(__linux__)
|
@ -1,85 +0,0 @@
|
||||
//===-- asan_benchmarks_test.cc ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Some benchmarks for the instrumented code.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
template<class T>
|
||||
__attribute__((noinline))
|
||||
static void ManyAccessFunc(T *x, size_t n_elements, size_t n_iter) {
|
||||
for (size_t iter = 0; iter < n_iter; iter++) {
|
||||
break_optimization(0);
|
||||
// hand unroll the loop to stress the reg alloc.
|
||||
for (size_t i = 0; i <= n_elements - 16; i += 16) {
|
||||
x[i + 0] = i;
|
||||
x[i + 1] = i;
|
||||
x[i + 2] = i;
|
||||
x[i + 3] = i;
|
||||
x[i + 4] = i;
|
||||
x[i + 5] = i;
|
||||
x[i + 6] = i;
|
||||
x[i + 7] = i;
|
||||
x[i + 8] = i;
|
||||
x[i + 9] = i;
|
||||
x[i + 10] = i;
|
||||
x[i + 11] = i;
|
||||
x[i + 12] = i;
|
||||
x[i + 13] = i;
|
||||
x[i + 14] = i;
|
||||
x[i + 15] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, ManyAccessBenchmark) {
|
||||
size_t kLen = 1024;
|
||||
int *int_array = new int[kLen];
|
||||
ManyAccessFunc(int_array, kLen, 1 << 24);
|
||||
delete [] int_array;
|
||||
}
|
||||
|
||||
// access 7 char elements in a 7 byte array (i.e. on the border).
|
||||
__attribute__((noinline))
|
||||
static void BorderAccessFunc(char *x, size_t n_iter) {
|
||||
for (size_t iter = 0; iter < n_iter; iter++) {
|
||||
break_optimization(x);
|
||||
x[0] = 0;
|
||||
x[1] = 0;
|
||||
x[2] = 0;
|
||||
x[3] = 0;
|
||||
x[4] = 0;
|
||||
x[5] = 0;
|
||||
x[6] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, BorderAccessBenchmark) {
|
||||
char *char_7_array = new char[7];
|
||||
BorderAccessFunc(char_7_array, 1 << 30);
|
||||
delete [] char_7_array;
|
||||
}
|
||||
|
||||
static void FunctionWithLargeStack() {
|
||||
int stack[1000];
|
||||
Ident(stack);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, FakeStackBenchmark) {
|
||||
for (int i = 0; i < 10000000; i++)
|
||||
Ident(&FunctionWithLargeStack)();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=11468
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
class Action {
|
||||
public:
|
||||
Action() {}
|
||||
void PrintString(const std::string& msg) const {
|
||||
fprintf(stderr, "%s\n", msg.c_str());
|
||||
}
|
||||
void Throw(const char& arg) const {
|
||||
PrintString("PrintString called!"); // this line is important
|
||||
throw arg;
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
const Action a;
|
||||
fprintf(stderr, "&a before = %p\n", &a);
|
||||
try {
|
||||
a.Throw('c');
|
||||
} catch(const char&) {
|
||||
fprintf(stderr, "&a in catch = %p\n", &a);
|
||||
}
|
||||
fprintf(stderr, "&a final = %p\n", &a);
|
||||
return 0;
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
//===-- asan_fake_stack_test.cc -------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Tests for FakeStack.
|
||||
// This test file should be compiled w/o asan instrumentation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_fake_stack.h"
|
||||
#include "asan_test_utils.h"
|
||||
#include "sanitizer_common/sanitizer_common.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace __asan {
|
||||
|
||||
TEST(FakeStack, FlagsSize) {
|
||||
EXPECT_EQ(FakeStack::SizeRequiredForFlags(10), 1U << 5);
|
||||
EXPECT_EQ(FakeStack::SizeRequiredForFlags(11), 1U << 6);
|
||||
EXPECT_EQ(FakeStack::SizeRequiredForFlags(20), 1U << 15);
|
||||
}
|
||||
|
||||
TEST(FakeStack, RequiredSize) {
|
||||
// for (int i = 15; i < 20; i++) {
|
||||
// uptr alloc_size = FakeStack::RequiredSize(i);
|
||||
// printf("%zdK ==> %zd\n", 1 << (i - 10), alloc_size);
|
||||
// }
|
||||
EXPECT_EQ(FakeStack::RequiredSize(15), 365568U);
|
||||
EXPECT_EQ(FakeStack::RequiredSize(16), 727040U);
|
||||
EXPECT_EQ(FakeStack::RequiredSize(17), 1449984U);
|
||||
EXPECT_EQ(FakeStack::RequiredSize(18), 2895872U);
|
||||
EXPECT_EQ(FakeStack::RequiredSize(19), 5787648U);
|
||||
}
|
||||
|
||||
TEST(FakeStack, FlagsOffset) {
|
||||
for (uptr stack_size_log = 15; stack_size_log <= 20; stack_size_log++) {
|
||||
uptr stack_size = 1UL << stack_size_log;
|
||||
uptr offset = 0;
|
||||
for (uptr class_id = 0; class_id < FakeStack::kNumberOfSizeClasses;
|
||||
class_id++) {
|
||||
uptr frame_size = FakeStack::BytesInSizeClass(class_id);
|
||||
uptr num_flags = stack_size / frame_size;
|
||||
EXPECT_EQ(offset, FakeStack::FlagsOffset(stack_size_log, class_id));
|
||||
// printf("%zd: %zd => %zd %zd\n", stack_size_log, class_id, offset,
|
||||
// FakeStack::FlagsOffset(stack_size_log, class_id));
|
||||
offset += num_flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // FIXME: Fails due to OOM on Windows.
|
||||
TEST(FakeStack, CreateDestroy) {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
for (uptr stack_size_log = 20; stack_size_log <= 22; stack_size_log++) {
|
||||
FakeStack *fake_stack = FakeStack::Create(stack_size_log);
|
||||
fake_stack->Destroy(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(FakeStack, ModuloNumberOfFrames) {
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, 0), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15)), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<10)), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<9)), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<8)), 1U<<8);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 0, (1<<15) + 1), 1U);
|
||||
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 0), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<9), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<8), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 1, 1<<7), 1U<<7);
|
||||
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 0), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 1), 1U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 15), 15U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 16), 0U);
|
||||
EXPECT_EQ(FakeStack::ModuloNumberOfFrames(15, 5, 17), 1U);
|
||||
}
|
||||
|
||||
TEST(FakeStack, GetFrame) {
|
||||
const uptr stack_size_log = 20;
|
||||
const uptr stack_size = 1 << stack_size_log;
|
||||
FakeStack *fs = FakeStack::Create(stack_size_log);
|
||||
u8 *base = fs->GetFrame(stack_size_log, 0, 0);
|
||||
EXPECT_EQ(base, reinterpret_cast<u8 *>(fs) +
|
||||
fs->SizeRequiredForFlags(stack_size_log) + 4096);
|
||||
EXPECT_EQ(base + 0*stack_size + 64 * 7, fs->GetFrame(stack_size_log, 0, 7U));
|
||||
EXPECT_EQ(base + 1*stack_size + 128 * 3, fs->GetFrame(stack_size_log, 1, 3U));
|
||||
EXPECT_EQ(base + 2*stack_size + 256 * 5, fs->GetFrame(stack_size_log, 2, 5U));
|
||||
fs->Destroy(0);
|
||||
}
|
||||
|
||||
TEST(FakeStack, Allocate) {
|
||||
const uptr stack_size_log = 19;
|
||||
FakeStack *fs = FakeStack::Create(stack_size_log);
|
||||
std::map<FakeFrame *, uptr> s;
|
||||
for (int iter = 0; iter < 2; iter++) {
|
||||
s.clear();
|
||||
for (uptr cid = 0; cid < FakeStack::kNumberOfSizeClasses; cid++) {
|
||||
uptr n = FakeStack::NumberOfFrames(stack_size_log, cid);
|
||||
uptr bytes_in_class = FakeStack::BytesInSizeClass(cid);
|
||||
for (uptr j = 0; j < n; j++) {
|
||||
FakeFrame *ff = fs->Allocate(stack_size_log, cid, 0);
|
||||
uptr x = reinterpret_cast<uptr>(ff);
|
||||
EXPECT_TRUE(s.insert(std::make_pair(ff, cid)).second);
|
||||
EXPECT_EQ(x, fs->AddrIsInFakeStack(x));
|
||||
EXPECT_EQ(x, fs->AddrIsInFakeStack(x + 1));
|
||||
EXPECT_EQ(x, fs->AddrIsInFakeStack(x + bytes_in_class - 1));
|
||||
EXPECT_NE(x, fs->AddrIsInFakeStack(x + bytes_in_class));
|
||||
}
|
||||
// We are out of fake stack, so Allocate should return 0.
|
||||
EXPECT_EQ(0UL, fs->Allocate(stack_size_log, cid, 0));
|
||||
}
|
||||
for (std::map<FakeFrame *, uptr>::iterator it = s.begin(); it != s.end();
|
||||
++it) {
|
||||
fs->Deallocate(reinterpret_cast<uptr>(it->first), it->second);
|
||||
}
|
||||
}
|
||||
fs->Destroy(0);
|
||||
}
|
||||
|
||||
static void RecursiveFunction(FakeStack *fs, int depth) {
|
||||
uptr class_id = depth / 3;
|
||||
FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, 0);
|
||||
if (depth) {
|
||||
RecursiveFunction(fs, depth - 1);
|
||||
RecursiveFunction(fs, depth - 1);
|
||||
}
|
||||
fs->Deallocate(reinterpret_cast<uptr>(ff), class_id);
|
||||
}
|
||||
|
||||
TEST(FakeStack, RecursiveStressTest) {
|
||||
const uptr stack_size_log = 16;
|
||||
FakeStack *fs = FakeStack::Create(stack_size_log);
|
||||
RecursiveFunction(fs, 22); // with 26 runs for 2-3 seconds.
|
||||
fs->Destroy(0);
|
||||
}
|
||||
|
||||
} // namespace __asan
|
@ -1,45 +0,0 @@
|
||||
//===-- asan_globals_test.cc ----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// Some globals in a separate file.
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
char glob1[1];
|
||||
char glob2[2];
|
||||
char glob3[3];
|
||||
char glob4[4];
|
||||
char glob5[5];
|
||||
char glob6[6];
|
||||
char glob7[7];
|
||||
char glob8[8];
|
||||
char glob9[9];
|
||||
char glob10[10];
|
||||
char glob11[11];
|
||||
char glob12[12];
|
||||
char glob13[13];
|
||||
char glob14[14];
|
||||
char glob15[15];
|
||||
char glob16[16];
|
||||
char glob17[17];
|
||||
char glob1000[1000];
|
||||
char glob10000[10000];
|
||||
char glob100000[100000];
|
||||
|
||||
static char static10[10];
|
||||
|
||||
int GlobalsTest(int zero) {
|
||||
static char func_static15[15];
|
||||
glob5[zero] = 0;
|
||||
static10[zero] = 0;
|
||||
func_static15[zero] = 0;
|
||||
return glob5[1] + func_static15[2];
|
||||
}
|
@ -1,433 +0,0 @@
|
||||
//===-- asan_interface_test.cc --------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
#include <sanitizer/allocator_interface.h>
|
||||
#include <sanitizer/asan_interface.h>
|
||||
|
||||
TEST(AddressSanitizerInterface, GetEstimatedAllocatedSize) {
|
||||
EXPECT_EQ(0U, __sanitizer_get_estimated_allocated_size(0));
|
||||
const size_t sizes[] = { 1, 30, 1<<30 };
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
EXPECT_EQ(sizes[i], __sanitizer_get_estimated_allocated_size(sizes[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kGetAllocatedSizeErrorMsg =
|
||||
"attempting to call __sanitizer_get_allocated_size";
|
||||
|
||||
TEST(AddressSanitizerInterface, GetAllocatedSizeAndOwnershipTest) {
|
||||
const size_t kArraySize = 100;
|
||||
char *array = Ident((char*)malloc(kArraySize));
|
||||
int *int_ptr = Ident(new int);
|
||||
|
||||
// Allocated memory is owned by allocator. Allocated size should be
|
||||
// equal to requested size.
|
||||
EXPECT_EQ(true, __sanitizer_get_ownership(array));
|
||||
EXPECT_EQ(kArraySize, __sanitizer_get_allocated_size(array));
|
||||
EXPECT_EQ(true, __sanitizer_get_ownership(int_ptr));
|
||||
EXPECT_EQ(sizeof(int), __sanitizer_get_allocated_size(int_ptr));
|
||||
|
||||
// We cannot call GetAllocatedSize from the memory we didn't map,
|
||||
// and from the interior pointers (not returned by previous malloc).
|
||||
void *wild_addr = (void*)0x1;
|
||||
EXPECT_FALSE(__sanitizer_get_ownership(wild_addr));
|
||||
EXPECT_DEATH(__sanitizer_get_allocated_size(wild_addr),
|
||||
kGetAllocatedSizeErrorMsg);
|
||||
EXPECT_FALSE(__sanitizer_get_ownership(array + kArraySize / 2));
|
||||
EXPECT_DEATH(__sanitizer_get_allocated_size(array + kArraySize / 2),
|
||||
kGetAllocatedSizeErrorMsg);
|
||||
|
||||
// NULL is not owned, but is a valid argument for
|
||||
// __sanitizer_get_allocated_size().
|
||||
EXPECT_FALSE(__sanitizer_get_ownership(NULL));
|
||||
EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL));
|
||||
|
||||
// When memory is freed, it's not owned, and call to GetAllocatedSize
|
||||
// is forbidden.
|
||||
free(array);
|
||||
EXPECT_FALSE(__sanitizer_get_ownership(array));
|
||||
EXPECT_DEATH(__sanitizer_get_allocated_size(array),
|
||||
kGetAllocatedSizeErrorMsg);
|
||||
delete int_ptr;
|
||||
|
||||
void *zero_alloc = Ident(malloc(0));
|
||||
if (zero_alloc != 0) {
|
||||
// If malloc(0) is not null, this pointer is owned and should have valid
|
||||
// allocated size.
|
||||
EXPECT_TRUE(__sanitizer_get_ownership(zero_alloc));
|
||||
// Allocated size is 0 or 1 depending on the allocator used.
|
||||
EXPECT_LT(__sanitizer_get_allocated_size(zero_alloc), 2U);
|
||||
}
|
||||
free(zero_alloc);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetCurrentAllocatedBytesTest) {
|
||||
size_t before_malloc, after_malloc, after_free;
|
||||
char *array;
|
||||
const size_t kMallocSize = 100;
|
||||
before_malloc = __sanitizer_get_current_allocated_bytes();
|
||||
|
||||
array = Ident((char*)malloc(kMallocSize));
|
||||
after_malloc = __sanitizer_get_current_allocated_bytes();
|
||||
EXPECT_EQ(before_malloc + kMallocSize, after_malloc);
|
||||
|
||||
free(array);
|
||||
after_free = __sanitizer_get_current_allocated_bytes();
|
||||
EXPECT_EQ(before_malloc, after_free);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GetHeapSizeTest) {
|
||||
// ASan allocator does not keep huge chunks in free list, but unmaps them.
|
||||
// The chunk should be greater than the quarantine size,
|
||||
// otherwise it will be stuck in quarantine instead of being unmaped.
|
||||
static const size_t kLargeMallocSize = (1 << 28) + 1; // 256M
|
||||
free(Ident(malloc(kLargeMallocSize))); // Drain quarantine.
|
||||
size_t old_heap_size = __sanitizer_get_heap_size();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
// fprintf(stderr, "allocating %zu bytes:\n", kLargeMallocSize);
|
||||
free(Ident(malloc(kLargeMallocSize)));
|
||||
EXPECT_EQ(old_heap_size, __sanitizer_get_heap_size());
|
||||
}
|
||||
}
|
||||
|
||||
static const size_t kManyThreadsMallocSizes[] = {5, 1UL<<10, 1UL<<14, 357};
|
||||
static const size_t kManyThreadsIterations = 250;
|
||||
static const size_t kManyThreadsNumThreads =
|
||||
(SANITIZER_WORDSIZE == 32) ? 40 : 200;
|
||||
|
||||
static void *ManyThreadsWithStatsWorker(void *arg) {
|
||||
(void)arg;
|
||||
for (size_t iter = 0; iter < kManyThreadsIterations; iter++) {
|
||||
for (size_t size_index = 0; size_index < 4; size_index++) {
|
||||
free(Ident(malloc(kManyThreadsMallocSizes[size_index])));
|
||||
}
|
||||
}
|
||||
// Just one large allocation.
|
||||
free(Ident(malloc(1 << 20)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, ManyThreadsWithStatsStressTest) {
|
||||
size_t before_test, after_test, i;
|
||||
pthread_t threads[kManyThreadsNumThreads];
|
||||
before_test = __sanitizer_get_current_allocated_bytes();
|
||||
for (i = 0; i < kManyThreadsNumThreads; i++) {
|
||||
PTHREAD_CREATE(&threads[i], 0,
|
||||
(void* (*)(void *x))ManyThreadsWithStatsWorker, (void*)i);
|
||||
}
|
||||
for (i = 0; i < kManyThreadsNumThreads; i++) {
|
||||
PTHREAD_JOIN(threads[i], 0);
|
||||
}
|
||||
after_test = __sanitizer_get_current_allocated_bytes();
|
||||
// ASan stats also reflect memory usage of internal ASan RTL structs,
|
||||
// so we can't check for equality here.
|
||||
EXPECT_LT(after_test, before_test + (1UL<<20));
|
||||
}
|
||||
|
||||
static void DoDoubleFree() {
|
||||
int *x = Ident(new int);
|
||||
delete Ident(x);
|
||||
delete Ident(x);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, ExitCode) {
|
||||
int original_exit_code = __asan_set_error_exit_code(7);
|
||||
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(7), "");
|
||||
EXPECT_EQ(7, __asan_set_error_exit_code(8));
|
||||
EXPECT_EXIT(DoDoubleFree(), ::testing::ExitedWithCode(8), "");
|
||||
EXPECT_EQ(8, __asan_set_error_exit_code(original_exit_code));
|
||||
EXPECT_EXIT(DoDoubleFree(),
|
||||
::testing::ExitedWithCode(original_exit_code), "");
|
||||
}
|
||||
|
||||
static void MyDeathCallback() {
|
||||
fprintf(stderr, "MyDeathCallback\n");
|
||||
fflush(0); // On Windows, stderr doesn't flush on crash.
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, DeathCallbackTest) {
|
||||
__asan_set_death_callback(MyDeathCallback);
|
||||
EXPECT_DEATH(DoDoubleFree(), "MyDeathCallback");
|
||||
__asan_set_death_callback(NULL);
|
||||
}
|
||||
|
||||
static const char* kUseAfterPoisonErrorMessage = "use-after-poison";
|
||||
|
||||
#define GOOD_ACCESS(ptr, offset) \
|
||||
EXPECT_FALSE(__asan_address_is_poisoned(ptr + offset))
|
||||
|
||||
#define BAD_ACCESS(ptr, offset) \
|
||||
EXPECT_TRUE(__asan_address_is_poisoned(ptr + offset))
|
||||
|
||||
TEST(AddressSanitizerInterface, SimplePoisonMemoryRegionTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
// poison array[40..80)
|
||||
__asan_poison_memory_region(array + 40, 40);
|
||||
GOOD_ACCESS(array, 39);
|
||||
GOOD_ACCESS(array, 80);
|
||||
BAD_ACCESS(array, 40);
|
||||
BAD_ACCESS(array, 60);
|
||||
BAD_ACCESS(array, 79);
|
||||
char value;
|
||||
EXPECT_DEATH(value = Ident(array[40]), kUseAfterPoisonErrorMessage);
|
||||
__asan_unpoison_memory_region(array + 40, 40);
|
||||
// access previously poisoned memory.
|
||||
GOOD_ACCESS(array, 40);
|
||||
GOOD_ACCESS(array, 79);
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, OverlappingPoisonMemoryRegionTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
// Poison [0..40) and [80..120)
|
||||
__asan_poison_memory_region(array, 40);
|
||||
__asan_poison_memory_region(array + 80, 40);
|
||||
BAD_ACCESS(array, 20);
|
||||
GOOD_ACCESS(array, 60);
|
||||
BAD_ACCESS(array, 100);
|
||||
// Poison whole array - [0..120)
|
||||
__asan_poison_memory_region(array, 120);
|
||||
BAD_ACCESS(array, 60);
|
||||
// Unpoison [24..96)
|
||||
__asan_unpoison_memory_region(array + 24, 72);
|
||||
BAD_ACCESS(array, 23);
|
||||
GOOD_ACCESS(array, 24);
|
||||
GOOD_ACCESS(array, 60);
|
||||
GOOD_ACCESS(array, 95);
|
||||
BAD_ACCESS(array, 96);
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PushAndPopWithPoisoningTest) {
|
||||
// Vector of capacity 20
|
||||
char *vec = Ident((char*)malloc(20));
|
||||
__asan_poison_memory_region(vec, 20);
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
// Simulate push_back.
|
||||
__asan_unpoison_memory_region(vec + i, 1);
|
||||
GOOD_ACCESS(vec, i);
|
||||
BAD_ACCESS(vec, i + 1);
|
||||
}
|
||||
for (size_t i = 7; i > 0; i--) {
|
||||
// Simulate pop_back.
|
||||
__asan_poison_memory_region(vec + i - 1, 1);
|
||||
BAD_ACCESS(vec, i - 1);
|
||||
if (i > 1) GOOD_ACCESS(vec, i - 2);
|
||||
}
|
||||
free(vec);
|
||||
}
|
||||
|
||||
// Make sure that each aligned block of size "2^granularity" doesn't have
|
||||
// "true" value before "false" value.
|
||||
static void MakeShadowValid(bool *shadow, int length, int granularity) {
|
||||
bool can_be_poisoned = true;
|
||||
for (int i = length - 1; i >= 0; i--) {
|
||||
if (!shadow[i])
|
||||
can_be_poisoned = false;
|
||||
if (!can_be_poisoned)
|
||||
shadow[i] = false;
|
||||
if (i % (1 << granularity) == 0) {
|
||||
can_be_poisoned = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PoisoningStressTest) {
|
||||
const size_t kSize = 24;
|
||||
bool expected[kSize];
|
||||
char *arr = Ident((char*)malloc(kSize));
|
||||
for (size_t l1 = 0; l1 < kSize; l1++) {
|
||||
for (size_t s1 = 1; l1 + s1 <= kSize; s1++) {
|
||||
for (size_t l2 = 0; l2 < kSize; l2++) {
|
||||
for (size_t s2 = 1; l2 + s2 <= kSize; s2++) {
|
||||
// Poison [l1, l1+s1), [l2, l2+s2) and check result.
|
||||
__asan_unpoison_memory_region(arr, kSize);
|
||||
__asan_poison_memory_region(arr + l1, s1);
|
||||
__asan_poison_memory_region(arr + l2, s2);
|
||||
memset(expected, false, kSize);
|
||||
memset(expected + l1, true, s1);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
memset(expected + l2, true, s2);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
|
||||
}
|
||||
// Unpoison [l1, l1+s1) and [l2, l2+s2) and check result.
|
||||
__asan_poison_memory_region(arr, kSize);
|
||||
__asan_unpoison_memory_region(arr + l1, s1);
|
||||
__asan_unpoison_memory_region(arr + l2, s2);
|
||||
memset(expected, true, kSize);
|
||||
memset(expected + l1, false, s1);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
memset(expected + l2, false, s2);
|
||||
MakeShadowValid(expected, kSize, /*granularity*/ 3);
|
||||
for (size_t i = 0; i < kSize; i++) {
|
||||
ASSERT_EQ(expected[i], __asan_address_is_poisoned(arr + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(arr);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, GlobalRedzones) {
|
||||
GOOD_ACCESS(glob1, 1 - 1);
|
||||
GOOD_ACCESS(glob2, 2 - 1);
|
||||
GOOD_ACCESS(glob3, 3 - 1);
|
||||
GOOD_ACCESS(glob4, 4 - 1);
|
||||
GOOD_ACCESS(glob5, 5 - 1);
|
||||
GOOD_ACCESS(glob6, 6 - 1);
|
||||
GOOD_ACCESS(glob7, 7 - 1);
|
||||
GOOD_ACCESS(glob8, 8 - 1);
|
||||
GOOD_ACCESS(glob9, 9 - 1);
|
||||
GOOD_ACCESS(glob10, 10 - 1);
|
||||
GOOD_ACCESS(glob11, 11 - 1);
|
||||
GOOD_ACCESS(glob12, 12 - 1);
|
||||
GOOD_ACCESS(glob13, 13 - 1);
|
||||
GOOD_ACCESS(glob14, 14 - 1);
|
||||
GOOD_ACCESS(glob15, 15 - 1);
|
||||
GOOD_ACCESS(glob16, 16 - 1);
|
||||
GOOD_ACCESS(glob17, 17 - 1);
|
||||
GOOD_ACCESS(glob1000, 1000 - 1);
|
||||
GOOD_ACCESS(glob10000, 10000 - 1);
|
||||
GOOD_ACCESS(glob100000, 100000 - 1);
|
||||
|
||||
BAD_ACCESS(glob1, 1);
|
||||
BAD_ACCESS(glob2, 2);
|
||||
BAD_ACCESS(glob3, 3);
|
||||
BAD_ACCESS(glob4, 4);
|
||||
BAD_ACCESS(glob5, 5);
|
||||
BAD_ACCESS(glob6, 6);
|
||||
BAD_ACCESS(glob7, 7);
|
||||
BAD_ACCESS(glob8, 8);
|
||||
BAD_ACCESS(glob9, 9);
|
||||
BAD_ACCESS(glob10, 10);
|
||||
BAD_ACCESS(glob11, 11);
|
||||
BAD_ACCESS(glob12, 12);
|
||||
BAD_ACCESS(glob13, 13);
|
||||
BAD_ACCESS(glob14, 14);
|
||||
BAD_ACCESS(glob15, 15);
|
||||
BAD_ACCESS(glob16, 16);
|
||||
BAD_ACCESS(glob17, 17);
|
||||
BAD_ACCESS(glob1000, 1000);
|
||||
BAD_ACCESS(glob1000, 1100); // Redzone is at least 101 bytes.
|
||||
BAD_ACCESS(glob10000, 10000);
|
||||
BAD_ACCESS(glob10000, 11000); // Redzone is at least 1001 bytes.
|
||||
BAD_ACCESS(glob100000, 100000);
|
||||
BAD_ACCESS(glob100000, 110000); // Redzone is at least 10001 bytes.
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, PoisonedRegion) {
|
||||
size_t rz = 16;
|
||||
for (size_t size = 1; size <= 64; size++) {
|
||||
char *p = new char[size];
|
||||
for (size_t beg = 0; beg < size + rz; beg++) {
|
||||
for (size_t end = beg; end < size + rz; end++) {
|
||||
void *first_poisoned = __asan_region_is_poisoned(p + beg, end - beg);
|
||||
if (beg == end) {
|
||||
EXPECT_FALSE(first_poisoned);
|
||||
} else if (beg < size && end <= size) {
|
||||
EXPECT_FALSE(first_poisoned);
|
||||
} else if (beg >= size) {
|
||||
EXPECT_EQ(p + beg, first_poisoned);
|
||||
} else {
|
||||
EXPECT_GT(end, size);
|
||||
EXPECT_EQ(p + size, first_poisoned);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete [] p;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a performance benchmark for manual runs.
|
||||
// asan's memset interceptor calls mem_is_zero for the entire shadow region.
|
||||
// the profile should look like this:
|
||||
// 89.10% [.] __memset_sse2
|
||||
// 10.50% [.] __sanitizer::mem_is_zero
|
||||
// I.e. mem_is_zero should consume ~ SHADOW_GRANULARITY less CPU cycles
|
||||
// than memset itself.
|
||||
TEST(AddressSanitizerInterface, DISABLED_StressLargeMemset) {
|
||||
size_t size = 1 << 20;
|
||||
char *x = new char[size];
|
||||
for (int i = 0; i < 100000; i++)
|
||||
Ident(memset)(x, 0, size);
|
||||
delete [] x;
|
||||
}
|
||||
|
||||
// Same here, but we run memset with small sizes.
|
||||
TEST(AddressSanitizerInterface, DISABLED_StressSmallMemset) {
|
||||
size_t size = 32;
|
||||
char *x = new char[size];
|
||||
for (int i = 0; i < 100000000; i++)
|
||||
Ident(memset)(x, 0, size);
|
||||
delete [] x;
|
||||
}
|
||||
static const char *kInvalidPoisonMessage = "invalid-poison-memory-range";
|
||||
static const char *kInvalidUnpoisonMessage = "invalid-unpoison-memory-range";
|
||||
|
||||
TEST(AddressSanitizerInterface, DISABLED_InvalidPoisonAndUnpoisonCallsTest) {
|
||||
char *array = Ident((char*)malloc(120));
|
||||
__asan_unpoison_memory_region(array, 120);
|
||||
// Try to unpoison not owned memory
|
||||
EXPECT_DEATH(__asan_unpoison_memory_region(array, 121),
|
||||
kInvalidUnpoisonMessage);
|
||||
EXPECT_DEATH(__asan_unpoison_memory_region(array - 1, 120),
|
||||
kInvalidUnpoisonMessage);
|
||||
|
||||
__asan_poison_memory_region(array, 120);
|
||||
// Try to poison not owned memory.
|
||||
EXPECT_DEATH(__asan_poison_memory_region(array, 121), kInvalidPoisonMessage);
|
||||
EXPECT_DEATH(__asan_poison_memory_region(array - 1, 120),
|
||||
kInvalidPoisonMessage);
|
||||
free(array);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // FIXME: This should really be a lit test.
|
||||
static void ErrorReportCallbackOneToZ(const char *report) {
|
||||
int report_len = strlen(report);
|
||||
ASSERT_EQ(6, write(2, "ABCDEF", 6));
|
||||
ASSERT_EQ(report_len, write(2, report, report_len));
|
||||
ASSERT_EQ(6, write(2, "ABCDEF", 6));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerInterface, SetErrorReportCallbackTest) {
|
||||
__asan_set_error_report_callback(ErrorReportCallbackOneToZ);
|
||||
EXPECT_DEATH(__asan_report_error(0, 0, 0, 0, true, 1),
|
||||
ASAN_PCRE_DOTALL "ABCDEF.*AddressSanitizer.*WRITE.*ABCDEF");
|
||||
__asan_set_error_report_callback(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AddressSanitizerInterface, GetOwnershipStressTest) {
|
||||
std::vector<char *> pointers;
|
||||
std::vector<size_t> sizes;
|
||||
const size_t kNumMallocs = 1 << 9;
|
||||
for (size_t i = 0; i < kNumMallocs; i++) {
|
||||
size_t size = i * 100 + 1;
|
||||
pointers.push_back((char*)malloc(size));
|
||||
sizes.push_back(size);
|
||||
}
|
||||
for (size_t i = 0; i < 4000000; i++) {
|
||||
EXPECT_FALSE(__sanitizer_get_ownership(&pointers));
|
||||
EXPECT_FALSE(__sanitizer_get_ownership((void*)0x1234));
|
||||
size_t idx = i % kNumMallocs;
|
||||
EXPECT_TRUE(__sanitizer_get_ownership(pointers[idx]));
|
||||
EXPECT_EQ(sizes[idx], __sanitizer_get_allocated_size(pointers[idx]));
|
||||
}
|
||||
for (size_t i = 0, n = pointers.size(); i < n; i++)
|
||||
free(pointers[i]);
|
||||
}
|
||||
|
@ -1,236 +0,0 @@
|
||||
//===-- asan_test_mac.cc --------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
#include "asan_mac_test.h"
|
||||
|
||||
#include <malloc/malloc.h>
|
||||
#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
|
||||
#include <CoreFoundation/CFString.h>
|
||||
|
||||
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree) {
|
||||
EXPECT_DEATH(
|
||||
CFAllocatorDefaultDoubleFree(NULL),
|
||||
"attempting double-free");
|
||||
}
|
||||
|
||||
void CFAllocator_DoubleFreeOnPthread() {
|
||||
pthread_t child;
|
||||
PTHREAD_CREATE(&child, NULL, CFAllocatorDefaultDoubleFree, NULL);
|
||||
PTHREAD_JOIN(child, NULL); // Shouldn't be reached.
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, CFAllocatorDefaultDoubleFree_ChildPhread) {
|
||||
EXPECT_DEATH(CFAllocator_DoubleFreeOnPthread(), "attempting double-free");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
void *GLOB;
|
||||
|
||||
void *CFAllocatorAllocateToGlob(void *unused) {
|
||||
GLOB = CFAllocatorAllocate(NULL, 100, /*hint*/0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *CFAllocatorDeallocateFromGlob(void *unused) {
|
||||
char *p = (char*)GLOB;
|
||||
p[100] = 'A'; // ASan should report an error here.
|
||||
CFAllocatorDeallocate(NULL, GLOB);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void CFAllocator_PassMemoryToAnotherThread() {
|
||||
pthread_t th1, th2;
|
||||
PTHREAD_CREATE(&th1, NULL, CFAllocatorAllocateToGlob, NULL);
|
||||
PTHREAD_JOIN(th1, NULL);
|
||||
PTHREAD_CREATE(&th2, NULL, CFAllocatorDeallocateFromGlob, NULL);
|
||||
PTHREAD_JOIN(th2, NULL);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, CFAllocator_PassMemoryToAnotherThread) {
|
||||
EXPECT_DEATH(CFAllocator_PassMemoryToAnotherThread(),
|
||||
"heap-buffer-overflow");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// TODO(glider): figure out whether we still need these tests. Is it correct
|
||||
// to intercept the non-default CFAllocators?
|
||||
TEST(AddressSanitizerMac, DISABLED_CFAllocatorSystemDefaultDoubleFree) {
|
||||
EXPECT_DEATH(
|
||||
CFAllocatorSystemDefaultDoubleFree(),
|
||||
"attempting double-free");
|
||||
}
|
||||
|
||||
// We're intercepting malloc, so kCFAllocatorMalloc is routed to ASan.
|
||||
TEST(AddressSanitizerMac, CFAllocatorMallocDoubleFree) {
|
||||
EXPECT_DEATH(CFAllocatorMallocDoubleFree(), "attempting double-free");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, DISABLED_CFAllocatorMallocZoneDoubleFree) {
|
||||
EXPECT_DEATH(CFAllocatorMallocZoneDoubleFree(), "attempting double-free");
|
||||
}
|
||||
|
||||
// For libdispatch tests below we check that ASan got to the shadow byte
|
||||
// legend, i.e. managed to print the thread stacks (this almost certainly
|
||||
// means that the libdispatch task creation has been intercepted correctly).
|
||||
TEST(AddressSanitizerMac, GCDDispatchAsync) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDDispatchAsync(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, GCDDispatchSync) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDDispatchSync(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
|
||||
TEST(AddressSanitizerMac, GCDReuseWqthreadsAsync) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDReuseWqthreadsAsync(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, GCDReuseWqthreadsSync) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDReuseWqthreadsSync(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, GCDDispatchAfter) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDDispatchAfter(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, GCDSourceEvent) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDSourceEvent(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, GCDSourceCancel) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDSourceCancel(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, GCDGroupAsync) {
|
||||
// Make sure the whole ASan report is printed, i.e. that we don't die
|
||||
// on a CHECK.
|
||||
EXPECT_DEATH(TestGCDGroupAsync(), "Shadow byte legend");
|
||||
}
|
||||
|
||||
void *MallocIntrospectionLockWorker(void *_) {
|
||||
const int kNumPointers = 100;
|
||||
int i;
|
||||
void *pointers[kNumPointers];
|
||||
for (i = 0; i < kNumPointers; i++) {
|
||||
pointers[i] = malloc(i + 1);
|
||||
}
|
||||
for (i = 0; i < kNumPointers; i++) {
|
||||
free(pointers[i]);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *MallocIntrospectionLockForker(void *_) {
|
||||
pid_t result = fork();
|
||||
if (result == -1) {
|
||||
perror("fork");
|
||||
}
|
||||
assert(result != -1);
|
||||
if (result == 0) {
|
||||
// Call malloc in the child process to make sure we won't deadlock.
|
||||
void *ptr = malloc(42);
|
||||
free(ptr);
|
||||
exit(0);
|
||||
} else {
|
||||
// Return in the parent process.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, MallocIntrospectionLock) {
|
||||
// Incorrect implementation of force_lock and force_unlock in our malloc zone
|
||||
// will cause forked processes to deadlock.
|
||||
// TODO(glider): need to detect that none of the child processes deadlocked.
|
||||
const int kNumWorkers = 5, kNumIterations = 100;
|
||||
int i, iter;
|
||||
for (iter = 0; iter < kNumIterations; iter++) {
|
||||
pthread_t workers[kNumWorkers], forker;
|
||||
for (i = 0; i < kNumWorkers; i++) {
|
||||
PTHREAD_CREATE(&workers[i], 0, MallocIntrospectionLockWorker, 0);
|
||||
}
|
||||
PTHREAD_CREATE(&forker, 0, MallocIntrospectionLockForker, 0);
|
||||
for (i = 0; i < kNumWorkers; i++) {
|
||||
PTHREAD_JOIN(workers[i], 0);
|
||||
}
|
||||
PTHREAD_JOIN(forker, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void *TSDAllocWorker(void *test_key) {
|
||||
if (test_key) {
|
||||
void *mem = malloc(10);
|
||||
pthread_setspecific(*(pthread_key_t*)test_key, mem);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, DISABLED_TSDWorkqueueTest) {
|
||||
pthread_t th;
|
||||
pthread_key_t test_key;
|
||||
pthread_key_create(&test_key, CallFreeOnWorkqueue);
|
||||
PTHREAD_CREATE(&th, NULL, TSDAllocWorker, &test_key);
|
||||
PTHREAD_JOIN(th, NULL);
|
||||
pthread_key_delete(test_key);
|
||||
}
|
||||
|
||||
// Test that CFStringCreateCopy does not copy constant strings.
|
||||
TEST(AddressSanitizerMac, CFStringCreateCopy) {
|
||||
CFStringRef str = CFSTR("Hello world!\n");
|
||||
CFStringRef str2 = CFStringCreateCopy(0, str);
|
||||
EXPECT_EQ(str, str2);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizerMac, NSObjectOOB) {
|
||||
// Make sure that our allocators are used for NSObjects.
|
||||
EXPECT_DEATH(TestOOBNSObjects(), "heap-buffer-overflow");
|
||||
}
|
||||
|
||||
// Make sure that correct pointer is passed to free() when deallocating a
|
||||
// NSURL object.
|
||||
// See http://code.google.com/p/address-sanitizer/issues/detail?id=70.
|
||||
TEST(AddressSanitizerMac, NSURLDeallocation) {
|
||||
TestNSURLDeallocation();
|
||||
}
|
||||
|
||||
// See http://code.google.com/p/address-sanitizer/issues/detail?id=109.
|
||||
TEST(AddressSanitizerMac, Mstats) {
|
||||
malloc_statistics_t stats1, stats2;
|
||||
malloc_zone_statistics(/*all zones*/NULL, &stats1);
|
||||
const size_t kMallocSize = 100000;
|
||||
void *alloc = Ident(malloc(kMallocSize));
|
||||
malloc_zone_statistics(/*all zones*/NULL, &stats2);
|
||||
EXPECT_GT(stats2.blocks_in_use, stats1.blocks_in_use);
|
||||
EXPECT_GE(stats2.size_in_use - stats1.size_in_use, kMallocSize);
|
||||
free(alloc);
|
||||
// Even the default OSX allocator may not change the stats after free().
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
extern "C" {
|
||||
void *CFAllocatorDefaultDoubleFree(void *unused);
|
||||
void CFAllocatorSystemDefaultDoubleFree();
|
||||
void CFAllocatorMallocDoubleFree();
|
||||
void CFAllocatorMallocZoneDoubleFree();
|
||||
void CallFreeOnWorkqueue(void *mem);
|
||||
void TestGCDDispatchAsync();
|
||||
void TestGCDDispatchSync();
|
||||
void TestGCDReuseWqthreadsAsync();
|
||||
void TestGCDReuseWqthreadsSync();
|
||||
void TestGCDDispatchAfter();
|
||||
void TestGCDInTSDDestructor();
|
||||
void TestGCDSourceEvent();
|
||||
void TestGCDSourceCancel();
|
||||
void TestGCDGroupAsync();
|
||||
void TestOOBNSObjects();
|
||||
void TestNSURLDeallocation();
|
||||
void TestPassCFMemoryToAnotherThread();
|
||||
}
|
@ -1,240 +0,0 @@
|
||||
// Mac OS X 10.6 or higher only.
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <pthread.h> // for pthread_yield_np()
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#import <CoreFoundation/CFBase.h>
|
||||
#import <Foundation/NSObject.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
// This is a (void*)(void*) function so it can be passed to pthread_create.
|
||||
void *CFAllocatorDefaultDoubleFree(void *unused) {
|
||||
void *mem = CFAllocatorAllocate(kCFAllocatorDefault, 5, 0);
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
|
||||
CFAllocatorDeallocate(kCFAllocatorDefault, mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CFAllocatorSystemDefaultDoubleFree() {
|
||||
void *mem = CFAllocatorAllocate(kCFAllocatorSystemDefault, 5, 0);
|
||||
CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
|
||||
CFAllocatorDeallocate(kCFAllocatorSystemDefault, mem);
|
||||
}
|
||||
|
||||
void CFAllocatorMallocDoubleFree() {
|
||||
void *mem = CFAllocatorAllocate(kCFAllocatorMalloc, 5, 0);
|
||||
CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
|
||||
CFAllocatorDeallocate(kCFAllocatorMalloc, mem);
|
||||
}
|
||||
|
||||
void CFAllocatorMallocZoneDoubleFree() {
|
||||
void *mem = CFAllocatorAllocate(kCFAllocatorMallocZone, 5, 0);
|
||||
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
|
||||
CFAllocatorDeallocate(kCFAllocatorMallocZone, mem);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
void access_memory(char *a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
// Test the +load instrumentation.
|
||||
// Because the +load methods are invoked before anything else is initialized,
|
||||
// it makes little sense to wrap the code below into a gTest test case.
|
||||
// If AddressSanitizer doesn't instrument the +load method below correctly,
|
||||
// everything will just crash.
|
||||
|
||||
char kStartupStr[] =
|
||||
"If your test didn't crash, AddressSanitizer is instrumenting "
|
||||
"the +load methods correctly.";
|
||||
|
||||
@interface LoadSomething : NSObject {
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation LoadSomething
|
||||
|
||||
+(void) load {
|
||||
for (size_t i = 0; i < strlen(kStartupStr); i++) {
|
||||
access_memory(&kStartupStr[i]); // make sure no optimizations occur.
|
||||
}
|
||||
// Don't print anything here not to interfere with the death tests.
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void worker_do_alloc(int size) {
|
||||
char * volatile mem = (char * volatile)malloc(size);
|
||||
mem[0] = 0; // Ok
|
||||
free(mem);
|
||||
}
|
||||
|
||||
void worker_do_crash(int size) {
|
||||
char * volatile mem = (char * volatile)malloc(size);
|
||||
access_memory(&mem[size]); // BOOM
|
||||
free(mem);
|
||||
}
|
||||
|
||||
// Used by the GCD tests to avoid a race between the worker thread reporting a
|
||||
// memory error and the main thread which may exit with exit code 0 before
|
||||
// that.
|
||||
void wait_forever() {
|
||||
volatile bool infinite = true;
|
||||
while (infinite) pthread_yield_np();
|
||||
}
|
||||
|
||||
// Tests for the Grand Central Dispatch. See
|
||||
// http://developer.apple.com/library/mac/#documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html
|
||||
// for the reference.
|
||||
void TestGCDDispatchAsync() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_block_t block = ^{ worker_do_crash(1024); };
|
||||
// dispatch_async() runs the task on a worker thread that does not go through
|
||||
// pthread_create(). We need to verify that AddressSanitizer notices that the
|
||||
// thread has started.
|
||||
dispatch_async(queue, block);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
void TestGCDDispatchSync() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(2, 0);
|
||||
dispatch_block_t block = ^{ worker_do_crash(1024); };
|
||||
// dispatch_sync() runs the task on a worker thread that does not go through
|
||||
// pthread_create(). We need to verify that AddressSanitizer notices that the
|
||||
// thread has started.
|
||||
dispatch_sync(queue, block);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
// libdispatch spawns a rather small number of threads and reuses them. We need
|
||||
// to make sure AddressSanitizer handles the reusing correctly.
|
||||
void TestGCDReuseWqthreadsAsync() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
|
||||
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
|
||||
for (int i = 0; i < 100; i++) {
|
||||
dispatch_async(queue, block_alloc);
|
||||
}
|
||||
dispatch_async(queue, block_crash);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
// Try to trigger abnormal behaviour of dispatch_sync() being unhandled by us.
|
||||
void TestGCDReuseWqthreadsSync() {
|
||||
dispatch_queue_t queue[4];
|
||||
queue[0] = dispatch_get_global_queue(2, 0);
|
||||
queue[1] = dispatch_get_global_queue(0, 0);
|
||||
queue[2] = dispatch_get_global_queue(-2, 0);
|
||||
queue[3] = dispatch_queue_create("my_queue", NULL);
|
||||
dispatch_block_t block_alloc = ^{ worker_do_alloc(1024); };
|
||||
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
dispatch_sync(queue[i % 4], block_alloc);
|
||||
}
|
||||
dispatch_sync(queue[3], block_crash);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
void TestGCDDispatchAfter() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_block_t block_crash = ^{ worker_do_crash(1024); };
|
||||
// Schedule the event one second from the current time.
|
||||
dispatch_time_t milestone =
|
||||
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
|
||||
dispatch_after(milestone, queue, block_crash);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
void worker_do_deallocate(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void CallFreeOnWorkqueue(void *tsd) {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_block_t block_dealloc = ^{ worker_do_deallocate(tsd); };
|
||||
dispatch_async(queue, block_dealloc);
|
||||
// Do not wait for the worker to free the memory -- nobody is going to touch
|
||||
// it.
|
||||
}
|
||||
|
||||
void TestGCDSourceEvent() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_source_t timer =
|
||||
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
||||
// Schedule the timer one second from the current time.
|
||||
dispatch_time_t milestone =
|
||||
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
|
||||
|
||||
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
|
||||
char * volatile mem = (char * volatile)malloc(10);
|
||||
dispatch_source_set_event_handler(timer, ^{
|
||||
access_memory(&mem[10]);
|
||||
});
|
||||
dispatch_resume(timer);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
void TestGCDSourceCancel() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_source_t timer =
|
||||
dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
|
||||
// Schedule the timer one second from the current time.
|
||||
dispatch_time_t milestone =
|
||||
dispatch_time(DISPATCH_TIME_NOW, 1LL * NSEC_PER_SEC);
|
||||
|
||||
dispatch_source_set_timer(timer, milestone, DISPATCH_TIME_FOREVER, 0);
|
||||
char * volatile mem = (char * volatile)malloc(10);
|
||||
// Both dispatch_source_set_cancel_handler() and
|
||||
// dispatch_source_set_event_handler() use dispatch_barrier_async_f().
|
||||
// It's tricky to test dispatch_source_set_cancel_handler() separately,
|
||||
// so we test both here.
|
||||
dispatch_source_set_event_handler(timer, ^{
|
||||
dispatch_source_cancel(timer);
|
||||
});
|
||||
dispatch_source_set_cancel_handler(timer, ^{
|
||||
access_memory(&mem[10]);
|
||||
});
|
||||
dispatch_resume(timer);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
void TestGCDGroupAsync() {
|
||||
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
char * volatile mem = (char * volatile)malloc(10);
|
||||
dispatch_group_async(group, queue, ^{
|
||||
access_memory(&mem[10]);
|
||||
});
|
||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||
wait_forever();
|
||||
}
|
||||
|
||||
@interface FixedArray : NSObject {
|
||||
int items[10];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FixedArray
|
||||
-(int) access: (int)index {
|
||||
return items[index];
|
||||
}
|
||||
@end
|
||||
|
||||
void TestOOBNSObjects() {
|
||||
id anObject = [FixedArray new];
|
||||
[anObject access:1];
|
||||
[anObject access:11];
|
||||
[anObject release];
|
||||
}
|
||||
|
||||
void TestNSURLDeallocation() {
|
||||
NSURL *base =
|
||||
[[NSURL alloc] initWithString:@"file://localhost/Users/glider/Library/"];
|
||||
volatile NSURL *u =
|
||||
[[NSURL alloc] initWithString:@"Saved Application State"
|
||||
relativeToURL:base];
|
||||
[u release];
|
||||
}
|
@ -1,241 +0,0 @@
|
||||
//===-- asan_mem_test.cc --------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
template<typename T>
|
||||
void MemSetOOBTestTemplate(size_t length) {
|
||||
if (length == 0) return;
|
||||
size_t size = Ident(sizeof(T) * length);
|
||||
T *array = Ident((T*)malloc(size));
|
||||
int element = Ident(42);
|
||||
int zero = Ident(0);
|
||||
void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset);
|
||||
// memset interval inside array
|
||||
MEMSET(array, element, size);
|
||||
MEMSET(array, element, size - 1);
|
||||
MEMSET(array + length - 1, element, sizeof(T));
|
||||
MEMSET(array, element, 1);
|
||||
|
||||
// memset 0 bytes
|
||||
MEMSET(array - 10, element, zero);
|
||||
MEMSET(array - 1, element, zero);
|
||||
MEMSET(array, element, zero);
|
||||
MEMSET(array + length, 0, zero);
|
||||
MEMSET(array + length + 1, 0, zero);
|
||||
|
||||
// try to memset bytes to the right of array
|
||||
EXPECT_DEATH(MEMSET(array, 0, size + 1),
|
||||
RightOOBWriteMessage(0));
|
||||
EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6),
|
||||
RightOOBWriteMessage(0));
|
||||
EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)),
|
||||
RightOOBWriteMessage(0));
|
||||
// whole interval is to the right
|
||||
EXPECT_DEATH(MEMSET(array + length + 1, 0, 10),
|
||||
RightOOBWriteMessage(sizeof(T)));
|
||||
|
||||
// try to memset bytes to the left of array
|
||||
EXPECT_DEATH(MEMSET((char*)array - 1, element, size),
|
||||
LeftOOBWriteMessage(1));
|
||||
EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6),
|
||||
LeftOOBWriteMessage(5));
|
||||
if (length >= 100) {
|
||||
// Large OOB, we find it only if the redzone is large enough.
|
||||
EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)),
|
||||
LeftOOBWriteMessage(5 * sizeof(T)));
|
||||
}
|
||||
// whole interval is to the left
|
||||
EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)),
|
||||
LeftOOBWriteMessage(2 * sizeof(T)));
|
||||
|
||||
// try to memset bytes both to the left & to the right
|
||||
EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4),
|
||||
LeftOOBWriteMessage(2));
|
||||
|
||||
free(array);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, MemSetOOBTest) {
|
||||
MemSetOOBTestTemplate<char>(100);
|
||||
MemSetOOBTestTemplate<int>(5);
|
||||
MemSetOOBTestTemplate<double>(256);
|
||||
// We can test arrays of structres/classes here, but what for?
|
||||
}
|
||||
|
||||
// Try to allocate two arrays of 'size' bytes that are near each other.
|
||||
// Strictly speaking we are not guaranteed to find such two pointers,
|
||||
// but given the structure of asan's allocator we will.
|
||||
static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) {
|
||||
vector<uintptr_t> v;
|
||||
bool res = false;
|
||||
for (size_t i = 0; i < 1000U && !res; i++) {
|
||||
v.push_back(reinterpret_cast<uintptr_t>(new char[size]));
|
||||
if (i == 0) continue;
|
||||
sort(v.begin(), v.end());
|
||||
for (size_t j = 1; j < v.size(); j++) {
|
||||
assert(v[j] > v[j-1]);
|
||||
if ((size_t)(v[j] - v[j-1]) < size * 2) {
|
||||
*x2 = reinterpret_cast<char*>(v[j]);
|
||||
*x1 = reinterpret_cast<char*>(v[j-1]);
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
char *p = reinterpret_cast<char *>(v[i]);
|
||||
if (res && p == *x1) continue;
|
||||
if (res && p == *x2) continue;
|
||||
delete [] p;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, LargeOOBInMemset) {
|
||||
for (size_t size = 200; size < 100000; size += size / 2) {
|
||||
char *x1, *x2;
|
||||
if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size))
|
||||
continue;
|
||||
// fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size);
|
||||
// Do a memset on x1 with huge out-of-bound access that will end up in x2.
|
||||
EXPECT_DEATH(Ident(memset)(x1, 0, size * 2),
|
||||
"is located 0 bytes to the right");
|
||||
delete [] x1;
|
||||
delete [] x2;
|
||||
return;
|
||||
}
|
||||
assert(0 && "Did not find two adjacent malloc-ed pointers");
|
||||
}
|
||||
|
||||
// Same test for memcpy and memmove functions
|
||||
template <typename T, class M>
|
||||
void MemTransferOOBTestTemplate(size_t length) {
|
||||
if (length == 0) return;
|
||||
size_t size = Ident(sizeof(T) * length);
|
||||
T *src = Ident((T*)malloc(size));
|
||||
T *dest = Ident((T*)malloc(size));
|
||||
int zero = Ident(0);
|
||||
|
||||
// valid transfer of bytes between arrays
|
||||
M::transfer(dest, src, size);
|
||||
M::transfer(dest + 1, src, size - sizeof(T));
|
||||
M::transfer(dest, src + length - 1, sizeof(T));
|
||||
M::transfer(dest, src, 1);
|
||||
|
||||
// transfer zero bytes
|
||||
M::transfer(dest - 1, src, 0);
|
||||
M::transfer(dest + length, src, zero);
|
||||
M::transfer(dest, src - 1, zero);
|
||||
M::transfer(dest, src, zero);
|
||||
|
||||
// try to change mem to the right of dest
|
||||
EXPECT_DEATH(M::transfer(dest + 1, src, size),
|
||||
RightOOBWriteMessage(0));
|
||||
EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5),
|
||||
RightOOBWriteMessage(0));
|
||||
|
||||
// try to change mem to the left of dest
|
||||
EXPECT_DEATH(M::transfer(dest - 2, src, size),
|
||||
LeftOOBWriteMessage(2 * sizeof(T)));
|
||||
EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4),
|
||||
LeftOOBWriteMessage(3));
|
||||
|
||||
// try to access mem to the right of src
|
||||
EXPECT_DEATH(M::transfer(dest, src + 2, size),
|
||||
RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6),
|
||||
RightOOBReadMessage(0));
|
||||
|
||||
// try to access mem to the left of src
|
||||
EXPECT_DEATH(M::transfer(dest, src - 1, size),
|
||||
LeftOOBReadMessage(sizeof(T)));
|
||||
EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7),
|
||||
LeftOOBReadMessage(6));
|
||||
|
||||
// Generally we don't need to test cases where both accessing src and writing
|
||||
// to dest address to poisoned memory.
|
||||
|
||||
T *big_src = Ident((T*)malloc(size * 2));
|
||||
T *big_dest = Ident((T*)malloc(size * 2));
|
||||
// try to change mem to both sides of dest
|
||||
EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2),
|
||||
LeftOOBWriteMessage(sizeof(T)));
|
||||
// try to access mem to both sides of src
|
||||
EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2),
|
||||
LeftOOBReadMessage(2 * sizeof(T)));
|
||||
|
||||
free(src);
|
||||
free(dest);
|
||||
free(big_src);
|
||||
free(big_dest);
|
||||
}
|
||||
|
||||
class MemCpyWrapper {
|
||||
public:
|
||||
static void* transfer(void *to, const void *from, size_t size) {
|
||||
return Ident(memcpy)(to, from, size);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(AddressSanitizer, MemCpyOOBTest) {
|
||||
MemTransferOOBTestTemplate<char, MemCpyWrapper>(100);
|
||||
MemTransferOOBTestTemplate<int, MemCpyWrapper>(1024);
|
||||
}
|
||||
|
||||
class MemMoveWrapper {
|
||||
public:
|
||||
static void* transfer(void *to, const void *from, size_t size) {
|
||||
return Ident(memmove)(to, from, size);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(AddressSanitizer, MemMoveOOBTest) {
|
||||
MemTransferOOBTestTemplate<char, MemMoveWrapper>(100);
|
||||
MemTransferOOBTestTemplate<int, MemMoveWrapper>(1024);
|
||||
}
|
||||
|
||||
|
||||
TEST(AddressSanitizer, MemCmpOOBTest) {
|
||||
size_t size = Ident(100);
|
||||
char *s1 = MallocAndMemsetString(size);
|
||||
char *s2 = MallocAndMemsetString(size);
|
||||
// Normal memcmp calls.
|
||||
Ident(memcmp(s1, s2, size));
|
||||
Ident(memcmp(s1 + size - 1, s2 + size - 1, 1));
|
||||
Ident(memcmp(s1 - 1, s2 - 1, 0));
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
|
||||
// Hit unallocated memory and die.
|
||||
EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
|
||||
// Zero bytes are not terminators and don't prevent from OOB.
|
||||
s1[size - 1] = '\0';
|
||||
s2[size - 1] = '\0';
|
||||
EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
|
||||
|
||||
// Even if the buffers differ in the first byte, we still assume that
|
||||
// memcmp may access the whole buffer and thus reporting the overflow here:
|
||||
s1[0] = 1;
|
||||
s2[0] = 123;
|
||||
EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0));
|
||||
|
||||
free(s1);
|
||||
free(s2);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,263 +0,0 @@
|
||||
//===-- asan_noinst_test.cc -----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
// This test file should be compiled w/o asan instrumentation.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "asan_allocator.h"
|
||||
#include "asan_internal.h"
|
||||
#include "asan_mapping.h"
|
||||
#include "asan_test_utils.h"
|
||||
#include <sanitizer/allocator_interface.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // for memset()
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
// ATTENTION!
|
||||
// Please don't call intercepted functions (including malloc() and friends)
|
||||
// in this test. The static runtime library is linked explicitly (without
|
||||
// -fsanitize=address), thus the interceptors do not work correctly on OS X.
|
||||
|
||||
// Make sure __asan_init is called before any test case is run.
|
||||
struct AsanInitCaller {
|
||||
AsanInitCaller() {
|
||||
__asan::DisableReexec();
|
||||
__asan_init();
|
||||
}
|
||||
};
|
||||
static AsanInitCaller asan_init_caller;
|
||||
|
||||
TEST(AddressSanitizer, InternalSimpleDeathTest) {
|
||||
EXPECT_DEATH(exit(1), "");
|
||||
}
|
||||
|
||||
static void MallocStress(size_t n) {
|
||||
u32 seed = my_rand();
|
||||
BufferedStackTrace stack1;
|
||||
stack1.trace_buffer[0] = 0xa123;
|
||||
stack1.trace_buffer[1] = 0xa456;
|
||||
stack1.size = 2;
|
||||
|
||||
BufferedStackTrace stack2;
|
||||
stack2.trace_buffer[0] = 0xb123;
|
||||
stack2.trace_buffer[1] = 0xb456;
|
||||
stack2.size = 2;
|
||||
|
||||
BufferedStackTrace stack3;
|
||||
stack3.trace_buffer[0] = 0xc123;
|
||||
stack3.trace_buffer[1] = 0xc456;
|
||||
stack3.size = 2;
|
||||
|
||||
std::vector<void *> vec;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if ((i % 3) == 0) {
|
||||
if (vec.empty()) continue;
|
||||
size_t idx = my_rand_r(&seed) % vec.size();
|
||||
void *ptr = vec[idx];
|
||||
vec[idx] = vec.back();
|
||||
vec.pop_back();
|
||||
__asan::asan_free(ptr, &stack1, __asan::FROM_MALLOC);
|
||||
} else {
|
||||
size_t size = my_rand_r(&seed) % 1000 + 1;
|
||||
switch ((my_rand_r(&seed) % 128)) {
|
||||
case 0: size += 1024; break;
|
||||
case 1: size += 2048; break;
|
||||
case 2: size += 4096; break;
|
||||
}
|
||||
size_t alignment = 1 << (my_rand_r(&seed) % 10 + 1);
|
||||
char *ptr = (char*)__asan::asan_memalign(alignment, size,
|
||||
&stack2, __asan::FROM_MALLOC);
|
||||
EXPECT_EQ(size, __asan::asan_malloc_usable_size(ptr, 0, 0));
|
||||
vec.push_back(ptr);
|
||||
ptr[0] = 0;
|
||||
ptr[size-1] = 0;
|
||||
ptr[size/2] = 0;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < vec.size(); i++)
|
||||
__asan::asan_free(vec[i], &stack3, __asan::FROM_MALLOC);
|
||||
}
|
||||
|
||||
|
||||
TEST(AddressSanitizer, NoInstMallocTest) {
|
||||
MallocStress(ASAN_LOW_MEMORY ? 300000 : 1000000);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, ThreadedMallocStressTest) {
|
||||
const int kNumThreads = 4;
|
||||
const int kNumIterations = (ASAN_LOW_MEMORY) ? 10000 : 100000;
|
||||
pthread_t t[kNumThreads];
|
||||
for (int i = 0; i < kNumThreads; i++) {
|
||||
PTHREAD_CREATE(&t[i], 0, (void* (*)(void *x))MallocStress,
|
||||
(void*)kNumIterations);
|
||||
}
|
||||
for (int i = 0; i < kNumThreads; i++) {
|
||||
PTHREAD_JOIN(t[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintShadow(const char *tag, uptr ptr, size_t size) {
|
||||
fprintf(stderr, "%s shadow: %lx size % 3ld: ", tag, (long)ptr, (long)size);
|
||||
uptr prev_shadow = 0;
|
||||
for (sptr i = -32; i < (sptr)size + 32; i++) {
|
||||
uptr shadow = __asan::MemToShadow(ptr + i);
|
||||
if (i == 0 || i == (sptr)size)
|
||||
fprintf(stderr, ".");
|
||||
if (shadow != prev_shadow) {
|
||||
prev_shadow = shadow;
|
||||
fprintf(stderr, "%02x", (int)*(u8*)shadow);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, DISABLED_InternalPrintShadow) {
|
||||
for (size_t size = 1; size <= 513; size++) {
|
||||
char *ptr = new char[size];
|
||||
PrintShadow("m", (uptr)ptr, size);
|
||||
delete [] ptr;
|
||||
PrintShadow("f", (uptr)ptr, size);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, QuarantineTest) {
|
||||
BufferedStackTrace stack;
|
||||
stack.trace_buffer[0] = 0x890;
|
||||
stack.size = 1;
|
||||
|
||||
const int size = 1024;
|
||||
void *p = __asan::asan_malloc(size, &stack);
|
||||
__asan::asan_free(p, &stack, __asan::FROM_MALLOC);
|
||||
size_t i;
|
||||
size_t max_i = 1 << 30;
|
||||
for (i = 0; i < max_i; i++) {
|
||||
void *p1 = __asan::asan_malloc(size, &stack);
|
||||
__asan::asan_free(p1, &stack, __asan::FROM_MALLOC);
|
||||
if (p1 == p) break;
|
||||
}
|
||||
EXPECT_GE(i, 10000U);
|
||||
EXPECT_LT(i, max_i);
|
||||
}
|
||||
|
||||
void *ThreadedQuarantineTestWorker(void *unused) {
|
||||
(void)unused;
|
||||
u32 seed = my_rand();
|
||||
BufferedStackTrace stack;
|
||||
stack.trace_buffer[0] = 0x890;
|
||||
stack.size = 1;
|
||||
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
void *p = __asan::asan_malloc(1 + (my_rand_r(&seed) % 4000), &stack);
|
||||
__asan::asan_free(p, &stack, __asan::FROM_MALLOC);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check that the thread local allocators are flushed when threads are
|
||||
// destroyed.
|
||||
TEST(AddressSanitizer, ThreadedQuarantineTest) {
|
||||
const int n_threads = 3000;
|
||||
size_t mmaped1 = __sanitizer_get_heap_size();
|
||||
for (int i = 0; i < n_threads; i++) {
|
||||
pthread_t t;
|
||||
PTHREAD_CREATE(&t, NULL, ThreadedQuarantineTestWorker, 0);
|
||||
PTHREAD_JOIN(t, 0);
|
||||
size_t mmaped2 = __sanitizer_get_heap_size();
|
||||
EXPECT_LT(mmaped2 - mmaped1, 320U * (1 << 20));
|
||||
}
|
||||
}
|
||||
|
||||
void *ThreadedOneSizeMallocStress(void *unused) {
|
||||
(void)unused;
|
||||
BufferedStackTrace stack;
|
||||
stack.trace_buffer[0] = 0x890;
|
||||
stack.size = 1;
|
||||
const size_t kNumMallocs = 1000;
|
||||
for (int iter = 0; iter < 1000; iter++) {
|
||||
void *p[kNumMallocs];
|
||||
for (size_t i = 0; i < kNumMallocs; i++) {
|
||||
p[i] = __asan::asan_malloc(32, &stack);
|
||||
}
|
||||
for (size_t i = 0; i < kNumMallocs; i++) {
|
||||
__asan::asan_free(p[i], &stack, __asan::FROM_MALLOC);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, ThreadedOneSizeMallocStressTest) {
|
||||
const int kNumThreads = 4;
|
||||
pthread_t t[kNumThreads];
|
||||
for (int i = 0; i < kNumThreads; i++) {
|
||||
PTHREAD_CREATE(&t[i], 0, ThreadedOneSizeMallocStress, 0);
|
||||
}
|
||||
for (int i = 0; i < kNumThreads; i++) {
|
||||
PTHREAD_JOIN(t[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, ShadowRegionIsPoisonedTest) {
|
||||
using __asan::kHighMemEnd;
|
||||
// Check that __asan_region_is_poisoned works for shadow regions.
|
||||
uptr ptr = kLowShadowBeg + 200;
|
||||
EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100));
|
||||
ptr = kShadowGapBeg + 200;
|
||||
EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100));
|
||||
ptr = kHighShadowBeg + 200;
|
||||
EXPECT_EQ(ptr, __asan_region_is_poisoned(ptr, 100));
|
||||
}
|
||||
|
||||
// Test __asan_load1 & friends.
|
||||
TEST(AddressSanitizer, LoadStoreCallbacks) {
|
||||
typedef void (*CB)(uptr p);
|
||||
CB cb[2][5] = {
|
||||
{
|
||||
__asan_load1, __asan_load2, __asan_load4, __asan_load8, __asan_load16,
|
||||
}, {
|
||||
__asan_store1, __asan_store2, __asan_store4, __asan_store8,
|
||||
__asan_store16,
|
||||
}
|
||||
};
|
||||
|
||||
uptr buggy_ptr;
|
||||
|
||||
__asan_test_only_reported_buggy_pointer = &buggy_ptr;
|
||||
BufferedStackTrace stack;
|
||||
stack.trace_buffer[0] = 0x890;
|
||||
stack.size = 1;
|
||||
|
||||
for (uptr len = 16; len <= 32; len++) {
|
||||
char *ptr = (char*) __asan::asan_malloc(len, &stack);
|
||||
uptr p = reinterpret_cast<uptr>(ptr);
|
||||
for (uptr is_write = 0; is_write <= 1; is_write++) {
|
||||
for (uptr size_log = 0; size_log <= 4; size_log++) {
|
||||
uptr size = 1 << size_log;
|
||||
CB call = cb[is_write][size_log];
|
||||
// Iterate only size-aligned offsets.
|
||||
for (uptr offset = 0; offset <= len; offset += size) {
|
||||
buggy_ptr = 0;
|
||||
call(p + offset);
|
||||
if (offset + size <= len)
|
||||
EXPECT_EQ(buggy_ptr, 0U);
|
||||
else
|
||||
EXPECT_EQ(buggy_ptr, p + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
__asan::asan_free(ptr, &stack, __asan::FROM_MALLOC);
|
||||
}
|
||||
__asan_test_only_reported_buggy_pointer = 0;
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
//===-- asan_oob_test.cc --------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) {
|
||||
EXPECT_EQ(0U, ((uintptr_t)p % size));
|
||||
if (size == 1) asan_write((uint8_t*)p);
|
||||
else if (size == 2) asan_write((uint16_t*)p);
|
||||
else if (size == 4) asan_write((uint32_t*)p);
|
||||
else if (size == 8) asan_write((uint64_t*)p);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
NOINLINE void oob_test(int size, int off) {
|
||||
char *p = (char*)malloc_aaa(size);
|
||||
// fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n",
|
||||
// sizeof(T), p, p + size, off);
|
||||
asan_write((T*)(p + off));
|
||||
free_aaa(p);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void OOBTest() {
|
||||
char expected_str[100];
|
||||
for (int size = sizeof(T); size < 20; size += 5) {
|
||||
for (int i = -5; i < 0; i++) {
|
||||
const char *str =
|
||||
"is located.*%d byte.*to the left";
|
||||
sprintf(expected_str, str, abs(i));
|
||||
EXPECT_DEATH(oob_test<T>(size, i), expected_str);
|
||||
}
|
||||
|
||||
for (int i = 0; i < (int)(size - sizeof(T) + 1); i++)
|
||||
oob_test<T>(size, i);
|
||||
|
||||
for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) {
|
||||
const char *str =
|
||||
"is located.*%d byte.*to the right";
|
||||
int off = i >= size ? (i - size) : 0;
|
||||
// we don't catch unaligned partially OOB accesses.
|
||||
if (i % sizeof(T)) continue;
|
||||
sprintf(expected_str, str, off);
|
||||
EXPECT_DEATH(oob_test<T>(size, i), expected_str);
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1),
|
||||
"is located.*1 byte.*to the left");
|
||||
EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc),
|
||||
"is located.*0 byte.*to the right");
|
||||
}
|
||||
|
||||
// TODO(glider): the following tests are EXTREMELY slow on Darwin:
|
||||
// AddressSanitizer.OOB_char (125503 ms)
|
||||
// AddressSanitizer.OOB_int (126890 ms)
|
||||
// AddressSanitizer.OOBRightTest (315605 ms)
|
||||
// AddressSanitizer.SimpleStackTest (366559 ms)
|
||||
|
||||
TEST(AddressSanitizer, OOB_char) {
|
||||
OOBTest<U1>();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, OOB_int) {
|
||||
OOBTest<U4>();
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, OOBRightTest) {
|
||||
size_t max_access_size = SANITIZER_WORDSIZE == 64 ? 8 : 4;
|
||||
for (size_t access_size = 1; access_size <= max_access_size;
|
||||
access_size *= 2) {
|
||||
for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) {
|
||||
for (size_t offset = 0; offset <= 8; offset += access_size) {
|
||||
void *p = malloc(alloc_size);
|
||||
// allocated: [p, p + alloc_size)
|
||||
// accessed: [p + offset, p + offset + access_size)
|
||||
uint8_t *addr = (uint8_t*)p + offset;
|
||||
if (offset + access_size <= alloc_size) {
|
||||
asan_write_sized_aligned(addr, access_size);
|
||||
} else {
|
||||
int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0;
|
||||
const char *str =
|
||||
"is located.%d *byte.*to the right";
|
||||
char expected_str[100];
|
||||
sprintf(expected_str, str, outside_bytes);
|
||||
EXPECT_DEATH(asan_write_sized_aligned(addr, access_size),
|
||||
expected_str);
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, LargeOOBRightTest) {
|
||||
size_t large_power_of_two = 1 << 19;
|
||||
for (size_t i = 16; i <= 256; i *= 2) {
|
||||
size_t size = large_power_of_two - i;
|
||||
char *p = Ident(new char[size]);
|
||||
EXPECT_DEATH(p[size] = 0, "is located 0 bytes to the right");
|
||||
delete [] p;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) {
|
||||
oob_test<U1>(10, -1);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) {
|
||||
oob_test<U1>(kLargeMalloc, -1);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) {
|
||||
oob_test<U1>(10, 10);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) {
|
||||
oob_test<U1>(kLargeMalloc, kLargeMalloc);
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
const int N = 1000;
|
||||
void *x[N];
|
||||
|
||||
void *Thread1(void *unused) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
fprintf(stderr, "%s %d\n", __func__, i);
|
||||
free(x[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *Thread2(void *unused) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
fprintf(stderr, "%s %d\n", __func__, i);
|
||||
free(x[i]);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main() {
|
||||
for (int i = 0; i < N; i++)
|
||||
x[i] = malloc(128);
|
||||
pthread_t t[2];
|
||||
pthread_create(&t[0], 0, Thread1, 0);
|
||||
pthread_create(&t[1], 0, Thread2, 0);
|
||||
pthread_join(t[0], 0);
|
||||
pthread_join(t[1], 0);
|
||||
}
|
@ -1,573 +0,0 @@
|
||||
//=-- asan_str_test.cc ----------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <AvailabilityMacros.h> // For MAC_OS_X_VERSION_*
|
||||
#endif
|
||||
|
||||
// Used for string functions tests
|
||||
static char global_string[] = "global";
|
||||
static size_t global_string_length = 6;
|
||||
|
||||
// Input to a test is a zero-terminated string str with given length
|
||||
// Accesses to the bytes to the left and to the right of str
|
||||
// are presumed to produce OOB errors
|
||||
void StrLenOOBTestTemplate(char *str, size_t length, bool is_global) {
|
||||
// Normal strlen calls
|
||||
EXPECT_EQ(strlen(str), length);
|
||||
if (length > 0) {
|
||||
EXPECT_EQ(length - 1, strlen(str + 1));
|
||||
EXPECT_EQ(0U, strlen(str + length));
|
||||
}
|
||||
// Arg of strlen is not malloced, OOB access
|
||||
if (!is_global) {
|
||||
// We don't insert RedZones to the left of global variables
|
||||
EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(5));
|
||||
}
|
||||
EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(0));
|
||||
// Overwrite terminator
|
||||
str[length] = 'a';
|
||||
// String is not zero-terminated, strlen will lead to OOB access
|
||||
EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(0));
|
||||
// Restore terminator
|
||||
str[length] = 0;
|
||||
}
|
||||
TEST(AddressSanitizer, StrLenOOBTest) {
|
||||
// Check heap-allocated string
|
||||
size_t length = Ident(10);
|
||||
char *heap_string = Ident((char*)malloc(length + 1));
|
||||
char stack_string[10 + 1];
|
||||
break_optimization(&stack_string);
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
heap_string[i] = 'a';
|
||||
stack_string[i] = 'b';
|
||||
}
|
||||
heap_string[length] = 0;
|
||||
stack_string[length] = 0;
|
||||
StrLenOOBTestTemplate(heap_string, length, false);
|
||||
// TODO(samsonov): Fix expected messages in StrLenOOBTestTemplate to
|
||||
// make test for stack_string work. Or move it to output tests.
|
||||
// StrLenOOBTestTemplate(stack_string, length, false);
|
||||
StrLenOOBTestTemplate(global_string, global_string_length, true);
|
||||
free(heap_string);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, WcsLenTest) {
|
||||
EXPECT_EQ(0U, wcslen(Ident(L"")));
|
||||
size_t hello_len = 13;
|
||||
size_t hello_size = (hello_len + 1) * sizeof(wchar_t);
|
||||
EXPECT_EQ(hello_len, wcslen(Ident(L"Hello, World!")));
|
||||
wchar_t *heap_string = Ident((wchar_t*)malloc(hello_size));
|
||||
memcpy(heap_string, L"Hello, World!", hello_size);
|
||||
EXPECT_EQ(hello_len, Ident(wcslen(heap_string)));
|
||||
EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0));
|
||||
free(heap_string);
|
||||
}
|
||||
|
||||
#if SANITIZER_TEST_HAS_STRNLEN
|
||||
TEST(AddressSanitizer, StrNLenOOBTest) {
|
||||
size_t size = Ident(123);
|
||||
char *str = MallocAndMemsetString(size);
|
||||
// Normal strnlen calls.
|
||||
Ident(strnlen(str - 1, 0));
|
||||
Ident(strnlen(str, size));
|
||||
Ident(strnlen(str + size - 1, 1));
|
||||
str[size - 1] = '\0';
|
||||
Ident(strnlen(str, 2 * size));
|
||||
// Argument points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0));
|
||||
// Overwrite the terminating '\0' and hit unallocated memory.
|
||||
str[size - 1] = 'z';
|
||||
EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0));
|
||||
free(str);
|
||||
}
|
||||
#endif // SANITIZER_TEST_HAS_STRNLEN
|
||||
|
||||
TEST(AddressSanitizer, StrDupOOBTest) {
|
||||
size_t size = Ident(42);
|
||||
char *str = MallocAndMemsetString(size);
|
||||
char *new_str;
|
||||
// Normal strdup calls.
|
||||
str[size - 1] = '\0';
|
||||
new_str = strdup(str);
|
||||
free(new_str);
|
||||
new_str = strdup(str + size - 1);
|
||||
free(new_str);
|
||||
// Argument points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0));
|
||||
// Overwrite the terminating '\0' and hit unallocated memory.
|
||||
str[size - 1] = 'z';
|
||||
EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0));
|
||||
free(str);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrCpyOOBTest) {
|
||||
size_t to_size = Ident(30);
|
||||
size_t from_size = Ident(6); // less than to_size
|
||||
char *to = Ident((char*)malloc(to_size));
|
||||
char *from = Ident((char*)malloc(from_size));
|
||||
// Normal strcpy calls.
|
||||
strcpy(from, "hello");
|
||||
strcpy(to, from);
|
||||
strcpy(to + to_size - from_size, from);
|
||||
// Length of "from" is too small.
|
||||
EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0));
|
||||
// "to" or "from" points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1));
|
||||
EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0));
|
||||
// Overwrite the terminating '\0' character and hit unallocated memory.
|
||||
from[from_size - 1] = '!';
|
||||
EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0));
|
||||
free(to);
|
||||
free(from);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrNCpyOOBTest) {
|
||||
size_t to_size = Ident(20);
|
||||
size_t from_size = Ident(6); // less than to_size
|
||||
char *to = Ident((char*)malloc(to_size));
|
||||
// From is a zero-terminated string "hello\0" of length 6
|
||||
char *from = Ident((char*)malloc(from_size));
|
||||
strcpy(from, "hello");
|
||||
// copy 0 bytes
|
||||
strncpy(to, from, 0);
|
||||
strncpy(to - 1, from - 1, 0);
|
||||
// normal strncpy calls
|
||||
strncpy(to, from, from_size);
|
||||
strncpy(to, from, to_size);
|
||||
strncpy(to, from + from_size - 1, to_size);
|
||||
strncpy(to + to_size - 1, from, 1);
|
||||
// One of {to, from} points to not allocated memory
|
||||
EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)),
|
||||
LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)),
|
||||
LeftOOBWriteMessage(1));
|
||||
EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)),
|
||||
RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)),
|
||||
RightOOBWriteMessage(0));
|
||||
// Length of "to" is too small
|
||||
EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)),
|
||||
RightOOBWriteMessage(0));
|
||||
EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)),
|
||||
RightOOBWriteMessage(0));
|
||||
// Overwrite terminator in from
|
||||
from[from_size - 1] = '!';
|
||||
// normal strncpy call
|
||||
strncpy(to, from, from_size);
|
||||
// Length of "from" is too small
|
||||
EXPECT_DEATH(Ident(strncpy(to, from, to_size)),
|
||||
RightOOBReadMessage(0));
|
||||
free(to);
|
||||
free(from);
|
||||
}
|
||||
|
||||
// Users may have different definitions of "strchr" and "index", so provide
|
||||
// function pointer typedefs and overload RunStrChrTest implementation.
|
||||
// We can't use macro for RunStrChrTest body here, as this macro would
|
||||
// confuse EXPECT_DEATH gtest macro.
|
||||
typedef char*(*PointerToStrChr1)(const char*, int);
|
||||
typedef char*(*PointerToStrChr2)(char*, int);
|
||||
|
||||
UNUSED static void RunStrChrTest(PointerToStrChr1 StrChr) {
|
||||
size_t size = Ident(100);
|
||||
char *str = MallocAndMemsetString(size);
|
||||
str[10] = 'q';
|
||||
str[11] = '\0';
|
||||
EXPECT_EQ(str, StrChr(str, 'z'));
|
||||
EXPECT_EQ(str + 10, StrChr(str, 'q'));
|
||||
EXPECT_EQ(NULL, StrChr(str, 'a'));
|
||||
// StrChr argument points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
|
||||
// Overwrite the terminator and hit not allocated memory.
|
||||
str[11] = 'z';
|
||||
EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
|
||||
free(str);
|
||||
}
|
||||
UNUSED static void RunStrChrTest(PointerToStrChr2 StrChr) {
|
||||
size_t size = Ident(100);
|
||||
char *str = MallocAndMemsetString(size);
|
||||
str[10] = 'q';
|
||||
str[11] = '\0';
|
||||
EXPECT_EQ(str, StrChr(str, 'z'));
|
||||
EXPECT_EQ(str + 10, StrChr(str, 'q'));
|
||||
EXPECT_EQ(NULL, StrChr(str, 'a'));
|
||||
// StrChr argument points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0));
|
||||
// Overwrite the terminator and hit not allocated memory.
|
||||
str[11] = 'z';
|
||||
EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0));
|
||||
free(str);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrChrAndIndexOOBTest) {
|
||||
RunStrChrTest(&strchr);
|
||||
// No index() on Windows and on Android L.
|
||||
#if !defined(_WIN32) && !defined(__ANDROID__)
|
||||
RunStrChrTest(&index);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) {
|
||||
// strcmp
|
||||
EXPECT_EQ(0, strcmp("", ""));
|
||||
EXPECT_EQ(0, strcmp("abcd", "abcd"));
|
||||
EXPECT_GT(0, strcmp("ab", "ac"));
|
||||
EXPECT_GT(0, strcmp("abc", "abcd"));
|
||||
EXPECT_LT(0, strcmp("acc", "abc"));
|
||||
EXPECT_LT(0, strcmp("abcd", "abc"));
|
||||
|
||||
// strncmp
|
||||
EXPECT_EQ(0, strncmp("a", "b", 0));
|
||||
EXPECT_EQ(0, strncmp("abcd", "abcd", 10));
|
||||
EXPECT_EQ(0, strncmp("abcd", "abcef", 3));
|
||||
EXPECT_GT(0, strncmp("abcde", "abcfa", 4));
|
||||
EXPECT_GT(0, strncmp("a", "b", 5));
|
||||
EXPECT_GT(0, strncmp("bc", "bcde", 4));
|
||||
EXPECT_LT(0, strncmp("xyz", "xyy", 10));
|
||||
EXPECT_LT(0, strncmp("baa", "aaa", 1));
|
||||
EXPECT_LT(0, strncmp("zyx", "", 2));
|
||||
|
||||
#if !defined(_WIN32) // no str[n]casecmp on Windows.
|
||||
// strcasecmp
|
||||
EXPECT_EQ(0, strcasecmp("", ""));
|
||||
EXPECT_EQ(0, strcasecmp("zzz", "zzz"));
|
||||
EXPECT_EQ(0, strcasecmp("abCD", "ABcd"));
|
||||
EXPECT_GT(0, strcasecmp("aB", "Ac"));
|
||||
EXPECT_GT(0, strcasecmp("ABC", "ABCd"));
|
||||
EXPECT_LT(0, strcasecmp("acc", "abc"));
|
||||
EXPECT_LT(0, strcasecmp("ABCd", "abc"));
|
||||
|
||||
// strncasecmp
|
||||
EXPECT_EQ(0, strncasecmp("a", "b", 0));
|
||||
EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10));
|
||||
EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3));
|
||||
EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4));
|
||||
EXPECT_GT(0, strncasecmp("a", "B", 5));
|
||||
EXPECT_GT(0, strncasecmp("bc", "BCde", 4));
|
||||
EXPECT_LT(0, strncasecmp("xyz", "xyy", 10));
|
||||
EXPECT_LT(0, strncasecmp("Baa", "aaa", 1));
|
||||
EXPECT_LT(0, strncasecmp("zyx", "", 2));
|
||||
#endif
|
||||
|
||||
// memcmp
|
||||
EXPECT_EQ(0, memcmp("a", "b", 0));
|
||||
EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4));
|
||||
EXPECT_GT(0, memcmp("\0ab", "\0ac", 3));
|
||||
EXPECT_GT(0, memcmp("abb\0", "abba", 4));
|
||||
EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5));
|
||||
EXPECT_LT(0, memcmp("zza", "zyx", 3));
|
||||
}
|
||||
|
||||
typedef int(*PointerToStrCmp)(const char*, const char*);
|
||||
void RunStrCmpTest(PointerToStrCmp StrCmp) {
|
||||
size_t size = Ident(100);
|
||||
int fill = 'o';
|
||||
char *s1 = MallocAndMemsetString(size, fill);
|
||||
char *s2 = MallocAndMemsetString(size, fill);
|
||||
s1[size - 1] = '\0';
|
||||
s2[size - 1] = '\0';
|
||||
// Normal StrCmp calls
|
||||
Ident(StrCmp(s1, s2));
|
||||
Ident(StrCmp(s1, s2 + size - 1));
|
||||
Ident(StrCmp(s1 + size - 1, s2 + size - 1));
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0));
|
||||
// Hit unallocated memory and die.
|
||||
s1[size - 1] = fill;
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0));
|
||||
free(s1);
|
||||
free(s2);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrCmpOOBTest) {
|
||||
RunStrCmpTest(&strcmp);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // no str[n]casecmp on Windows.
|
||||
TEST(AddressSanitizer, StrCaseCmpOOBTest) {
|
||||
RunStrCmpTest(&strcasecmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef int(*PointerToStrNCmp)(const char*, const char*, size_t);
|
||||
void RunStrNCmpTest(PointerToStrNCmp StrNCmp) {
|
||||
size_t size = Ident(100);
|
||||
char *s1 = MallocAndMemsetString(size);
|
||||
char *s2 = MallocAndMemsetString(size);
|
||||
s1[size - 1] = '\0';
|
||||
s2[size - 1] = '\0';
|
||||
// Normal StrNCmp calls
|
||||
Ident(StrNCmp(s1, s2, size + 2));
|
||||
s1[size - 1] = 'z';
|
||||
s2[size - 1] = 'x';
|
||||
Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size));
|
||||
s2[size - 1] = 'z';
|
||||
Ident(StrNCmp(s1 - 1, s2 - 1, 0));
|
||||
Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1));
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0));
|
||||
// Hit unallocated memory and die.
|
||||
EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0));
|
||||
free(s1);
|
||||
free(s2);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrNCmpOOBTest) {
|
||||
RunStrNCmpTest(&strncmp);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // no str[n]casecmp on Windows.
|
||||
TEST(AddressSanitizer, StrNCaseCmpOOBTest) {
|
||||
RunStrNCmpTest(&strncasecmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(AddressSanitizer, StrCatOOBTest) {
|
||||
// strcat() reads strlen(to) bytes from |to| before concatenating.
|
||||
size_t to_size = Ident(100);
|
||||
char *to = MallocAndMemsetString(to_size);
|
||||
to[0] = '\0';
|
||||
size_t from_size = Ident(20);
|
||||
char *from = MallocAndMemsetString(from_size);
|
||||
from[from_size - 1] = '\0';
|
||||
// Normal strcat calls.
|
||||
strcat(to, from);
|
||||
strcat(to, from);
|
||||
strcat(to + from_size, from + from_size - 2);
|
||||
// Passing an invalid pointer is an error even when concatenating an empty
|
||||
// string.
|
||||
EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1));
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1));
|
||||
EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0));
|
||||
|
||||
// "from" is not zero-terminated.
|
||||
from[from_size - 1] = 'z';
|
||||
EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0));
|
||||
from[from_size - 1] = '\0';
|
||||
// "to" is too short to fit "from".
|
||||
memset(to, 'z', to_size);
|
||||
to[to_size - from_size + 1] = '\0';
|
||||
EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0));
|
||||
// length of "to" is just enough.
|
||||
strcat(to, from + 1);
|
||||
|
||||
free(to);
|
||||
free(from);
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrNCatOOBTest) {
|
||||
// strncat() reads strlen(to) bytes from |to| before concatenating.
|
||||
size_t to_size = Ident(100);
|
||||
char *to = MallocAndMemsetString(to_size);
|
||||
to[0] = '\0';
|
||||
size_t from_size = Ident(20);
|
||||
char *from = MallocAndMemsetString(from_size);
|
||||
// Normal strncat calls.
|
||||
strncat(to, from, 0);
|
||||
strncat(to, from, from_size);
|
||||
from[from_size - 1] = '\0';
|
||||
strncat(to, from, 2 * from_size);
|
||||
// Catenating empty string with an invalid string is still an error.
|
||||
EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1));
|
||||
strncat(to, from + from_size - 1, 10);
|
||||
// One of arguments points to not allocated memory.
|
||||
EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1));
|
||||
EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1));
|
||||
EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0));
|
||||
|
||||
memset(from, 'z', from_size);
|
||||
memset(to, 'z', to_size);
|
||||
to[0] = '\0';
|
||||
// "from" is too short.
|
||||
EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0));
|
||||
// "to" is too short to fit "from".
|
||||
to[0] = 'z';
|
||||
to[to_size - from_size + 1] = '\0';
|
||||
EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0));
|
||||
// "to" is just enough.
|
||||
strncat(to, from, from_size - 2);
|
||||
|
||||
free(to);
|
||||
free(from);
|
||||
}
|
||||
|
||||
static string OverlapErrorMessage(const string &func) {
|
||||
return func + "-param-overlap";
|
||||
}
|
||||
|
||||
TEST(AddressSanitizer, StrArgsOverlapTest) {
|
||||
size_t size = Ident(100);
|
||||
char *str = Ident((char*)malloc(size));
|
||||
|
||||
// Do not check memcpy() on OS X 10.7 and later, where it actually aliases
|
||||
// memmove().
|
||||
#if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \
|
||||
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7)
|
||||
// Check "memcpy". Use Ident() to avoid inlining.
|
||||
memset(str, 'z', size);
|
||||
Ident(memcpy)(str + 1, str + 11, 10);
|
||||
Ident(memcpy)(str, str, 0);
|
||||
EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy"));
|
||||
EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy"));
|
||||
#endif
|
||||
|
||||
// We do not treat memcpy with to==from as a bug.
|
||||
// See http://llvm.org/bugs/show_bug.cgi?id=11763.
|
||||
// EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1),
|
||||
// OverlapErrorMessage("memcpy"));
|
||||
|
||||
// Check "strcpy".
|
||||
memset(str, 'z', size);
|
||||
str[9] = '\0';
|
||||
strcpy(str + 10, str);
|
||||
EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy"));
|
||||
EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy"));
|
||||
strcpy(str, str + 5);
|
||||
|
||||
// Check "strncpy".
|
||||
memset(str, 'z', size);
|
||||
strncpy(str, str + 10, 10);
|
||||
EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy"));
|
||||
EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy"));
|
||||
str[10] = '\0';
|
||||
strncpy(str + 11, str, 20);
|
||||
EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy"));
|
||||
|
||||
// Check "strcat".
|
||||
memset(str, 'z', size);
|
||||
str[10] = '\0';
|
||||
str[20] = '\0';
|
||||
strcat(str, str + 10);
|
||||
EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat"));
|
||||
str[10] = '\0';
|
||||
strcat(str + 11, str);
|
||||
EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
|
||||
EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat"));
|
||||
EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat"));
|
||||
|
||||
// Check "strncat".
|
||||
memset(str, 'z', size);
|
||||
str[10] = '\0';
|
||||
strncat(str, str + 10, 10); // from is empty
|
||||
EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat"));
|
||||
str[10] = '\0';
|
||||
str[20] = '\0';
|
||||
strncat(str + 5, str, 5);
|
||||
str[10] = '\0';
|
||||
EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat"));
|
||||
EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat"));
|
||||
|
||||
free(str);
|
||||
}
|
||||
|
||||
typedef void(*PointerToCallAtoi)(const char*);
|
||||
|
||||
void RunAtoiOOBTest(PointerToCallAtoi Atoi) {
|
||||
char *array = MallocAndMemsetString(10, '1');
|
||||
// Invalid pointer to the string.
|
||||
EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1));
|
||||
EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1));
|
||||
// Die if a buffer doesn't have terminating NULL.
|
||||
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
|
||||
// Make last symbol a terminating NULL
|
||||
array[9] = '\0';
|
||||
Atoi(array);
|
||||
// Sometimes we need to detect overflow if no digits are found.
|
||||
memset(array, ' ', 10);
|
||||
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
|
||||
array[9] = '-';
|
||||
EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0));
|
||||
free(array);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // FIXME: Fix and enable on Windows.
|
||||
void CallAtoi(const char *nptr) {
|
||||
Ident(atoi(nptr));
|
||||
}
|
||||
void CallAtol(const char *nptr) {
|
||||
Ident(atol(nptr));
|
||||
}
|
||||
void CallAtoll(const char *nptr) {
|
||||
Ident(atoll(nptr));
|
||||
}
|
||||
TEST(AddressSanitizer, AtoiAndFriendsOOBTest) {
|
||||
RunAtoiOOBTest(&CallAtoi);
|
||||
RunAtoiOOBTest(&CallAtol);
|
||||
RunAtoiOOBTest(&CallAtoll);
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef void(*PointerToCallStrtol)(const char*, char**, int);
|
||||
|
||||
void RunStrtolOOBTest(PointerToCallStrtol Strtol) {
|
||||
char *array = MallocAndMemsetString(3);
|
||||
array[0] = '1';
|
||||
array[1] = '2';
|
||||
array[2] = '3';
|
||||
// Invalid pointer to the string.
|
||||
EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0));
|
||||
EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1));
|
||||
// Buffer overflow if there is no terminating null (depends on base).
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
array[2] = 'z';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0));
|
||||
// Add terminating zero to get rid of overflow.
|
||||
array[2] = '\0';
|
||||
Strtol(array, NULL, 36);
|
||||
// Sometimes we need to detect overflow if no digits are found.
|
||||
array[0] = array[1] = array[2] = ' ';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
array[2] = '+';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
array[2] = '-';
|
||||
EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0));
|
||||
free(array);
|
||||
}
|
||||
|
||||
#if !defined(_WIN32) // FIXME: Fix and enable on Windows.
|
||||
void CallStrtol(const char *nptr, char **endptr, int base) {
|
||||
Ident(strtol(nptr, endptr, base));
|
||||
}
|
||||
void CallStrtoll(const char *nptr, char **endptr, int base) {
|
||||
Ident(strtoll(nptr, endptr, base));
|
||||
}
|
||||
TEST(AddressSanitizer, StrtollOOBTest) {
|
||||
RunStrtolOOBTest(&CallStrtoll);
|
||||
}
|
||||
TEST(AddressSanitizer, StrtolOOBTest) {
|
||||
RunStrtolOOBTest(&CallStrtol);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +0,0 @@
|
||||
# blacklisted functions for instrumented ASan unit test
|
||||
fun:*IgnoreTest*
|
||||
fun:*SomeOtherFunc*
|
@ -1,54 +0,0 @@
|
||||
//===-- asan_test_config.h --------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#if !defined(INCLUDED_FROM_ASAN_TEST_UTILS_H)
|
||||
# error "This file should be included into asan_test_utils.h only"
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_TEST_CONFIG_H
|
||||
#define ASAN_TEST_CONFIG_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::map;
|
||||
|
||||
#ifndef ASAN_UAR
|
||||
# error "please define ASAN_UAR"
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_HAS_EXCEPTIONS
|
||||
# error "please define ASAN_HAS_EXCEPTIONS"
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_HAS_BLACKLIST
|
||||
# error "please define ASAN_HAS_BLACKLIST"
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_NEEDS_SEGV
|
||||
# if defined(_WIN32)
|
||||
# define ASAN_NEEDS_SEGV 0
|
||||
# else
|
||||
# define ASAN_NEEDS_SEGV 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ASAN_AVOID_EXPENSIVE_TESTS
|
||||
# define ASAN_AVOID_EXPENSIVE_TESTS 0
|
||||
#endif
|
||||
|
||||
#define ASAN_PCRE_DOTALL ""
|
||||
|
||||
#endif // ASAN_TEST_CONFIG_H
|
@ -1,19 +0,0 @@
|
||||
//===-- asan_test_main.cc -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "asan_test_utils.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::GTEST_FLAG(death_test_style) = "threadsafe";
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
//===-- asan_test_utils.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file is a part of AddressSanitizer, an address sanity checker.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef ASAN_TEST_UTILS_H
|
||||
#define ASAN_TEST_UTILS_H
|
||||
|
||||
#if !defined(SANITIZER_EXTERNAL_TEST_CONFIG)
|
||||
# define INCLUDED_FROM_ASAN_TEST_UTILS_H
|
||||
# include "asan_test_config.h"
|
||||
# undef INCLUDED_FROM_ASAN_TEST_UTILS_H
|
||||
#endif
|
||||
|
||||
#include "sanitizer_test_utils.h"
|
||||
#include "sanitizer_pthread_wrappers.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include <strings.h>
|
||||
# include <sys/mman.h>
|
||||
# include <setjmp.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
# include <sys/prctl.h>
|
||||
# include <sys/types.h>
|
||||
# include <sys/stat.h>
|
||||
# include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#if ASAN_HAS_EXCEPTIONS
|
||||
# define ASAN_THROW(x) throw (x)
|
||||
#else
|
||||
# define ASAN_THROW(x)
|
||||
#endif
|
||||
|
||||
typedef uint8_t U1;
|
||||
typedef uint16_t U2;
|
||||
typedef uint32_t U4;
|
||||
typedef uint64_t U8;
|
||||
|
||||
static const int kPageSize = 4096;
|
||||
|
||||
const size_t kLargeMalloc = 1 << 24;
|
||||
|
||||
extern void free_aaa(void *p);
|
||||
extern void *malloc_aaa(size_t size);
|
||||
|
||||
template<typename T>
|
||||
NOINLINE void asan_write(T *a) {
|
||||
*a = 0;
|
||||
}
|
||||
|
||||
string RightOOBErrorMessage(int oob_distance, bool is_write);
|
||||
string RightOOBWriteMessage(int oob_distance);
|
||||
string RightOOBReadMessage(int oob_distance);
|
||||
string LeftOOBErrorMessage(int oob_distance, bool is_write);
|
||||
string LeftOOBWriteMessage(int oob_distance);
|
||||
string LeftOOBReadMessage(int oob_distance);
|
||||
string LeftOOBAccessMessage(int oob_distance);
|
||||
char* MallocAndMemsetString(size_t size, char ch);
|
||||
char* MallocAndMemsetString(size_t size);
|
||||
|
||||
extern char glob1[1];
|
||||
extern char glob2[2];
|
||||
extern char glob3[3];
|
||||
extern char glob4[4];
|
||||
extern char glob5[5];
|
||||
extern char glob6[6];
|
||||
extern char glob7[7];
|
||||
extern char glob8[8];
|
||||
extern char glob9[9];
|
||||
extern char glob10[10];
|
||||
extern char glob11[11];
|
||||
extern char glob12[12];
|
||||
extern char glob13[13];
|
||||
extern char glob14[14];
|
||||
extern char glob15[15];
|
||||
extern char glob16[16];
|
||||
extern char glob17[17];
|
||||
extern char glob1000[1000];
|
||||
extern char glob10000[10000];
|
||||
extern char glob100000[100000];
|
||||
extern int GlobalsTest(int x);
|
||||
|
||||
#endif // ASAN_TEST_UTILS_H
|
@ -220,7 +220,9 @@ _Unwind_Reason_Code __gcc_personality_v0(int version, _Unwind_Action actions,
|
||||
// for use with some implementations of assert() in <assert.h>
|
||||
void __eprintf(const char* format, const char* assertion_expression,
|
||||
const char* line, const char* file);
|
||||
|
||||
|
||||
// for systems with emulated thread local storage
|
||||
void* __emutls_get_address(struct __emutls_control*);
|
||||
|
||||
|
||||
// Power PC specific functions
|
||||
|
96
contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S
Normal file
96
contrib/compiler-rt/lib/builtins/arm/aeabi_cdcmp.S
Normal file
@ -0,0 +1,96 @@
|
||||
//===-- aeabi_cdcmp.S - EABI cdcmp* implementation ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
#error big endian support not implemented
|
||||
#endif
|
||||
|
||||
#define APSR_Z (1 << 30)
|
||||
#define APSR_C (1 << 29)
|
||||
|
||||
// void __aeabi_cdcmpeq(double a, double b) {
|
||||
// if (isnan(a) || isnan(b)) {
|
||||
// Z = 0; C = 1;
|
||||
// } else {
|
||||
// __aeabi_cdcmple(a, b);
|
||||
// }
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
|
||||
push {r0-r3, lr}
|
||||
bl __aeabi_cdcmpeq_check_nan
|
||||
cmp r0, #1
|
||||
pop {r0-r3, lr}
|
||||
|
||||
// NaN has been ruled out, so __aeabi_cdcmple can't trap
|
||||
bne __aeabi_cdcmple
|
||||
|
||||
msr CPSR_f, #APSR_C
|
||||
JMP(lr)
|
||||
END_COMPILERRT_FUNCTION(__aeabi_cdcmpeq)
|
||||
|
||||
|
||||
// void __aeabi_cdcmple(double a, double b) {
|
||||
// if (__aeabi_dcmplt(a, b)) {
|
||||
// Z = 0; C = 0;
|
||||
// } else if (__aeabi_dcmpeq(a, b)) {
|
||||
// Z = 1; C = 1;
|
||||
// } else {
|
||||
// Z = 0; C = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_cdcmple)
|
||||
// Per the RTABI, this function must preserve r0-r11.
|
||||
// Save lr in the same instruction for compactness
|
||||
push {r0-r3, lr}
|
||||
|
||||
bl __aeabi_dcmplt
|
||||
cmp r0, #1
|
||||
moveq ip, #0
|
||||
beq 1f
|
||||
|
||||
ldm sp, {r0-r3}
|
||||
bl __aeabi_dcmpeq
|
||||
cmp r0, #1
|
||||
moveq ip, #(APSR_C | APSR_Z)
|
||||
movne ip, #(APSR_C)
|
||||
|
||||
1:
|
||||
msr CPSR_f, ip
|
||||
pop {r0-r3}
|
||||
POP_PC()
|
||||
END_COMPILERRT_FUNCTION(__aeabi_cdcmple)
|
||||
|
||||
// int __aeabi_cdrcmple(double a, double b) {
|
||||
// return __aeabi_cdcmple(b, a);
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_cdrcmple)
|
||||
// Swap r0 and r2
|
||||
mov ip, r0
|
||||
mov r0, r2
|
||||
mov r2, ip
|
||||
|
||||
// Swap r1 and r3
|
||||
mov ip, r1
|
||||
mov r1, r3
|
||||
mov r3, ip
|
||||
|
||||
b __aeabi_cdcmple
|
||||
END_COMPILERRT_FUNCTION(__aeabi_cdrcmple)
|
||||
|
@ -0,0 +1,16 @@
|
||||
//===-- lib/arm/aeabi_cdcmpeq_helper.c - Helper for cdcmpeq ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
__attribute__((pcs("aapcs")))
|
||||
__attribute__((visibility("hidden")))
|
||||
int __aeabi_cdcmpeq_check_nan(double a, double b) {
|
||||
return __builtin_isnan(a) || __builtin_isnan(b);
|
||||
}
|
91
contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S
Normal file
91
contrib/compiler-rt/lib/builtins/arm/aeabi_cfcmp.S
Normal file
@ -0,0 +1,91 @@
|
||||
//===-- aeabi_cfcmp.S - EABI cfcmp* implementation ------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "../assembly.h"
|
||||
|
||||
#if __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__
|
||||
#error big endian support not implemented
|
||||
#endif
|
||||
|
||||
#define APSR_Z (1 << 30)
|
||||
#define APSR_C (1 << 29)
|
||||
|
||||
// void __aeabi_cfcmpeq(float a, float b) {
|
||||
// if (isnan(a) || isnan(b)) {
|
||||
// Z = 0; C = 1;
|
||||
// } else {
|
||||
// __aeabi_cfcmple(a, b);
|
||||
// }
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
|
||||
push {r0-r3, lr}
|
||||
bl __aeabi_cfcmpeq_check_nan
|
||||
cmp r0, #1
|
||||
pop {r0-r3, lr}
|
||||
|
||||
// NaN has been ruled out, so __aeabi_cfcmple can't trap
|
||||
bne __aeabi_cfcmple
|
||||
|
||||
msr CPSR_f, #APSR_C
|
||||
JMP(lr)
|
||||
END_COMPILERRT_FUNCTION(__aeabi_cfcmpeq)
|
||||
|
||||
|
||||
// void __aeabi_cfcmple(float a, float b) {
|
||||
// if (__aeabi_fcmplt(a, b)) {
|
||||
// Z = 0; C = 0;
|
||||
// } else if (__aeabi_fcmpeq(a, b)) {
|
||||
// Z = 1; C = 1;
|
||||
// } else {
|
||||
// Z = 0; C = 1;
|
||||
// }
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_cfcmple)
|
||||
// Per the RTABI, this function must preserve r0-r11.
|
||||
// Save lr in the same instruction for compactness
|
||||
push {r0-r3, lr}
|
||||
|
||||
bl __aeabi_fcmplt
|
||||
cmp r0, #1
|
||||
moveq ip, #0
|
||||
beq 1f
|
||||
|
||||
ldm sp, {r0-r3}
|
||||
bl __aeabi_fcmpeq
|
||||
cmp r0, #1
|
||||
moveq ip, #(APSR_C | APSR_Z)
|
||||
movne ip, #(APSR_C)
|
||||
|
||||
1:
|
||||
msr CPSR_f, ip
|
||||
pop {r0-r3}
|
||||
POP_PC()
|
||||
END_COMPILERRT_FUNCTION(__aeabi_cfcmple)
|
||||
|
||||
// int __aeabi_cfrcmple(float a, float b) {
|
||||
// return __aeabi_cfcmple(b, a);
|
||||
// }
|
||||
|
||||
.syntax unified
|
||||
.p2align 2
|
||||
DEFINE_COMPILERRT_FUNCTION(__aeabi_cfrcmple)
|
||||
// Swap r0 and r1
|
||||
mov ip, r0
|
||||
mov r0, r1
|
||||
mov r1, ip
|
||||
|
||||
b __aeabi_cfcmple
|
||||
END_COMPILERRT_FUNCTION(__aeabi_cfrcmple)
|
||||
|
@ -0,0 +1,16 @@
|
||||
//===-- lib/arm/aeabi_cfcmpeq_helper.c - Helper for cdcmpeq ---------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
__attribute__((pcs("aapcs")))
|
||||
__attribute__((visibility("hidden")))
|
||||
int __aeabi_cfcmpeq_check_nan(float a, float b) {
|
||||
return __builtin_isnan(a) || __builtin_isnan(b);
|
||||
}
|
19
contrib/compiler-rt/lib/builtins/arm/aeabi_drsub.c
Normal file
19
contrib/compiler-rt/lib/builtins/arm/aeabi_drsub.c
Normal file
@ -0,0 +1,19 @@
|
||||
//===-- lib/arm/aeabi_drsub.c - Double-precision subtraction --------------===//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define DOUBLE_PRECISION
|
||||
#include "../fp_lib.h"
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__aeabi_dsub(fp_t, fp_t);
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__aeabi_drsub(fp_t a, fp_t b) {
|
||||
return __aeabi_dsub(b, a);
|
||||
}
|
19
contrib/compiler-rt/lib/builtins/arm/aeabi_frsub.c
Normal file
19
contrib/compiler-rt/lib/builtins/arm/aeabi_frsub.c
Normal file
@ -0,0 +1,19 @@
|
||||
//===-- lib/arm/aeabi_frsub.c - Single-precision subtraction --------------===//
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#define SINGLE_PRECISION
|
||||
#include "../fp_lib.h"
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__aeabi_fsub(fp_t, fp_t);
|
||||
|
||||
COMPILER_RT_ABI fp_t
|
||||
__aeabi_frsub(fp_t a, fp_t b) {
|
||||
return __aeabi_fsub(b, a);
|
||||
}
|
@ -73,6 +73,15 @@
|
||||
#define JMPc(r, c) mov##c pc, r
|
||||
#endif
|
||||
|
||||
// pop {pc} can't switch Thumb mode on ARMv4T
|
||||
#if __ARM_ARCH >= 5
|
||||
#define POP_PC() pop {pc}
|
||||
#else
|
||||
#define POP_PC() \
|
||||
pop {ip}; \
|
||||
JMP(ip)
|
||||
#endif
|
||||
|
||||
#if __ARM_ARCH_ISA_THUMB == 2
|
||||
#define IT(cond) it cond
|
||||
#define ITT(cond) itt cond
|
||||
|
@ -56,13 +56,13 @@ static const long SPINLOCK_MASK = SPINLOCK_COUNT - 1;
|
||||
#include <machine/atomic.h>
|
||||
#include <sys/umtx.h>
|
||||
typedef struct _usem Lock;
|
||||
inline static void unlock(Lock *l) {
|
||||
__inline static void unlock(Lock *l) {
|
||||
__c11_atomic_store((_Atomic(uint32_t)*)&l->_count, 1, __ATOMIC_RELEASE);
|
||||
__c11_atomic_thread_fence(__ATOMIC_SEQ_CST);
|
||||
if (l->_has_waiters)
|
||||
_umtx_op(l, UMTX_OP_SEM_WAKE, 1, 0, 0);
|
||||
}
|
||||
inline static void lock(Lock *l) {
|
||||
__inline static void lock(Lock *l) {
|
||||
uint32_t old = 1;
|
||||
while (!__c11_atomic_compare_exchange_weak((_Atomic(uint32_t)*)&l->_count, &old,
|
||||
0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) {
|
||||
@ -76,12 +76,12 @@ static Lock locks[SPINLOCK_COUNT] = { [0 ... SPINLOCK_COUNT-1] = {0,1,0} };
|
||||
#elif defined(__APPLE__)
|
||||
#include <libkern/OSAtomic.h>
|
||||
typedef OSSpinLock Lock;
|
||||
inline static void unlock(Lock *l) {
|
||||
__inline static void unlock(Lock *l) {
|
||||
OSSpinLockUnlock(l);
|
||||
}
|
||||
/// Locks a lock. In the current implementation, this is potentially
|
||||
/// unbounded in the contended case.
|
||||
inline static void lock(Lock *l) {
|
||||
__inline static void lock(Lock *l) {
|
||||
OSSpinLockLock(l);
|
||||
}
|
||||
static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0
|
||||
@ -89,12 +89,12 @@ static Lock locks[SPINLOCK_COUNT]; // initialized to OS_SPINLOCK_INIT which is 0
|
||||
#else
|
||||
typedef _Atomic(uintptr_t) Lock;
|
||||
/// Unlock a lock. This is a release operation.
|
||||
inline static void unlock(Lock *l) {
|
||||
__inline static void unlock(Lock *l) {
|
||||
__c11_atomic_store(l, 0, __ATOMIC_RELEASE);
|
||||
}
|
||||
/// Locks a lock. In the current implementation, this is potentially
|
||||
/// unbounded in the contended case.
|
||||
inline static void lock(Lock *l) {
|
||||
__inline static void lock(Lock *l) {
|
||||
uintptr_t old = 0;
|
||||
while (!__c11_atomic_compare_exchange_weak(l, &old, 1, __ATOMIC_ACQUIRE,
|
||||
__ATOMIC_RELAXED))
|
||||
@ -106,7 +106,7 @@ static Lock locks[SPINLOCK_COUNT];
|
||||
|
||||
|
||||
/// Returns a lock to use for a given pointer.
|
||||
static inline Lock *lock_for_pointer(void *ptr) {
|
||||
static __inline Lock *lock_for_pointer(void *ptr) {
|
||||
intptr_t hash = (intptr_t)ptr;
|
||||
// Disregard the lowest 4 bits. We want all values that may be part of the
|
||||
// same memory operation to hash to the same value and therefore use the same
|
||||
|
@ -12,8 +12,16 @@
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(inc) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<stdatomic.h>)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_clear
|
||||
void atomic_flag_clear(volatile atomic_flag *object) {
|
||||
return __c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST);
|
||||
__c11_atomic_store(&(object)->_Value, 0, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -12,9 +12,17 @@
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(inc) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<stdatomic.h>)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_clear_explicit
|
||||
void atomic_flag_clear_explicit(volatile atomic_flag *object,
|
||||
memory_order order) {
|
||||
return __c11_atomic_store(&(object)->_Value, 0, order);
|
||||
__c11_atomic_store(&(object)->_Value, 0, order);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -12,8 +12,16 @@
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(inc) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<stdatomic.h>)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_test_and_set
|
||||
_Bool atomic_flag_test_and_set(volatile atomic_flag *object) {
|
||||
return __c11_atomic_exchange(&(object)->_Value, 1, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -12,9 +12,17 @@
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(inc) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<stdatomic.h>)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_flag_test_and_set_explicit
|
||||
_Bool atomic_flag_test_and_set_explicit(volatile atomic_flag *object,
|
||||
memory_order order) {
|
||||
return __c11_atomic_exchange(&(object)->_Value, 1, order);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -12,8 +12,16 @@
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(inc) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<stdatomic.h>)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_signal_fence
|
||||
void atomic_signal_fence(memory_order order) {
|
||||
__c11_atomic_signal_fence(order);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -12,8 +12,16 @@
|
||||
*===------------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#ifndef __has_include
|
||||
#define __has_include(inc) 0
|
||||
#endif
|
||||
|
||||
#if __has_include(<stdatomic.h>)
|
||||
|
||||
#include <stdatomic.h>
|
||||
#undef atomic_thread_fence
|
||||
void atomic_thread_fence(memory_order order) {
|
||||
__c11_atomic_thread_fence(order);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -80,6 +80,11 @@ __ledf2(fp_t a, fp_t b) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ELF__)
|
||||
// Alias for libgcc compatibility
|
||||
FNALIAS(__cmpdf2, __ledf2);
|
||||
#endif
|
||||
|
||||
enum GE_RESULT {
|
||||
GE_LESS = -1,
|
||||
GE_EQUAL = 0,
|
||||
|
@ -80,6 +80,11 @@ __lesf2(fp_t a, fp_t b) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ELF__)
|
||||
// Alias for libgcc compatibility
|
||||
FNALIAS(__cmpsf2, __lesf2);
|
||||
#endif
|
||||
|
||||
enum GE_RESULT {
|
||||
GE_LESS = -1,
|
||||
GE_EQUAL = 0,
|
||||
|
@ -79,6 +79,11 @@ COMPILER_RT_ABI enum LE_RESULT __letf2(fp_t a, fp_t b) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ELF__)
|
||||
// Alias for libgcc compatibility
|
||||
FNALIAS(__cmptf2, __letf2);
|
||||
#endif
|
||||
|
||||
enum GE_RESULT {
|
||||
GE_LESS = -1,
|
||||
GE_EQUAL = 0,
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
/* Returns: the quotient of (a + ib) / (c + id) */
|
||||
|
||||
COMPILER_RT_ABI double _Complex
|
||||
COMPILER_RT_ABI Dcomplex
|
||||
__divdc3(double __a, double __b, double __c, double __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
@ -29,31 +29,31 @@ __divdc3(double __a, double __b, double __c, double __d)
|
||||
__d = crt_scalbn(__d, -__ilogbw);
|
||||
}
|
||||
double __denom = __c * __c + __d * __d;
|
||||
double _Complex z;
|
||||
__real__ z = crt_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
__imag__ z = crt_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
|
||||
Dcomplex z;
|
||||
COMPLEX_REAL(z) = crt_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
COMPLEX_IMAGINARY(z) = crt_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z)))
|
||||
{
|
||||
if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b)))
|
||||
{
|
||||
__real__ z = crt_copysign(CRT_INFINITY, __c) * __a;
|
||||
__imag__ z = crt_copysign(CRT_INFINITY, __c) * __b;
|
||||
COMPLEX_REAL(z) = crt_copysign(CRT_INFINITY, __c) * __a;
|
||||
COMPLEX_IMAGINARY(z) = crt_copysign(CRT_INFINITY, __c) * __b;
|
||||
}
|
||||
else if ((crt_isinf(__a) || crt_isinf(__b)) &&
|
||||
crt_isfinite(__c) && crt_isfinite(__d))
|
||||
{
|
||||
__a = crt_copysign(crt_isinf(__a) ? 1.0 : 0.0, __a);
|
||||
__b = crt_copysign(crt_isinf(__b) ? 1.0 : 0.0, __b);
|
||||
__real__ z = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
__imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
}
|
||||
else if (crt_isinf(__logbw) && __logbw > 0.0 &&
|
||||
crt_isfinite(__a) && crt_isfinite(__b))
|
||||
{
|
||||
__c = crt_copysign(crt_isinf(__c) ? 1.0 : 0.0, __c);
|
||||
__d = crt_copysign(crt_isinf(__d) ? 1.0 : 0.0, __d);
|
||||
__real__ z = 0.0 * (__a * __c + __b * __d);
|
||||
__imag__ z = 0.0 * (__b * __c - __a * __d);
|
||||
COMPLEX_REAL(z) = 0.0 * (__a * __c + __b * __d);
|
||||
COMPLEX_IMAGINARY(z) = 0.0 * (__b * __c - __a * __d);
|
||||
}
|
||||
}
|
||||
return z;
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
/* Returns: the quotient of (a + ib) / (c + id) */
|
||||
|
||||
COMPILER_RT_ABI float _Complex
|
||||
COMPILER_RT_ABI Fcomplex
|
||||
__divsc3(float __a, float __b, float __c, float __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
@ -29,31 +29,31 @@ __divsc3(float __a, float __b, float __c, float __d)
|
||||
__d = crt_scalbnf(__d, -__ilogbw);
|
||||
}
|
||||
float __denom = __c * __c + __d * __d;
|
||||
float _Complex z;
|
||||
__real__ z = crt_scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
__imag__ z = crt_scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
|
||||
Fcomplex z;
|
||||
COMPLEX_REAL(z) = crt_scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
COMPLEX_IMAGINARY(z) = crt_scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z)))
|
||||
{
|
||||
if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b)))
|
||||
{
|
||||
__real__ z = crt_copysignf(CRT_INFINITY, __c) * __a;
|
||||
__imag__ z = crt_copysignf(CRT_INFINITY, __c) * __b;
|
||||
COMPLEX_REAL(z) = crt_copysignf(CRT_INFINITY, __c) * __a;
|
||||
COMPLEX_IMAGINARY(z) = crt_copysignf(CRT_INFINITY, __c) * __b;
|
||||
}
|
||||
else if ((crt_isinf(__a) || crt_isinf(__b)) &&
|
||||
crt_isfinite(__c) && crt_isfinite(__d))
|
||||
{
|
||||
__a = crt_copysignf(crt_isinf(__a) ? 1 : 0, __a);
|
||||
__b = crt_copysignf(crt_isinf(__b) ? 1 : 0, __b);
|
||||
__real__ z = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
__imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
}
|
||||
else if (crt_isinf(__logbw) && __logbw > 0 &&
|
||||
crt_isfinite(__a) && crt_isfinite(__b))
|
||||
{
|
||||
__c = crt_copysignf(crt_isinf(__c) ? 1 : 0, __c);
|
||||
__d = crt_copysignf(crt_isinf(__d) ? 1 : 0, __d);
|
||||
__real__ z = 0 * (__a * __c + __b * __d);
|
||||
__imag__ z = 0 * (__b * __c - __a * __d);
|
||||
COMPLEX_REAL(z) = 0 * (__a * __c + __b * __d);
|
||||
COMPLEX_IMAGINARY(z) = 0 * (__b * __c - __a * __d);
|
||||
}
|
||||
}
|
||||
return z;
|
||||
|
60
contrib/compiler-rt/lib/builtins/divtc3.c
Normal file
60
contrib/compiler-rt/lib/builtins/divtc3.c
Normal file
@ -0,0 +1,60 @@
|
||||
/*===-- divtc3.c - Implement __divtc3 -------------------------------------===
|
||||
*
|
||||
* 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 implements __divtc3 for the compiler_rt library.
|
||||
*
|
||||
*===----------------------------------------------------------------------===
|
||||
*/
|
||||
|
||||
#include "int_lib.h"
|
||||
#include "int_math.h"
|
||||
|
||||
/* Returns: the quotient of (a + ib) / (c + id) */
|
||||
|
||||
COMPILER_RT_ABI long double _Complex
|
||||
__divtc3(long double __a, long double __b, long double __c, long double __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
long double __logbw = crt_logbl(crt_fmaxl(crt_fabsl(__c), crt_fabsl(__d)));
|
||||
if (crt_isfinite(__logbw))
|
||||
{
|
||||
__ilogbw = (int)__logbw;
|
||||
__c = crt_scalbnl(__c, -__ilogbw);
|
||||
__d = crt_scalbnl(__d, -__ilogbw);
|
||||
}
|
||||
long double __denom = __c * __c + __d * __d;
|
||||
long double _Complex z;
|
||||
__real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
__imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
|
||||
{
|
||||
if ((__denom == 0.0) && (!crt_isnan(__a) || !crt_isnan(__b)))
|
||||
{
|
||||
__real__ z = crt_copysignl(CRT_INFINITY, __c) * __a;
|
||||
__imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b;
|
||||
}
|
||||
else if ((crt_isinf(__a) || crt_isinf(__b)) &&
|
||||
crt_isfinite(__c) && crt_isfinite(__d))
|
||||
{
|
||||
__a = crt_copysignl(crt_isinf(__a) ? 1.0 : 0.0, __a);
|
||||
__b = crt_copysignl(crt_isinf(__b) ? 1.0 : 0.0, __b);
|
||||
__real__ z = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
__imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
}
|
||||
else if (crt_isinf(__logbw) && __logbw > 0.0 &&
|
||||
crt_isfinite(__a) && crt_isfinite(__b))
|
||||
{
|
||||
__c = crt_copysignl(crt_isinf(__c) ? 1.0 : 0.0, __c);
|
||||
__d = crt_copysignl(crt_isinf(__d) ? 1.0 : 0.0, __d);
|
||||
__real__ z = 0.0 * (__a * __c + __b * __d);
|
||||
__imag__ z = 0.0 * (__b * __c - __a * __d);
|
||||
}
|
||||
}
|
||||
return z;
|
||||
}
|
@ -18,7 +18,7 @@
|
||||
|
||||
/* Returns: the quotient of (a + ib) / (c + id) */
|
||||
|
||||
COMPILER_RT_ABI long double _Complex
|
||||
COMPILER_RT_ABI Lcomplex
|
||||
__divxc3(long double __a, long double __b, long double __c, long double __d)
|
||||
{
|
||||
int __ilogbw = 0;
|
||||
@ -30,31 +30,31 @@ __divxc3(long double __a, long double __b, long double __c, long double __d)
|
||||
__d = crt_scalbnl(__d, -__ilogbw);
|
||||
}
|
||||
long double __denom = __c * __c + __d * __d;
|
||||
long double _Complex z;
|
||||
__real__ z = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
__imag__ z = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(__real__ z) && crt_isnan(__imag__ z))
|
||||
Lcomplex z;
|
||||
COMPLEX_REAL(z) = crt_scalbnl((__a * __c + __b * __d) / __denom, -__ilogbw);
|
||||
COMPLEX_IMAGINARY(z) = crt_scalbnl((__b * __c - __a * __d) / __denom, -__ilogbw);
|
||||
if (crt_isnan(COMPLEX_REAL(z)) && crt_isnan(COMPLEX_IMAGINARY(z)))
|
||||
{
|
||||
if ((__denom == 0) && (!crt_isnan(__a) || !crt_isnan(__b)))
|
||||
{
|
||||
__real__ z = crt_copysignl(CRT_INFINITY, __c) * __a;
|
||||
__imag__ z = crt_copysignl(CRT_INFINITY, __c) * __b;
|
||||
COMPLEX_REAL(z) = crt_copysignl(CRT_INFINITY, __c) * __a;
|
||||
COMPLEX_IMAGINARY(z) = crt_copysignl(CRT_INFINITY, __c) * __b;
|
||||
}
|
||||
else if ((crt_isinf(__a) || crt_isinf(__b)) &&
|
||||
crt_isfinite(__c) && crt_isfinite(__d))
|
||||
{
|
||||
__a = crt_copysignl(crt_isinf(__a) ? 1 : 0, __a);
|
||||
__b = crt_copysignl(crt_isinf(__b) ? 1 : 0, __b);
|
||||
__real__ z = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
__imag__ z = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
COMPLEX_REAL(z) = CRT_INFINITY * (__a * __c + __b * __d);
|
||||
COMPLEX_IMAGINARY(z) = CRT_INFINITY * (__b * __c - __a * __d);
|
||||
}
|
||||
else if (crt_isinf(__logbw) && __logbw > 0 &&
|
||||
crt_isfinite(__a) && crt_isfinite(__b))
|
||||
{
|
||||
__c = crt_copysignl(crt_isinf(__c) ? 1 : 0, __c);
|
||||
__d = crt_copysignl(crt_isinf(__d) ? 1 : 0, __d);
|
||||
__real__ z = 0 * (__a * __c + __b * __d);
|
||||
__imag__ z = 0 * (__b * __c - __a * __d);
|
||||
COMPLEX_REAL(z) = 0 * (__a * __c + __b * __d);
|
||||
COMPLEX_IMAGINARY(z) = 0 * (__b * __c - __a * __d);
|
||||
}
|
||||
}
|
||||
return z;
|
||||
|
183
contrib/compiler-rt/lib/builtins/emutls.c
Normal file
183
contrib/compiler-rt/lib/builtins/emutls.c
Normal file
@ -0,0 +1,183 @@
|
||||
/* ===---------- emutls.c - Implements __emutls_get_address ---------------===
|
||||
*
|
||||
* The LLVM Compiler Infrastructure
|
||||
*
|
||||
* This file is dual licensed under the MIT and the University of Illinois Open
|
||||
* Source Licenses. See LICENSE.TXT for details.
|
||||
*
|
||||
* ===----------------------------------------------------------------------===
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "int_lib.h"
|
||||
#include "int_util.h"
|
||||
|
||||
/* Default is not to use posix_memalign, so systems like Android
|
||||
* can use thread local data without heavier POSIX memory allocators.
|
||||
*/
|
||||
#ifndef EMUTLS_USE_POSIX_MEMALIGN
|
||||
#define EMUTLS_USE_POSIX_MEMALIGN 0
|
||||
#endif
|
||||
|
||||
/* For every TLS variable xyz,
|
||||
* there is one __emutls_control variable named __emutls_v.xyz.
|
||||
* If xyz has non-zero initial value, __emutls_v.xyz's "value"
|
||||
* will point to __emutls_t.xyz, which has the initial value.
|
||||
*/
|
||||
typedef struct __emutls_control {
|
||||
size_t size; /* size of the object in bytes */
|
||||
size_t align; /* alignment of the object in bytes */
|
||||
union {
|
||||
uintptr_t index; /* data[index-1] is the object address */
|
||||
void* address; /* object address, when in single thread env */
|
||||
} object;
|
||||
void* value; /* null or non-zero initial value for the object */
|
||||
} __emutls_control;
|
||||
|
||||
static __inline void *emutls_memalign_alloc(size_t align, size_t size) {
|
||||
void *base;
|
||||
#if EMUTLS_USE_POSIX_MEMALIGN
|
||||
if (posix_memalign(&base, align, size) != 0)
|
||||
abort();
|
||||
#else
|
||||
#define EXTRA_ALIGN_PTR_BYTES (align - 1 + sizeof(void*))
|
||||
char* object;
|
||||
if ((object = malloc(EXTRA_ALIGN_PTR_BYTES + size)) == NULL)
|
||||
abort();
|
||||
base = (void*)(((uintptr_t)(object + EXTRA_ALIGN_PTR_BYTES))
|
||||
& ~(uintptr_t)(align - 1));
|
||||
|
||||
((void**)base)[-1] = object;
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
static __inline void emutls_memalign_free(void *base) {
|
||||
#if EMUTLS_USE_POSIX_MEMALIGN
|
||||
free(base);
|
||||
#else
|
||||
/* The mallocated address is in ((void**)base)[-1] */
|
||||
free(((void**)base)[-1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Emulated TLS objects are always allocated at run-time. */
|
||||
static __inline void *emutls_allocate_object(__emutls_control *control) {
|
||||
/* Use standard C types, check with gcc's emutls.o. */
|
||||
typedef unsigned int gcc_word __attribute__((mode(word)));
|
||||
typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
|
||||
COMPILE_TIME_ASSERT(sizeof(size_t) == sizeof(gcc_word));
|
||||
COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(gcc_pointer));
|
||||
COMPILE_TIME_ASSERT(sizeof(uintptr_t) == sizeof(void*));
|
||||
|
||||
size_t size = control->size;
|
||||
size_t align = control->align;
|
||||
if (align < sizeof(void*))
|
||||
align = sizeof(void*);
|
||||
/* Make sure that align is power of 2. */
|
||||
if ((align & (align - 1)) != 0)
|
||||
abort();
|
||||
|
||||
void* base = emutls_memalign_alloc(align, size);
|
||||
if (control->value)
|
||||
memcpy(base, control->value, size);
|
||||
else
|
||||
memset(base, 0, size);
|
||||
return base;
|
||||
}
|
||||
|
||||
static pthread_mutex_t emutls_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
static size_t emutls_num_object = 0; /* number of allocated TLS objects */
|
||||
|
||||
typedef struct emutls_address_array {
|
||||
uintptr_t size; /* number of elements in the 'data' array */
|
||||
void* data[];
|
||||
} emutls_address_array;
|
||||
|
||||
static pthread_key_t emutls_pthread_key;
|
||||
|
||||
static void emutls_key_destructor(void* ptr) {
|
||||
emutls_address_array* array = (emutls_address_array*)ptr;
|
||||
uintptr_t i;
|
||||
for (i = 0; i < array->size; ++i) {
|
||||
if (array->data[i])
|
||||
emutls_memalign_free(array->data[i]);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void emutls_init(void) {
|
||||
if (pthread_key_create(&emutls_pthread_key, emutls_key_destructor) != 0)
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Returns control->object.index; set index if not allocated yet. */
|
||||
static __inline uintptr_t emutls_get_index(__emutls_control *control) {
|
||||
uintptr_t index = __atomic_load_n(&control->object.index, __ATOMIC_ACQUIRE);
|
||||
if (!index) {
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
pthread_once(&once, emutls_init);
|
||||
pthread_mutex_lock(&emutls_mutex);
|
||||
index = control->object.index;
|
||||
if (!index) {
|
||||
index = ++emutls_num_object;
|
||||
__atomic_store_n(&control->object.index, index, __ATOMIC_RELEASE);
|
||||
}
|
||||
pthread_mutex_unlock(&emutls_mutex);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/* Updates newly allocated thread local emutls_address_array. */
|
||||
static __inline void emutls_check_array_set_size(emutls_address_array *array,
|
||||
uintptr_t size) {
|
||||
if (array == NULL)
|
||||
abort();
|
||||
array->size = size;
|
||||
pthread_setspecific(emutls_pthread_key, (void*)array);
|
||||
}
|
||||
|
||||
/* Returns the new 'data' array size, number of elements,
|
||||
* which must be no smaller than the given index.
|
||||
*/
|
||||
static __inline uintptr_t emutls_new_data_array_size(uintptr_t index) {
|
||||
/* Need to allocate emutls_address_array with one extra slot
|
||||
* to store the data array size.
|
||||
* Round up the emutls_address_array size to multiple of 16.
|
||||
*/
|
||||
return ((index + 1 + 15) & ~((uintptr_t)15)) - 1;
|
||||
}
|
||||
|
||||
/* Returns the thread local emutls_address_array.
|
||||
* Extends its size if necessary to hold address at index.
|
||||
*/
|
||||
static __inline emutls_address_array *
|
||||
emutls_get_address_array(uintptr_t index) {
|
||||
emutls_address_array* array = pthread_getspecific(emutls_pthread_key);
|
||||
if (array == NULL) {
|
||||
uintptr_t new_size = emutls_new_data_array_size(index);
|
||||
array = calloc(new_size + 1, sizeof(void*));
|
||||
emutls_check_array_set_size(array, new_size);
|
||||
} else if (index > array->size) {
|
||||
uintptr_t orig_size = array->size;
|
||||
uintptr_t new_size = emutls_new_data_array_size(index);
|
||||
array = realloc(array, (new_size + 1) * sizeof(void*));
|
||||
if (array)
|
||||
memset(array->data + orig_size, 0,
|
||||
(new_size - orig_size) * sizeof(void*));
|
||||
emutls_check_array_set_size(array, new_size);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
void* __emutls_get_address(__emutls_control* control) {
|
||||
uintptr_t index = emutls_get_index(control);
|
||||
emutls_address_array* array = emutls_get_address_array(index);
|
||||
if (array->data[index - 1] == NULL)
|
||||
array->data[index - 1] = emutls_allocate_object(control);
|
||||
return array->data[index - 1];
|
||||
}
|
@ -21,8 +21,8 @@
|
||||
#define HAVE_SYSCONF 1
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#ifndef __APPLE__
|
||||
#include <unistd.h>
|
||||
|
@ -12,9 +12,11 @@
|
||||
#define DST_SINGLE
|
||||
#include "fp_extend_impl.inc"
|
||||
|
||||
ARM_EABI_FNALIAS(h2f, extendhfsf2)
|
||||
|
||||
// Use a forwarding definition and noinline to implement a poor man's alias,
|
||||
// as there isn't a good cross-platform way of defining one.
|
||||
COMPILER_RT_ABI __attribute__((noinline)) float __extendhfsf2(uint16_t a) {
|
||||
COMPILER_RT_ABI NOINLINE float __extendhfsf2(uint16_t a) {
|
||||
return __extendXfYf2__(a);
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,8 @@ COMPILER_RT_ABI du_int
|
||||
__fixunsdfdi(double a)
|
||||
{
|
||||
if (a <= 0.0) return 0;
|
||||
su_int high = a/0x1p32f;
|
||||
su_int low = a - (double)high*0x1p32f;
|
||||
su_int high = a / 4294967296.f; /* a / 0x1p32f; */
|
||||
su_int low = a - (double)high * 4294967296.f; /* high * 0x1p32f; */
|
||||
return ((du_int)high << 32) | low;
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ __fixunssfdi(float a)
|
||||
{
|
||||
if (a <= 0.0f) return 0;
|
||||
double da = a;
|
||||
su_int high = da/0x1p32f;
|
||||
su_int low = da - (double)high*0x1p32f;
|
||||
su_int high = da / 4294967296.f; /* da / 0x1p32f; */
|
||||
su_int low = da - (double)high * 4294967296.f; /* high * 0x1p32f; */
|
||||
return ((du_int)high << 32) | low;
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,8 @@ ARM_EABI_FNALIAS(l2d, floatdidf)
|
||||
COMPILER_RT_ABI double
|
||||
__floatdidf(di_int a)
|
||||
{
|
||||
static const double twop52 = 0x1.0p52;
|
||||
static const double twop32 = 0x1.0p32;
|
||||
static const double twop52 = 4503599627370496.0; // 0x1.0p52
|
||||
static const double twop32 = 4294967296.0; // 0x1.0p32
|
||||
|
||||
union { int64_t x; double d; } low = { .d = twop52 };
|
||||
|
||||
|
@ -27,19 +27,17 @@ COMPILER_RT_ABI fp_t __floatditf(di_int a) {
|
||||
|
||||
// All other cases begin by extracting the sign and absolute value of a
|
||||
rep_t sign = 0;
|
||||
unsigned aAbs = (unsigned)a;
|
||||
du_int aAbs = (du_int)a;
|
||||
if (a < 0) {
|
||||
sign = signBit;
|
||||
aAbs += 0x80000000;
|
||||
aAbs = ~(du_int)a + 1U;
|
||||
}
|
||||
|
||||
// Exponent of (fp_t)a is the width of abs(a).
|
||||
const int exponent = (aWidth - 1) - __builtin_clzll(a);
|
||||
const int exponent = (aWidth - 1) - __builtin_clzll(aAbs);
|
||||
rep_t result;
|
||||
|
||||
// Shift a into the significand field and clear the implicit bit. Extra
|
||||
// cast to unsigned int is necessary to get the correct behavior for
|
||||
// the input INT_MIN.
|
||||
// Shift a into the significand field, rounding if it is a right-shift
|
||||
const int shift = significandBits - exponent;
|
||||
result = (rep_t)aAbs << shift ^ implicitBit;
|
||||
|
||||
|
@ -30,16 +30,14 @@ COMPILER_RT_ABI fp_t __floatsitf(int a) {
|
||||
unsigned aAbs = (unsigned)a;
|
||||
if (a < 0) {
|
||||
sign = signBit;
|
||||
aAbs += 0x80000000;
|
||||
aAbs = ~(unsigned)a + 1U;
|
||||
}
|
||||
|
||||
// Exponent of (fp_t)a is the width of abs(a).
|
||||
const int exponent = (aWidth - 1) - __builtin_clz(a);
|
||||
const int exponent = (aWidth - 1) - __builtin_clz(aAbs);
|
||||
rep_t result;
|
||||
|
||||
// Shift a into the significand field and clear the implicit bit. Extra
|
||||
// cast to unsigned int is necessary to get the correct behavior for
|
||||
// the input INT_MIN.
|
||||
// Shift a into the significand field and clear the implicit bit.
|
||||
const int shift = significandBits - exponent;
|
||||
result = (rep_t)aAbs << shift ^ implicitBit;
|
||||
|
||||
|
@ -32,9 +32,9 @@ ARM_EABI_FNALIAS(ul2d, floatundidf)
|
||||
COMPILER_RT_ABI double
|
||||
__floatundidf(du_int a)
|
||||
{
|
||||
static const double twop52 = 0x1.0p52;
|
||||
static const double twop84 = 0x1.0p84;
|
||||
static const double twop84_plus_twop52 = 0x1.00000001p84;
|
||||
static const double twop52 = 4503599627370496.0; // 0x1.0p52
|
||||
static const double twop84 = 19342813113834066795298816.0; // 0x1.0p84
|
||||
static const double twop84_plus_twop52 = 19342813118337666422669312.0; // 0x1.00000001p84
|
||||
|
||||
union { uint64_t x; double d; } high = { .d = twop84 };
|
||||
union { uint64_t x; double d; } low = { .d = twop52 };
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "fp_lib.h"
|
||||
|
||||
static inline fp_t __addXf3__(fp_t a, fp_t b) {
|
||||
static __inline fp_t __addXf3__(fp_t a, fp_t b) {
|
||||
rep_t aRep = toRep(a);
|
||||
rep_t bRep = toRep(b);
|
||||
const rep_t aAbs = aRep & absMask;
|
||||
|
@ -28,7 +28,7 @@ typedef double src_t;
|
||||
typedef uint64_t src_rep_t;
|
||||
#define SRC_REP_C UINT64_C
|
||||
static const int srcSigBits = 52;
|
||||
static inline int src_rep_t_clz(src_rep_t a) {
|
||||
static __inline int src_rep_t_clz(src_rep_t a) {
|
||||
#if defined __LP64__
|
||||
return __builtin_clzl(a);
|
||||
#else
|
||||
@ -75,12 +75,12 @@ static const int dstSigBits = 112;
|
||||
// End of specialization parameters. Two helper routines for conversion to and
|
||||
// from the representation of floating-point data as integer values follow.
|
||||
|
||||
static inline src_rep_t srcToRep(src_t x) {
|
||||
static __inline src_rep_t srcToRep(src_t x) {
|
||||
const union { src_t f; src_rep_t i; } rep = {.f = x};
|
||||
return rep.i;
|
||||
}
|
||||
|
||||
static inline dst_t dstFromRep(dst_rep_t x) {
|
||||
static __inline dst_t dstFromRep(dst_rep_t x) {
|
||||
const union { dst_t f; dst_rep_t i; } rep = {.i = x};
|
||||
return rep.f;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
#include "fp_extend.h"
|
||||
|
||||
static inline dst_t __extendXfYf2__(src_t a) {
|
||||
static __inline dst_t __extendXfYf2__(src_t a) {
|
||||
// Various constants whose values follow from the type parameters.
|
||||
// Any reasonable optimizer will fold and propagate all of these.
|
||||
const int srcBits = sizeof(src_t)*CHAR_BIT;
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "fp_lib.h"
|
||||
|
||||
static inline fixint_t __fixint(fp_t a) {
|
||||
static __inline fixint_t __fixint(fp_t a) {
|
||||
const fixint_t fixint_max = (fixint_t)((~(fixuint_t)0) / 2);
|
||||
const fixint_t fixint_min = -fixint_max - 1;
|
||||
// Break a into sign, exponent, significand
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "fp_lib.h"
|
||||
|
||||
static inline fixuint_t __fixuint(fp_t a) {
|
||||
static __inline fixuint_t __fixuint(fp_t a) {
|
||||
// Break a into sign, exponent, significand
|
||||
const rep_t aRep = toRep(a);
|
||||
const rep_t aAbs = aRep & absMask;
|
||||
@ -27,7 +27,7 @@ static inline fixuint_t __fixuint(fp_t a) {
|
||||
return 0;
|
||||
|
||||
// If the value is too large for the integer type, saturate.
|
||||
if ((unsigned)exponent > sizeof(fixuint_t) * CHAR_BIT)
|
||||
if ((unsigned)exponent >= sizeof(fixuint_t) * CHAR_BIT)
|
||||
return ~(fixuint_t)0;
|
||||
|
||||
// If 0 <= exponent < significandBits, right shift to get the result.
|
||||
|
@ -46,12 +46,12 @@ typedef float fp_t;
|
||||
#define REP_C UINT32_C
|
||||
#define significandBits 23
|
||||
|
||||
static inline int rep_clz(rep_t a) {
|
||||
static __inline int rep_clz(rep_t a) {
|
||||
return __builtin_clz(a);
|
||||
}
|
||||
|
||||
// 32x32 --> 64 bit multiply
|
||||
static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
const uint64_t product = (uint64_t)a*b;
|
||||
*hi = product >> 32;
|
||||
*lo = product;
|
||||
@ -66,7 +66,7 @@ typedef double fp_t;
|
||||
#define REP_C UINT64_C
|
||||
#define significandBits 52
|
||||
|
||||
static inline int rep_clz(rep_t a) {
|
||||
static __inline int rep_clz(rep_t a) {
|
||||
#if defined __LP64__
|
||||
return __builtin_clzl(a);
|
||||
#else
|
||||
@ -83,7 +83,7 @@ static inline int rep_clz(rep_t a) {
|
||||
// 64x64 -> 128 wide multiply for platforms that don't have such an operation;
|
||||
// many 64-bit platforms have this operation, but they tend to have hardware
|
||||
// floating-point, so we don't bother with a special case for them here.
|
||||
static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
// Each of the component 32x32 -> 64 products
|
||||
const uint64_t plolo = loWord(a) * loWord(b);
|
||||
const uint64_t plohi = loWord(a) * hiWord(b);
|
||||
@ -112,7 +112,7 @@ typedef long double fp_t;
|
||||
// 128-bit integer, we let the constant be casted to 128-bit integer
|
||||
#define significandBits 112
|
||||
|
||||
static inline int rep_clz(rep_t a) {
|
||||
static __inline int rep_clz(rep_t a) {
|
||||
const union
|
||||
{
|
||||
__uint128_t ll;
|
||||
@ -148,7 +148,7 @@ static inline int rep_clz(rep_t a) {
|
||||
// 128x128 -> 256 wide multiply for platforms that don't have such an operation;
|
||||
// many 64-bit platforms have this operation, but they tend to have hardware
|
||||
// floating-point, so we don't bother with a special case for them here.
|
||||
static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
static __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
|
||||
const uint64_t product11 = Word_1(a) * Word_1(b);
|
||||
const uint64_t product12 = Word_1(a) * Word_2(b);
|
||||
@ -228,28 +228,28 @@ static inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {
|
||||
#define quietBit (implicitBit >> 1)
|
||||
#define qnanRep (exponentMask | quietBit)
|
||||
|
||||
static inline rep_t toRep(fp_t x) {
|
||||
static __inline rep_t toRep(fp_t x) {
|
||||
const union { fp_t f; rep_t i; } rep = {.f = x};
|
||||
return rep.i;
|
||||
}
|
||||
|
||||
static inline fp_t fromRep(rep_t x) {
|
||||
static __inline fp_t fromRep(rep_t x) {
|
||||
const union { fp_t f; rep_t i; } rep = {.i = x};
|
||||
return rep.f;
|
||||
}
|
||||
|
||||
static inline int normalize(rep_t *significand) {
|
||||
static __inline int normalize(rep_t *significand) {
|
||||
const int shift = rep_clz(*significand) - rep_clz(implicitBit);
|
||||
*significand <<= shift;
|
||||
return 1 - shift;
|
||||
}
|
||||
|
||||
static inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) {
|
||||
static __inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) {
|
||||
*hi = *hi << count | *lo >> (typeWidth - count);
|
||||
*lo = *lo << count;
|
||||
}
|
||||
|
||||
static inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int count) {
|
||||
static __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo, unsigned int count) {
|
||||
if (count < typeWidth) {
|
||||
const bool sticky = *lo << (typeWidth - count);
|
||||
*lo = *hi << (typeWidth - count) | *lo >> count | sticky;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user