diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2015-09-06 18:41:23 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2015-09-06 18:41:23 +0000 |
commit | f31bcc68c72371a2bf63aead9f3373a1ff2053b6 (patch) | |
tree | b259e5d585da0f8cde9579939a74d5ef44c72abd | |
parent | cd2dd3df15523e2be8d2bbace27641d6ac9fa40d (diff) |
Notes
785 files changed, 13345 insertions, 5375 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f3485853f0f2..cae5981b2b46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,6 +139,13 @@ string(REPLACE "-" ";" TARGET_TRIPLE_LIST ${COMPILER_RT_TEST_TARGET_TRIPLE}) list(GET TARGET_TRIPLE_LIST 0 COMPILER_RT_TEST_TARGET_ARCH) list(GET TARGET_TRIPLE_LIST 1 COMPILER_RT_TEST_TARGET_OS) list(GET TARGET_TRIPLE_LIST 2 COMPILER_RT_TEST_TARGET_ABI) +# Determine if test target triple is specified explicitly, and doesn't match the +# default. +if(NOT COMPILER_RT_TEST_TARGET_TRIPLE STREQUAL TARGET_TRIPLE) + set(COMPILER_RT_HAS_EXPLICIT_TEST_TARGET_TRIPLE TRUE) +else() + set(COMPILER_RT_HAS_EXPLICIT_TEST_TARGET_TRIPLE FALSE) +endif() if ("${COMPILER_RT_TEST_TARGET_ABI}" STREQUAL "androideabi") set(ANDROID 1) @@ -160,13 +167,11 @@ include(CompilerRTUtils) set(COMPILER_RT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(COMPILER_RT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -# Setup custom SDK sysroots. -set(COMPILER_RT_LINUX_SDK_SYSROOT ${COMPILER_RT_SOURCE_DIR}/SDKs/linux) # We support running instrumented tests when we're not cross compiling # and target a UNIX-like system or Windows. # We can run tests on Android even when we are cross-compiling. -if(("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND (UNIX OR MSVC)) OR ANDROID +if(("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND (UNIX OR WIN32)) OR ANDROID OR COMPILER_RT_EMULATOR) option(COMPILER_RT_CAN_EXECUTE_TESTS "Can we execute instrumented tests" ON) else() @@ -209,22 +214,22 @@ append_list_if(COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG -fno-exceptions SANITIZER_COM append_list_if(COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG -fomit-frame-pointer SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FUNWIND_TABLES_FLAG -funwind-tables SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG -fno-stack-protector SANITIZER_COMMON_CFLAGS) +append_list_if(COMPILER_RT_HAS_FNO_SANITIZE_SAFE_STACK_FLAG -fno-sanitize=safe-stack SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG -fvisibility=hidden SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG -fno-function-sections SANITIZER_COMMON_CFLAGS) append_list_if(COMPILER_RT_HAS_FNO_LTO_FLAG -fno-lto SANITIZER_COMMON_CFLAGS) if(MSVC) - # Replace the /MD[d] flags with /MT. + # Replace the /M[DT][d] flags with /MT, and strip any definitions of _DEBUG, + # which cause definition mismatches at link time. # FIXME: In fact, sanitizers should support both /MT and /MD, see PR20214. if(COMPILER_RT_HAS_MT_FLAG) foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) - if(${flag_var} MATCHES "/MD") - string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") - elseif(${flag_var} MATCHES "/MDd") - string(REGEX REPLACE "/MDd" "/MT" ${flag_var} "${${flag_var}}") - endif() + string(REGEX REPLACE "/M[DT]d" "/MT" ${flag_var} "${${flag_var}}") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + string(REGEX REPLACE "/D_DEBUG" "" ${flag_var} "${${flag_var}}") endforeach() endif() append_list_if(COMPILER_RT_HAS_Oy_FLAG /Oy- SANITIZER_COMMON_CFLAGS) @@ -240,12 +245,13 @@ if(NOT COMPILER_RT_DEBUG AND NOT MSVC) endif() # Determine if we should restrict stack frame sizes. -# Stack frames on PowerPC and in debug biuld can be much larger than +# Stack frames on PowerPC and Mips and in debug biuld can be much larger than # anticipated. # FIXME: Fix all sanitizers and add -Wframe-larger-than to # SANITIZER_COMMON_FLAGS if(COMPILER_RT_HAS_WFRAME_LARGER_THAN_FLAG AND NOT COMPILER_RT_DEBUG - AND NOT ${LLVM_NATIVE_ARCH} STREQUAL "PowerPC") + AND NOT ${LLVM_NATIVE_ARCH} STREQUAL "PowerPC" + AND NOT ${LLVM_NATIVE_ARCH} STREQUAL "Mips") set(SANITIZER_LIMIT_FRAME_SIZE TRUE) else() set(SANITIZER_LIMIT_FRAME_SIZE FALSE) @@ -292,26 +298,51 @@ if(APPLE) find_darwin_sdk_dir(OSX_SDK_DIR macosx) find_darwin_sdk_dir(IOSSIM_SDK_DIR iphonesimulator) - string(REGEX MATCH "-mmacosx-version-min=" + set(SANITIZER_COMMON_SUPPORTED_OS osx) + string(REGEX MATCH "-mmacosx-version-min=([.0-9]+)" MACOSX_VERSION_MIN_FLAG "${CMAKE_CXX_FLAGS}") - set(SANITIZER_COMMON_SUPPORTED_DARWIN_OS osx) - if (IOSSIM_SDK_DIR AND NOT MACOSX_VERSION_MIN_FLAG) - list(APPEND SANITIZER_COMMON_SUPPORTED_DARWIN_OS iossim) + if(MACOSX_VERSION_MIN_FLAG) + set(SANITIZER_MIN_OSX_VERSION "${CMAKE_MATCH_1}") + elseif(CMAKE_OSX_DEPLOYMENT_TARGET) + set(SANITIZER_MIN_OSX_VERSION ${CMAKE_OSX_DEPLOYMENT_TARGET}) + else() + set(SANITIZER_MIN_OSX_VERSION 10.9) + if(IOSSIM_SDK_DIR) + list(APPEND SANITIZER_COMMON_SUPPORTED_OS iossim) + endif() + endif() + if(SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.7") + message(FATAL_ERROR "Too old OS X version: ${SANITIZER_MIN_OSX_VERSION}") endif() - set(SANITIZER_MIN_OSX_VERSION 10.7) - set(CMAKE_OSX_DEPLOYMENT_TARGET "") # We're setting the flag manually below. + set(CMAKE_OSX_DEPLOYMENT_TARGET "") # We evaluate target OS X version above. set(DARWIN_osx_CFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION} - -isysroot ${OSX_SDK_DIR} -stdlib=libc++) + -stdlib=libc++) set(DARWIN_iossim_CFLAGS + -stdlib=libc++ -mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR}) set(DARWIN_osx_LINKFLAGS -mmacosx-version-min=${SANITIZER_MIN_OSX_VERSION} - -isysroot ${OSX_SDK_DIR} -stdlib=libc++) + -stdlib=libc++ -lc++ -lc++abi) set(DARWIN_iossim_LINKFLAGS + -stdlib=libc++ -lc++ -lc++abi -Wl,-ios_simulator_version_min,7.0.0 -mios-simulator-version-min=7.0 -isysroot ${IOSSIM_SDK_DIR}) + + if(OSX_SDK_DIR) + list(APPEND DARWIN_osx_CFLAGS -isysroot ${OSX_SDK_DIR}) + list(APPEND DARWIN_osx_LINKFLAGS -isysroot ${OSX_SDK_DIR}) + endif() +endif() + +if(APPLE AND SANITIZER_MIN_OSX_VERSION VERSION_LESS "10.9") + # Mac OS X prior to 10.9 had problems with exporting symbols from + # libc++/libc++abi. + set(SANITIZER_CAN_USE_CXXABI FALSE) +else() + set(SANITIZER_CAN_USE_CXXABI TRUE) endif() +pythonize_bool(SANITIZER_CAN_USE_CXXABI) add_subdirectory(include) diff --git a/LICENSE.TXT b/LICENSE.TXT index aee8347b0c35..aa4115e2a790 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -14,7 +14,7 @@ Full text of the relevant licenses is included below. University of Illinois/NCSA Open Source License -Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT +Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT All rights reserved. @@ -55,7 +55,7 @@ SOFTWARE. ============================================================================== -Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT +Copyright (c) 2009-2015 by the contributors listed in CREDITS.TXT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/SDKs/README.txt b/SDKs/README.txt deleted file mode 100644 index b95575e8c9a1..000000000000 --- a/SDKs/README.txt +++ /dev/null @@ -1,9 +0,0 @@ -It is often convenient to be able to build compiler-rt libraries for a certain -platform without having a full SDK or development environment installed. - -This makes it easy for users to build a compiler which can target a number of -different platforms, without having to actively maintain full development -environments for those platforms. - -Since compiler-rt's libraries typically have minimal interaction with the -system, we achieve this by stubbing out the SDKs of certain platforms. diff --git a/SDKs/linux/README.txt b/SDKs/linux/README.txt deleted file mode 100644 index aa0604af7992..000000000000 --- a/SDKs/linux/README.txt +++ /dev/null @@ -1,2 +0,0 @@ -This is a stub SDK for Linux. Currently, this has only been tested on i386 and -x86_64 using the Clang compiler. diff --git a/SDKs/linux/usr/include/endian.h b/SDKs/linux/usr/include/endian.h deleted file mode 100644 index 95528db157a9..000000000000 --- a/SDKs/linux/usr/include/endian.h +++ /dev/null @@ -1,29 +0,0 @@ -/* ===-- endian.h - stub SDK header for compiler-rt -------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __ENDIAN_H__ -#define __ENDIAN_H__ - -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 - -#if defined(__LITTLE_ENDIAN__) || defined(__ORDER_LITTLE_ENDIAN__) -#define __BYTE_ORDER __LITTLE_ENDIAN -#else -#define __BYTE_ORDER __BIG_ENDIAN -#endif - -#endif /* __ENDIAN_H__ */ diff --git a/SDKs/linux/usr/include/fcntl.h b/SDKs/linux/usr/include/fcntl.h deleted file mode 100644 index a5f91e3a5bc6..000000000000 --- a/SDKs/linux/usr/include/fcntl.h +++ /dev/null @@ -1,17 +0,0 @@ -/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#include <sys/fcntl.h> diff --git a/SDKs/linux/usr/include/limits.h b/SDKs/linux/usr/include/limits.h deleted file mode 100644 index 5495a784f12f..000000000000 --- a/SDKs/linux/usr/include/limits.h +++ /dev/null @@ -1,23 +0,0 @@ -/* ===-- limits.h - stub SDK header for compiler-rt -------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __LIMITS_H__ -#define __LIMITS_H__ - -/* This is only here as a landing pad for the include_next from the compiler's - built-in limits.h. */ - -#endif /* __LIMITS_H__ */ diff --git a/SDKs/linux/usr/include/stdio.h b/SDKs/linux/usr/include/stdio.h deleted file mode 100644 index e2161daa4680..000000000000 --- a/SDKs/linux/usr/include/stdio.h +++ /dev/null @@ -1,44 +0,0 @@ -/* ===-- stdio.h - stub SDK header for compiler-rt --------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __STDIO_H__ -#define __STDIO_H__ - -typedef __SIZE_TYPE__ size_t; - -struct _IO_FILE; -typedef struct _IO_FILE FILE; - -extern struct _IO_FILE *stdin; -extern struct _IO_FILE *stdout; -extern struct _IO_FILE *stderr; - -#define SEEK_SET 0 /* set file offset to offset */ -#define SEEK_CUR 1 /* set file offset to current plus offset */ -#define SEEK_END 2 /* set file offset to EOF plus offset */ - -extern int fclose(FILE *); -extern int fflush(FILE *); -extern FILE *fopen(const char * restrict, const char * restrict); -extern FILE *fdopen(int, const char * restrict); -extern int fprintf(FILE * restrict, const char * restrict, ...); -extern int fputc(int, FILE *); -extern size_t fwrite(const void * restrict, size_t, size_t, FILE * restrict); -extern size_t fread(void * restrict, size_t, size_t, FILE * restrict); -extern long ftell(FILE *); -extern int fseek(FILE *, long, int); - -#endif /* __STDIO_H__ */ diff --git a/SDKs/linux/usr/include/stdlib.h b/SDKs/linux/usr/include/stdlib.h deleted file mode 100644 index 966b29db6e10..000000000000 --- a/SDKs/linux/usr/include/stdlib.h +++ /dev/null @@ -1,36 +0,0 @@ -/* ===-- stdlib.h - stub SDK header for compiler-rt -------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __STDLIB_H__ -#define __STDLIB_H__ - -#define NULL ((void *)0) - -typedef __SIZE_TYPE__ size_t; - -void abort(void) __attribute__((__nothrow__)) __attribute__((__noreturn__)); -int atexit(void (*)(void)) __attribute__((__nothrow__)); -int atoi(const char *) __attribute__((__nothrow__)); -void free(void *) __attribute__((__nothrow__)); -char *getenv(const char *) __attribute__((__nothrow__)) - __attribute__((__nonnull__(1))); - __attribute__((__warn_unused_result__)); -void *malloc(size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) - __attribute__((__warn_unused_result__)); -void *realloc(void *, size_t) __attribute__((__nothrow__)) __attribute((__malloc__)) - __attribute__((__warn_unused_result__)); - -#endif /* __STDLIB_H__ */ diff --git a/SDKs/linux/usr/include/string.h b/SDKs/linux/usr/include/string.h deleted file mode 100644 index c7da1f57ba57..000000000000 --- a/SDKs/linux/usr/include/string.h +++ /dev/null @@ -1,31 +0,0 @@ -/* ===-- string.h - stub SDK header for compiler-rt -------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __STRING_H__ -#define __STRING_H__ - -typedef __SIZE_TYPE__ size_t; - -int memcmp(const void *, const void *, size_t); -void *memcpy(void *, const void *, size_t); -void *memset(void *, int, size_t); -char *strcat(char *, const char *); -char *strcpy(char *, const char *); -char *strdup(const char *); -size_t strlen(const char *); -char *strncpy(char *, const char *, size_t); - -#endif /* __STRING_H__ */ diff --git a/SDKs/linux/usr/include/sys/fcntl.h b/SDKs/linux/usr/include/sys/fcntl.h deleted file mode 100644 index 1512bf9b4e55..000000000000 --- a/SDKs/linux/usr/include/sys/fcntl.h +++ /dev/null @@ -1,29 +0,0 @@ -/* ===-- fcntl.h - stub SDK header for compiler-rt --------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef _SYS_FCNTL_H_ -#define _SYS_FCNTL_H_ - -#define O_RDONLY 0x0000 -#define O_WRONLY 0x0001 -#define O_RDWR 0x0002 -#define O_ACCMODE 0x0003 - -#define O_CREAT 0x0200 - -int open(const char *, int, ...); - -#endif /* _SYS_FCNTL_H_ */ diff --git a/SDKs/linux/usr/include/sys/mman.h b/SDKs/linux/usr/include/sys/mman.h deleted file mode 100644 index bfb7f8bb02de..000000000000 --- a/SDKs/linux/usr/include/sys/mman.h +++ /dev/null @@ -1,47 +0,0 @@ -/* ===-- limits.h - stub SDK header for compiler-rt -------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __SYS_MMAN_H__ -#define __SYS_MMAN_H__ - -typedef __SIZE_TYPE__ size_t; - -#define PROT_NONE 0x00 -#define PROT_READ 0x01 -#define PROT_WRITE 0x02 -#define PROT_EXEC 0x04 - -#define MAP_SHARED 0x0001 -#define MAP_PRIVATE 0x0002 - -#define MAP_FILE 0x0000 -#define MAP_ANON 0x1000 - -#define MS_ASYNC 0x0001 -#define MS_INVALIDATE 0x0002 -#define MS_SYNC 0x0010 - -extern void *mmap(void *addr, size_t len, int prot, int flags, int fd, - long long offset) - __attribute__((__nothrow__)); -extern int munmap(void *addr, size_t len) - __attribute__((__nothrow__)); -extern int msync(void *addr, size_t len, int flags) - __attribute__((__nothrow__)); -extern int mprotect (void *__addr, size_t __len, int __prot) - __attribute__((__nothrow__)); - -#endif /* __SYS_MMAN_H__ */ diff --git a/SDKs/linux/usr/include/sys/stat.h b/SDKs/linux/usr/include/sys/stat.h deleted file mode 100644 index 0449fddb0665..000000000000 --- a/SDKs/linux/usr/include/sys/stat.h +++ /dev/null @@ -1,24 +0,0 @@ -/* ===-- stat.h - stub SDK header for compiler-rt ---------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __SYS_STAT_H__ -#define __SYS_STAT_H__ - -typedef unsigned int mode_t; - -int mkdir(const char *, mode_t); - -#endif /* __SYS_STAT_H__ */ diff --git a/SDKs/linux/usr/include/sys/types.h b/SDKs/linux/usr/include/sys/types.h deleted file mode 100644 index 10e74bbd0b02..000000000000 --- a/SDKs/linux/usr/include/sys/types.h +++ /dev/null @@ -1,20 +0,0 @@ -/* ===-- stat.h - stub SDK header for compiler-rt ---------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __SYS_TYPES_H__ -#define __SYS_TYPES_H__ - -#endif /* __SYS_TYPES_H__ */ diff --git a/SDKs/linux/usr/include/unistd.h b/SDKs/linux/usr/include/unistd.h deleted file mode 100644 index 773b081d4516..000000000000 --- a/SDKs/linux/usr/include/unistd.h +++ /dev/null @@ -1,26 +0,0 @@ -/* ===-- unistd.h - stub SDK header for compiler-rt -------------------------=== - * - * 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 is a stub SDK header file. This file is not part of the interface of - * this library nor an official version of the appropriate SDK header. It is - * intended only to stub the features of this header required by compiler-rt. - * - * ===-----------------------------------------------------------------------=== - */ - -#ifndef __UNISTD_H__ -#define __UNISTD_H__ - -enum { - _SC_PAGESIZE = 30 -}; - -extern long int sysconf (int __name) __attribute__ ((__nothrow__)); - -#endif /* __UNISTD_H__ */ diff --git a/cmake/Modules/AddCompilerRT.cmake b/cmake/Modules/AddCompilerRT.cmake index a7782a194847..5ea313ba7162 100644 --- a/cmake/Modules/AddCompilerRT.cmake +++ b/cmake/Modules/AddCompilerRT.cmake @@ -1,42 +1,48 @@ include(AddLLVM) include(ExternalProject) -include(LLVMParseArguments) include(CompilerRTUtils) -# Tries to add "object library" target for a given architecture -# with name "<name>.<arch>" if architecture can be targeted. -# add_compiler_rt_object_library(<name> <arch> -# SOURCES <source files> -# CFLAGS <compile flags> -# DEFS <compile definitions>) -macro(add_compiler_rt_object_library name arch) - if(CAN_TARGET_${arch}) - parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) - add_library(${name}.${arch} OBJECT ${LIB_SOURCES}) - set_target_compile_flags(${name}.${arch} - ${CMAKE_CXX_FLAGS} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) - set_property(TARGET ${name}.${arch} APPEND PROPERTY - COMPILE_DEFINITIONS ${LIB_DEFS}) +# Tries to add an "object library" target for a given list of OSs and/or +# architectures with name "<name>.<arch>" for non-Darwin platforms if +# architecture can be targeted, and "<name>.<os>" for Darwin platforms. +# add_compiler_rt_object_libraries(<name> +# OS <os names> +# ARCHS <architectures> +# SOURCES <source files> +# CFLAGS <compile flags> +# DEFS <compile definitions>) +function(add_compiler_rt_object_libraries name) + cmake_parse_arguments(LIB "" "" "OS;ARCHS;SOURCES;CFLAGS;DEFS" ${ARGN}) + set(libnames) + if(APPLE) + foreach(os ${LIB_OS}) + set(libname "${name}.${os}") + set(libnames ${libnames} ${libname}) + set(extra_cflags_${libname} ${DARWIN_${os}_CFLAGS}) + endforeach() else() - message(FATAL_ERROR "Archtecture ${arch} can't be targeted") + foreach(arch ${LIB_ARCHS}) + set(libname "${name}.${arch}") + set(libnames ${libnames} ${libname}) + set(extra_cflags_${libname} ${TARGET_${arch}_CFLAGS}) + if(NOT CAN_TARGET_${arch}) + message(FATAL_ERROR "Archtecture ${arch} can't be targeted") + return() + endif() + endforeach() endif() -endmacro() - -# Same as above, but adds universal osx library for either OSX or iOS simulator -# with name "<name>.<os>" targeting multiple architectures. -# add_compiler_rt_darwin_object_library(<name> <os> ARCH <architectures> -# SOURCES <source files> -# CFLAGS <compile flags> -# DEFS <compile definitions>) -macro(add_compiler_rt_darwin_object_library name os) - parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) - set(libname "${name}.${os}") - add_library(${libname} OBJECT ${LIB_SOURCES}) - set_target_compile_flags(${libname} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS}) - set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCH}") - set_property(TARGET ${libname} APPEND PROPERTY - COMPILE_DEFINITIONS ${LIB_DEFS}) -endmacro() + + foreach(libname ${libnames}) + add_library(${libname} OBJECT ${LIB_SOURCES}) + set_target_compile_flags(${libname} + ${CMAKE_CXX_FLAGS} ${extra_cflags_${libname}} ${LIB_CFLAGS}) + set_property(TARGET ${libname} APPEND PROPERTY + COMPILE_DEFINITIONS ${LIB_DEFS}) + if(APPLE) + set_target_properties(${libname} PROPERTIES OSX_ARCHITECTURES "${LIB_ARCHS}") + endif() + endforeach() +endfunction() # Adds static or shared runtime for a given architecture and puts it in the # proper directory in the build and install trees. @@ -47,13 +53,13 @@ endmacro() # OUTPUT_NAME <output library name>) macro(add_compiler_rt_runtime name arch type) if(CAN_TARGET_${arch}) - parse_arguments(LIB "SOURCES;CFLAGS;DEFS;OUTPUT_NAME" "" ${ARGN}) + cmake_parse_arguments(LIB "" "OUTPUT_NAME" "SOURCES;CFLAGS;LINKFLAGS;DEFS" ${ARGN}) add_library(${name} ${type} ${LIB_SOURCES}) # Setup compile flags and definitions. set_target_compile_flags(${name} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) set_target_link_flags(${name} - ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS} ${LIB_LINKFLAGS}) set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) # Setup correct output directory in the build tree. @@ -80,18 +86,18 @@ endmacro() # Same as add_compiler_rt_runtime(... STATIC), but creates a universal library # for several architectures. -# add_compiler_rt_osx_static_runtime(<name> ARCH <architectures> +# add_compiler_rt_osx_static_runtime(<name> ARCHS <architectures> # SOURCES <source files> # CFLAGS <compile flags> # DEFS <compile definitions>) macro(add_compiler_rt_osx_static_runtime name) - parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS" "" ${ARGN}) + cmake_parse_arguments(LIB "" "" "ARCHS;SOURCES;CFLAGS;DEFS" ${ARGN}) add_library(${name} STATIC ${LIB_SOURCES}) set_target_compile_flags(${name} ${LIB_CFLAGS}) set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) set_target_properties(${name} PROPERTIES - OSX_ARCHITECTURES "${LIB_ARCH}" + OSX_ARCHITECTURES "${LIB_ARCHS}" ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) install(TARGETS ${name} ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) @@ -100,20 +106,20 @@ endmacro() # Adds dynamic runtime library on osx/iossim, which supports multiple # architectures. # add_compiler_rt_darwin_dynamic_runtime(<name> <os> -# ARCH <architectures> +# ARCHS <architectures> # SOURCES <source files> # CFLAGS <compile flags> # DEFS <compile definitions> # LINKFLAGS <link flags>) macro(add_compiler_rt_darwin_dynamic_runtime name os) - parse_arguments(LIB "ARCH;SOURCES;CFLAGS;DEFS;LINKFLAGS" "" ${ARGN}) + cmake_parse_arguments(LIB "" "" "ARCHS;SOURCES;CFLAGS;DEFS;LINKFLAGS" ${ARGN}) add_library(${name} SHARED ${LIB_SOURCES}) set_target_compile_flags(${name} ${LIB_CFLAGS} ${DARWIN_${os}_CFLAGS}) set_target_link_flags(${name} ${LIB_LINKFLAGS} ${DARWIN_${os}_LINKFLAGS}) set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) set_target_properties(${name} PROPERTIES - OSX_ARCHITECTURES "${LIB_ARCH}" + OSX_ARCHITECTURES "${LIB_ARCHS}" LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) install(TARGETS ${name} LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) @@ -162,7 +168,7 @@ endif() # DEPS <deps (e.g. runtime libs)> # LINK_FLAGS <link flags>) macro(add_compiler_rt_test test_suite test_name) - parse_arguments(TEST "SUBDIR;OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) + cmake_parse_arguments(TEST "" "SUBDIR" "OBJECTS;DEPS;LINK_FLAGS" "" ${ARGN}) if(TEST_SUBDIR) set(output_bin "${CMAKE_CURRENT_BINARY_DIR}/${TEST_SUBDIR}/${test_name}") else() @@ -229,7 +235,7 @@ macro(add_custom_libcxx name prefix) message(FATAL_ERROR "libcxx not found!") endif() - parse_arguments(LIBCXX "DEPS;CFLAGS" "" ${ARGN}) + cmake_parse_arguments(LIBCXX "" "" "DEPS;CFLAGS" ${ARGN}) foreach(flag ${LIBCXX_CFLAGS}) set(flagstr "${flagstr} ${flag}") endforeach() @@ -252,6 +258,7 @@ macro(add_custom_libcxx name prefix) LOG_CONFIGURE 1 LOG_INSTALL 1 ) + set_target_properties(${name} PROPERTIES EXCLUDE_FROM_ALL TRUE) ExternalProject_Add_Step(${name} force-reconfigure DEPENDERS configure diff --git a/cmake/Modules/CompilerRTCompile.cmake b/cmake/Modules/CompilerRTCompile.cmake index de73ccfc5cb8..b2e62dd0bac6 100644 --- a/cmake/Modules/CompilerRTCompile.cmake +++ b/cmake/Modules/CompilerRTCompile.cmake @@ -1,4 +1,28 @@ -include(LLVMParseArguments) +# On Windows, CMAKE_*_FLAGS are built for MSVC but we use the GCC clang.exe, +# which uses completely different flags. Translate some common flag types, and +# drop the rest. +function(translate_msvc_cflags out_flags msvc_flags) + # Insert an empty string in the list to simplify processing. + set(msvc_flags ";${msvc_flags}") + + # Canonicalize /flag to -flag. + string(REPLACE ";/" ";-" msvc_flags "${msvc_flags}") + + # Make space separated -D and -U flags into joined flags. + string(REGEX REPLACE ";-\([DU]\);" ";-\\1" msvc_flags "${msvc_flags}") + + set(clang_flags "") + foreach(flag ${msvc_flags}) + if ("${flag}" MATCHES "^-[DU]") + # Pass through basic command line macro definitions (-DNDEBUG). + list(APPEND clang_flags "${flag}") + elseif ("${flag}" MATCHES "^-O[2x]") + # Canonicalize normal optimization flags to -O2. + list(APPEND clang_flags "-O2") + endif() + endforeach() + set(${out_flags} "${clang_flags}" PARENT_SCOPE) +endfunction() # Compile a source into an object file with COMPILER_RT_TEST_COMPILER using # a provided compile flags and dependenices. @@ -6,7 +30,7 @@ include(LLVMParseArguments) # CFLAGS <list of compile flags> # DEPS <list of dependencies>) macro(clang_compile object_file source) - parse_arguments(SOURCE "CFLAGS;DEPS" "" ${ARGN}) + cmake_parse_arguments(SOURCE "" "" "CFLAGS;DEPS" ${ARGN}) get_filename_component(source_rpath ${source} REALPATH) if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND SOURCE_DEPS clang compiler-rt-headers) @@ -20,13 +44,11 @@ macro(clang_compile object_file source) else() string(REPLACE " " ";" global_flags "${CMAKE_C_FLAGS}") endif() - # On Windows, CMAKE_*_FLAGS are built for MSVC but we use the GCC clang.exe - # which doesn't support flags starting with "/smth". Replace those with - # "-smth" equivalents. - if(MSVC) - string(REGEX REPLACE "^/" "-" global_flags "${global_flags}") - string(REPLACE ";/" ";-" global_flags "${global_flags}") + + if (MSVC) + translate_msvc_cflags(global_flags "${global_flags}") endif() + # Ignore unknown warnings. CMAKE_CXX_FLAGS may contain GCC-specific options # which are not supported by Clang. list(APPEND global_flags -Wno-unknown-warning-option) diff --git a/cmake/Modules/CompilerRTLink.cmake b/cmake/Modules/CompilerRTLink.cmake index 0f0e53a3b2f4..bb96869844c1 100644 --- a/cmake/Modules/CompilerRTLink.cmake +++ b/cmake/Modules/CompilerRTLink.cmake @@ -1,12 +1,10 @@ -include(LLVMParseArguments) - # Link a shared library with COMPILER_RT_TEST_COMPILER. # clang_link_shared(<output.so> # OBJECTS <list of input objects> # LINKFLAGS <list of link flags> # DEPS <list of dependencies>) macro(clang_link_shared so_file) - parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN}) + cmake_parse_arguments(SOURCE "" "" "OBJECTS;LINKFLAGS;DEPS" ${ARGN}) if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND SOURCE_DEPS clang) endif() diff --git a/cmake/Modules/CompilerRTUtils.cmake b/cmake/Modules/CompilerRTUtils.cmake index ae59732928a1..f7f60a4ac6f4 100644 --- a/cmake/Modules/CompilerRTUtils.cmake +++ b/cmake/Modules/CompilerRTUtils.cmake @@ -49,3 +49,11 @@ macro(append_no_rtti_flag list) append_list_if(COMPILER_RT_HAS_FNO_RTTI_FLAG -fno-rtti ${list}) append_list_if(COMPILER_RT_HAS_GR_FLAG /GR- ${list}) endmacro() + +macro(append_have_file_definition filename varname list) + check_include_file("${filename}" "${varname}") + if (NOT ${varname}) + set("${varname}" 0) + endif() + list(APPEND ${list} "${varname}=${${varname}}") +endmacro() diff --git a/cmake/Modules/SanitizerUtils.cmake b/cmake/Modules/SanitizerUtils.cmake index 1ebc7030a57b..c040b42122ce 100644 --- a/cmake/Modules/SanitizerUtils.cmake +++ b/cmake/Modules/SanitizerUtils.cmake @@ -1,5 +1,3 @@ -include(LLVMParseArguments) - set(SANITIZER_GEN_DYNAMIC_LIST ${COMPILER_RT_SOURCE_DIR}/lib/sanitizer_common/scripts/gen_dynamic_list.py) @@ -13,9 +11,13 @@ set(SANITIZER_LINT_SCRIPT # add_sanitizer_rt_symbols(<name> <files with extra symbols to export>) macro(add_sanitizer_rt_symbols name) set(stamp ${CMAKE_CURRENT_BINARY_DIR}/${name}.syms-stamp) + set(extra_args) + foreach(arg ${ARGN}) + list(APPEND extra_args "--extra" ${arg}) + endforeach() add_custom_command(OUTPUT ${stamp} COMMAND ${PYTHON_EXECUTABLE} - ${SANITIZER_GEN_DYNAMIC_LIST} $<TARGET_FILE:${name}> ${ARGN} + ${SANITIZER_GEN_DYNAMIC_LIST} ${extra_args} $<TARGET_FILE:${name}> > $<TARGET_FILE:${name}>.syms COMMAND ${CMAKE_COMMAND} -E touch ${stamp} DEPENDS ${name} ${SANITIZER_GEN_DYNAMIC_LIST} ${ARGN} @@ -44,6 +46,29 @@ macro(add_sanitizer_rt_symbols name) endif() endmacro() +macro(add_sanitizer_rt_version_list name) + set(vers ${CMAKE_CURRENT_BINARY_DIR}/${name}.vers) + cmake_parse_arguments(ARG "" "" "LIBS;EXTRA" ${ARGN}) + set(args) + foreach(arg ${ARG_EXTRA}) + list(APPEND args "--extra" ${arg}) + endforeach() + foreach(arg ${ARG_LIBS}) + list(APPEND args "$<TARGET_FILE:${arg}>") + endforeach() + add_custom_command(OUTPUT ${vers} + COMMAND ${PYTHON_EXECUTABLE} + ${SANITIZER_GEN_DYNAMIC_LIST} --version-list ${args} + > ${vers} + DEPENDS ${SANITIZER_GEN_DYNAMIC_LIST} ${ARG_EXTRA} ${ARG_LIBS} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating version list for ${name}" + VERBATIM) + + add_custom_target(${name}-version-list ALL + DEPENDS ${vers}) +endmacro() + # Add target to check code style for sanitizer runtimes. if(UNIX) add_custom_target(SanitizerLintCheck diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 97a22cbdc7b9..c645be4d88d6 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -1,8 +1,16 @@ +include(CMakePushCheckState) include(CheckCXXCompilerFlag) include(CheckLibraryExists) include(CheckSymbolExists) include(TestBigEndian) +function(check_linker_flag flag out_var) + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${flag}") + check_cxx_compiler_flag("" ${out_var}) + cmake_pop_check_state() +endfunction() + # CodeGen options. check_cxx_compiler_flag(-fPIC COMPILER_RT_HAS_FPIC_FLAG) check_cxx_compiler_flag(-fPIE COMPILER_RT_HAS_FPIE_FLAG) @@ -11,6 +19,7 @@ check_cxx_compiler_flag(-fno-exceptions COMPILER_RT_HAS_FNO_EXCEPTIONS_FLAG check_cxx_compiler_flag(-fomit-frame-pointer COMPILER_RT_HAS_FOMIT_FRAME_POINTER_FLAG) check_cxx_compiler_flag(-funwind-tables COMPILER_RT_HAS_FUNWIND_TABLES_FLAG) check_cxx_compiler_flag(-fno-stack-protector COMPILER_RT_HAS_FNO_STACK_PROTECTOR_FLAG) +check_cxx_compiler_flag(-fno-sanitize=safe-stack COMPILER_RT_HAS_FNO_SANITIZE_SAFE_STACK_FLAG) check_cxx_compiler_flag(-fvisibility=hidden COMPILER_RT_HAS_FVISIBILITY_HIDDEN_FLAG) check_cxx_compiler_flag(-fno-rtti COMPILER_RT_HAS_FNO_RTTI_FLAG) check_cxx_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG) @@ -54,10 +63,16 @@ check_symbol_exists(__func__ "" COMPILER_RT_HAS_FUNC_SYMBOL) # Libraries. check_library_exists(c printf "" COMPILER_RT_HAS_LIBC) check_library_exists(dl dlopen "" COMPILER_RT_HAS_LIBDL) +check_library_exists(rt shm_open "" COMPILER_RT_HAS_LIBRT) check_library_exists(m pow "" COMPILER_RT_HAS_LIBM) check_library_exists(pthread pthread_create "" COMPILER_RT_HAS_LIBPTHREAD) check_library_exists(stdc++ __cxa_throw "" COMPILER_RT_HAS_LIBSTDCXX) +# Linker flags. +if(ANDROID) + check_linker_flag("-Wl,-z,global" COMPILER_RT_HAS_Z_GLOBAL) +endif() + # Architectures. # List of all architectures we can target. @@ -70,22 +85,43 @@ set(COMPILER_RT_SUPPORTED_ARCH) set(SIMPLE_SOURCE ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/simple.cc) file(WRITE ${SIMPLE_SOURCE} "#include <stdlib.h>\n#include <limits>\nint main() {}\n") -# test_target_arch(<arch> <target flags...>) -# Sets the target flags for a given architecture and determines if this -# architecture is supported by trying to build a simple file. -macro(test_target_arch arch) +function(check_compile_definition def argstring out_var) + if("${def}" STREQUAL "") + set(${out_var} TRUE PARENT_SCOPE) + return() + endif() + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${argstring}") + check_symbol_exists(${def} "" ${out_var}) + cmake_pop_check_state() +endfunction() + +# test_target_arch(<arch> <def> <target flags...>) +# Checks if architecture is supported: runs host compiler with provided +# flags to verify that: +# 1) <def> is defined (if non-empty) +# 2) simple file can be successfully built. +# If successful, saves target flags for this architecture. +macro(test_target_arch arch def) set(TARGET_${arch}_CFLAGS ${ARGN}) - set(argstring "${CMAKE_EXE_LINKER_FLAGS}") + set(argstring "") foreach(arg ${ARGN}) set(argstring "${argstring} ${arg}") endforeach() - try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE} - COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}" - OUTPUT_VARIABLE TARGET_${arch}_OUTPUT - CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}") + check_compile_definition("${def}" "${argstring}" HAS_${arch}_DEF) + if(NOT HAS_${arch}_DEF) + set(CAN_TARGET_${arch} FALSE) + else() + set(argstring "${CMAKE_EXE_LINKER_FLAGS} ${argstring}") + try_compile(CAN_TARGET_${arch} ${CMAKE_BINARY_DIR} ${SIMPLE_SOURCE} + COMPILE_DEFINITIONS "${TARGET_${arch}_CFLAGS}" + OUTPUT_VARIABLE TARGET_${arch}_OUTPUT + CMAKE_FLAGS "-DCMAKE_EXE_LINKER_FLAGS:STRING=${argstring}") + endif() if(${CAN_TARGET_${arch}}) list(APPEND COMPILER_RT_SUPPORTED_ARCH ${arch}) - elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "${arch}") + elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "${arch}" AND + COMPILER_RT_HAS_EXPLICIT_TEST_TARGET_TRIPLE) # Bail out if we cannot target the architecture we plan to test. message(FATAL_ERROR "Cannot compile for ${arch}:\n${TARGET_${arch}_OUTPUT}") endif() @@ -139,33 +175,46 @@ if(ANDROID) else() if("${LLVM_NATIVE_ARCH}" STREQUAL "X86") if(NOT MSVC) - test_target_arch(x86_64 "-m64") - test_target_arch(i386 "-m32") + test_target_arch(x86_64 "" "-m64") + # FIXME: We build runtimes for both i686 and i386, as "clang -m32" may + # target different variant than "$CMAKE_C_COMPILER -m32". This part should + # be gone after we resolve PR14109. + test_target_arch(i686 __i686__ "-m32") + test_target_arch(i386 __i386__ "-m32") else() - test_target_arch(i386 "") + if (CMAKE_SIZEOF_VOID_P EQUAL 4) + test_target_arch(i386 "" "") + else() + test_target_arch(x86_64 "" "") + endif() endif() elseif("${LLVM_NATIVE_ARCH}" STREQUAL "PowerPC") TEST_BIG_ENDIAN(HOST_IS_BIG_ENDIAN) if(HOST_IS_BIG_ENDIAN) - test_target_arch(powerpc64 "-m64") + test_target_arch(powerpc64 "" "-m64") else() - test_target_arch(powerpc64le "-m64") + test_target_arch(powerpc64le "" "-m64") endif() elseif("${LLVM_NATIVE_ARCH}" STREQUAL "Mips") + # Gcc doesn't accept -m32/-m64 so we do the next best thing and use + # -mips32r2/-mips64r2. We don't use -mips1/-mips3 because we want to match + # clang's default CPU's. In the 64-bit case, we must also specify the ABI + # since the default ABI differs between gcc and clang. + # FIXME: Ideally, we would build the N32 library too. if("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "mipsel|mips64el") # regex for mipsel, mips64el - test_target_arch(mipsel "-m32") - test_target_arch(mips64el "-m64") + test_target_arch(mipsel "" "-mips32r2" "--target=mipsel-linux-gnu") + test_target_arch(mips64el "" "-mips64r2" "-mabi=n64") else() - test_target_arch(mips "-m32") - test_target_arch(mips64 "-m64") + test_target_arch(mips "" "-mips32r2" "--target=mips-linux-gnu") + test_target_arch(mips64 "" "-mips64r2" "-mabi=n64") endif() elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "arm") - test_target_arch(arm "-march=armv7-a") + test_target_arch(arm "" "-march=armv7-a") elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "aarch32") - test_target_arch(aarch32 "-march=armv8-a") + test_target_arch(aarch32 "" "-march=armv8-a") elseif("${COMPILER_RT_TEST_TARGET_ARCH}" MATCHES "aarch64") - test_target_arch(aarch64 "-march=armv8-a") + test_target_arch(aarch64 "" "-march=armv8-a") endif() set(COMPILER_RT_OS_SUFFIX "") endif() @@ -184,6 +233,7 @@ function(filter_available_targets out_var) set(${out_var} ${archs} PARENT_SCOPE) endfunction() +# Returns a list of architecture specific target cflags in @out_var list. function(get_target_flags_for_arch arch out_var) list(FIND COMPILER_RT_SUPPORTED_ARCH ${arch} ARCH_INDEX) if(ARCH_INDEX EQUAL -1) @@ -196,19 +246,23 @@ endfunction() # Architectures supported by compiler-rt libraries. filter_available_targets(SANITIZER_COMMON_SUPPORTED_ARCH x86_64 i386 i686 powerpc64 powerpc64le arm aarch64 mips mips64 mipsel mips64el) +# LSan and UBSan common files should be available on all architectures supported +# by other sanitizers (even if they build into dummy object files). +filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH + ${SANITIZER_COMMON_SUPPORTED_ARCH}) +filter_available_targets(UBSAN_COMMON_SUPPORTED_ARCH + ${SANITIZER_COMMON_SUPPORTED_ARCH}) filter_available_targets(ASAN_SUPPORTED_ARCH x86_64 i386 i686 powerpc64 powerpc64le arm mips mipsel mips64 mips64el) filter_available_targets(DFSAN_SUPPORTED_ARCH x86_64 mips64 mips64el) filter_available_targets(LSAN_SUPPORTED_ARCH x86_64 mips64 mips64el) -# LSan common files should be available on all architectures supported -# by other sanitizers (even if they build into dummy object files). -filter_available_targets(LSAN_COMMON_SUPPORTED_ARCH - ${SANITIZER_COMMON_SUPPORTED_ARCH}) filter_available_targets(MSAN_SUPPORTED_ARCH x86_64 mips64 mips64el) filter_available_targets(PROFILE_SUPPORTED_ARCH x86_64 i386 i686 arm mips mips64 mipsel mips64el aarch64 powerpc64 powerpc64le) filter_available_targets(TSAN_SUPPORTED_ARCH x86_64 mips64 mips64el) -filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips mipsel mips64 mips64el) +filter_available_targets(UBSAN_SUPPORTED_ARCH x86_64 i386 i686 arm aarch64 mips + mipsel mips64 mips64el powerpc64 powerpc64le) +filter_available_targets(SAFESTACK_SUPPORTED_ARCH x86_64 i386 i686) if(ANDROID) set(OS_NAME "Android") @@ -218,13 +272,21 @@ endif() if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD" OR - (OS_NAME MATCHES "Windows" AND MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4))) + (OS_NAME MATCHES "Windows" AND MSVC))) set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE) else() set(COMPILER_RT_HAS_SANITIZER_COMMON FALSE) endif() -if (COMPILER_RT_HAS_SANITIZER_COMMON AND ASAN_SUPPORTED_ARCH) +if (COMPILER_RT_HAS_SANITIZER_COMMON AND + (NOT OS_NAME MATCHES "Windows" OR CMAKE_SIZEOF_VOID_P EQUAL 4)) + set(COMPILER_RT_HAS_INTERCEPTION TRUE) +else() + set(COMPILER_RT_HAS_INTERCEPTION FALSE) +endif() + +if (COMPILER_RT_HAS_SANITIZER_COMMON AND ASAN_SUPPORTED_ARCH AND + (NOT OS_NAME MATCHES "Windows" OR CMAKE_SIZEOF_VOID_P EQUAL 4)) set(COMPILER_RT_HAS_ASAN TRUE) else() set(COMPILER_RT_HAS_ASAN FALSE) @@ -246,19 +308,12 @@ else() endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD") + OS_NAME MATCHES "Linux|FreeBSD") set(COMPILER_RT_HAS_LSAN TRUE) else() set(COMPILER_RT_HAS_LSAN FALSE) endif() -if (COMPILER_RT_HAS_SANITIZER_COMMON AND LSAN_COMMON_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD|Android") - set(COMPILER_RT_HAS_LSAN_COMMON TRUE) -else() - set(COMPILER_RT_HAS_LSAN_COMMON FALSE) -endif() - if (COMPILER_RT_HAS_SANITIZER_COMMON AND MSAN_SUPPORTED_ARCH AND OS_NAME MATCHES "Linux") set(COMPILER_RT_HAS_MSAN TRUE) @@ -281,9 +336,23 @@ else() endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows") set(COMPILER_RT_HAS_UBSAN TRUE) else() set(COMPILER_RT_HAS_UBSAN FALSE) endif() +# -msse3 flag is not valid for Mips therefore clang gives a warning +# message with -msse3. But check_c_compiler_flags() checks only for +# compiler error messages. Therefore COMPILER_RT_HAS_MSSE3_FLAG turns out to be +# true on Mips, so we make it false here. +if("${LLVM_NATIVE_ARCH}" STREQUAL "Mips") + set(COMPILER_RT_HAS_MSSE3_FLAG FALSE) +endif() + +if (COMPILER_RT_HAS_SANITIZER_COMMON AND SAFESTACK_SUPPORTED_ARCH AND + OS_NAME MATCHES "Darwin|Linux|FreeBSD") + set(COMPILER_RT_HAS_SAFESTACK TRUE) +else() + set(COMPILER_RT_HAS_SAFESTACK FALSE) +endif() diff --git a/include/sanitizer/coverage_interface.h b/include/sanitizer/coverage_interface.h index 88a7e480081d..404b71e3086f 100644 --- a/include/sanitizer/coverage_interface.h +++ b/include/sanitizer/coverage_interface.h @@ -39,6 +39,23 @@ extern "C" { // Some of the entries in *data will be zero. uintptr_t __sanitizer_get_coverage_guards(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. + // Every counter is associated with 8 bits in the bitset. + // We define 8 value ranges: 1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+ + // The i-th bit is set to 1 if the counter value is in the i-th range. + // This counter-based coverage implementation is *not* thread-safe. + + // Returns the number of registered coverage counters. + uintptr_t __sanitizer_get_number_of_counters(); + // Updates the counter 'bitset', clears the counters and returns the number of + // new bits in 'bitset'. + // If 'bitset' is nullptr, only clears the counters. + // Otherwise 'bitset' should be at least + // __sanitizer_get_number_of_counters bytes long and 8-aligned. + uintptr_t + __sanitizer_update_counter_bitset_and_clear_counters(uint8_t *bitset); #ifdef __cplusplus } // extern "C" #endif diff --git a/include/sanitizer/dfsan_interface.h b/include/sanitizer/dfsan_interface.h index 79dbf2f364c2..84ffd49f8afe 100644 --- a/include/sanitizer/dfsan_interface.h +++ b/include/sanitizer/dfsan_interface.h @@ -91,6 +91,16 @@ 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); +/// 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); #ifdef __cplusplus } // extern "C" diff --git a/include/sanitizer/lsan_interface.h b/include/sanitizer/lsan_interface.h index 46d2668cec24..db017c4de1a3 100644 --- a/include/sanitizer/lsan_interface.h +++ b/include/sanitizer/lsan_interface.h @@ -41,14 +41,25 @@ extern "C" { void __lsan_register_root_region(const void *p, size_t size); void __lsan_unregister_root_region(const void *p, size_t size); - // Calling this function makes LSan enter the leak checking phase immediately. - // Use this if normal end-of-process leak checking happens too late (e.g. if - // you have intentional memory leaks in your shutdown code). Calling this - // function overrides end-of-process leak checking; it must be called at - // most once per process. This function will terminate the process if there - // are memory leaks and the exit_code flag is non-zero. + // 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. + // 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. + // By calling this function early during process shutdown, you can instruct + // LSan to ignore shutdown-only leaks which happen later on. void __lsan_do_leak_check(); + // Check for leaks now. Returns zero if no leaks have been found or if leak + // detection is disabled, non-zero otherwise. + // This function may be called repeatedly, e.g. to periodically check a + // long-running process. It prints a leak report if appropriate, but does not + // terminate the process. It does not affect the behavior of + // __lsan_do_leak_check() or the end-of-process leak check, and is not + // affected by them. + int __lsan_do_recoverable_leak_check(); + // The user may optionally provide this function to disallow leak checking // for the program it is linked into (if the return value is non-zero). This // function must be defined as returning a constant value; any behavior beyond diff --git a/include/sanitizer/msan_interface.h b/include/sanitizer/msan_interface.h index c3a19bf345ac..f54bcaa3e157 100644 --- a/include/sanitizer/msan_interface.h +++ b/include/sanitizer/msan_interface.h @@ -25,6 +25,11 @@ extern "C" { /* Get raw origin for an address. */ uint32_t __msan_get_origin(const volatile void *a); + /* Test that this_id is a descendant of prev_id (or they are simply equal). + * "descendant" here means they are part of the same chain, created with + * __msan_chain_origin. */ + int __msan_origin_is_descendant_or_same(uint32_t this_id, uint32_t prev_id); + /* Returns non-zero if tracking origins. */ int __msan_get_track_origins(); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6929e682fac6..009c59f4d68e 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -4,9 +4,14 @@ include(AddCompilerRT) include(SanitizerUtils) -if(COMPILER_RT_HAS_SANITIZER_COMMON) +if(COMPILER_RT_HAS_INTERCEPTION) add_subdirectory(interception) +endif() + +if(COMPILER_RT_HAS_SANITIZER_COMMON) add_subdirectory(sanitizer_common) + add_subdirectory(lsan) + add_subdirectory(ubsan) endif() if(COMPILER_RT_HAS_ASAN) @@ -19,10 +24,6 @@ if(COMPILER_RT_HAS_DFSAN) add_subdirectory(dfsan) endif() -if(COMPILER_RT_HAS_LSAN OR COMPILER_RT_HAS_LSAN_COMMON) - add_subdirectory(lsan) -endif() - if(COMPILER_RT_HAS_MSAN) add_subdirectory(msan) endif() @@ -36,7 +37,6 @@ if(COMPILER_RT_HAS_TSAN) add_subdirectory(tsan/dd) endif() -if(COMPILER_RT_HAS_UBSAN) - add_subdirectory(ubsan) +if(COMPILER_RT_HAS_SAFESTACK) + add_subdirectory(safestack) endif() - diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt index d4c5c17d36a2..28611a8a4659 100644 --- a/lib/asan/CMakeLists.txt +++ b/lib/asan/CMakeLists.txt @@ -37,9 +37,22 @@ append_no_rtti_flag(ASAN_CFLAGS) set(ASAN_COMMON_DEFINITIONS ASAN_HAS_EXCEPTIONS=1) +set(ASAN_DYNAMIC_LINK_FLAGS) + if(ANDROID) list(APPEND ASAN_COMMON_DEFINITIONS ASAN_LOW_MEMORY=1) +# On Android, -z global does not do what it is documented to do. +# On Android, -z global moves the library ahead in the lookup order, +# placing it right after the LD_PRELOADs. This is used to compensate for the fact +# that Android linker does not look at the dependencies of the main executable +# that aren't dependencies of the current DSO when resolving symbols from said DSO. +# As a net result, this allows running ASan executables without LD_PRELOAD-ing the +# ASan runtime library. +# The above is applicable to L MR1 or newer. + if (COMPILER_RT_HAS_Z_GLOBAL) + list(APPEND ASAN_DYNAMIC_LINK_FLAGS -Wl,-z,global) + endif() endif() set(ASAN_DYNAMIC_DEFINITIONS @@ -53,6 +66,7 @@ append_list_if(MSVC /DEBUG ASAN_DYNAMIC_CFLAGS) append_list_if(COMPILER_RT_HAS_LIBC c ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBDL dl ASAN_DYNAMIC_LIBS) +append_list_if(COMPILER_RT_HAS_LIBRT rt ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBM m ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS) append_list_if(COMPILER_RT_HAS_LIBSTDCXX stdc++ ASAN_DYNAMIC_LIBS) @@ -61,41 +75,50 @@ append_list_if(ANDROID log ASAN_DYNAMIC_LIBS) # Compile ASan sources into an object library. if(APPLE) - foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) - add_compiler_rt_darwin_object_library(RTAsan ${os} - ARCH ${ASAN_SUPPORTED_ARCH} - SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} - CFLAGS ${ASAN_DYNAMIC_CFLAGS} - DEFS ${ASAN_DYNAMIC_DEFINITIONS}) - endforeach() + add_compiler_rt_object_libraries(RTAsan + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) else() - foreach(arch ${ASAN_SUPPORTED_ARCH}) - add_compiler_rt_object_library(RTAsan ${arch} - SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS}) - add_compiler_rt_object_library(RTAsan_cxx ${arch} - SOURCES ${ASAN_CXX_SOURCES} CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS}) - add_compiler_rt_object_library(RTAsan_preinit ${arch} - SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} - DEFS ${ASAN_COMMON_DEFINITIONS}) - add_compiler_rt_object_library(RTAsan_dynamic ${arch} - SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} - CFLAGS ${ASAN_DYNAMIC_CFLAGS} - DEFS ${ASAN_DYNAMIC_DEFINITIONS}) - endforeach() + add_compiler_rt_object_libraries(RTAsan + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_object_libraries(RTAsan_cxx + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_CXX_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_object_libraries(RTAsan_preinit + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_object_libraries(RTAsan_dynamic + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc "") + add_compiler_rt_object_libraries(RTAsan_dynamic_version_script_dummy + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) endif() # Build ASan runtimes shipped with Clang. add_custom_target(asan) if(APPLE) - foreach (os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) + foreach (os ${SANITIZER_COMMON_SUPPORTED_OS}) add_compiler_rt_darwin_dynamic_runtime(clang_rt.asan_${os}_dynamic ${os} - ARCH ${ASAN_SUPPORTED_ARCH} + ARCHS ${ASAN_SUPPORTED_ARCH} SOURCES $<TARGET_OBJECTS:RTAsan.${os}> $<TARGET_OBJECTS:RTInterception.${os}> $<TARGET_OBJECTS:RTSanitizerCommon.${os}> $<TARGET_OBJECTS:RTLSanCommon.${os}> + $<TARGET_OBJECTS:RTUbsan.${os}> CFLAGS ${ASAN_DYNAMIC_CFLAGS} DEFS ${ASAN_DYNAMIC_DEFINITIONS}) add_dependencies(asan clang_rt.asan_${os}_dynamic) @@ -106,12 +129,9 @@ else() set(ASAN_COMMON_RUNTIME_OBJECTS $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>) - if(NOT WIN32) - # We can't build Leak Sanitizer on Windows yet. - list(APPEND ASAN_COMMON_RUNTIME_OBJECTS - $<TARGET_OBJECTS:RTLSanCommon.${arch}>) - endif() + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTLSanCommon.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}>) add_compiler_rt_runtime(clang_rt.asan-${arch} ${arch} STATIC SOURCES $<TARGET_OBJECTS:RTAsan_preinit.${arch}> @@ -123,6 +143,7 @@ else() add_compiler_rt_runtime(clang_rt.asan_cxx-${arch} ${arch} STATIC SOURCES $<TARGET_OBJECTS:RTAsan_cxx.${arch}> + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}> CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) add_dependencies(asan clang_rt.asan_cxx-${arch}) @@ -133,6 +154,20 @@ else() DEFS ${ASAN_COMMON_DEFINITIONS}) add_dependencies(asan clang_rt.asan-preinit-${arch}) + if (UNIX AND NOT ${arch} MATCHES "i386|i686") + add_sanitizer_rt_version_list(clang_rt.asan-dynamic-${arch} + LIBS clang_rt.asan-${arch} clang_rt.asan_cxx-${arch} + EXTRA asan.syms.extra) + set(VERSION_SCRIPT_FLAG + -Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers) + set_source_files_properties( + ${CMAKE_CURRENT_BINARY_DIR}/dummy.cc + PROPERTIES + OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers) + else() + set(VERSION_SCRIPT_FLAG) + endif() + if (WIN32) set(SHARED_ASAN_NAME clang_rt.asan_dynamic-${arch}${COMPILER_RT_OS_SUFFIX}) else() @@ -141,13 +176,21 @@ else() add_compiler_rt_runtime(clang_rt.asan-dynamic-${arch} ${arch} SHARED OUTPUT_NAME ${SHARED_ASAN_NAME} SOURCES $<TARGET_OBJECTS:RTAsan_dynamic.${arch}> + # The only purpose of RTAsan_dynamic_version_script_dummy is to carry + # a dependency of the shared runtime on the version script. With CMake + # 3.1 or later it can be replaced with a straightforward + # add_dependencies(clang_rt.asan-dynamic-${arch} clang_rt.asan-dynamic-${arch}-version-list) + $<TARGET_OBJECTS:RTAsan_dynamic_version_script_dummy.${arch}> + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}> ${ASAN_COMMON_RUNTIME_OBJECTS} CFLAGS ${ASAN_DYNAMIC_CFLAGS} + LINKFLAGS ${ASAN_DYNAMIC_LINK_FLAGS} + ${VERSION_SCRIPT_FLAG} DEFS ${ASAN_DYNAMIC_DEFINITIONS}) target_link_libraries(clang_rt.asan-dynamic-${arch} ${ASAN_DYNAMIC_LIBS}) add_dependencies(asan clang_rt.asan-dynamic-${arch}) - if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686") + if (UNIX AND NOT ${arch} MATCHES "i386|i686") add_sanitizer_rt_symbols(clang_rt.asan_cxx-${arch}) add_dependencies(asan clang_rt.asan_cxx-${arch}-symbols) add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra) diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc index fd63ac68c09e..2df9a510bd9a 100644 --- a/lib/asan/asan_allocator.cc +++ b/lib/asan/asan_allocator.cc @@ -223,7 +223,7 @@ void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { struct Allocator { static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 64UL << 30); + FIRST_32_SECOND_64(3UL << 30, 1UL << 40); static const uptr kMaxThreadLocalQuarantine = FIRST_32_SECOND_64(1 << 18, 1 << 20); @@ -354,7 +354,7 @@ struct Allocator { } CHECK(IsAligned(needed_size, min_alignment)); if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { - Report("WARNING: AddressSanitizer failed to allocate %p bytes\n", + Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", (void*)size); return allocator.ReturnNullOrDie(); } @@ -437,11 +437,10 @@ struct Allocator { thread_stats.mallocs++; thread_stats.malloced += size; thread_stats.malloced_redzones += needed_size - size; - uptr class_id = - Min(kNumberOfSizeClasses, SizeClassMap::ClassID(needed_size)); - thread_stats.malloced_by_size[class_id]++; if (needed_size > SizeClassMap::kMaxSize) thread_stats.malloc_large++; + else + thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++; void *res = reinterpret_cast<void *>(user_beg); if (can_fill && fl.max_malloc_fill_size) { diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h index 3208d1f950cd..5ccd00c97bab 100644 --- a/lib/asan/asan_allocator.h +++ b/lib/asan/asan_allocator.h @@ -29,7 +29,6 @@ enum AllocType { FROM_NEW_BR = 3 // Memory block came from operator new [ ] }; -static const uptr kNumberOfSizeClasses = 255; struct AsanChunk; struct AllocatorOptions { @@ -137,6 +136,7 @@ typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, 16, AsanMapUnmapCallback> PrimaryAllocator; #endif // SANITIZER_CAN_USE_ALLOCATOR64 +static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses; typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<AsanMapUnmapCallback> SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc index bf4f1eb4c781..d20641155b88 100644 --- a/lib/asan/asan_fake_stack.cc +++ b/lib/asan/asan_fake_stack.cc @@ -22,6 +22,9 @@ static const u64 kMagic2 = (kMagic1 << 8) | kMagic1; static const u64 kMagic4 = (kMagic2 << 16) | kMagic2; static const u64 kMagic8 = (kMagic4 << 32) | kMagic4; +static const u64 kAllocaRedzoneSize = 32UL; +static const u64 kAllocaRedzoneMask = 31UL; + // For small size classes inline PoisonShadow for better performance. ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) { CHECK_EQ(SHADOW_SCALE, 3); // This code expects SHADOW_SCALE=3. @@ -253,4 +256,24 @@ void *__asan_addr_is_in_fake_stack(void *fake_stack, void *addr, void **beg, if (end) *end = reinterpret_cast<void*>(frame_end); return reinterpret_cast<void*>(frame->real_stack); } + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_alloca_poison(uptr addr, uptr size) { + uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize; + uptr PartialRzAddr = addr + size; + uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask; + uptr PartialRzAligned = PartialRzAddr & ~(SHADOW_GRANULARITY - 1); + FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic); + FastPoisonShadowPartialRightRedzone( + PartialRzAligned, PartialRzAddr % SHADOW_GRANULARITY, + RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic); + FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void __asan_allocas_unpoison(uptr top, uptr bottom) { + if ((!top) || (top > bottom)) return; + REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0, + (bottom - top) / SHADOW_GRANULARITY); +} } // extern "C" diff --git a/lib/asan/asan_flags.cc b/lib/asan/asan_flags.cc index efb7767d5d91..e8ea549b62e3 100644 --- a/lib/asan/asan_flags.cc +++ b/lib/asan/asan_flags.cc @@ -20,6 +20,8 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" +#include "ubsan/ubsan_flags.h" +#include "ubsan/ubsan_platform.h" namespace __asan { @@ -72,8 +74,8 @@ void InitializeFlags() { RegisterAsanFlags(&asan_parser, f); RegisterCommonFlags(&asan_parser); - // Set the default values and prepare for parsing LSan flags (which can also - // overwrite common flags). + // Set the default values and prepare for parsing LSan and UBSan flags + // (which can also overwrite common flags). #if CAN_SANITIZE_LEAKS __lsan::Flags *lf = __lsan::flags(); lf->SetDefaults(); @@ -83,6 +85,15 @@ void InitializeFlags() { RegisterCommonFlags(&lsan_parser); #endif +#if CAN_SANITIZE_UB + __ubsan::Flags *uf = __ubsan::flags(); + uf->SetDefaults(); + + FlagParser ubsan_parser; + __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); + RegisterCommonFlags(&ubsan_parser); +#endif + // Override from ASan compile definition. const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); asan_parser.ParseString(asan_compile_def); @@ -90,12 +101,19 @@ void InitializeFlags() { // Override from user-specified string. const char *asan_default_options = MaybeCallAsanDefaultOptions(); asan_parser.ParseString(asan_default_options); +#if CAN_SANITIZE_UB + const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); + ubsan_parser.ParseString(ubsan_default_options); +#endif // Override from command line. asan_parser.ParseString(GetEnv("ASAN_OPTIONS")); #if CAN_SANITIZE_LEAKS lsan_parser.ParseString(GetEnv("LSAN_OPTIONS")); #endif +#if CAN_SANITIZE_UB + ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); +#endif // Let activation flags override current settings. On Android they come // from a system property. On other platforms this is no-op. diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc index 06140bbb360a..c34b1d3cedf2 100644 --- a/lib/asan/asan_globals.cc +++ b/lib/asan/asan_globals.cc @@ -18,6 +18,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" +#include "asan_suppressions.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -73,7 +74,7 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) { const uptr kMinimalDistanceFromAnotherGlobal = 64; -bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { +static bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; if (addr >= g.beg + g.size_with_redzone) return false; return true; @@ -90,36 +91,40 @@ static void ReportGlobal(const Global &g, const char *prefix) { } } -static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print, - Global *output_global) { - if (!flags()->report_globals) return false; +static u32 FindRegistrationSite(const Global *g) { + mu_for_globals.CheckLocked(); + CHECK(global_registration_site_vector); + for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { + GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; + if (g >= grs.g_first && g <= grs.g_last) + return grs.stack_id; + } + return 0; +} + +int GetGlobalsForAddress(uptr addr, Global *globals, u32 *reg_sites, + int max_globals) { + if (!flags()->report_globals) return 0; BlockingMutexLock lock(&mu_for_globals); - bool res = false; + int res = 0; for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; - if (print) { - if (flags()->report_globals >= 2) - ReportGlobal(g, "Search"); - res |= DescribeAddressRelativeToGlobal(addr, size, g); - } else { - if (IsAddressNearGlobal(addr, g)) { - CHECK(output_global); - *output_global = g; - return true; - } + if (flags()->report_globals >= 2) + ReportGlobal(g, "Search"); + if (IsAddressNearGlobal(addr, g)) { + globals[res] = g; + if (reg_sites) + reg_sites[res] = FindRegistrationSite(&g); + res++; + if (res == max_globals) break; } } return res; } -bool DescribeAddressIfGlobal(uptr addr, uptr size) { - return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true, - /* output_global */ nullptr); -} - bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) { Global g = {}; - if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) { + if (GetGlobalsForAddress(addr, &g, nullptr, 1)) { internal_strncpy(descr->name, g.name, descr->name_size); descr->region_address = g.beg; descr->region_size = g.size; @@ -129,16 +134,6 @@ bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) { return false; } -u32 FindRegistrationSite(const Global *g) { - CHECK(global_registration_site_vector); - for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { - GlobalRegistrationSite &grs = (*global_registration_site_vector)[i]; - if (g >= grs.g_first && g <= grs.g_last) - return grs.stack_id; - } - return 0; -} - // Register a global variable. // This function may be called more than once for every global // so we store the globals in a map. @@ -158,7 +153,8 @@ static void RegisterGlobal(const Global *g) { // the entire redzone of the second global may be within the first global. for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { if (g->beg == l->g->beg && - (flags()->detect_odr_violation >= 2 || g->size != l->g->size)) + (flags()->detect_odr_violation >= 2 || g->size != l->g->size) && + !IsODRViolationSuppressed(g->name)) ReportODRViolation(g, FindRegistrationSite(g), l->g, FindRegistrationSite(l->g)); } @@ -210,20 +206,6 @@ void StopInitOrderChecking() { } } -#if SANITIZER_WINDOWS // Should only be called on Windows. -SANITIZER_INTERFACE_ATTRIBUTE -void UnregisterGlobalsInRange(void *beg, void *end) { - if (!flags()->report_globals) - return; - BlockingMutexLock lock(&mu_for_globals); - for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { - void *address = (void *)l->g->beg; - if (beg <= address && address < end) - UnregisterGlobal(l->g); - } -} -#endif - } // namespace __asan // ---------------------- Interface ---------------- {{{1 @@ -232,7 +214,7 @@ using namespace __asan; // NOLINT // Register an array of globals. void __asan_register_globals(__asan_global *globals, uptr n) { if (!flags()->report_globals) return; - GET_STACK_TRACE_FATAL_HERE; + GET_STACK_TRACE_MALLOC; u32 stack_id = StackDepotPut(stack); BlockingMutexLock lock(&mu_for_globals); if (!global_registration_site_vector) diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc index 3dc7ec67a3e5..d8b48d391ab8 100644 --- a/lib/asan/asan_interceptors.cc +++ b/lib/asan/asan_interceptors.cc @@ -23,6 +23,10 @@ #include "asan_suppressions.h" #include "sanitizer_common/sanitizer_libc.h" +#if SANITIZER_POSIX +#include "sanitizer_common/sanitizer_posix.h" +#endif + namespace __asan { // Return true if we can quickly decide that the region is unpoisoned. @@ -65,7 +69,7 @@ struct AsanInterceptorContext { } \ if (!suppressed) { \ GET_CURRENT_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, __bad, isWrite, __size); \ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0); \ } \ } \ } while (0) @@ -75,6 +79,13 @@ struct AsanInterceptorContext { #define ASAN_WRITE_RANGE(ctx, offset, size) \ ACCESS_MEMORY_RANGE(ctx, offset, size, true) +#define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \ + ASAN_READ_RANGE((ctx), (s), \ + common_flags()->strict_string_checks ? (len) + 1 : (n)) + +#define ASAN_READ_STRING(ctx, s, n) \ + ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) + // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. // Macro is used to avoid creation of new frames. @@ -120,17 +131,6 @@ using namespace __asan; // NOLINT DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) -#if !SANITIZER_MAC -#define ASAN_INTERCEPT_FUNC(name) \ - do { \ - if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ - VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \ - } while (0) -#else -// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. -#define ASAN_INTERCEPT_FUNC(name) -#endif // SANITIZER_MAC - #define ASAN_INTERCEPTOR_ENTER(ctx, func) \ AsanInterceptorContext _ctx = {#func}; \ ctx = (void *)&_ctx; \ @@ -171,11 +171,24 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) do { \ } 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 +#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ + if (flags()->strict_init_order) { \ + StopInitOrderChecking(); \ + } #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ CoverageUpdateMapping() #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping() #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ + if (AsanThread *t = GetCurrentThread()) { \ + *begin = t->tls_begin(); \ + *end = t->tls_end(); \ + } else { \ + *begin = *end = 0; \ + } #include "sanitizer_common/sanitizer_common_interceptors.inc" // Syscall interceptors don't have contexts, we don't support suppressions @@ -200,12 +213,6 @@ struct ThreadStartParam { }; static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { -#if SANITIZER_WINDOWS - // FIXME: this is a bandaid fix for PR22025. - AsanThread *t = (AsanThread*)arg; - SetCurrentThread(t); - return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); -#else ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg); AsanThread *t = nullptr; while ((t = reinterpret_cast<AsanThread *>( @@ -213,7 +220,6 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { internal_sched_yield(); SetCurrentThread(t); return t->ThreadStart(GetTid(), ¶m->is_registered); -#endif } #if ASAN_INTERCEPT_PTHREAD_CREATE @@ -302,7 +308,7 @@ static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { ssize += stack - bottom; ssize = RoundUpTo(ssize, PageSize); static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb - if (ssize && ssize <= kMaxSaneContextStackSize) { + if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) { PoisonShadow(bottom, ssize, 0); } } @@ -357,30 +363,6 @@ INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { } #endif -#if SANITIZER_WINDOWS -INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) { - CHECK(REAL(RaiseException)); - __asan_handle_no_return(); - REAL(RaiseException)(a, b, c, d); -} - -INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) { - CHECK(REAL(_except_handler3)); - __asan_handle_no_return(); - return REAL(_except_handler3)(a, b, c, d); -} - -#if ASAN_DYNAMIC -// This handler is named differently in -MT and -MD CRTs. -#define _except_handler4 _except_handler4_common -#endif -INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { - CHECK(REAL(_except_handler4)); - __asan_handle_no_return(); - return REAL(_except_handler4)(a, b, c, d); -} -#endif - static inline int CharCmp(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } @@ -511,8 +493,9 @@ INTERCEPTOR(char*, strchr, const char *str, int c) { ENSURE_ASAN_INITED(); char *result = REAL(strchr)(str, c); if (flags()->replace_str) { - uptr bytes_read = (result ? result - str : REAL(strlen)(str)) + 1; - ASAN_READ_RANGE(ctx, str, bytes_read); + uptr len = REAL(strlen)(str); + uptr bytes_read = (result ? result - str : len) + 1; + ASAN_READ_STRING_OF_LEN(ctx, str, len, bytes_read); } return result; } @@ -541,7 +524,7 @@ INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT uptr from_length = REAL(strlen)(from); ASAN_READ_RANGE(ctx, from, from_length + 1); uptr to_length = REAL(strlen)(to); - ASAN_READ_RANGE(ctx, to, to_length); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); // If the copying actually happens, the |from| string should not overlap // with the resulting string starting at |to|, which has a length of @@ -563,7 +546,7 @@ INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { uptr copy_length = Min(size, from_length + 1); ASAN_READ_RANGE(ctx, from, copy_length); uptr to_length = REAL(strlen)(to); - ASAN_READ_RANGE(ctx, to, to_length); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); if (from_length > 0) { CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, @@ -665,23 +648,6 @@ INTERCEPTOR(uptr, strnlen, const char *s, uptr maxlen) { } #endif // ASAN_INTERCEPT_STRNLEN -static inline bool IsValidStrtolBase(int base) { - return (base == 0) || (2 <= base && base <= 36); -} - -static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { - CHECK(endptr); - if (nptr == *endptr) { - // No digits were found at strtol call, we need to find out the last - // symbol accessed by strtoll on our own. - // We get this symbol by skipping leading blanks and optional +/- sign. - while (IsSpace(*nptr)) nptr++; - if (*nptr == '+' || *nptr == '-') nptr++; - *endptr = const_cast<char *>(nptr); - } - CHECK(*endptr >= nptr); -} - INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; @@ -692,13 +658,7 @@ INTERCEPTOR(long, strtol, const char *nptr, // NOLINT } char *real_endptr; long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT - if (endptr != 0) { - *endptr = real_endptr; - } - if (IsValidStrtolBase(base)) { - FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); - } + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return result; } @@ -719,7 +679,7 @@ INTERCEPTOR(int, atoi, const char *nptr) { // different from int). So, we just imitate this behavior. int result = REAL(strtol)(nptr, &real_endptr, 10); FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); + ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); return result; } @@ -736,7 +696,7 @@ INTERCEPTOR(long, atol, const char *nptr) { // NOLINT char *real_endptr; long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); + ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); return result; } @@ -751,16 +711,7 @@ INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT } char *real_endptr; long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT - if (endptr != 0) { - *endptr = real_endptr; - } - // If base has unsupported value, strtoll can exit with EINVAL - // without reading any characters. So do additional checks only - // if base is valid. - if (IsValidStrtolBase(base)) { - FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); - } + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return result; } @@ -774,7 +725,7 @@ INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT char *real_endptr; long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT FixRealStrtolEndptr(nptr, &real_endptr); - ASAN_READ_RANGE(ctx, nptr, (real_endptr - nptr) + 1); + ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); return result; } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL @@ -807,36 +758,6 @@ INTERCEPTOR(int, fork, void) { } #endif // ASAN_INTERCEPT_FORK -#if SANITIZER_WINDOWS -INTERCEPTOR_WINAPI(DWORD, CreateThread, - void* security, uptr stack_size, - DWORD (__stdcall *start_routine)(void*), void* arg, - DWORD thr_flags, void* tid) { - // Strict init-order checking is thread-hostile. - if (flags()->strict_init_order) - StopInitOrderChecking(); - GET_STACK_TRACE_THREAD; - // FIXME: The CreateThread interceptor is not the same as a pthread_create - // one. This is a bandaid fix for PR22025. - bool detached = false; // FIXME: how can we determine it on Windows? - u32 current_tid = GetCurrentTidOrInvalid(); - AsanThread *t = - AsanThread::Create(start_routine, arg, current_tid, &stack, detached); - return REAL(CreateThread)(security, stack_size, - asan_thread_start, t, thr_flags, tid); -} - -namespace __asan { -void InitializeWindowsInterceptors() { - ASAN_INTERCEPT_FUNC(CreateThread); - ASAN_INTERCEPT_FUNC(RaiseException); - ASAN_INTERCEPT_FUNC(_except_handler3); - ASAN_INTERCEPT_FUNC(_except_handler4); -} - -} // namespace __asan -#endif - // ---------------------- InitializeAsanInterceptors ---------------- {{{1 namespace __asan { void InitializeAsanInterceptors() { @@ -919,10 +840,7 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(fork); #endif - // Some Windows-specific interceptors. -#if SANITIZER_WINDOWS - InitializeWindowsInterceptors(); -#endif + InitializePlatformInterceptors(); VReport(1, "AddressSanitizer: libc interceptors initialized\n"); } diff --git a/lib/asan/asan_interceptors.h b/lib/asan/asan_interceptors.h index ee3b82aa7ef9..488ada78ab8b 100644 --- a/lib/asan/asan_interceptors.h +++ b/lib/asan/asan_interceptors.h @@ -92,9 +92,21 @@ struct sigaction; DECLARE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) +#if !SANITIZER_MAC +#define ASAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ + VReport(1, "AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) +#else +// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. +#define ASAN_INTERCEPT_FUNC(name) +#endif // SANITIZER_MAC + namespace __asan { void InitializeAsanInterceptors(); +void InitializePlatformInterceptors(); #define ENSURE_ASAN_INITED() do { \ CHECK(!asan_init_is_running); \ diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h index ea7540f6bb56..ad8ebcd91ad9 100644 --- a/lib/asan/asan_interface_internal.h +++ b/lib/asan/asan_interface_internal.h @@ -128,7 +128,7 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __asan_report_error(uptr pc, uptr bp, uptr sp, - uptr addr, int is_write, uptr access_size); + uptr addr, int is_write, uptr access_size, u32 exp); SANITIZER_INTERFACE_ATTRIBUTE int __asan_set_error_exit_code(int exit_code); @@ -165,6 +165,21 @@ 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_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); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load8(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_load16(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store1(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store2(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store4(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store8(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_store16(uptr p, u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_loadN(uptr p, uptr size, + u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void __asan_exp_storeN(uptr p, uptr size, + u32 exp); + SANITIZER_INTERFACE_ATTRIBUTE void* __asan_memcpy(void *dst, const void *src, uptr size); SANITIZER_INTERFACE_ATTRIBUTE @@ -180,6 +195,10 @@ extern "C" { void __asan_poison_intra_object_redzone(uptr p, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __asan_unpoison_intra_object_redzone(uptr p, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_alloca_poison(uptr addr, uptr size); + SANITIZER_INTERFACE_ATTRIBUTE + void __asan_allocas_unpoison(uptr top, uptr bottom); } // extern "C" #endif // ASAN_INTERFACE_INTERNAL_H diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index ffd3ff82d71e..107e16ee31b9 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -62,21 +62,6 @@ namespace __asan { class AsanThread; using __sanitizer::StackTrace; -struct SignalContext { - void *context; - uptr addr; - uptr pc; - uptr sp; - uptr bp; - - SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) : - context(context), addr(addr), pc(pc), sp(sp), bp(bp) { - } - - // Creates signal context in a platform-specific manner. - static SignalContext Create(void *siginfo, void *context); -}; - void AsanInitFromRtl(); // asan_rtl.cc @@ -90,7 +75,6 @@ void *AsanDoesNotSupportStaticLinkage(); void AsanCheckDynamicRTPrereqs(); void AsanCheckIncompatibleRT(); -void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void AsanOnSIGSEGV(int, void *siginfo, void *context); void DisableReexec(); @@ -109,7 +93,7 @@ void AppendToErrorMessageBuffer(const char *buffer); void *AsanDlSymNext(const char *sym); -void ReserveShadowMemoryRange(uptr beg, uptr end); +void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name); // Platform-specific options. #if SANITIZER_MAC diff --git a/lib/asan/asan_linux.cc b/lib/asan/asan_linux.cc index 8e8bafd47af6..9580fc7c06d4 100644 --- a/lib/asan/asan_linux.cc +++ b/lib/asan/asan_linux.cc @@ -68,6 +68,8 @@ asan_rt_version_t __asan_rt_version; namespace __asan { +void InitializePlatformInterceptors() {} + void DisableReexec() { // No need to re-exec on Linux. } @@ -111,6 +113,9 @@ static void ReportIncompatibleRT() { } void AsanCheckDynamicRTPrereqs() { + if (!ASAN_DYNAMIC) + return; + // Ensure that dynamic RT is the first DSO in the list const char *first_dso_name = 0; dl_iterate_phdr(FindFirstDSOCallback, &first_dso_name); @@ -152,78 +157,6 @@ void AsanCheckIncompatibleRT() { } #endif // SANITIZER_ANDROID -void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { -#if defined(__arm__) - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.arm_pc; - *bp = ucontext->uc_mcontext.arm_fp; - *sp = ucontext->uc_mcontext.arm_sp; -#elif defined(__aarch64__) - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.pc; - *bp = ucontext->uc_mcontext.regs[29]; - *sp = ucontext->uc_mcontext.sp; -#elif defined(__hppa__) - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.sc_iaoq[0]; - /* GCC uses %r3 whenever a frame pointer is needed. */ - *bp = ucontext->uc_mcontext.sc_gr[3]; - *sp = ucontext->uc_mcontext.sc_gr[30]; -#elif defined(__x86_64__) -# if SANITIZER_FREEBSD - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.mc_rip; - *bp = ucontext->uc_mcontext.mc_rbp; - *sp = ucontext->uc_mcontext.mc_rsp; -# else - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.gregs[REG_RIP]; - *bp = ucontext->uc_mcontext.gregs[REG_RBP]; - *sp = ucontext->uc_mcontext.gregs[REG_RSP]; -# endif -#elif defined(__i386__) -# if SANITIZER_FREEBSD - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.mc_eip; - *bp = ucontext->uc_mcontext.mc_ebp; - *sp = ucontext->uc_mcontext.mc_esp; -# else - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.gregs[REG_EIP]; - *bp = ucontext->uc_mcontext.gregs[REG_EBP]; - *sp = ucontext->uc_mcontext.gregs[REG_ESP]; -# endif -#elif defined(__powerpc__) || defined(__powerpc64__) - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.regs->nip; - *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; - // The powerpc{,64}-linux ABIs do not specify r31 as the frame - // pointer, but GCC always uses r31 when we need a frame pointer. - *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; -#elif defined(__sparc__) - ucontext_t *ucontext = (ucontext_t*)context; - uptr *stk_ptr; -# if defined (__arch64__) - *pc = ucontext->uc_mcontext.mc_gregs[MC_PC]; - *sp = ucontext->uc_mcontext.mc_gregs[MC_O6]; - stk_ptr = (uptr *) (*sp + 2047); - *bp = stk_ptr[15]; -# else - *pc = ucontext->uc_mcontext.gregs[REG_PC]; - *sp = ucontext->uc_mcontext.gregs[REG_O6]; - stk_ptr = (uptr *) *sp; - *bp = stk_ptr[15]; -# endif -#elif defined(__mips__) - ucontext_t *ucontext = (ucontext_t*)context; - *pc = ucontext->uc_mcontext.gregs[31]; - *bp = ucontext->uc_mcontext.gregs[30]; - *sp = ucontext->uc_mcontext.gregs[29]; -#else -# error "Unsupported arch" -#endif -} - void AsanPlatformThreadInit() { // Nothing here for now. } diff --git a/lib/asan/asan_mac.cc b/lib/asan/asan_mac.cc index b35368617dca..3e028378df28 100644 --- a/lib/asan/asan_mac.cc +++ b/lib/asan/asan_mac.cc @@ -24,7 +24,14 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mac.h" -#include <crt_externs.h> // for _NSGetArgv +#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 <mach-o/dyld.h> #include <mach-o/loader.h> @@ -40,19 +47,7 @@ namespace __asan { -void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { - ucontext_t *ucontext = (ucontext_t*)context; -# if SANITIZER_WORDSIZE == 64 - *pc = ucontext->uc_mcontext->__ss.__rip; - *bp = ucontext->uc_mcontext->__ss.__rbp; - *sp = ucontext->uc_mcontext->__ss.__rsp; -# else - *pc = ucontext->uc_mcontext->__ss.__eip; - *bp = ucontext->uc_mcontext->__ss.__ebp; - *sp = ucontext->uc_mcontext->__ss.__esp; -# endif // SANITIZER_WORDSIZE -} - +void InitializePlatformInterceptors() {} bool PlatformHasDifferentMemcpyAndMemmove() { // On OS X 10.7 memcpy() and memmove() are both resolved @@ -74,30 +69,27 @@ LowLevelAllocator allocator_for_env; // otherwise the corresponding "NAME=value" string is replaced with // |name_value|. void LeakyResetEnv(const char *name, const char *name_value) { - char ***env_ptr = _NSGetEnviron(); - CHECK(env_ptr); - char **environ = *env_ptr; - CHECK(environ); + char **env = GetEnviron(); uptr name_len = internal_strlen(name); - while (*environ != 0) { - uptr len = internal_strlen(*environ); + while (*env != 0) { + uptr len = internal_strlen(*env); if (len > name_len) { - const char *p = *environ; + 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. - *environ = const_cast<char*>(name_value); + *env = const_cast<char*>(name_value); } else { // Shift the subsequent pointers back. - char **del = environ; + char **del = env; do { del[0] = del[1]; } while (*del++); } } } - environ++; + env++; } } @@ -107,6 +99,23 @@ 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; @@ -122,8 +131,10 @@ void MaybeReexec() { uptr fname_len = internal_strlen(info.dli_fname); const char *dylib_name = StripModuleName(info.dli_fname); uptr dylib_name_len = internal_strlen(dylib_name); - if (!dyld_insert_libraries || - !REAL(strstr)(dyld_insert_libraries, 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]; @@ -160,6 +171,9 @@ void MaybeReexec() { 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. diff --git a/lib/asan/asan_mapping.h b/lib/asan/asan_mapping.h index 5cb011d683d3..f9e1a527de18 100644 --- a/lib/asan/asan_mapping.h +++ b/lib/asan/asan_mapping.h @@ -98,9 +98,12 @@ static const u64 kDefaultShadowScale = 3; static const u64 kDefaultShadowOffset32 = 1ULL << 29; // 0x20000000 -static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000 static const u64 kDefaultShadowOffset64 = 1ULL << 44; static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G. +static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000 +static const u64 kIosShadowOffset64 = 0x130000000; +static const u64 kIosSimShadowOffset32 = 1ULL << 30; +static const u64 kIosSimShadowOffset64 = kDefaultShadowOffset64; static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; @@ -118,10 +121,12 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # define SHADOW_OFFSET kMIPS32_ShadowOffset32 # elif SANITIZER_FREEBSD # define SHADOW_OFFSET kFreeBSD_ShadowOffset32 -# elif SANITIZER_IOS -# define SHADOW_OFFSET kIosShadowOffset32 # elif SANITIZER_WINDOWS # define SHADOW_OFFSET kWindowsShadowOffset32 +# elif SANITIZER_IOSSIM +# define SHADOW_OFFSET kIosSimShadowOffset32 +# elif SANITIZER_IOS +# define SHADOW_OFFSET kIosShadowOffset32 # else # define SHADOW_OFFSET kDefaultShadowOffset32 # endif @@ -136,6 +141,10 @@ static const u64 kWindowsShadowOffset32 = 3ULL << 28; // 0x30000000 # define SHADOW_OFFSET kDefaultShadowOffset64 # elif defined(__mips64) # define SHADOW_OFFSET kMIPS64_ShadowOffset64 +# elif SANITIZER_IOSSIM +# define SHADOW_OFFSET kIosSimShadowOffset64 +# elif SANITIZER_IOS +# define SHADOW_OFFSET kIosShadowOffset64 # else # define SHADOW_OFFSET kDefaultShort64bitShadowOffset # endif diff --git a/lib/asan/asan_poisoning.cc b/lib/asan/asan_poisoning.cc index e2b1f4dc4d5e..569d359aa425 100644 --- a/lib/asan/asan_poisoning.cc +++ b/lib/asan/asan_poisoning.cc @@ -112,7 +112,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) { if (!flags()->allow_user_poisoning || size == 0) return; uptr beg_addr = (uptr)addr; uptr end_addr = beg_addr + size; - VPrintf(1, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr, + VPrintf(3, "Trying to poison memory region [%p, %p)\n", (void *)beg_addr, (void *)end_addr); ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); @@ -152,7 +152,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { if (!flags()->allow_user_poisoning || size == 0) return; uptr beg_addr = (uptr)addr; uptr end_addr = beg_addr + size; - VPrintf(1, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr, + VPrintf(3, "Trying to unpoison memory region [%p, %p)\n", (void *)beg_addr, (void *)end_addr); ShadowSegmentEndpoint beg(beg_addr); ShadowSegmentEndpoint end(end_addr); @@ -218,7 +218,7 @@ uptr __asan_region_is_poisoned(uptr beg, uptr size) { __asan::AddressIsPoisoned(__p + __size - 1))) { \ GET_CURRENT_PC_BP_SP; \ uptr __bad = __asan_region_is_poisoned(__p, __size); \ - __asan_report_error(pc, bp, sp, __bad, isWrite, __size);\ + __asan_report_error(pc, bp, sp, __bad, isWrite, __size, 0);\ } \ } while (false); \ diff --git a/lib/asan/asan_poisoning.h b/lib/asan/asan_poisoning.h index 3fc94649fb39..6344225f0f64 100644 --- a/lib/asan/asan_poisoning.h +++ b/lib/asan/asan_poisoning.h @@ -64,7 +64,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, if (page_end != shadow_end) { REAL(memset)((void *)page_end, 0, shadow_end - page_end); } - ReserveShadowMemoryRange(page_beg, page_end - 1); + ReserveShadowMemoryRange(page_beg, page_end - 1, nullptr); } } } diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc index ad31458df28d..2e857f6f624c 100644 --- a/lib/asan/asan_posix.cc +++ b/lib/asan/asan_posix.cc @@ -21,6 +21,7 @@ #include "asan_report.h" #include "asan_stack.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_procmaps.h" #include <pthread.h> @@ -32,13 +33,6 @@ namespace __asan { -SignalContext SignalContext::Create(void *siginfo, void *context) { - uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr; - uptr pc, sp, bp; - GetPcSpBp(context, &pc, &sp, &bp); - return SignalContext(context, addr, pc, sp, bp); -} - void AsanOnSIGSEGV(int, void *siginfo, void *context) { ScopedDeadlySignal signal_scope(GetCurrentThread()); int code = (int)((siginfo_t*)siginfo)->si_code; diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index 8706d5decc0b..c1681e644464 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -281,9 +281,8 @@ static void PrintGlobalLocation(InternalScopedString *str, str->append(":%d", g.location->column_no); } -bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, - const __asan_global &g) { - if (!IsAddressNearGlobal(addr, g)) return false; +static void DescribeAddressRelativeToGlobal(uptr addr, uptr size, + const __asan_global &g) { InternalScopedString str(4096); Decorator d; str.append("%s", d.Location()); @@ -306,6 +305,26 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, str.append("%s", d.EndLocation()); PrintGlobalNameIfASCII(&str, g); Printf("%s", str.data()); +} + +static bool DescribeAddressIfGlobal(uptr addr, uptr size, + const char *bug_type) { + // Assume address is close to at most four globals. + const int kMaxGlobalsInReport = 4; + __asan_global globals[kMaxGlobalsInReport]; + u32 reg_sites[kMaxGlobalsInReport]; + int globals_num = + GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals)); + if (globals_num == 0) + return false; + for (int i = 0; i < globals_num; i++) { + DescribeAddressRelativeToGlobal(addr, size, globals[i]); + if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") && + reg_sites[i]) { + Printf(" registered at:\n"); + StackDepotGet(reg_sites[i]).Print(); + } + } return true; } @@ -551,12 +570,12 @@ void DescribeHeapAddress(uptr addr, uptr access_size) { DescribeThread(alloc_thread); } -void DescribeAddress(uptr addr, uptr access_size) { +static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) { // Check if this is shadow or shadow gap. if (DescribeAddressIfShadow(addr)) return; CHECK(AddrIsInMem(addr)); - if (DescribeAddressIfGlobal(addr, access_size)) + if (DescribeAddressIfGlobal(addr, access_size, bug_type)) return; if (DescribeAddressIfStack(addr, access_size)) return; @@ -578,6 +597,11 @@ void DescribeThread(AsanThreadContext *context) { InternalScopedString str(1024); str.append("Thread T%d%s", context->tid, ThreadNameWithParenthesis(context->tid, tname, sizeof(tname))); + if (context->parent_tid == kInvalidTid) { + str.append(" created by unknown thread\n"); + Printf("%s", str.data()); + return; + } str.append( " created by T%d%s here:\n", context->parent_tid, ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); @@ -805,8 +829,8 @@ void ReportStringFunctionMemoryRangesOverlap(const char *function, bug_type, offset1, offset1 + length1, offset2, offset2 + length2); Printf("%s", d.EndWarning()); stack->Print(); - DescribeAddress((uptr)offset1, length1); - DescribeAddress((uptr)offset2, length2); + DescribeAddress((uptr)offset1, length1, bug_type); + DescribeAddress((uptr)offset2, length2, bug_type); ReportErrorSummary(bug_type, stack); } @@ -819,7 +843,7 @@ void ReportStringFunctionSizeOverflow(uptr offset, uptr size, Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size); Printf("%s", d.EndWarning()); stack->Print(); - DescribeAddress(offset, size); + DescribeAddress(offset, size, bug_type); ReportErrorSummary(bug_type, stack); } @@ -834,6 +858,9 @@ void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, " old_mid : %p\n" " new_mid : %p\n", beg, end, old_mid, new_mid); + uptr granularity = SHADOW_GRANULARITY; + if (!IsAligned(beg, granularity)) + Report("ERROR: beg is not aligned by %d\n", granularity); stack->Print(); ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack); } @@ -871,15 +898,16 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1, static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) { ScopedInErrorReport in_report; + const char *bug_type = "invalid-pointer-pair"; Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2); Printf("%s", d.EndWarning()); GET_STACK_TRACE_FATAL(pc, bp); stack.Print(); - DescribeAddress(a1, 1); - DescribeAddress(a2, 1); - ReportErrorSummary("invalid-pointer-pair", &stack); + DescribeAddress(a1, 1, bug_type); + DescribeAddress(a2, 1, bug_type); + ReportErrorSummary(bug_type, &stack); } static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) { @@ -936,9 +964,18 @@ void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name, using namespace __asan; // NOLINT void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, - uptr access_size) { + uptr access_size, u32 exp) { ENABLE_FRAME_POINTER; + // Optimization experiments. + // The experiments can be used to evaluate potential optimizations that remove + // instrumentation (assess false negatives). Instead of completely removing + // some instrumentation, compiler can emit special calls into runtime + // (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass + // mask of experiments (exp). + // The reaction to a non-zero value of exp is to be defined. + (void)exp; + // Determine the error type. const char *bug_descr = "unknown-crash"; if (AddrIsInMem(addr)) { @@ -1017,7 +1054,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, GET_STACK_TRACE_FATAL(pc, bp); stack.Print(); - DescribeAddress(addr, access_size); + DescribeAddress(addr, access_size, bug_descr); ReportErrorSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } @@ -1035,7 +1072,7 @@ void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) { void __asan_describe_address(uptr addr) { // Thread registry must be locked while we're describing an address. asanThreadRegistry().Lock(); - DescribeAddress(addr, 1); + DescribeAddress(addr, 1, ""); asanThreadRegistry().Unlock(); } diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h index 029c914b8a04..e2786b0f260c 100644 --- a/lib/asan/asan_report.h +++ b/lib/asan/asan_report.h @@ -33,22 +33,19 @@ struct AddressDescription { const char *region_kind; }; +// Returns the number of globals close to the provided address and copies +// them to "globals" array. +int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites, + int max_globals); +bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr); // The following functions prints address description depending // on the memory type (shadow/heap/stack/global). void DescribeHeapAddress(uptr addr, uptr access_size); -bool DescribeAddressIfGlobal(uptr addr, uptr access_size); -bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, - const __asan_global &g); -bool IsAddressNearGlobal(uptr addr, const __asan_global &g); -bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr); bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr, bool print = true); bool ParseFrameDescription(const char *frame_descr, InternalMmapVector<StackVarDescr> *vars); bool DescribeAddressIfStack(uptr addr, uptr access_size); -// Determines memory type on its own. -void DescribeAddress(uptr addr, uptr access_size); - void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index 9126e71a6437..a8d92b915a9a 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -28,6 +28,8 @@ #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "lsan/lsan_common.h" +#include "ubsan/ubsan_init.h" +#include "ubsan/ubsan_platform.h" int __asan_option_detect_stack_use_after_return; // Global interface symbol. uptr *__asan_test_only_reported_buggy_pointer; // Used only for testing asan. @@ -87,12 +89,12 @@ void ShowStatsAndAbort() { // ---------------------- mmap -------------------- {{{1 // Reserve memory range [beg, end]. // We need to use inclusive range because end+1 may not be representable. -void ReserveShadowMemoryRange(uptr beg, uptr end) { +void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name) { CHECK_EQ((beg % GetPageSizeCached()), 0); CHECK_EQ(((end + 1) % GetPageSizeCached()), 0); uptr size = end - beg + 1; DecreaseTotalMmap(size); // Don't count the shadow against mmap_limit_mb. - void *res = MmapFixedNoReserve(beg, size); + void *res = MmapFixedNoReserve(beg, size, name); if (res != (void*)beg) { Report("ReserveShadowMemoryRange failed while trying to map 0x%zx bytes. " "Perhaps you're using ulimit -v\n", size); @@ -112,11 +114,15 @@ static void OnLowLevelAllocate(uptr ptr, uptr size) { // -------------------------- Run-time entry ------------------- {{{1 // exported functions #define ASAN_REPORT_ERROR(type, is_write, size) \ -extern "C" NOINLINE INTERFACE_ATTRIBUTE \ -void __asan_report_ ## type ## size(uptr addr); \ -void __asan_report_ ## type ## size(uptr addr) { \ +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); \ +} \ +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); \ + __asan_report_error(pc, bp, sp, addr, is_write, size, exp); \ } ASAN_REPORT_ERROR(load, false, 1) @@ -132,18 +138,20 @@ 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); \ void __asan_report_ ## type ## _n(uptr addr, uptr size) { \ GET_CALLER_PC_BP_SP; \ - __asan_report_error(pc, bp, sp, addr, is_write, size); \ + __asan_report_error(pc, bp, sp, addr, is_write, size, 0); \ +} \ +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); \ } ASAN_REPORT_ERROR_N(load, false) 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); \ - void __asan_##type##size(uptr addr) { \ +#define ASAN_MEMORY_ACCESS_CALLBACK_BODY(type, is_write, size, exp_arg) \ uptr sp = MEM_TO_SHADOW(addr); \ uptr s = size <= SHADOW_GRANULARITY ? *reinterpret_cast<u8 *>(sp) \ : *reinterpret_cast<u16 *>(sp); \ @@ -155,10 +163,19 @@ 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); \ + __asan_report_error(pc, bp, sp, addr, is_write, size, exp_arg); \ } \ } \ - } \ + } + +#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) \ + } \ + 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(load, false, 1) @@ -173,18 +190,38 @@ ASAN_MEMORY_ACCESS_CALLBACK(store, true, 8) ASAN_MEMORY_ACCESS_CALLBACK(store, true, 16) extern "C" -NOINLINE INTERFACE_ATTRIBUTE void __asan_loadN(uptr addr, uptr size) { +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); + __asan_report_error(pc, bp, sp, addr, false, size, 0); } } extern "C" -NOINLINE INTERFACE_ATTRIBUTE void __asan_storeN(uptr addr, uptr size) { +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, true, size); + __asan_report_error(pc, bp, sp, addr, false, size, exp); + } +} + +extern "C" +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); + } +} + +extern "C" +NOINLINE INTERFACE_ATTRIBUTE +void __asan_exp_storeN(uptr addr, uptr size, u32 exp) { + if (__asan_region_is_poisoned(addr, size)) { + GET_CALLER_PC_BP_SP; + __asan_report_error(pc, bp, sp, addr, true, size, exp); } } @@ -203,26 +240,40 @@ static NOINLINE void force_interface_symbols() { case 3: __asan_report_load4(0); break; case 4: __asan_report_load8(0); break; case 5: __asan_report_load16(0); break; - case 6: __asan_report_store1(0); break; - case 7: __asan_report_store2(0); break; - case 8: __asan_report_store4(0); break; - case 9: __asan_report_store8(0); break; - case 10: __asan_report_store16(0); break; - case 12: __asan_register_globals(0, 0); break; - case 13: __asan_unregister_globals(0, 0); break; - case 14: __asan_set_death_callback(0); break; - case 15: __asan_set_error_report_callback(0); break; - case 16: __asan_handle_no_return(); break; - case 17: __asan_address_is_poisoned(0); break; - case 25: __asan_poison_memory_region(0, 0); break; - case 26: __asan_unpoison_memory_region(0, 0); break; - case 27: __asan_set_error_exit_code(0); break; - case 30: __asan_before_dynamic_init(0); break; - case 31: __asan_after_dynamic_init(); break; - case 32: __asan_poison_stack_memory(0, 0); break; - case 33: __asan_unpoison_stack_memory(0, 0); break; - case 34: __asan_region_is_poisoned(0, 0); break; - case 35: __asan_describe_address(0); break; + case 6: __asan_report_load_n(0, 0); break; + case 7: __asan_report_store1(0); break; + case 8: __asan_report_store2(0); break; + case 9: __asan_report_store4(0); break; + case 10: __asan_report_store8(0); break; + case 11: __asan_report_store16(0); break; + case 12: __asan_report_store_n(0, 0); break; + case 13: __asan_report_exp_load1(0, 0); break; + case 14: __asan_report_exp_load2(0, 0); break; + case 15: __asan_report_exp_load4(0, 0); break; + case 16: __asan_report_exp_load8(0, 0); break; + case 17: __asan_report_exp_load16(0, 0); break; + case 18: __asan_report_exp_load_n(0, 0, 0); break; + case 19: __asan_report_exp_store1(0, 0); break; + case 20: __asan_report_exp_store2(0, 0); break; + case 21: __asan_report_exp_store4(0, 0); break; + case 22: __asan_report_exp_store8(0, 0); break; + case 23: __asan_report_exp_store16(0, 0); break; + case 24: __asan_report_exp_store_n(0, 0, 0); break; + case 25: __asan_register_globals(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 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 35: __asan_after_dynamic_init(); break; + case 36: __asan_poison_stack_memory(0, 0); break; + case 37: __asan_unpoison_stack_memory(0, 0); break; + case 38: __asan_region_is_poisoned(0, 0); break; + case 39: __asan_describe_address(0); break; } } @@ -246,9 +297,9 @@ static void InitializeHighMemEnd() { CHECK_EQ((kHighMemBeg % GetPageSizeCached()), 0); } -static void ProtectGap(uptr a, uptr size) { - void *res = Mprotect(a, size); - if (a == (uptr)res) +static void ProtectGap(uptr addr, uptr size) { + 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"); @@ -296,9 +347,9 @@ static void PrintAddressSpaceLayout() { Printf("malloc_context_size=%zu\n", (uptr)common_flags()->malloc_context_size); - Printf("SHADOW_SCALE: %zx\n", (uptr)SHADOW_SCALE); - Printf("SHADOW_GRANULARITY: %zx\n", (uptr)SHADOW_GRANULARITY); - Printf("SHADOW_OFFSET: %zx\n", (uptr)SHADOW_OFFSET); + Printf("SHADOW_SCALE: %d\n", (int)SHADOW_SCALE); + Printf("SHADOW_GRANULARITY: %d\n", (int)SHADOW_GRANULARITY); + Printf("SHADOW_OFFSET: 0x%zx\n", (uptr)SHADOW_OFFSET); CHECK(SHADOW_SCALE >= 3 && SHADOW_SCALE <= 7); if (kMidMemBeg) CHECK(kMidShadowBeg > kLowShadowEnd && @@ -316,6 +367,11 @@ static void AsanInitInternal() { // initialization steps look at flags(). InitializeFlags(); + CacheBinaryName(); + + AsanCheckIncompatibleRT(); + AsanCheckDynamicRTPrereqs(); + SetCanPoisonMemory(flags()->poison_heap); SetMallocContextSize(common_flags()->malloc_context_size); @@ -371,9 +427,9 @@ static void AsanInitInternal() { if (full_shadow_is_available) { // mmap the low shadow plus at least one page at the left. if (kLowShadowBeg) - ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); // protect the gap. ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); CHECK_EQ(kShadowGapEnd, kHighShadowBeg - 1); @@ -382,11 +438,11 @@ static void AsanInitInternal() { MemoryRangeIsAvailable(kMidMemEnd + 1, kHighShadowEnd)) { CHECK(kLowShadowBeg != kLowShadowEnd); // mmap the low shadow plus at least one page at the left. - ReserveShadowMemoryRange(shadow_start, kLowShadowEnd); + ReserveShadowMemoryRange(shadow_start, kLowShadowEnd, "low shadow"); // mmap the mid shadow. - ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd); + ReserveShadowMemoryRange(kMidShadowBeg, kMidShadowEnd, "mid shadow"); // mmap the high shadow. - ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd); + ReserveShadowMemoryRange(kHighShadowBeg, kHighShadowEnd, "high shadow"); // protect the gaps. ProtectGap(kShadowGapBeg, kShadowGapEnd - kShadowGapBeg + 1); ProtectGap(kShadowGap2Beg, kShadowGap2End - kShadowGap2Beg + 1); @@ -446,6 +502,10 @@ static void AsanInitInternal() { } #endif // CAN_SANITIZE_LEAKS +#if CAN_SANITIZE_UB + __ubsan::InitAsPlugin(); +#endif + InitializeSuppressions(); VReport(1, "AddressSanitizer Init done\n"); @@ -459,13 +519,11 @@ void AsanInitFromRtl() { #if ASAN_DYNAMIC // Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable -// (and thus normal initializer from .preinit_array haven't run). +// (and thus normal initializers from .preinit_array or modules haven't run). class AsanInitializer { public: // NOLINT AsanInitializer() { - AsanCheckIncompatibleRT(); - AsanCheckDynamicRTPrereqs(); AsanInitFromRtl(); } }; @@ -517,7 +575,6 @@ void NOINLINE __asan_set_death_callback(void (*callback)(void)) { // Initialize as requested from instrumented application code. // We use this call as a trigger to wake up ASan from deactivated state. void __asan_init() { - AsanCheckIncompatibleRT(); AsanActivate(); AsanInitInternal(); } diff --git a/lib/asan/asan_stats.cc b/lib/asan/asan_stats.cc index a78b7b1e926d..b8c68c32dfbf 100644 --- a/lib/asan/asan_stats.cc +++ b/lib/asan/asan_stats.cc @@ -51,12 +51,8 @@ void AsanStats::Print() { (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, mmaps, munmaps); - PrintMallocStatsArray(" mmaps by size class: ", mmaped_by_size); PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); - PrintMallocStatsArray(" frees by size class: ", freed_by_size); - PrintMallocStatsArray(" rfrees by size class: ", really_freed_by_size); - Printf("Stats: malloc large: %zu small slow: %zu\n", - malloc_large, malloc_small_slow); + Printf("Stats: malloc large: %zu\n", malloc_large); } void AsanStats::MergeFrom(const AsanStats *stats) { @@ -161,8 +157,7 @@ uptr __sanitizer_get_free_bytes() { GetAccumulatedStats(&stats); uptr total_free = stats.mmaped - stats.munmaped - + stats.really_freed - + stats.really_freed_redzones; + + stats.really_freed; uptr total_used = stats.malloced + stats.malloced_redzones; // Return sane value if total_free < total_used due to racy diff --git a/lib/asan/asan_stats.h b/lib/asan/asan_stats.h index c66848dc5713..4605135e166f 100644 --- a/lib/asan/asan_stats.h +++ b/lib/asan/asan_stats.h @@ -32,20 +32,14 @@ struct AsanStats { uptr freed; uptr real_frees; uptr really_freed; - uptr really_freed_redzones; uptr reallocs; uptr realloced; uptr mmaps; uptr mmaped; uptr munmaps; uptr munmaped; - uptr mmaped_by_size[kNumberOfSizeClasses]; - uptr malloced_by_size[kNumberOfSizeClasses]; - uptr freed_by_size[kNumberOfSizeClasses]; - uptr really_freed_by_size[kNumberOfSizeClasses]; - uptr malloc_large; - uptr malloc_small_slow; + uptr malloced_by_size[kNumberOfSizeClasses]; // Ctor for global AsanStats (accumulated stats for dead threads). explicit AsanStats(LinkerInitialized) { } diff --git a/lib/asan/asan_suppressions.cc b/lib/asan/asan_suppressions.cc index 62198aec64e7..41887b5c88b4 100644 --- a/lib/asan/asan_suppressions.cc +++ b/lib/asan/asan_suppressions.cc @@ -26,14 +26,28 @@ static SuppressionContext *suppression_ctx = nullptr; static const char kInterceptorName[] = "interceptor_name"; static const char kInterceptorViaFunction[] = "interceptor_via_fun"; static const char kInterceptorViaLibrary[] = "interceptor_via_lib"; +static const char kODRViolation[] = "odr_violation"; static const char *kSuppressionTypes[] = { - kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary}; + kInterceptorName, kInterceptorViaFunction, kInterceptorViaLibrary, + kODRViolation}; + +extern "C" { +#if SANITIZER_SUPPORTS_WEAK_HOOKS +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +const char *__asan_default_suppressions(); +#else +// No week hooks, provide empty implementation. +const char *__asan_default_suppressions() { return ""; } +#endif // SANITIZER_SUPPORTS_WEAK_HOOKS +} // extern "C" void InitializeSuppressions() { CHECK_EQ(nullptr, suppression_ctx); suppression_ctx = new (suppression_placeholder) // NOLINT SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); suppression_ctx->ParseFromFile(flags()->suppressions); + if (&__asan_default_suppressions) + suppression_ctx->Parse(__asan_default_suppressions()); } bool IsInterceptorSuppressed(const char *interceptor_name) { @@ -49,6 +63,13 @@ bool HaveStackTraceBasedSuppressions() { suppression_ctx->HasSuppressionType(kInterceptorViaLibrary); } +bool IsODRViolationSuppressed(const char *global_var_name) { + CHECK(suppression_ctx); + Suppression *s; + // Match "odr_violation" suppressions. + return suppression_ctx->Match(global_var_name, kODRViolation, &s); +} + bool IsStackTraceSuppressed(const StackTrace *stack) { if (!HaveStackTraceBasedSuppressions()) return false; @@ -60,14 +81,10 @@ bool IsStackTraceSuppressed(const StackTrace *stack) { uptr addr = stack->trace[i]; if (suppression_ctx->HasSuppressionType(kInterceptorViaLibrary)) { - const char *module_name; - uptr module_offset; // Match "interceptor_via_lib" suppressions. - if (symbolizer->GetModuleNameAndOffsetForPC(addr, &module_name, - &module_offset) && - suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) { - return true; - } + if (const char *module_name = symbolizer->GetModuleNameForPc(addr)) + if (suppression_ctx->Match(module_name, kInterceptorViaLibrary, &s)) + return true; } if (suppression_ctx->HasSuppressionType(kInterceptorViaFunction)) { diff --git a/lib/asan/asan_suppressions.h b/lib/asan/asan_suppressions.h index cd7ba2ef0ae7..5246b4b30334 100644 --- a/lib/asan/asan_suppressions.h +++ b/lib/asan/asan_suppressions.h @@ -23,6 +23,7 @@ void InitializeSuppressions(); bool IsInterceptorSuppressed(const char *interceptor_name); bool HaveStackTraceBasedSuppressions(); bool IsStackTraceSuppressed(const StackTrace *stack); +bool IsODRViolationSuppressed(const char *global_var_name); } // namespace __asan diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index 9da136c87ef1..50acfc42d6a2 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -34,19 +34,16 @@ class AsanThread; class AsanThreadContext : public ThreadContextBase { public: explicit AsanThreadContext(int tid) - : ThreadContextBase(tid), - announced(false), - destructor_iterations(kPthreadDestructorIterations), - stack_id(0), - thread(0) { - } + : ThreadContextBase(tid), announced(false), + destructor_iterations(GetPthreadDestructorIterations()), stack_id(0), + thread(0) {} bool announced; u8 destructor_iterations; u32 stack_id; AsanThread *thread; - void OnCreated(void *arg); - void OnFinished(); + void OnCreated(void *arg) override; + void OnFinished() override; }; // AsanThreadContext objects are never freed, so we need many of them. diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 693f0bcdee8e..addb3d40a696 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -22,21 +22,134 @@ #include "asan_interceptors.h" #include "asan_internal.h" #include "asan_report.h" +#include "asan_stack.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_mutex.h" +using namespace __asan; // NOLINT + extern "C" { - SANITIZER_INTERFACE_ATTRIBUTE - int __asan_should_detect_stack_use_after_return() { - __asan_init(); - return __asan_option_detect_stack_use_after_return; - } +SANITIZER_INTERFACE_ATTRIBUTE +int __asan_should_detect_stack_use_after_return() { + __asan_init(); + return __asan_option_detect_stack_use_after_return; +} + +// -------------------- A workaround for the abscence of weak symbols ----- {{{ +// We don't have a direct equivalent of weak symbols when using MSVC, but we can +// use the /alternatename directive to tell the linker to default a specific +// symbol to a specific value, which works nicely for allocator hooks and +// __asan_default_options(). +void __sanitizer_default_malloc_hook(void *ptr, uptr size) { } +void __sanitizer_default_free_hook(void *ptr) { } +const char* __asan_default_default_options() { return ""; } +const char* __asan_default_default_suppressions() { return ""; } +void __asan_default_on_error() {} +#pragma comment(linker, "/alternatename:___sanitizer_malloc_hook=___sanitizer_default_malloc_hook") // NOLINT +#pragma comment(linker, "/alternatename:___sanitizer_free_hook=___sanitizer_default_free_hook") // NOLINT +#pragma comment(linker, "/alternatename:___asan_default_options=___asan_default_default_options") // NOLINT +#pragma comment(linker, "/alternatename:___asan_default_suppressions=___asan_default_default_suppressions") // NOLINT +#pragma comment(linker, "/alternatename:___asan_on_error=___asan_default_on_error") // NOLINT +// }}} +} // extern "C" + +// ---------------------- Windows-specific inteceptors ---------------- {{{ +INTERCEPTOR_WINAPI(void, RaiseException, void *a, void *b, void *c, void *d) { + CHECK(REAL(RaiseException)); + __asan_handle_no_return(); + REAL(RaiseException)(a, b, c, d); +} + +INTERCEPTOR(int, _except_handler3, void *a, void *b, void *c, void *d) { + CHECK(REAL(_except_handler3)); + __asan_handle_no_return(); + return REAL(_except_handler3)(a, b, c, d); +} + +#if ASAN_DYNAMIC +// This handler is named differently in -MT and -MD CRTs. +#define _except_handler4 _except_handler4_common +#endif +INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { + CHECK(REAL(_except_handler4)); + __asan_handle_no_return(); + return REAL(_except_handler4)(a, b, c, d); +} + +static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { + AsanThread *t = (AsanThread*)arg; + SetCurrentThread(t); + return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); +} + +INTERCEPTOR_WINAPI(DWORD, CreateThread, + void* security, uptr stack_size, + DWORD (__stdcall *start_routine)(void*), void* arg, + DWORD thr_flags, void* tid) { + // Strict init-order checking is thread-hostile. + if (flags()->strict_init_order) + StopInitOrderChecking(); + GET_STACK_TRACE_THREAD; + // FIXME: The CreateThread interceptor is not the same as a pthread_create + // one. This is a bandaid fix for PR22025. + bool detached = false; // FIXME: how can we determine it on Windows? + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = + AsanThread::Create(start_routine, arg, current_tid, &stack, detached); + return REAL(CreateThread)(security, stack_size, + asan_thread_start, t, thr_flags, tid); +} + +namespace { +BlockingMutex mu_for_thread_tracking(LINKER_INITIALIZED); + +void EnsureWorkerThreadRegistered() { + // FIXME: GetCurrentThread relies on TSD, which might not play well with + // system thread pools. We might want to use something like reference + // counting to zero out GetCurrentThread() underlying storage when the last + // work item finishes? Or can we disable reclaiming of threads in the pool? + BlockingMutexLock l(&mu_for_thread_tracking); + if (__asan::GetCurrentThread()) + return; + + AsanThread *t = AsanThread::Create( + /* start_routine */ nullptr, /* arg */ nullptr, + /* parent_tid */ -1, /* stack */ nullptr, /* detached */ true); + t->Init(); + asanThreadRegistry().StartThread(t->tid(), 0, 0); + SetCurrentThread(t); +} +} // namespace + +INTERCEPTOR_WINAPI(DWORD, NtWaitForWorkViaWorkerFactory, DWORD a, DWORD b) { + // NtWaitForWorkViaWorkerFactory is called from system worker pool threads to + // query work scheduled by BindIoCompletionCallback, QueueUserWorkItem, etc. + // System worker pool threads are created at arbitraty point in time and + // without using CreateThread, so we wrap NtWaitForWorkViaWorkerFactory + // instead and don't register a specific parent_tid/stack. + EnsureWorkerThreadRegistered(); + return REAL(NtWaitForWorkViaWorkerFactory)(a, b); } +// }}} + namespace __asan { -// ---------------------- TSD ---------------- {{{1 +void InitializePlatformInterceptors() { + ASAN_INTERCEPT_FUNC(CreateThread); + ASAN_INTERCEPT_FUNC(RaiseException); + ASAN_INTERCEPT_FUNC(_except_handler3); + ASAN_INTERCEPT_FUNC(_except_handler4); + + // NtWaitForWorkViaWorkerFactory is always linked dynamically. + CHECK(::__interception::OverrideFunction( + "NtWaitForWorkViaWorkerFactory", + (uptr)WRAP(NtWaitForWorkViaWorkerFactory), + (uptr *)&REAL(NtWaitForWorkViaWorkerFactory))); +} + +// ---------------------- TSD ---------------- {{{ static bool tsd_key_inited = false; static __declspec(thread) void *fake_tsd = 0; @@ -59,7 +172,9 @@ void AsanTSDSet(void *tsd) { void PlatformTSDDtor(void *tsd) { AsanThread::TSDDtor(tsd); } -// ---------------------- Various stuff ---------------- {{{1 +// }}} + +// ---------------------- Various stuff ---------------- {{{ void DisableReexec() { // No need to re-exec on Windows. } @@ -93,23 +208,6 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) { static LPTOP_LEVEL_EXCEPTION_FILTER default_seh_handler; -SignalContext SignalContext::Create(void *siginfo, void *context) { - EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo; - CONTEXT *context_record = (CONTEXT*)context; - - uptr pc = (uptr)exception_record->ExceptionAddress; -#ifdef _WIN64 - uptr bp = (uptr)context_record->Rbp; - uptr sp = (uptr)context_record->Rsp; -#else - uptr bp = (uptr)context_record->Ebp; - uptr sp = (uptr)context_record->Esp; -#endif - uptr access_addr = exception_record->ExceptionInformation[1]; - - return SignalContext(context, access_addr, pc, sp, bp); -} - static long WINAPI SEHHandler(EXCEPTION_POINTERS *info) { EXCEPTION_RECORD *exception_record = info->ExceptionRecord; CONTEXT *context = info->ContextRecord; @@ -162,7 +260,7 @@ int __asan_set_seh_filter() { static __declspec(allocate(".CRT$XIZ")) int (*__intercept_seh)() = __asan_set_seh_filter; #endif - +// }}} } // namespace __asan #endif // _WIN32 diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc index 5d39e33096a8..b77f18168ae5 100644 --- a/lib/asan/asan_win_dll_thunk.cc +++ b/lib/asan/asan_win_dll_thunk.cc @@ -303,8 +303,8 @@ INTERFACE_FUNCTION(__sanitizer_cov_init) 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_with_check) -INTERFACE_FUNCTION(__sanitizer_free_hook) INTERFACE_FUNCTION(__sanitizer_get_allocated_size) INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes) @@ -314,13 +314,14 @@ INTERFACE_FUNCTION(__sanitizer_get_heap_size) INTERFACE_FUNCTION(__sanitizer_get_ownership) INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) -INTERFACE_FUNCTION(__sanitizer_malloc_hook) INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) INTERFACE_FUNCTION(__sanitizer_print_stack_trace) INTERFACE_FUNCTION(__sanitizer_ptr_cmp) INTERFACE_FUNCTION(__sanitizer_ptr_sub) INTERFACE_FUNCTION(__sanitizer_report_error_summary) INTERFACE_FUNCTION(__sanitizer_reset_coverage) +INTERFACE_FUNCTION(__sanitizer_get_number_of_counters) +INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters) INTERFACE_FUNCTION(__sanitizer_sandbox_on_notify) INTERFACE_FUNCTION(__sanitizer_set_death_callback) INTERFACE_FUNCTION(__sanitizer_set_report_path) @@ -380,11 +381,15 @@ INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT INTERCEPT_LIBRARY_FUNCTION(strchr); INTERCEPT_LIBRARY_FUNCTION(strcmp); INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT +INTERCEPT_LIBRARY_FUNCTION(strcspn); INTERCEPT_LIBRARY_FUNCTION(strlen); INTERCEPT_LIBRARY_FUNCTION(strncat); INTERCEPT_LIBRARY_FUNCTION(strncmp); INTERCEPT_LIBRARY_FUNCTION(strncpy); INTERCEPT_LIBRARY_FUNCTION(strnlen); +INTERCEPT_LIBRARY_FUNCTION(strpbrk); +INTERCEPT_LIBRARY_FUNCTION(strspn); +INTERCEPT_LIBRARY_FUNCTION(strstr); INTERCEPT_LIBRARY_FUNCTION(strtol); INTERCEPT_LIBRARY_FUNCTION(wcslen); diff --git a/lib/asan/asan_win_dynamic_runtime_thunk.cc b/lib/asan/asan_win_dynamic_runtime_thunk.cc index 19456141c1ec..d59f9f5768a0 100644 --- a/lib/asan/asan_win_dynamic_runtime_thunk.cc +++ b/lib/asan/asan_win_dynamic_runtime_thunk.cc @@ -15,7 +15,8 @@ // // This includes: // - forwarding the detect_stack_use_after_return runtime option -// - installing a custom SEH handler +// - working around deficiencies of the MD runtime +// - installing a custom SEH handlerx // //===----------------------------------------------------------------------===// @@ -24,9 +25,13 @@ // simplifies the build procedure. #ifdef ASAN_DYNAMIC_RUNTIME_THUNK #include <windows.h> -#include <psapi.h> -extern "C" { +// First, declare CRT sections we'll be using in this file +#pragma section(".CRT$XID", long, read) // NOLINT +#pragma section(".CRT$XIZ", long, read) // NOLINT +#pragma section(".CRT$XTW", long, read) // NOLINT +#pragma section(".CRT$XTY", long, read) // NOLINT + //////////////////////////////////////////////////////////////////////////////// // Define a copy of __asan_option_detect_stack_use_after_return that should be // used when linking an MD runtime with a set of object files on Windows. @@ -38,82 +43,55 @@ extern "C" { // with a MT or MD runtime and we don't want to use ugly __imp_ names on Windows // just to work around this issue, let's clone the a variable that is // constant after initialization anyways. +extern "C" { __declspec(dllimport) int __asan_should_detect_stack_use_after_return(); int __asan_option_detect_stack_use_after_return = __asan_should_detect_stack_use_after_return(); } //////////////////////////////////////////////////////////////////////////////// -// For some reason, the MD CRT doesn't call the C/C++ terminators as MT does. -// To work around this, for each DLL we schedule a call to -// UnregisterGlobalsInRange atexit() specifying the address range of the DLL -// image to unregister globals in that range. We don't do the same -// for the main module (.exe) as the asan_globals.cc allocator is destroyed -// by the time UnregisterGlobalsInRange is executed. -// See PR22545 for the details. -namespace __asan { -__declspec(dllimport) -void UnregisterGlobalsInRange(void *beg, void *end); -} +// For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL +// unload or on exit. ASan relies on LLVM global_dtors to call +// __asan_unregister_globals on these events, which unfortunately doesn't work +// with the MD runtime, see PR22545 for the details. +// To work around this, for each DLL we schedule a call to UnregisterGlobals +// using atexit() that calls a small subset of C terminators +// where LLVM global_dtors is placed. Fingers crossed, no other C terminators +// are there. +extern "C" void __cdecl _initterm(void *a, void *b); namespace { -void *this_module_base, *this_module_end; +__declspec(allocate(".CRT$XTW")) void* before_global_dtors = 0; +__declspec(allocate(".CRT$XTY")) void* after_global_dtors = 0; void UnregisterGlobals() { - __asan::UnregisterGlobalsInRange(this_module_base, this_module_end); + _initterm(&before_global_dtors, &after_global_dtors); } int ScheduleUnregisterGlobals() { - HMODULE this_module = 0; - // Increments the reference counter of the DLL module, so need to call - // FreeLibrary later. - if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, - (LPCTSTR)&UnregisterGlobals, &this_module)) - return 1; - - // Skip the main module. - if (this_module == GetModuleHandle(0)) - return 0; - - MODULEINFO mi; - bool success = - GetModuleInformation(GetCurrentProcess(), this_module, &mi, sizeof(mi)); - if (!FreeLibrary(this_module)) - return 2; - if (!success) - return 3; - - this_module_base = mi.lpBaseOfDll; - this_module_end = (char*)mi.lpBaseOfDll + mi.SizeOfImage; - return atexit(UnregisterGlobals); } -} // namespace - -/////////////////////////////////////////////////////////////////////////////// -// ASan SEH handling. -extern "C" __declspec(dllimport) int __asan_set_seh_filter(); -static int SetSEHFilter() { return __asan_set_seh_filter(); } -/////////////////////////////////////////////////////////////////////////////// -// We schedule some work at start-up by placing callbacks to our code to the -// list of CRT C initializers. -// -// First, declare sections we'll be using: -#pragma section(".CRT$XID", long, read) // NOLINT -#pragma section(".CRT$XIZ", long, read) // NOLINT - -// We need to call 'atexit(UnregisterGlobals);' after atexit() is initialized -// (.CRT$XIC) but before the C++ constructors (.CRT$XCA). +// We need to call 'atexit(UnregisterGlobals);' as early as possible, but after +// atexit() is initialized (.CRT$XIC). As this is executed before C++ +// initializers (think ctors for globals), UnregisterGlobals gets executed after +// dtors for C++ globals. __declspec(allocate(".CRT$XID")) -static int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; +int (*__asan_schedule_unregister_globals)() = ScheduleUnregisterGlobals; +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// ASan SEH handling. // We need to set the ASan-specific SEH handler at the end of CRT initialization // of each module (see also asan_win.cc). -// +extern "C" { +__declspec(dllimport) int __asan_set_seh_filter(); +static int SetSEHFilter() { return __asan_set_seh_filter(); } + // Unfortunately, putting a pointer to __asan_set_seh_filter into // __asan_intercept_seh gets optimized out, so we have to use an extra function. -extern "C" __declspec(allocate(".CRT$XIZ")) -int (*__asan_seh_interceptor)() = SetSEHFilter; +__declspec(allocate(".CRT$XIZ")) int (*__asan_seh_interceptor)() = SetSEHFilter; +} #endif // ASAN_DYNAMIC_RUNTIME_THUNK diff --git a/lib/asan/scripts/asan_symbolize.py b/lib/asan/scripts/asan_symbolize.py index 59fceaaed814..b9d3ad3ad2fe 100755 --- a/lib/asan/scripts/asan_symbolize.py +++ b/lib/asan/scripts/asan_symbolize.py @@ -23,6 +23,7 @@ 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): @@ -392,6 +393,8 @@ class SymbolizationLoop(object): [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)) diff --git a/lib/asan/tests/CMakeLists.txt b/lib/asan/tests/CMakeLists.txt index 513d1282f8a6..afdd2adf0887 100644 --- a/lib/asan/tests/CMakeLists.txt +++ b/lib/asan/tests/CMakeLists.txt @@ -52,6 +52,11 @@ if(APPLE) list(APPEND ASAN_UNITTEST_COMMON_LINKFLAGS ${DARWIN_osx_LINKFLAGS}) endif() +if(MSVC) + # Disable exceptions on Windows until they work reliably. + list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -fno-exceptions -DGTEST_HAS_SEH=0) +endif() + set(ASAN_BLACKLIST_FILE "${CMAKE_CURRENT_SOURCE_DIR}/asan_test.ignore") set(ASAN_UNITTEST_INSTRUMENTED_CFLAGS ${ASAN_UNITTEST_COMMON_CFLAGS} @@ -94,6 +99,7 @@ append_list_if(ANDROID atomic ASAN_UNITTEST_INSTRUMENTED_LIBS) set(ASAN_UNITTEST_NOINST_LINKFLAGS ${ASAN_UNITTEST_COMMON_LINKFLAGS}) append_list_if(COMPILER_RT_HAS_LIBM -lm ASAN_UNITTEST_NOINST_LINKFLAGS) append_list_if(COMPILER_RT_HAS_LIBDL -ldl ASAN_UNITTEST_NOINST_LINKFLAGS) +append_list_if(COMPILER_RT_HAS_LIBRT -lrt ASAN_UNITTEST_NOINST_LINKFLAGS) append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_UNITTEST_NOINST_LINKFLAGS) append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread ASAN_DYNAMIC_UNITTEST_INSTRUMENTED_LINKFLAGS) @@ -123,7 +129,7 @@ endmacro() # Link ASan unit test for a given architecture from a set # of objects in with given linker flags. macro(add_asan_test test_suite test_name arch kind) - parse_arguments(TEST "OBJECTS;LINKFLAGS;SUBDIR" "WITH_TEST_RUNTIME" ${ARGN}) + cmake_parse_arguments(TEST "WITH_TEST_RUNTIME" "" "OBJECTS;LINKFLAGS;SUBDIR" ${ARGN}) get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) set(TEST_DEPS ${TEST_OBJECTS}) if(NOT COMPILER_RT_STANDALONE_BUILD) @@ -214,18 +220,18 @@ macro(add_asan_tests_for_arch_and_kind arch kind) $<TARGET_OBJECTS:RTAsan.osx> $<TARGET_OBJECTS:RTInterception.osx> $<TARGET_OBJECTS:RTSanitizerCommon.osx> - $<TARGET_OBJECTS:RTLSanCommon.osx>) + $<TARGET_OBJECTS:RTLSanCommon.osx> + $<TARGET_OBJECTS:RTUbsan.osx>) else() set(ASAN_TEST_RUNTIME_OBJECTS $<TARGET_OBJECTS:RTAsan.${arch}> $<TARGET_OBJECTS:RTAsan_cxx.${arch}> $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}>) - if(NOT WIN32) - list(APPEND ASAN_TEST_RUNTIME_OBJECTS - $<TARGET_OBJECTS:RTLSanCommon.${arch}>) - endif() + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTLSanCommon.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}> + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}>) endif() add_library(${ASAN_TEST_RUNTIME} STATIC ${ASAN_TEST_RUNTIME_OBJECTS}) set_target_properties(${ASAN_TEST_RUNTIME} PROPERTIES @@ -270,6 +276,8 @@ if(ANDROID) $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}> + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}> ${COMPILER_RT_GTEST_SOURCE} ${ASAN_NOINST_TEST_SOURCES}) set_target_compile_flags(AsanNoinstTest ${ASAN_UNITTEST_COMMON_CFLAGS}) diff --git a/lib/asan/tests/asan_asm_test.cc b/lib/asan/tests/asan_asm_test.cc index 1d8b04d611bd..200de2c137a5 100644 --- a/lib/asan/tests/asan_asm_test.cc +++ b/lib/asan/tests/asan_asm_test.cc @@ -232,7 +232,7 @@ TEST(AddressSanitizer, asm_flags) { long magic = 0x1234; long r = 0x0; -#if defined(__x86_64__) +#if defined(__x86_64__) && !defined(__ILP32__) __asm__("xorq %%rax, %%rax \n\t" "movq (%[p]), %%rax \n\t" "sete %%al \n\t" @@ -248,7 +248,7 @@ TEST(AddressSanitizer, asm_flags) { : [r] "=r"(r) : [p] "r"(&magic) : "eax", "memory"); -#endif // defined(__x86_64__) +#endif // defined(__x86_64__) && !defined(__ILP32__) ASSERT_EQ(0x1, r); } diff --git a/lib/asan/tests/asan_str_test.cc b/lib/asan/tests/asan_str_test.cc index 1cd2a08b9021..89b0d3d2d0f9 100644 --- a/lib/asan/tests/asan_str_test.cc +++ b/lib/asan/tests/asan_str_test.cc @@ -290,9 +290,6 @@ void RunStrCmpTest(PointerToStrCmp StrCmp) { Ident(StrCmp(s1, s2)); Ident(StrCmp(s1, s2 + size - 1)); Ident(StrCmp(s1 + size - 1, s2 + size - 1)); - s1[size - 1] = 'z'; - s2[size - 1] = 'x'; - Ident(StrCmp(s1, s2)); // 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)); @@ -371,17 +368,14 @@ TEST(AddressSanitizer, StrCatOOBTest) { // 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 + to_size, from), RightOOBWriteMessage(0)); 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 not zero-terminated. - memset(to, 'z', to_size); - EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(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. @@ -409,7 +403,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) { // 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 + to_size, from, 2), RightOOBWriteMessage(0)); EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0)); memset(from, 'z', from_size); @@ -417,8 +410,6 @@ TEST(AddressSanitizer, StrNCatOOBTest) { to[0] = '\0'; // "from" is too short. EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0)); - // "to" is not zero-terminated. - EXPECT_DEATH(strncat(to + 1, from, 1), RightOOBWriteMessage(0)); // "to" is too short to fit "from". to[0] = 'z'; to[to_size - from_size + 1] = '\0'; @@ -508,20 +499,15 @@ void RunAtoiOOBTest(PointerToCallAtoi Atoi) { 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 or other non-digit. + // Make last symbol a terminating NULL array[9] = '\0'; Atoi(array); - array[9] = 'a'; - Atoi(array); - Atoi(array + 9); // Sometimes we need to detect overflow if no digits are found. memset(array, ' ', 10); EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); array[9] = '-'; EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0)); - array[8] = '-'; - Atoi(array); free(array); } @@ -546,7 +532,6 @@ typedef void(*PointerToCallStrtol)(const char*, char**, int); void RunStrtolOOBTest(PointerToCallStrtol Strtol) { char *array = MallocAndMemsetString(3); - char *endptr = NULL; array[0] = '1'; array[1] = '2'; array[2] = '3'; @@ -554,19 +539,12 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) { 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). - Strtol(array, &endptr, 3); - EXPECT_EQ(array + 2, endptr); EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); array[2] = 'z'; - Strtol(array, &endptr, 35); - EXPECT_EQ(array + 2, endptr); EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0)); // Add terminating zero to get rid of overflow. array[2] = '\0'; Strtol(array, NULL, 36); - // Don't check for overflow if base is invalid. - Strtol(array - 1, NULL, -1); - Strtol(array + 3, NULL, 1); // Sometimes we need to detect overflow if no digits are found. array[0] = array[1] = array[2] = ' '; EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); @@ -574,13 +552,6 @@ void RunStrtolOOBTest(PointerToCallStrtol Strtol) { EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); array[2] = '-'; EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); - array[1] = '+'; - Strtol(array, NULL, 0); - array[1] = array[2] = 'z'; - Strtol(array, &endptr, 0); - EXPECT_EQ(array, endptr); - Strtol(array + 2, NULL, 0); - EXPECT_EQ(array, endptr); free(array); } diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 952b05e21fe2..07d59e09a72f 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -31,13 +31,13 @@ NOINLINE void free_aaa(void *p) { free_bbb(p); break_optimization(0);} template<typename T> NOINLINE void uaf_test(int size, int off) { - char *p = (char *)malloc_aaa(size); + void *p = malloc_aaa(size); free_aaa(p); for (int i = 1; i < 100; i++) free_aaa(malloc_aaa(i)); fprintf(stderr, "writing %ld byte(s) at %p with offset %d\n", (long)sizeof(T), p, off); - asan_write((T*)(p + off)); + asan_write((T *)((char *)p + off)); } TEST(AddressSanitizer, HasFeatureAddressSanitizerTest) { @@ -436,10 +436,10 @@ TEST(AddressSanitizer, WrongFreeTest) { void DoubleFree() { int *x = (int*)malloc(100 * sizeof(int)); - fprintf(stderr, "DoubleFree: x=%p\n", x); + fprintf(stderr, "DoubleFree: x=%p\n", (void *)x); free(x); free(x); - fprintf(stderr, "should have failed in the second free(%p)\n", x); + fprintf(stderr, "should have failed in the second free(%p)\n", (void *)x); abort(); } @@ -569,7 +569,7 @@ TEST(AddressSanitizer, LongJmpTest) { } #if !defined(_WIN32) // Only basic longjmp is available on Windows. -NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) { +NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects. int a; int b; @@ -577,10 +577,10 @@ NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) { int *A = Ident(&a); int *B = Ident(&b); *A = *B; - __builtin_longjmp((void**)buf, 1); + _longjmp(buf, 1); } -NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { +NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { // create three red zones for these two stack objects. int a; int b; @@ -588,10 +588,14 @@ NOINLINE void UnderscopeLongJmpFunc1(jmp_buf buf) { int *A = Ident(&a); int *B = Ident(&b); *A = *B; - _longjmp(buf, 1); + siglongjmp(buf, 1); } -NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { +#if !defined(__ANDROID__) && !defined(__arm__) && \ + !defined(__powerpc64__) && !defined(__powerpc__) && \ + !defined(__aarch64__) && !defined(__mips__) && \ + !defined(__mips64) +NOINLINE void BuiltinLongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects. int a; int b; @@ -599,12 +603,9 @@ NOINLINE void SigLongJmpFunc1(sigjmp_buf buf) { int *A = Ident(&a); int *B = Ident(&b); *A = *B; - siglongjmp(buf, 1); + __builtin_longjmp((void**)buf, 1); } -#if !defined(__ANDROID__) && !defined(__arm__) && \ - !defined(__powerpc64__) && !defined(__powerpc__) && \ - !defined(__aarch64__) // Does not work on Power and ARM: // https://code.google.com/p/address-sanitizer/issues/detail?id=185 TEST(AddressSanitizer, BuiltinLongJmpTest) { @@ -616,7 +617,8 @@ TEST(AddressSanitizer, BuiltinLongJmpTest) { } } #endif // !defined(__ANDROID__) && !defined(__powerpc64__) && - // !defined(__powerpc__) && !defined(__arm__) + // !defined(__powerpc__) && !defined(__arm__) && + // !defined(__mips__) && !defined(__mips64) TEST(AddressSanitizer, UnderscopeLongJmpTest) { static jmp_buf buf; @@ -1243,7 +1245,7 @@ TEST(AddressSanitizer, DISABLED_DemoTooMuchMemoryTest) { const size_t kAllocSize = (1 << 28) - 1024; size_t total_size = 0; while (true) { - char *x = (char*)malloc(kAllocSize); + void *x = malloc(kAllocSize); memset(x, 0, kAllocSize); total_size += kAllocSize; fprintf(stderr, "total: %ldM %p\n", (long)total_size >> 20, x); diff --git a/lib/builtins/CMakeLists.txt b/lib/builtins/CMakeLists.txt index 4d102c615003..98d518a83e2c 100644 --- a/lib/builtins/CMakeLists.txt +++ b/lib/builtins/CMakeLists.txt @@ -44,6 +44,7 @@ set(GENERIC_SOURCES enable_execute_stack.c eprintf.c extendsfdf2.c + extendhfsf2.c ffsdi2.c ffsti2.c fixdfdi.c @@ -123,7 +124,9 @@ set(GENERIC_SOURCES subvti3.c subtf3.c trampoline_setup.c + truncdfhf2.c truncdfsf2.c + truncsfhf2.c ucmpdi2.c ucmpti2.c udivdi3.c @@ -151,6 +154,12 @@ set(x86_64_SOURCES x86_64/floatundixf.S ${GENERIC_SOURCES}) +if(WIN32) + set(x86_64_SOURCES + ${x86_64_SOURCES} + x86_64/chkstk.S) +endif() + set(i386_SOURCES i386/ashldi3.S i386/ashrdi3.S @@ -168,6 +177,12 @@ set(i386_SOURCES i386/umoddi3.S ${GENERIC_SOURCES}) +if(WIN32) + set(i386_SOURCES + ${i386_SOURCES} + i386/chkstk.S) +endif() + set(i686_SOURCES ${i386_SOURCES}) @@ -257,7 +272,7 @@ set(arm_SOURCES add_custom_target(builtins) -if (NOT WIN32) +if (NOT WIN32 OR MINGW) foreach (arch x86_64 i386 i686 arm) if (CAN_TARGET_${arch}) # Filter out generic versions of routines that are re-implemented in diff --git a/lib/builtins/atomic_flag_clear.c b/lib/builtins/atomic_flag_clear.c new file mode 100644 index 000000000000..15984cd5267d --- /dev/null +++ b/lib/builtins/atomic_flag_clear.c @@ -0,0 +1,19 @@ +/*===-- atomic_flag_clear.c -------------------------------------------------=== + * + * 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 atomic_flag_clear from C11's 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); +} diff --git a/lib/builtins/atomic_flag_clear_explicit.c b/lib/builtins/atomic_flag_clear_explicit.c new file mode 100644 index 000000000000..0f7859c2cd82 --- /dev/null +++ b/lib/builtins/atomic_flag_clear_explicit.c @@ -0,0 +1,20 @@ +/*===-- atomic_flag_clear_explicit.c ----------------------------------------=== + * + * 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 atomic_flag_clear_explicit from C11's 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); +} diff --git a/lib/builtins/atomic_flag_test_and_set.c b/lib/builtins/atomic_flag_test_and_set.c new file mode 100644 index 000000000000..07209fc02d5e --- /dev/null +++ b/lib/builtins/atomic_flag_test_and_set.c @@ -0,0 +1,19 @@ +/*===-- atomic_flag_test_and_set.c ------------------------------------------=== + * + * 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 atomic_flag_test_and_set from C11's 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); +} diff --git a/lib/builtins/atomic_flag_test_and_set_explicit.c b/lib/builtins/atomic_flag_test_and_set_explicit.c new file mode 100644 index 000000000000..eaa5be08df46 --- /dev/null +++ b/lib/builtins/atomic_flag_test_and_set_explicit.c @@ -0,0 +1,20 @@ +/*===-- atomic_flag_test_and_set_explicit.c ---------------------------------=== + * + * 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 atomic_flag_test_and_set_explicit from C11's 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); +} diff --git a/lib/builtins/atomic_signal_fence.c b/lib/builtins/atomic_signal_fence.c new file mode 100644 index 000000000000..ad292d2f1c72 --- /dev/null +++ b/lib/builtins/atomic_signal_fence.c @@ -0,0 +1,19 @@ +/*===-- atomic_signal_fence.c -----------------------------------------------=== + * + * 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 atomic_signal_fence from C11's stdatomic.h. + * + *===------------------------------------------------------------------------=== + */ + +#include <stdatomic.h> +#undef atomic_signal_fence +void atomic_signal_fence(memory_order order) { + __c11_atomic_signal_fence(order); +} diff --git a/lib/builtins/atomic_thread_fence.c b/lib/builtins/atomic_thread_fence.c new file mode 100644 index 000000000000..71f698c9de75 --- /dev/null +++ b/lib/builtins/atomic_thread_fence.c @@ -0,0 +1,19 @@ +/*===-- atomic_thread_fence.c -----------------------------------------------=== + * + * 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 atomic_thread_fence from C11's stdatomic.h. + * + *===------------------------------------------------------------------------=== + */ + +#include <stdatomic.h> +#undef atomic_thread_fence +void atomic_thread_fence(memory_order order) { + __c11_atomic_thread_fence(order); +} diff --git a/lib/builtins/clear_cache.c b/lib/builtins/clear_cache.c index 8dc0fb1c5907..ede7659a051d 100644 --- a/lib/builtins/clear_cache.c +++ b/lib/builtins/clear_cache.c @@ -9,11 +9,12 @@ */ #include "int_lib.h" +#include <stddef.h> #if __APPLE__ #include <libkern/OSCacheControl.h> #endif -#if defined(__FreeBSD__) && defined(__arm__) +#if (defined(__FreeBSD__) || defined(__Bitrig__)) && defined(__arm__) #include <sys/types.h> #include <machine/sysarch.h> #endif @@ -25,6 +26,7 @@ #if defined(__mips__) #include <sys/cachectl.h> #include <sys/syscall.h> + #include <unistd.h> #if defined(__ANDROID__) && defined(__LP64__) /* * clear_mips_cache - Invalidates instruction cache for Mips. @@ -89,7 +91,7 @@ void __clear_cache(void *start, void *end) { * so there is nothing to do */ #elif defined(__arm__) && !defined(__APPLE__) - #if defined(__FreeBSD__) || defined(__NetBSD__) + #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__Bitrig__) struct arm_sync_icache_args arg; arg.addr = (uintptr_t)start; @@ -126,6 +128,7 @@ void __clear_cache(void *start, void *end) { #elif defined(__aarch64__) && !defined(__APPLE__) uint64_t xstart = (uint64_t)(uintptr_t) start; uint64_t xend = (uint64_t)(uintptr_t) end; + uint64_t addr; // Get Cache Type Info uint64_t ctr_el0; @@ -136,12 +139,12 @@ void __clear_cache(void *start, void *end) { * uintptr_t in case this runs in an IPL32 environment. */ const size_t dcache_line_size = 4 << ((ctr_el0 >> 16) & 15); - for (uint64_t addr = xstart; addr < xend; addr += dcache_line_size) + for (addr = xstart; addr < xend; addr += dcache_line_size) __asm __volatile("dc cvau, %0" :: "r"(addr)); __asm __volatile("dsb ish"); const size_t icache_line_size = 4 << ((ctr_el0 >> 0) & 15); - for (uint64_t addr = xstart; addr < xend; addr += icache_line_size) + for (addr = xstart; addr < xend; addr += icache_line_size) __asm __volatile("ic ivau, %0" :: "r"(addr)); __asm __volatile("isb sy"); #else diff --git a/lib/builtins/enable_execute_stack.c b/lib/builtins/enable_execute_stack.c index 63d836b31d17..23e494051adf 100644 --- a/lib/builtins/enable_execute_stack.c +++ b/lib/builtins/enable_execute_stack.c @@ -10,7 +10,9 @@ #include "int_lib.h" +#ifndef _WIN32 #include <sys/mman.h> +#endif /* #include "config.h" * FIXME: CMake - include when cmake system is ready. @@ -18,9 +20,14 @@ */ #define HAVE_SYSCONF 1 +#ifdef _WIN32 +#include <windef.h> +#include <winbase.h> +#else #ifndef __APPLE__ #include <unistd.h> #endif /* __APPLE__ */ +#endif /* _WIN32 */ #if __LP64__ #define TRAMPOLINE_SIZE 48 @@ -40,6 +47,12 @@ COMPILER_RT_ABI void __enable_execute_stack(void* addr) { +#if _WIN32 + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery (addr, &mbi, sizeof(mbi))) + return; /* We should probably assert here because there is no return value */ + VirtualProtect (mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect); +#else #if __APPLE__ /* On Darwin, pagesize is always 4096 bytes */ const uintptr_t pageSize = 4096; @@ -55,4 +68,5 @@ __enable_execute_stack(void* addr) unsigned char* endPage = (unsigned char*)((p+TRAMPOLINE_SIZE+pageSize) & pageAlignMask); size_t length = endPage - startPage; (void) mprotect((void *)startPage, length, PROT_READ | PROT_WRITE | PROT_EXEC); +#endif } diff --git a/lib/builtins/extendhfsf2.c b/lib/builtins/extendhfsf2.c new file mode 100644 index 000000000000..7524e2ea7ed6 --- /dev/null +++ b/lib/builtins/extendhfsf2.c @@ -0,0 +1,23 @@ +//===-- lib/extendhfsf2.c - half -> single conversion -------------*- C -*-===// +// +// 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 SRC_HALF +#define DST_SINGLE +#include "fp_extend_impl.inc" + +// 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) { + return __extendXfYf2__(a); +} + +COMPILER_RT_ABI float __gnu_h2f_ieee(uint16_t a) { + return __extendhfsf2(a); +} diff --git a/lib/builtins/fixdfdi.c b/lib/builtins/fixdfdi.c index 86f9f6ce8db3..14283ef42e61 100644 --- a/lib/builtins/fixdfdi.c +++ b/lib/builtins/fixdfdi.c @@ -6,40 +6,41 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixdfdi for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== */ -#include "int_lib.h" - -/* Returns: convert a to a signed long long, rounding toward zero. */ +#define DOUBLE_PRECISION +#include "fp_lib.h" +ARM_EABI_FNALIAS(d2lz, fixdfdi) -/* Assumption: double is a IEEE 64 bit floating point type - * su_int is a 32 bit integral type - * value in double is representable in di_int (no range checking performed) +#ifndef __SOFT_FP__ +/* Support for systems that have hardware floating-point; can set the invalid + * flag as a side-effect of computation. */ -/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ - -ARM_EABI_FNALIAS(d2lz, fixdfdi) +COMPILER_RT_ABI du_int __fixunsdfdi(double a); COMPILER_RT_ABI di_int __fixdfdi(double a) { - double_bits fb; - fb.f = a; - int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; - if (e < 0) - return 0; - di_int s = (si_int)(fb.u.s.high & 0x80000000) >> 31; - dwords r; - r.s.high = (fb.u.s.high & 0x000FFFFF) | 0x00100000; - r.s.low = fb.u.s.low; - if (e > 52) - r.all <<= (e - 52); - else - r.all >>= (52 - e); - return (r.all ^ s) - s; -} + if (a < 0.0) { + return -__fixunsdfdi(-a); + } + return __fixunsdfdi(a); +} + +#else +/* Support for systems that don't have hardware floating-point; there are no + * flags to set, and we don't want to code-gen to an unknown soft-float + * implementation. + */ + +typedef di_int fixint_t; +typedef du_int fixuint_t; +#include "fp_fixint_impl.inc" + +COMPILER_RT_ABI di_int +__fixdfdi(fp_t a) { + return __fixint(a); +} + +#endif diff --git a/lib/builtins/fixdfsi.c b/lib/builtins/fixdfsi.c index 88b2ff5e74a0..704e65bc43a1 100644 --- a/lib/builtins/fixdfsi.c +++ b/lib/builtins/fixdfsi.c @@ -1,50 +1,22 @@ -//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===// -// -// 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 double-precision to integer conversion for the -// compiler-rt library. No range checking is performed; the behavior of this -// conversion is undefined for out of range values in the C standard. -// -//===----------------------------------------------------------------------===// +/* ===-- fixdfsi.c - Implement __fixdfsi -----------------------------------=== + * + * 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" - -#include "int_lib.h" +typedef si_int fixint_t; +typedef su_int fixuint_t; +#include "fp_fixint_impl.inc" ARM_EABI_FNALIAS(d2iz, fixdfsi) -COMPILER_RT_ABI int +COMPILER_RT_ABI si_int __fixdfsi(fp_t a) { - - // Break a into sign, exponent, significand - const rep_t aRep = toRep(a); - const rep_t aAbs = aRep & absMask; - const int sign = aRep & signBit ? -1 : 1; - const int exponent = (aAbs >> significandBits) - exponentBias; - const rep_t significand = (aAbs & significandMask) | implicitBit; - - // If 0 < exponent < significandBits, right shift to get the result. - if ((unsigned int)exponent < significandBits) { - return sign * (significand >> (significandBits - exponent)); - } - - // If exponent is negative, the result is zero. - else if (exponent < 0) { - return 0; - } - - // If significandBits < exponent, left shift to get the result. This shift - // may end up being larger than the type width, which incurs undefined - // behavior, but the conversion itself is undefined in that case, so - // whatever the compiler decides to do is fine. - else { - return sign * (significand << (exponent - significandBits)); - } + return __fixint(a); } diff --git a/lib/builtins/fixdfti.c b/lib/builtins/fixdfti.c index 2c27f4bae3b9..aaf225e74f86 100644 --- a/lib/builtins/fixdfti.c +++ b/lib/builtins/fixdfti.c @@ -6,40 +6,21 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixdfti for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== */ #include "int_lib.h" #ifdef CRT_HAS_128BIT +#define DOUBLE_PRECISION +#include "fp_lib.h" -/* Returns: convert a to a signed long long, rounding toward zero. */ - -/* Assumption: double is a IEEE 64 bit floating point type - * su_int is a 32 bit integral type - * value in double is representable in ti_int (no range checking performed) - */ - -/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ +typedef ti_int fixint_t; +typedef tu_int fixuint_t; +#include "fp_fixint_impl.inc" COMPILER_RT_ABI ti_int -__fixdfti(double a) -{ - double_bits fb; - fb.f = a; - int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; - if (e < 0) - return 0; - ti_int s = (si_int)(fb.u.s.high & 0x80000000) >> 31; - ti_int r = 0x0010000000000000uLL | (0x000FFFFFFFFFFFFFuLL & fb.u.all); - if (e > 52) - r <<= (e - 52); - else - r >>= (52 - e); - return (r ^ s) - s; +__fixdfti(fp_t a) { + return __fixint(a); } #endif /* CRT_HAS_128BIT */ diff --git a/lib/builtins/fixsfdi.c b/lib/builtins/fixsfdi.c index 4f6cfdd7a5c6..fab47e272a25 100644 --- a/lib/builtins/fixsfdi.c +++ b/lib/builtins/fixsfdi.c @@ -1,43 +1,47 @@ /* ===-- fixsfdi.c - Implement __fixsfdi -----------------------------------=== * - * The LLVM Compiler Infrastructure + * 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 __fixsfdi for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== */ -#include "int_lib.h" +#define SINGLE_PRECISION +#include "fp_lib.h" -/* Returns: convert a to a signed long long, rounding toward zero. */ +ARM_EABI_FNALIAS(f2lz, fixsfdi) -/* Assumption: float is a IEEE 32 bit floating point type - * su_int is a 32 bit integral type - * value in float is representable in di_int (no range checking performed) +#ifndef __SOFT_FP__ +/* Support for systems that have hardware floating-point; can set the invalid + * flag as a side-effect of computation. */ -/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ - -ARM_EABI_FNALIAS(f2lz, fixsfdi) +COMPILER_RT_ABI du_int __fixunssfdi(float a); COMPILER_RT_ABI di_int __fixsfdi(float a) { - float_bits fb; - fb.f = a; - int e = ((fb.u & 0x7F800000) >> 23) - 127; - if (e < 0) - return 0; - di_int s = (si_int)(fb.u & 0x80000000) >> 31; - di_int r = (fb.u & 0x007FFFFF) | 0x00800000; - if (e > 23) - r <<= (e - 23); - else - r >>= (23 - e); - return (r ^ s) - s; + if (a < 0.0f) { + return -__fixunssfdi(-a); + } + return __fixunssfdi(a); } + +#else +/* Support for systems that don't have hardware floating-point; there are no + * flags to set, and we don't want to code-gen to an unknown soft-float + * implementation. + */ + +typedef di_int fixint_t; +typedef du_int fixuint_t; +#include "fp_fixint_impl.inc" + +COMPILER_RT_ABI di_int +__fixsfdi(fp_t a) { + return __fixint(a); +} + +#endif diff --git a/lib/builtins/fixsfsi.c b/lib/builtins/fixsfsi.c index e3cc42d5255a..f045536d6857 100644 --- a/lib/builtins/fixsfsi.c +++ b/lib/builtins/fixsfsi.c @@ -1,47 +1,22 @@ -//===-- lib/fixsfsi.c - Single-precision -> integer conversion ----*- C -*-===// -// -// 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 single-precision to integer conversion for the -// compiler-rt library. No range checking is performed; the behavior of this -// conversion is undefined for out of range values in the C standard. -// -//===----------------------------------------------------------------------===// +/* ===-- fixsfsi.c - Implement __fixsfsi -----------------------------------=== + * + * 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" +typedef si_int fixint_t; +typedef su_int fixuint_t; +#include "fp_fixint_impl.inc" ARM_EABI_FNALIAS(f2iz, fixsfsi) -COMPILER_RT_ABI int +COMPILER_RT_ABI si_int __fixsfsi(fp_t a) { - // Break a into sign, exponent, significand - const rep_t aRep = toRep(a); - const rep_t aAbs = aRep & absMask; - const int sign = aRep & signBit ? -1 : 1; - const int exponent = (aAbs >> significandBits) - exponentBias; - const rep_t significand = (aAbs & significandMask) | implicitBit; - - // If 0 < exponent < significandBits, right shift to get the result. - if ((unsigned int)exponent < significandBits) { - return sign * (significand >> (significandBits - exponent)); - } - - // If exponent is negative, the result is zero. - else if (exponent < 0) { - return 0; - } - - // If significandBits < exponent, left shift to get the result. This shift - // may end up being larger than the type width, which incurs undefined - // behavior, but the conversion itself is undefined in that case, so - // whatever the compiler decides to do is fine. - else { - return sign * (significand << (exponent - significandBits)); - } + return __fixint(a); } diff --git a/lib/builtins/fixsfti.c b/lib/builtins/fixsfti.c index 6a1a1c6d5465..3a159b3e18e4 100644 --- a/lib/builtins/fixsfti.c +++ b/lib/builtins/fixsfti.c @@ -6,40 +6,21 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixsfti for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== */ #include "int_lib.h" #ifdef CRT_HAS_128BIT +#define SINGLE_PRECISION +#include "fp_lib.h" -/* Returns: convert a to a signed long long, rounding toward zero. */ - -/* Assumption: float is a IEEE 32 bit floating point type - * su_int is a 32 bit integral type - * value in float is representable in ti_int (no range checking performed) - */ - -/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ +typedef ti_int fixint_t; +typedef tu_int fixuint_t; +#include "fp_fixint_impl.inc" COMPILER_RT_ABI ti_int -__fixsfti(float a) -{ - float_bits fb; - fb.f = a; - int e = ((fb.u & 0x7F800000) >> 23) - 127; - if (e < 0) - return 0; - ti_int s = (si_int)(fb.u & 0x80000000) >> 31; - ti_int r = (fb.u & 0x007FFFFF) | 0x00800000; - if (e > 23) - r <<= (e - 23); - else - r >>= (23 - e); - return (r ^ s) - s; +__fixsfti(fp_t a) { + return __fixint(a); } #endif /* CRT_HAS_128BIT */ diff --git a/lib/builtins/fixtfdi.c b/lib/builtins/fixtfdi.c new file mode 100644 index 000000000000..bc9dea1f4f81 --- /dev/null +++ b/lib/builtins/fixtfdi.c @@ -0,0 +1,23 @@ +/* ===-- fixtfdi.c - Implement __fixtfdi -----------------------------------=== + * + * 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 QUAD_PRECISION +#include "fp_lib.h" + +#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT) +typedef di_int fixint_t; +typedef du_int fixuint_t; +#include "fp_fixint_impl.inc" + +COMPILER_RT_ABI di_int +__fixtfdi(fp_t a) { + return __fixint(a); +} +#endif diff --git a/lib/builtins/fixtfsi.c b/lib/builtins/fixtfsi.c new file mode 100644 index 000000000000..feb3de885090 --- /dev/null +++ b/lib/builtins/fixtfsi.c @@ -0,0 +1,23 @@ +/* ===-- fixtfsi.c - Implement __fixtfsi -----------------------------------=== + * + * 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 QUAD_PRECISION +#include "fp_lib.h" + +#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT) +typedef si_int fixint_t; +typedef su_int fixuint_t; +#include "fp_fixint_impl.inc" + +COMPILER_RT_ABI si_int +__fixtfsi(fp_t a) { + return __fixint(a); +} +#endif diff --git a/lib/builtins/fixtfti.c b/lib/builtins/fixtfti.c new file mode 100644 index 000000000000..ee4ada85cb4a --- /dev/null +++ b/lib/builtins/fixtfti.c @@ -0,0 +1,23 @@ +/* ===-- fixtfti.c - Implement __fixtfti -----------------------------------=== + * + * 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 QUAD_PRECISION +#include "fp_lib.h" + +#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT) +typedef ti_int fixint_t; +typedef tu_int fixuint_t; +#include "fp_fixint_impl.inc" + +COMPILER_RT_ABI ti_int +__fixtfti(fp_t a) { + return __fixint(a); +} +#endif diff --git a/lib/builtins/fixunsdfdi.c b/lib/builtins/fixunsdfdi.c index 9e6371390d5c..2e0d87eacf05 100644 --- a/lib/builtins/fixunsdfdi.c +++ b/lib/builtins/fixunsdfdi.c @@ -6,42 +6,39 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixunsdfdi for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== */ -#include "int_lib.h" +#define DOUBLE_PRECISION +#include "fp_lib.h" -/* Returns: convert a to a unsigned long long, rounding toward zero. - * Negative values all become zero. - */ +ARM_EABI_FNALIAS(d2ulz, fixunsdfdi) -/* Assumption: double is a IEEE 64 bit floating point type - * du_int is a 64 bit integral type - * value in double is representable in du_int or is negative - * (no range checking performed) +#ifndef __SOFT_FP__ +/* Support for systems that have hardware floating-point; can set the invalid + * flag as a side-effect of computation. */ -/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ - -ARM_EABI_FNALIAS(d2ulz, fixunsdfdi) - COMPILER_RT_ABI du_int __fixunsdfdi(double a) { - double_bits fb; - fb.f = a; - int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; - if (e < 0 || (fb.u.s.high & 0x80000000)) - return 0; - udwords r; - r.s.high = (fb.u.s.high & 0x000FFFFF) | 0x00100000; - r.s.low = fb.u.s.low; - if (e > 52) - r.all <<= (e - 52); - else - r.all >>= (52 - e); - return r.all; + if (a <= 0.0) return 0; + su_int high = a/0x1p32f; + su_int low = a - (double)high*0x1p32f; + return ((du_int)high << 32) | low; } + +#else +/* Support for systems that don't have hardware floating-point; there are no + * flags to set, and we don't want to code-gen to an unknown soft-float + * implementation. + */ + +typedef du_int fixuint_t; +#include "fp_fixuint_impl.inc" + +COMPILER_RT_ABI du_int +__fixunsdfdi(fp_t a) { + return __fixuint(a); +} + +#endif diff --git a/lib/builtins/fixunsdfsi.c b/lib/builtins/fixunsdfsi.c index c6a3c755e90f..232d342d77da 100644 --- a/lib/builtins/fixunsdfsi.c +++ b/lib/builtins/fixunsdfsi.c @@ -6,39 +6,16 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixunsdfsi for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== - */ - -#include "int_lib.h" - -/* Returns: convert a to a unsigned int, rounding toward zero. - * Negative values all become zero. - */ - -/* Assumption: double is a IEEE 64 bit floating point type - * su_int is a 32 bit integral type - * value in double is representable in su_int or is negative - * (no range checking performed) */ -/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ +#define DOUBLE_PRECISION +#include "fp_lib.h" +typedef su_int fixuint_t; +#include "fp_fixuint_impl.inc" ARM_EABI_FNALIAS(d2uiz, fixunsdfsi) COMPILER_RT_ABI su_int -__fixunsdfsi(double a) -{ - double_bits fb; - fb.f = a; - int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; - if (e < 0 || (fb.u.s.high & 0x80000000)) - return 0; - return ( - 0x80000000u | - ((fb.u.s.high & 0x000FFFFF) << 11) | - (fb.u.s.low >> 21) - ) >> (31 - e); +__fixunsdfsi(fp_t a) { + return __fixuint(a); } diff --git a/lib/builtins/fixunsdfti.c b/lib/builtins/fixunsdfti.c index cc6c84ff5cb5..f8046a02632b 100644 --- a/lib/builtins/fixunsdfti.c +++ b/lib/builtins/fixunsdfti.c @@ -6,42 +6,18 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixunsdfti for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== */ #include "int_lib.h" #ifdef CRT_HAS_128BIT - -/* Returns: convert a to a unsigned long long, rounding toward zero. - * Negative values all become zero. - */ - -/* Assumption: double is a IEEE 64 bit floating point type - * tu_int is a 64 bit integral type - * value in double is representable in tu_int or is negative - * (no range checking performed) - */ - -/* seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm */ +#define DOUBLE_PRECISION +#include "fp_lib.h" +typedef tu_int fixuint_t; +#include "fp_fixuint_impl.inc" COMPILER_RT_ABI tu_int -__fixunsdfti(double a) -{ - double_bits fb; - fb.f = a; - int e = ((fb.u.s.high & 0x7FF00000) >> 20) - 1023; - if (e < 0 || (fb.u.s.high & 0x80000000)) - return 0; - tu_int r = 0x0010000000000000uLL | (fb.u.all & 0x000FFFFFFFFFFFFFuLL); - if (e > 52) - r <<= (e - 52); - else - r >>= (52 - e); - return r; +__fixunsdfti(fp_t a) { + return __fixuint(a); } - #endif /* CRT_HAS_128BIT */ diff --git a/lib/builtins/fixunssfdi.c b/lib/builtins/fixunssfdi.c index 69d5952e9607..5a154e82cff4 100644 --- a/lib/builtins/fixunssfdi.c +++ b/lib/builtins/fixunssfdi.c @@ -6,39 +6,40 @@ * Source Licenses. See LICENSE.TXT for details. * * ===----------------------------------------------------------------------=== - * - * This file implements __fixunssfdi for the compiler_rt library. - * - * ===----------------------------------------------------------------------=== - */ - -#include "int_lib.h" -/* Returns: convert a to a unsigned long long, rounding toward zero. - * Negative values all become zero. - */ - -/* Assumption: float is a IEEE 32 bit floating point type - * du_int is a 64 bit integral type - * value in float is representable in du_int or is negative - * (no range checking performed) */ -/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ +#define SINGLE_PRECISION +#include "fp_lib.h" ARM_EABI_FNALIAS(f2ulz, fixunssfdi) +#ifndef __SOFT_FP__ +/* Support for systems that have hardware floating-point; can set the invalid + * flag as a side-effect of computation. + */ + COMPILER_RT_ABI du_int __fixunssfdi(float a) { - float_bits fb; - fb.f = a; - int e = ((fb.u & 0x7F800000) >> 23) - 127; - if (e < 0 || (fb.u & 0x80000000)) - return 0; - du_int r = (fb.u & 0x007FFFFF) | 0x00800000; - if (e > 23) - r <<= (e - 23); - else - r >>= (23 - e); - return r; + if (a <= 0.0f) return 0; + double da = a; + su_int high = da/0x1p32f; + su_int low = da - (double)high*0x1p32f; + return ((du_int)high << 32) | low; +} + +#else +/* Support for systems that don't have hardware floating-point; there are no + * flags to set, and we don't want to code-gen to an unknown soft-float + * implementation. + */ + +typedef du_int fixuint_t; +#include "fp_fixuint_impl.inc" + +COMPILER_RT_ABI du_int +__fixunssfdi(fp_t a) { + return __fixuint(a); } + +#endif diff --git a/lib/builtins/fixunssfsi.c b/lib/builtins/fixunssfsi.c index e034139ea277..cc2b05bd84f8 100644 --- a/lib/builtins/fixunssfsi.c +++ b/lib/builtins/fixunssfsi.c @@ -12,34 +12,14 @@ * ===----------------------------------------------------------------------=== */ -#include "int_lib.h" - -/* Returns: convert a to a unsigned int, rounding toward zero. - * Negative values all become zero. - */ - -/* Assumption: float is a IEEE 32 bit floating point type - * su_int is a 32 bit integral type - * value in float is representable in su_int or is negative - * (no range checking performed) - */ - -/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ +#define SINGLE_PRECISION +#include "fp_lib.h" +typedef su_int fixuint_t; +#include "fp_fixuint_impl.inc" ARM_EABI_FNALIAS(f2uiz, fixunssfsi) COMPILER_RT_ABI su_int -__fixunssfsi(float a) -{ - float_bits fb; - fb.f = a; - int e = ((fb.u & 0x7F800000) >> 23) - 127; - if (e < 0 || (fb.u & 0x80000000)) - return 0; - su_int r = (fb.u & 0x007FFFFF) | 0x00800000; - if (e > 23) - r <<= (e - 23); - else - r >>= (23 - e); - return r; +__fixunssfsi(fp_t a) { + return __fixuint(a); } diff --git a/lib/builtins/fixunssfti.c b/lib/builtins/fixunssfti.c index 4da9e242ad05..862d7bd6c7af 100644 --- a/lib/builtins/fixunssfti.c +++ b/lib/builtins/fixunssfti.c @@ -12,36 +12,15 @@ * ===----------------------------------------------------------------------=== */ -#include "int_lib.h" +#define SINGLE_PRECISION +#include "fp_lib.h" -#ifdef CRT_HAS_128BIT - -/* Returns: convert a to a unsigned long long, rounding toward zero. - * Negative values all become zero. - */ - -/* Assumption: float is a IEEE 32 bit floating point type - * tu_int is a 64 bit integral type - * value in float is representable in tu_int or is negative - * (no range checking performed) - */ - -/* seee eeee emmm mmmm mmmm mmmm mmmm mmmm */ +#if defined(CRT_HAS_128BIT) +typedef tu_int fixuint_t; +#include "fp_fixuint_impl.inc" COMPILER_RT_ABI tu_int -__fixunssfti(float a) -{ - float_bits fb; - fb.f = a; - int e = ((fb.u & 0x7F800000) >> 23) - 127; - if (e < 0 || (fb.u & 0x80000000)) - return 0; - tu_int r = (fb.u & 0x007FFFFF) | 0x00800000; - if (e > 23) - r <<= (e - 23); - else - r >>= (23 - e); - return r; +__fixunssfti(fp_t a) { + return __fixuint(a); } - -#endif /* CRT_HAS_128BIT */ +#endif diff --git a/lib/builtins/fixunstfdi.c b/lib/builtins/fixunstfdi.c new file mode 100644 index 000000000000..b2995f65834a --- /dev/null +++ b/lib/builtins/fixunstfdi.c @@ -0,0 +1,22 @@ +/* ===-- fixunstfdi.c - Implement __fixunstfdi -----------------------------=== + * + * 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 QUAD_PRECISION +#include "fp_lib.h" + +#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT) +typedef du_int fixuint_t; +#include "fp_fixuint_impl.inc" + +COMPILER_RT_ABI du_int +__fixunstfdi(fp_t a) { + return __fixuint(a); +} +#endif diff --git a/lib/builtins/fixunstfsi.c b/lib/builtins/fixunstfsi.c new file mode 100644 index 000000000000..b5d3f6a7d38d --- /dev/null +++ b/lib/builtins/fixunstfsi.c @@ -0,0 +1,22 @@ +/* ===-- fixunstfsi.c - Implement __fixunstfsi -----------------------------=== + * + * 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 QUAD_PRECISION +#include "fp_lib.h" + +#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT) +typedef su_int fixuint_t; +#include "fp_fixuint_impl.inc" + +COMPILER_RT_ABI su_int +__fixunstfsi(fp_t a) { + return __fixuint(a); +} +#endif diff --git a/lib/builtins/fixunstfti.c b/lib/builtins/fixunstfti.c new file mode 100644 index 000000000000..22ff9dfc0339 --- /dev/null +++ b/lib/builtins/fixunstfti.c @@ -0,0 +1,22 @@ +/* ===-- fixunstfsi.c - Implement __fixunstfsi -----------------------------=== + * + * 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 QUAD_PRECISION +#include "fp_lib.h" + +#if defined(CRT_HAS_128BIT) && defined(CRT_LDBL_128BIT) +typedef tu_int fixuint_t; +#include "fp_fixuint_impl.inc" + +COMPILER_RT_ABI tu_int +__fixunstfti(fp_t a) { + return __fixuint(a); +} +#endif diff --git a/lib/builtins/fixunsxfdi.c b/lib/builtins/fixunsxfdi.c index 7224d467e7c9..075304e78dc9 100644 --- a/lib/builtins/fixunsxfdi.c +++ b/lib/builtins/fixunsxfdi.c @@ -38,6 +38,8 @@ __fixunsxfdi(long double a) int e = (fb.u.high.s.low & 0x00007FFF) - 16383; if (e < 0 || (fb.u.high.s.low & 0x00008000)) return 0; + if ((unsigned)e > sizeof(du_int) * CHAR_BIT) + return ~(du_int)0; return fb.u.low.all >> (63 - e); } diff --git a/lib/builtins/fixunsxfsi.c b/lib/builtins/fixunsxfsi.c index df0a18e2c1ab..c3c70f743de8 100644 --- a/lib/builtins/fixunsxfsi.c +++ b/lib/builtins/fixunsxfsi.c @@ -23,7 +23,6 @@ /* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes * su_int is a 32 bit integral type * value in long double is representable in su_int or is negative - * (no range checking performed) */ /* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | @@ -38,6 +37,8 @@ __fixunsxfsi(long double a) int e = (fb.u.high.s.low & 0x00007FFF) - 16383; if (e < 0 || (fb.u.high.s.low & 0x00008000)) return 0; + if ((unsigned)e > sizeof(su_int) * CHAR_BIT) + return ~(su_int)0; return fb.u.low.s.high >> (31 - e); } diff --git a/lib/builtins/fixunsxfti.c b/lib/builtins/fixunsxfti.c index 42e507304718..fb39d00ff5b2 100644 --- a/lib/builtins/fixunsxfti.c +++ b/lib/builtins/fixunsxfti.c @@ -21,9 +21,8 @@ */ /* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes - * tu_int is a 64 bit integral type + * tu_int is a 128 bit integral type * value in long double is representable in tu_int or is negative - * (no range checking performed) */ /* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | @@ -38,6 +37,8 @@ __fixunsxfti(long double a) int e = (fb.u.high.s.low & 0x00007FFF) - 16383; if (e < 0 || (fb.u.high.s.low & 0x00008000)) return 0; + if ((unsigned)e > sizeof(tu_int) * CHAR_BIT) + return ~(tu_int)0; tu_int r = fb.u.low.all; if (e > 63) r <<= (e - 63); diff --git a/lib/builtins/fixxfdi.c b/lib/builtins/fixxfdi.c index afc79d8664d8..011787f9e4b4 100644 --- a/lib/builtins/fixxfdi.c +++ b/lib/builtins/fixxfdi.c @@ -19,7 +19,7 @@ /* Returns: convert a to a signed long long, rounding toward zero. */ /* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes - * su_int is a 32 bit integral type + * di_int is a 64 bit integral type * value in long double is representable in di_int (no range checking performed) */ @@ -30,11 +30,15 @@ COMPILER_RT_ABI di_int __fixxfdi(long double a) { + const di_int di_max = (di_int)((~(du_int)0) / 2); + const di_int di_min = -di_max - 1; long_double_bits fb; fb.f = a; int e = (fb.u.high.s.low & 0x00007FFF) - 16383; if (e < 0) return 0; + if ((unsigned)e >= sizeof(di_int) * CHAR_BIT) + return a > 0 ? di_max : di_min; di_int s = -(si_int)((fb.u.high.s.low & 0x00008000) >> 15); di_int r = fb.u.low.all; r = (du_int)r >> (63 - e); diff --git a/lib/builtins/fixxfti.c b/lib/builtins/fixxfti.c index 3d0a279b3842..968a4f0d5eea 100644 --- a/lib/builtins/fixxfti.c +++ b/lib/builtins/fixxfti.c @@ -19,8 +19,8 @@ /* Returns: convert a to a signed long long, rounding toward zero. */ /* Assumption: long double is an intel 80 bit floating point type padded with 6 bytes - * su_int is a 32 bit integral type - * value in long double is representable in ti_int (no range checking performed) + * ti_int is a 128 bit integral type + * value in long double is representable in ti_int */ /* gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | @@ -30,6 +30,8 @@ COMPILER_RT_ABI ti_int __fixxfti(long double a) { + const ti_int ti_max = (ti_int)((~(tu_int)0) / 2); + const ti_int ti_min = -ti_max - 1; long_double_bits fb; fb.f = a; int e = (fb.u.high.s.low & 0x00007FFF) - 16383; @@ -37,6 +39,8 @@ __fixxfti(long double a) return 0; ti_int s = -(si_int)((fb.u.high.s.low & 0x00008000) >> 15); ti_int r = fb.u.low.all; + if ((unsigned)e >= sizeof(ti_int) * CHAR_BIT) + return a > 0 ? ti_max : ti_min; if (e > 63) r <<= (e - 63); else diff --git a/lib/builtins/fp_extend.h b/lib/builtins/fp_extend.h index fff676eeca8c..5c2b92310df1 100644 --- a/lib/builtins/fp_extend.h +++ b/lib/builtins/fp_extend.h @@ -39,11 +39,24 @@ static inline int src_rep_t_clz(src_rep_t a) { #endif } +#elif defined SRC_HALF +typedef uint16_t src_t; +typedef uint16_t src_rep_t; +#define SRC_REP_C UINT16_C +static const int srcSigBits = 10; +#define src_rep_t_clz __builtin_clz + #else -#error Source should be single precision or double precision! +#error Source should be half, single, or double precision! #endif //end source precision -#if defined DST_DOUBLE +#if defined DST_SINGLE +typedef float dst_t; +typedef uint32_t dst_rep_t; +#define DST_REP_C UINT32_C +static const int dstSigBits = 23; + +#elif defined DST_DOUBLE typedef double dst_t; typedef uint64_t dst_rep_t; #define DST_REP_C UINT64_C @@ -56,7 +69,7 @@ typedef __uint128_t dst_rep_t; static const int dstSigBits = 112; #else -#error Destination should be double precision or quad precision! +#error Destination should be single, double, or quad precision! #endif //end destination precision // End of specialization parameters. Two helper routines for conversion to and diff --git a/lib/builtins/fp_extend_impl.inc b/lib/builtins/fp_extend_impl.inc index f6953ff4bfae..edcfa8d2329d 100644 --- a/lib/builtins/fp_extend_impl.inc +++ b/lib/builtins/fp_extend_impl.inc @@ -66,7 +66,9 @@ static inline dst_t __extendXfYf2__(src_t a) { const src_rep_t sign = aRep & srcSignMask; dst_rep_t absResult; - if (aAbs - srcMinNormal < srcInfinity - srcMinNormal) { + // If sizeof(src_rep_t) < sizeof(int), the subtraction result is promoted + // to (signed) int. To avoid that, explicitly cast to src_rep_t. + if ((src_rep_t)(aAbs - srcMinNormal) < srcInfinity - srcMinNormal) { // a is a normal number. // Extend to the destination type by shifting the significand and // exponent into the proper position and rebiasing the exponent. diff --git a/lib/builtins/fp_fixint_impl.inc b/lib/builtins/fp_fixint_impl.inc new file mode 100644 index 000000000000..035e87ca10e0 --- /dev/null +++ b/lib/builtins/fp_fixint_impl.inc @@ -0,0 +1,41 @@ +//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===// +// +// 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 float to integer conversion for the +// compiler-rt library. +// +//===----------------------------------------------------------------------===// + +#include "fp_lib.h" + +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 + const rep_t aRep = toRep(a); + const rep_t aAbs = aRep & absMask; + const fixint_t sign = aRep & signBit ? -1 : 1; + const int exponent = (aAbs >> significandBits) - exponentBias; + const rep_t significand = (aAbs & significandMask) | implicitBit; + + // If exponent is negative, the result is zero. + if (exponent < 0) + return 0; + + // If the value is too large for the integer type, saturate. + if ((unsigned)exponent >= sizeof(fixint_t) * CHAR_BIT) + return sign == 1 ? fixint_max : fixint_min; + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if (exponent < significandBits) + return sign * (significand >> (significandBits - exponent)); + else + return sign * ((fixint_t)significand << (exponent - significandBits)); +} diff --git a/lib/builtins/fp_fixuint_impl.inc b/lib/builtins/fp_fixuint_impl.inc new file mode 100644 index 000000000000..5fefab0e2d8a --- /dev/null +++ b/lib/builtins/fp_fixuint_impl.inc @@ -0,0 +1,39 @@ +//===-- lib/fixdfsi.c - Double-precision -> integer conversion ----*- C -*-===// +// +// 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 float to unsigned integer conversion for the +// compiler-rt library. +// +//===----------------------------------------------------------------------===// + +#include "fp_lib.h" + +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; + const int sign = aRep & signBit ? -1 : 1; + const int exponent = (aAbs >> significandBits) - exponentBias; + const rep_t significand = (aAbs & significandMask) | implicitBit; + + // If either the value or the exponent is negative, the result is zero. + if (sign == -1 || exponent < 0) + return 0; + + // If the value is too large for the integer type, saturate. + if ((unsigned)exponent > sizeof(fixuint_t) * CHAR_BIT) + return ~(fixuint_t)0; + + // If 0 <= exponent < significandBits, right shift to get the result. + // Otherwise, shift left. + if (exponent < significandBits) + return significand >> (significandBits - exponent); + else + return (fixuint_t)significand << (exponent - significandBits); +} diff --git a/lib/builtins/fp_trunc.h b/lib/builtins/fp_trunc.h index 49a9aebbc67d..373ba1b0411d 100644 --- a/lib/builtins/fp_trunc.h +++ b/lib/builtins/fp_trunc.h @@ -16,7 +16,13 @@ #include "int_lib.h" -#if defined SRC_DOUBLE +#if defined SRC_SINGLE +typedef float src_t; +typedef uint32_t src_rep_t; +#define SRC_REP_C UINT32_C +static const int srcSigBits = 23; + +#elif defined SRC_DOUBLE typedef double src_t; typedef uint64_t src_rep_t; #define SRC_REP_C UINT64_C @@ -44,6 +50,12 @@ typedef uint32_t dst_rep_t; #define DST_REP_C UINT32_C static const int dstSigBits = 23; +#elif defined DST_HALF +typedef uint16_t dst_t; +typedef uint16_t dst_rep_t; +#define DST_REP_C UINT16_C +static const int dstSigBits = 10; + #else #error Destination should be single precision or double precision! #endif //end destination precision diff --git a/lib/builtins/fp_trunc_impl.inc b/lib/builtins/fp_trunc_impl.inc index 21bffae02bf4..372e8d6014dd 100644 --- a/lib/builtins/fp_trunc_impl.inc +++ b/lib/builtins/fp_trunc_impl.inc @@ -99,7 +99,7 @@ static inline dst_t __truncXfYf2__(src_t a) { absResult |= dstQNaN; absResult |= ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode; } - else if (aAbs > overflow) { + else if (aAbs >= overflow) { // a overflows to infinity. absResult = (dst_rep_t)dstInfExp << dstSigBits; } diff --git a/lib/builtins/i386/chkstk.S b/lib/builtins/i386/chkstk.S new file mode 100644 index 000000000000..3733d722ef19 --- /dev/null +++ b/lib/builtins/i386/chkstk.S @@ -0,0 +1,34 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// _chkstk routine +// This routine is windows specific +// http://msdn.microsoft.com/en-us/library/ms648426.aspx + +#ifdef __i386__ + +.text +.balign 4 +DEFINE_COMPILERRT_FUNCTION(__chkstk_ms) + push %ecx + push %eax + cmp $0x1000,%eax + lea 12(%esp),%ecx + jb 1f +2: + sub $0x1000,%ecx + orl $0,(%ecx) + sub $0x1000,%eax + cmp $0x1000,%eax + ja 2b +1: + sub %eax,%ecx + orl $0,(%ecx) + pop %eax + pop %ecx + ret +END_COMPILERRT_FUNCTION(__chkstk_ms) + +#endif // __i386__ diff --git a/lib/builtins/int_endianness.h b/lib/builtins/int_endianness.h index 4b35bde079e8..7995ddbb953c 100644 --- a/lib/builtins/int_endianness.h +++ b/lib/builtins/int_endianness.h @@ -16,6 +16,20 @@ #ifndef INT_ENDIANNESS_H #define INT_ENDIANNESS_H +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + defined(__ORDER_LITTLE_ENDIAN__) + +/* Clang and GCC provide built-in endianness definitions. */ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 0 +#define _YUGA_BIG_ENDIAN 1 +#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define _YUGA_LITTLE_ENDIAN 1 +#define _YUGA_BIG_ENDIAN 0 +#endif /* __BYTE_ORDER__ */ + +#else /* Compilers other than Clang or GCC. */ + #if defined(__SVR4) && defined(__sun) #include <sys/byteorder.h> @@ -84,18 +98,6 @@ /* .. */ -#if defined(__linux__) - -#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ -#define _YUGA_LITTLE_ENDIAN 0 -#define _YUGA_BIG_ENDIAN 1 -#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ -#define _YUGA_LITTLE_ENDIAN 1 -#define _YUGA_BIG_ENDIAN 0 -#endif /* __BYTE_ORDER__ */ - -#endif /* GNU/Linux */ - #if defined(_WIN32) #define _YUGA_LITTLE_ENDIAN 1 @@ -103,6 +105,8 @@ #endif /* Windows */ +#endif /* Clang or GCC. */ + /* . */ #if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN) diff --git a/lib/builtins/int_lib.h b/lib/builtins/int_lib.h index ff314da986f1..bca5d81d4414 100644 --- a/lib/builtins/int_lib.h +++ b/lib/builtins/int_lib.h @@ -28,7 +28,11 @@ # define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) #else # define ARM_EABI_FNALIAS(aeabi_name, name) -# define COMPILER_RT_ABI +# if defined(__arm__) && defined(_WIN32) +# define COMPILER_RT_ABI __attribute__((pcs("aapcs"))) +# else +# define COMPILER_RT_ABI +# endif #endif #if defined(__NetBSD__) && (defined(_KERNEL) || defined(_STANDALONE)) diff --git a/lib/builtins/truncdfhf2.c b/lib/builtins/truncdfhf2.c new file mode 100644 index 000000000000..0852df369625 --- /dev/null +++ b/lib/builtins/truncdfhf2.c @@ -0,0 +1,16 @@ +//===-- lib/truncdfhf2.c - double -> half conversion --------------*- C -*-===// +// +// 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 SRC_DOUBLE +#define DST_HALF +#include "fp_trunc_impl.inc" + +COMPILER_RT_ABI uint16_t __truncdfhf2(double a) { + return __truncXfYf2__(a); +} diff --git a/lib/builtins/truncsfhf2.c b/lib/builtins/truncsfhf2.c new file mode 100644 index 000000000000..381e590c342f --- /dev/null +++ b/lib/builtins/truncsfhf2.c @@ -0,0 +1,22 @@ +//===-- lib/truncsfhf2.c - single -> half conversion --------------*- C -*-===// +// +// 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 SRC_SINGLE +#define DST_HALF +#include "fp_trunc_impl.inc" + +// 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)) uint16_t __truncsfhf2(float a) { + return __truncXfYf2__(a); +} + +COMPILER_RT_ABI uint16_t __gnu_f2h_ieee(float a) { + return __truncsfhf2(a); +} diff --git a/lib/builtins/x86_64/chkstk.S b/lib/builtins/x86_64/chkstk.S new file mode 100644 index 000000000000..5759e84498c6 --- /dev/null +++ b/lib/builtins/x86_64/chkstk.S @@ -0,0 +1,39 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +#include "../assembly.h" + +// _chkstk routine +// This routine is windows specific +// http://msdn.microsoft.com/en-us/library/ms648426.aspx + +// Notes from r227519 +// MSVC x64s __chkstk and cygmings ___chkstk_ms do not adjust %rsp +// themselves. It also does not clobber %rax so we can reuse it when +// adjusting %rsp. + +#ifdef __x86_64__ + +.text +.balign 4 +DEFINE_COMPILERRT_FUNCTION(___chkstk_ms) + push %rcx + push %rax + cmp $0x1000,%rax + lea 24(%rsp),%rcx + jb 1f +2: + sub $0x1000,%rcx + orl $0,(%rcx) + sub $0x1000,%rax + cmp $0x1000,%rax + ja 2b +1: + sub %rax,%rcx + orl $0,(%rcx) + pop %rax + pop %rcx + ret +END_COMPILERRT_FUNCTION(___chkstk_ms) + +#endif // __x86_64__ diff --git a/lib/dfsan/dfsan.cc b/lib/dfsan/dfsan.cc index de5b2ce107b4..d2e137e129c1 100644 --- a/lib/dfsan/dfsan.cc +++ b/lib/dfsan/dfsan.cc @@ -302,12 +302,12 @@ dfsan_dump_labels(int fd) { char buf[64]; internal_snprintf(buf, sizeof(buf), "%u %u %u ", l, __dfsan_label_info[l].l1, __dfsan_label_info[l].l2); - internal_write(fd, buf, internal_strlen(buf)); + WriteToFile(fd, buf, internal_strlen(buf)); if (__dfsan_label_info[l].l1 == 0 && __dfsan_label_info[l].desc) { - internal_write(fd, __dfsan_label_info[l].desc, - internal_strlen(__dfsan_label_info[l].desc)); + WriteToFile(fd, __dfsan_label_info[l].desc, + internal_strlen(__dfsan_label_info[l].desc)); } - internal_write(fd, "\n", 1); + WriteToFile(fd, "\n", 1); } } @@ -333,7 +333,7 @@ static void InitializeFlags() { static void dfsan_fini() { if (internal_strcmp(flags().dump_labels_at_exit, "") != 0) { - fd_t fd = OpenFile(flags().dump_labels_at_exit, true /* write */); + fd_t fd = OpenFile(flags().dump_labels_at_exit, WrOnly); if (fd == kInvalidFd) { Report("WARNING: DataFlowSanitizer: unable to open output file %s\n", flags().dump_labels_at_exit); @@ -343,7 +343,7 @@ static void dfsan_fini() { Report("INFO: DataFlowSanitizer: dumping labels to %s\n", flags().dump_labels_at_exit); dfsan_dump_labels(fd); - internal_close(fd); + CloseFile(fd); } } @@ -361,7 +361,7 @@ static void dfsan_init(int argc, char **argv, char **envp) { // case by disabling memory protection when ASLR is disabled. uptr init_addr = (uptr)&dfsan_init; if (!(init_addr >= kUnusedAddr && init_addr < kAppAddr)) - Mprotect(kUnusedAddr, kAppAddr - kUnusedAddr); + MmapNoAccess(kUnusedAddr, kAppAddr - kUnusedAddr); InitializeFlags(); InitializeInterceptors(); diff --git a/lib/dfsan/dfsan_custom.cc b/lib/dfsan/dfsan_custom.cc index 318ecd6fb317..c58b471db53c 100644 --- a/lib/dfsan/dfsan_custom.cc +++ b/lib/dfsan/dfsan_custom.cc @@ -82,11 +82,20 @@ SANITIZER_INTERFACE_ATTRIBUTE char *__dfsw_strchr(const char *s, int c, } } +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void +dfsan_weak_hook_memcmp(uptr caller_pc, const void *s1, const void *s2, size_t n, + dfsan_label s1_label, dfsan_label s2_label, + dfsan_label n_label); + SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_memcmp(const void *s1, const void *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label, dfsan_label *ret_label) { + if (dfsan_weak_hook_memcmp) + dfsan_weak_hook_memcmp(GET_CALLER_PC(), s1, s2, n, s1_label, s2_label, + n_label); const char *cs1 = (const char *) s1, *cs2 = (const char *) s2; for (size_t i = 0; i != n; ++i) { if (cs1[i] != cs2[i]) { @@ -847,266 +856,247 @@ __dfsw_write(int fd, const void *buf, size_t count, *ret_label = 0; return write(fd, buf, count); } +} // Type used to extract a dfsan_label with va_arg() typedef int dfsan_label_va; -// A chunk of data representing the output of formatting either a constant -// string or a single format directive. -struct Chunk { - // Address of the beginning of the formatted string - const char *ptr; - // Size of the formatted string - size_t size; +// Formats a chunk either a constant string or a single format directive (e.g., +// '%.3f'). +struct Formatter { + Formatter(char *str_, const char *fmt_, size_t size_) + : str(str_), str_off(0), size(size_), fmt_start(fmt_), fmt_cur(fmt_), + width(-1) {} + + int format() { + char *tmp_fmt = build_format_string(); + int retval = + snprintf(str + str_off, str_off < size ? size - str_off : 0, tmp_fmt, + 0 /* used only to avoid warnings */); + free(tmp_fmt); + return retval; + } - // Type of DFSan label (depends on the format directive) - enum { - // Constant string, no argument and thus no label - NONE = 0, - // Label for an argument of '%n' - IGNORED, - // Label for a '%s' argument - STRING, - // Label for any other type of argument - NUMERIC, - } label_type; - - // Value of the argument (if label_type == STRING) - const char *arg; -}; + template <typename T> int format(T arg) { + char *tmp_fmt = build_format_string(); + int retval; + if (width >= 0) { + retval = snprintf(str + str_off, str_off < size ? size - str_off : 0, + tmp_fmt, width, arg); + } else { + retval = snprintf(str + str_off, str_off < size ? size - str_off : 0, + tmp_fmt, arg); + } + free(tmp_fmt); + return retval; + } -// Formats the input. The output is stored in 'str' starting from offset -// 'off'. The format directive is represented by the first 'format_size' bytes -// of 'format'. If 'has_size' is true, 'size' bounds the number of output -// bytes. Returns the return value of the vsnprintf call used to format the -// input. -static int format_chunk(char *str, size_t off, bool has_size, size_t size, - const char *format, size_t format_size, ...) { - char *chunk_format = (char *) malloc(format_size + 1); - assert(chunk_format); - internal_memcpy(chunk_format, format, format_size); - chunk_format[format_size] = '\0'; + char *build_format_string() { + size_t fmt_size = fmt_cur - fmt_start + 1; + char *new_fmt = (char *)malloc(fmt_size + 1); + assert(new_fmt); + internal_memcpy(new_fmt, fmt_start, fmt_size); + new_fmt[fmt_size] = '\0'; + return new_fmt; + } - va_list ap; - va_start(ap, format_size); - int r = 0; - if (has_size) { - r = vsnprintf(str + off, off < size ? size - off : 0, chunk_format, ap); - } else { - r = vsprintf(str + off, chunk_format, ap); + char *str_cur() { return str + str_off; } + + size_t num_written_bytes(int retval) { + if (retval < 0) { + return 0; + } + + size_t num_avail = str_off < size ? size - str_off : 0; + if (num_avail == 0) { + return 0; + } + + size_t num_written = retval; + // A return value of {v,}snprintf of size or more means that the output was + // truncated. + if (num_written >= num_avail) { + num_written -= num_avail; + } + + return num_written; } - va_end(ap); - free(chunk_format); - return r; -} + char *str; + size_t str_off; + size_t size; + const char *fmt_start; + const char *fmt_cur; + int width; +}; // Formats the input and propagates the input labels to the output. The output -// is stored in 'str'. If 'has_size' is true, 'size' bounds the number of -// output bytes. 'format' and 'ap' are the format string and the list of -// arguments for formatting. Returns the return value vsnprintf would return. +// is stored in 'str'. 'size' bounds the number of output bytes. 'format' and +// 'ap' are the format string and the list of arguments for formatting. Returns +// the return value vsnprintf would return. // // The function tokenizes the format string in chunks representing either a // constant string or a single format directive (e.g., '%.3f') and formats each // chunk independently into the output string. This approach allows to figure // out which bytes of the output string depends on which argument and thus to // propagate labels more precisely. -static int format_buffer(char *str, bool has_size, size_t size, - const char *format, dfsan_label *va_labels, - dfsan_label *ret_label, va_list ap) { - InternalMmapVector<Chunk> chunks(8); - size_t off = 0; - - while (*format) { - chunks.push_back(Chunk()); - Chunk& chunk = chunks.back(); - chunk.ptr = str + off; - chunk.arg = nullptr; - - int status = 0; - - if (*format != '%') { +// +// WARNING: This implementation does not support conversion specifiers with +// positional arguments. +static int format_buffer(char *str, size_t size, const char *fmt, + dfsan_label *va_labels, dfsan_label *ret_label, + va_list ap) { + Formatter formatter(str, fmt, size); + + while (*formatter.fmt_cur) { + formatter.fmt_start = formatter.fmt_cur; + formatter.width = -1; + int retval = 0; + + if (*formatter.fmt_cur != '%') { // Ordinary character. Consume all the characters until a '%' or the end // of the string. - size_t format_size = 0; - for (; *format && *format != '%'; ++format, ++format_size) {} - status = format_chunk(str, off, has_size, size, format - format_size, - format_size); - chunk.label_type = Chunk::NONE; + for (; *(formatter.fmt_cur + 1) && *(formatter.fmt_cur + 1) != '%'; + ++formatter.fmt_cur) {} + retval = formatter.format(); + dfsan_set_label(0, formatter.str_cur(), + formatter.num_written_bytes(retval)); } else { // Conversion directive. Consume all the characters until a conversion // specifier or the end of the string. - bool end_format = false; -#define FORMAT_CHUNK(t) \ - format_chunk(str, off, has_size, size, format - format_size, \ - format_size + 1, va_arg(ap, t)) - - for (size_t format_size = 1; *++format && !end_format; ++format_size) { - switch (*format) { - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - switch (*(format - 1)) { - case 'h': - // Also covers the 'hh' case (since the size of the arg is still - // an int). - status = FORMAT_CHUNK(int); - break; - case 'l': - if (format_size >= 2 && *(format - 2) == 'l') { - status = FORMAT_CHUNK(long long int); - } else { - status = FORMAT_CHUNK(long int); - } - break; - case 'q': - status = FORMAT_CHUNK(long long int); - break; - case 'j': - status = FORMAT_CHUNK(intmax_t); - break; - case 'z': - status = FORMAT_CHUNK(size_t); - break; - case 't': - status = FORMAT_CHUNK(size_t); - break; - default: - status = FORMAT_CHUNK(int); - } - chunk.label_type = Chunk::NUMERIC; - end_format = true; + bool end_fmt = false; + for (; *formatter.fmt_cur && !end_fmt; ) { + switch (*++formatter.fmt_cur) { + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + switch (*(formatter.fmt_cur - 1)) { + case 'h': + // Also covers the 'hh' case (since the size of the arg is still + // an int). + retval = formatter.format(va_arg(ap, int)); break; - - case 'a': - case 'A': - case 'e': - case 'E': - case 'f': - case 'F': - case 'g': - case 'G': - if (*(format - 1) == 'L') { - status = FORMAT_CHUNK(long double); + case 'l': + if (formatter.fmt_cur - formatter.fmt_start >= 2 && + *(formatter.fmt_cur - 2) == 'l') { + retval = formatter.format(va_arg(ap, long long int)); } else { - status = FORMAT_CHUNK(double); + retval = formatter.format(va_arg(ap, long int)); } - chunk.label_type = Chunk::NUMERIC; - end_format = true; break; - - case 'c': - status = FORMAT_CHUNK(int); - chunk.label_type = Chunk::NUMERIC; - end_format = true; + case 'q': + retval = formatter.format(va_arg(ap, long long int)); break; - - case 's': - chunk.arg = va_arg(ap, char *); - status = - format_chunk(str, off, has_size, size, - format - format_size, format_size + 1, - chunk.arg); - chunk.label_type = Chunk::STRING; - end_format = true; + case 'j': + retval = formatter.format(va_arg(ap, intmax_t)); break; - - case 'p': - status = FORMAT_CHUNK(void *); - chunk.label_type = Chunk::NUMERIC; - end_format = true; + case 'z': + case 't': + retval = formatter.format(va_arg(ap, size_t)); break; + default: + retval = formatter.format(va_arg(ap, int)); + } + dfsan_set_label(*va_labels++, formatter.str_cur(), + formatter.num_written_bytes(retval)); + end_fmt = true; + break; + + case 'a': + case 'A': + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + if (*(formatter.fmt_cur - 1) == 'L') { + retval = formatter.format(va_arg(ap, long double)); + } else { + retval = formatter.format(va_arg(ap, double)); + } + dfsan_set_label(*va_labels++, formatter.str_cur(), + formatter.num_written_bytes(retval)); + end_fmt = true; + break; + + case 'c': + retval = formatter.format(va_arg(ap, int)); + dfsan_set_label(*va_labels++, formatter.str_cur(), + formatter.num_written_bytes(retval)); + end_fmt = true; + break; + + case 's': { + char *arg = va_arg(ap, char *); + retval = formatter.format(arg); + va_labels++; + internal_memcpy(shadow_for(formatter.str_cur()), shadow_for(arg), + sizeof(dfsan_label) * + formatter.num_written_bytes(retval)); + end_fmt = true; + break; + } - case 'n': - *(va_arg(ap, int *)) = (int)off; - chunk.label_type = Chunk::IGNORED; - end_format = true; - break; + case 'p': + retval = formatter.format(va_arg(ap, void *)); + dfsan_set_label(*va_labels++, formatter.str_cur(), + formatter.num_written_bytes(retval)); + end_fmt = true; + break; + + case 'n': { + int *ptr = va_arg(ap, int *); + *ptr = (int)formatter.str_off; + va_labels++; + dfsan_set_label(0, ptr, sizeof(ptr)); + end_fmt = true; + break; + } - case '%': - status = format_chunk(str, off, has_size, size, - format - format_size, format_size + 1); - chunk.label_type = Chunk::NONE; - end_format = true; - break; + case '%': + retval = formatter.format(); + dfsan_set_label(0, formatter.str_cur(), + formatter.num_written_bytes(retval)); + end_fmt = true; + break; - default: - break; + case '*': + formatter.width = va_arg(ap, int); + va_labels++; + break; + + default: + break; } } -#undef FORMAT_CHUNK } - if (status < 0) { - return status; + if (retval < 0) { + return retval; } - // A return value of {v,}snprintf of size or more means that the output was - // truncated. - if (has_size) { - if (off < size) { - size_t ustatus = (size_t) status; - chunk.size = ustatus >= (size - off) ? - ustatus - (size - off) : ustatus; - } else { - chunk.size = 0; - } - } else { - chunk.size = status; - } - off += status; - } - - // TODO(martignlo): Decide how to combine labels (e.g., whether to ignore or - // not the label of the format string). - - // Label each output chunk according to the label supplied as argument to the - // function. We need to go through all the chunks and arguments even if the - // string was only partially printed ({v,}snprintf case). - for (size_t i = 0; i < chunks.size(); ++i) { - const Chunk& chunk = chunks[i]; - void *chunk_ptr = const_cast<char *>(chunk.ptr); - - switch (chunk.label_type) { - case Chunk::NONE: - dfsan_set_label(0, chunk_ptr, chunk.size); - break; - case Chunk::IGNORED: - va_labels++; - dfsan_set_label(0, chunk_ptr, chunk.size); - break; - case Chunk::NUMERIC: { - dfsan_label label = *va_labels++; - dfsan_set_label(label, chunk_ptr, chunk.size); - break; - } - case Chunk::STRING: { - // Consume the label of the pointer to the string - va_labels++; - internal_memcpy(shadow_for(chunk_ptr), - shadow_for(chunk.arg), - sizeof(dfsan_label) * (strlen(chunk.arg))); - break; - } - } + formatter.fmt_cur++; + formatter.str_off += retval; } *ret_label = 0; // Number of bytes written in total. - return off; + return formatter.str_off; } +extern "C" { SANITIZER_INTERFACE_ATTRIBUTE int __dfsw_sprintf(char *str, const char *format, dfsan_label str_label, dfsan_label format_label, dfsan_label *va_labels, dfsan_label *ret_label, ...) { va_list ap; va_start(ap, ret_label); - int ret = format_buffer(str, false, 0, format, va_labels, ret_label, ap); + int ret = format_buffer(str, ~0ul, format, va_labels, ret_label, ap); va_end(ap); return ret; } @@ -1118,7 +1108,7 @@ int __dfsw_snprintf(char *str, size_t size, const char *format, dfsan_label *ret_label, ...) { va_list ap; va_start(ap, ret_label); - int ret = format_buffer(str, true, size, format, va_labels, ret_label, ap); + int ret = format_buffer(str, size, format, va_labels, ret_label, ap); va_end(ap); return ret; } diff --git a/lib/dfsan/done_abilist.txt b/lib/dfsan/done_abilist.txt index 15c6d1bb2e39..e6c077ff1208 100644 --- a/lib/dfsan/done_abilist.txt +++ b/lib/dfsan/done_abilist.txt @@ -258,3 +258,18 @@ fun:reflect.MakeFuncStubGo=uninstrumented fun:reflect.MakeFuncStubGo=discard fun:reflect.makeFuncStub=uninstrumented fun:reflect.makeFuncStub=discard + + +############################################################################### +# lib/Fuzzer +############################################################################### +# Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp +fun:__sanitizer_cov_trace_cmp=custom +fun:__sanitizer_cov_trace_cmp=uninstrumented + +# Ignores all other __sanitizer callbacks. +fun:__sanitizer_*=uninstrumented +fun:__sanitizer_*=discard + +# Don't add extra parameters to the Fuzzer callback. +fun:LLVMFuzzerTestOneInput=uninstrumented diff --git a/lib/dfsan/scripts/check_custom_wrappers.sh b/lib/dfsan/scripts/check_custom_wrappers.sh index 50bc85d4aef5..99bf50cbbd08 100755 --- a/lib/dfsan/scripts/check_custom_wrappers.sh +++ b/lib/dfsan/scripts/check_custom_wrappers.sh @@ -17,8 +17,10 @@ on_exit() { rm -f ${DIFF_B} 2> /dev/null } +# Ignore __sanitizer_cov_trace* because they are implemented elsewhere. trap on_exit EXIT -grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} | grep -v "dfsan_get_label" \ +grep -E "^fun:.*=custom" ${DFSAN_ABI_LIST} \ + | grep -v "dfsan_get_label\|__sanitizer_cov_trace" \ | sed "s/^fun:\(.*\)=custom.*/\1/" | sort > $DIFF_A grep -E "__dfsw.*\(" ${DFSAN_CUSTOM_WRAPPERS} \ | sed "s/.*__dfsw_\(.*\)(.*/\1/" | sort > $DIFF_B @@ -32,7 +34,7 @@ fi grep -E __dfsw_ ${DFSAN_CUSTOM_WRAPPERS} \ | sed "s/.*__dfsw_\([^(]*\).*/\1/" | sort > $DIFF_A -grep -E "^\\s*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \ +grep -E "^[[:space:]]*test_.*\(\);" ${DFSAN_CUSTOM_TESTS} \ | sed "s/.*test_\(.*\)();/\1/" | sort > $DIFF_B diff -u $DIFF_A $DIFF_B > ${DIFFOUT} if [ $? -ne 0 ] diff --git a/lib/interception/CMakeLists.txt b/lib/interception/CMakeLists.txt index b77f2d1562b7..16b41c976d6b 100644 --- a/lib/interception/CMakeLists.txt +++ b/lib/interception/CMakeLists.txt @@ -12,18 +12,8 @@ include_directories(..) set(INTERCEPTION_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(INTERCEPTION_CFLAGS) -if(APPLE) - # Build universal binary on APPLE. - foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) - add_compiler_rt_darwin_object_library(RTInterception ${os} - ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} - SOURCES ${INTERCEPTION_SOURCES} - CFLAGS ${INTERCEPTION_CFLAGS}) - endforeach() -else() - # Otherwise, build separate libraries for each target. - foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH}) - add_compiler_rt_object_library(RTInterception ${arch} - SOURCES ${INTERCEPTION_SOURCES} CFLAGS ${INTERCEPTION_CFLAGS}) - endforeach() -endif() +add_compiler_rt_object_libraries(RTInterception + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${INTERCEPTION_SOURCES} + CFLAGS ${INTERCEPTION_CFLAGS}) diff --git a/lib/interception/interception.h b/lib/interception/interception.h index 52573258bf98..9e9aca215c4d 100644 --- a/lib/interception/interception.h +++ b/lib/interception/interception.h @@ -219,7 +219,6 @@ const interpose_substitution substitution_##func_name[] \ namespace __interception { \ FUNC_TYPE(func) PTR_TO_REAL(func); \ } \ - DECLARE_WRAPPER_WINAPI(ret_type, func, __VA_ARGS__) \ extern "C" \ INTERCEPTOR_ATTRIBUTE \ ret_type __stdcall WRAP(func)(__VA_ARGS__) diff --git a/lib/interception/interception_win.cc b/lib/interception/interception_win.cc index cd241c3d23c3..19cf184948b9 100644 --- a/lib/interception/interception_win.cc +++ b/lib/interception/interception_win.cc @@ -84,6 +84,7 @@ static size_t RoundUpToInstrBoundary(size_t size, char *code) { cursor += 2; continue; case '\xE9': // E9 XX YY ZZ WW = jmp WWZZYYXX + case '\xB8': // B8 XX YY ZZ WW = mov eax, WWZZYYXX cursor += 5; continue; } @@ -182,10 +183,14 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) { } static const void **InterestingDLLsAvailable() { - const char *InterestingDLLs[] = {"kernel32.dll", - "msvcr110.dll", // VS2012 - "msvcr120.dll", // VS2013 - NULL}; + const char *InterestingDLLs[] = { + "kernel32.dll", + "msvcr110.dll", // VS2012 + "msvcr120.dll", // VS2013 + // NTDLL should go last as it exports some functions that we should override + // in the CRT [presumably only used internally]. + "ntdll.dll", NULL + }; static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 }; if (!result[0]) { for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) { diff --git a/lib/lsan/CMakeLists.txt b/lib/lsan/CMakeLists.txt index 2ea765de1bc7..37f794e2e11b 100644 --- a/lib/lsan/CMakeLists.txt +++ b/lib/lsan/CMakeLists.txt @@ -18,20 +18,13 @@ set(LSAN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) add_custom_target(lsan) -if(APPLE) - foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) - add_compiler_rt_darwin_object_library(RTLSanCommon ${os} - ARCH ${LSAN_COMMON_SUPPORTED_ARCH} - SOURCES ${LSAN_COMMON_SOURCES} - CFLAGS ${LSAN_CFLAGS}) - endforeach() -else() - foreach(arch ${LSAN_COMMON_SUPPORTED_ARCH}) - add_compiler_rt_object_library(RTLSanCommon ${arch} - SOURCES ${LSAN_COMMON_SOURCES} - CFLAGS ${LSAN_CFLAGS}) - endforeach() +add_compiler_rt_object_libraries(RTLSanCommon + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${LSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${LSAN_COMMON_SOURCES} + CFLAGS ${LSAN_CFLAGS}) +if(COMPILER_RT_HAS_LSAN) foreach(arch ${LSAN_SUPPORTED_ARCH}) add_compiler_rt_runtime(clang_rt.lsan-${arch} ${arch} STATIC SOURCES ${LSAN_SOURCES} diff --git a/lib/lsan/lsan_common.cc b/lib/lsan/lsan_common.cc index b9e2a1104326..0ffba505cc70 100644 --- a/lib/lsan/lsan_common.cc +++ b/lib/lsan/lsan_common.cc @@ -21,7 +21,6 @@ #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" -#include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_report_decorator.h" @@ -127,13 +126,14 @@ static inline bool CanBeAHeapPointer(uptr p) { // Scans the memory range, looking for byte patterns that point into allocator // chunks. Marks those chunks with |tag| and adds them to |frontier|. -// There are two usage modes for this function: finding reachable or ignored -// chunks (|tag| = kReachable or kIgnored) and finding indirectly leaked chunks +// There are two usage modes for this function: finding reachable chunks +// (|tag| = kReachable) and finding indirectly leaked chunks // (|tag| = kIndirectlyLeaked). In the second case, there's no flood fill, // so |frontier| = 0. void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, const char *region_type, ChunkTag tag) { + CHECK(tag == kReachable || tag == kIndirectlyLeaked); const uptr alignment = flags()->pointer_alignment(); LOG_POINTERS("Scanning %s range %p-%p.\n", region_type, begin, end); uptr pp = begin; @@ -147,9 +147,7 @@ void ScanRangeForPointers(uptr begin, uptr end, // Pointers to self don't count. This matters when tag == kIndirectlyLeaked. if (chunk == begin) continue; LsanMetadata m(chunk); - // Reachable beats ignored beats leaked. - if (m.tag() == kReachable) continue; - if (m.tag() == kIgnored && tag != kReachable) continue; + if (m.tag() == kReachable || m.tag() == kIgnored) continue; // Do this check relatively late so we can log only the interesting cases. if (!flags()->use_poisoned && WordIsPoisoned(pp)) { @@ -288,7 +286,7 @@ static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { LsanMetadata m(chunk); if (m.allocated() && m.tag() != kReachable) { ScanRangeForPointers(chunk, chunk + m.requested_size(), - /* frontier */ 0, "HEAP", kIndirectlyLeaked); + /* frontier */ nullptr, "HEAP", kIndirectlyLeaked); } } @@ -298,8 +296,11 @@ static void CollectIgnoredCb(uptr chunk, void *arg) { CHECK(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); - if (m.allocated() && m.tag() == kIgnored) + if (m.allocated() && m.tag() == kIgnored) { + LOG_POINTERS("Ignored: chunk %p-%p of size %zu.\n", + chunk, chunk + m.requested_size(), m.requested_size()); reinterpret_cast<Frontier *>(arg)->push_back(chunk); + } } // Sets the appropriate tag on each chunk. @@ -307,26 +308,33 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // Holds the flood fill frontier. Frontier frontier(1); + ForEachChunk(CollectIgnoredCb, &frontier); ProcessGlobalRegions(&frontier); ProcessThreads(suspended_threads, &frontier); ProcessRootRegions(&frontier); FloodFillTag(&frontier, kReachable); + // The check here is relatively expensive, so we do this in a separate flood // fill. That way we can skip the check for chunks that are reachable // otherwise. LOG_POINTERS("Processing platform-specific allocations.\n"); + CHECK_EQ(0, frontier.size()); ProcessPlatformSpecificAllocations(&frontier); FloodFillTag(&frontier, kReachable); - LOG_POINTERS("Scanning ignored chunks.\n"); - CHECK_EQ(0, frontier.size()); - ForEachChunk(CollectIgnoredCb, &frontier); - FloodFillTag(&frontier, kIgnored); - // Iterate over leaked chunks and mark those that are reachable from other // leaked chunks. LOG_POINTERS("Scanning leaked chunks.\n"); - ForEachChunk(MarkIndirectlyLeakedCb, 0 /* arg */); + ForEachChunk(MarkIndirectlyLeakedCb, nullptr); +} + +// ForEachChunk callback. Resets the tags to pre-leak-check state. +static void ResetTagsCb(uptr chunk, void *arg) { + (void)arg; + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (m.allocated() && m.tag() != kIgnored) + m.set_tag(kDirectlyLeaked); } static void PrintStackTraceById(u32 stack_trace_id) { @@ -372,35 +380,33 @@ static void PrintMatchedSuppressions() { Printf("%s\n\n", line); } -struct DoLeakCheckParam { +struct CheckForLeaksParam { bool success; LeakReport leak_report; }; -static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads, - void *arg) { - DoLeakCheckParam *param = reinterpret_cast<DoLeakCheckParam *>(arg); +static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads, + void *arg) { + CheckForLeaksParam *param = reinterpret_cast<CheckForLeaksParam *>(arg); CHECK(param); CHECK(!param->success); ClassifyAllChunks(suspended_threads); ForEachChunk(CollectLeaksCb, ¶m->leak_report); + // Clean up for subsequent leak checks. This assumes we did not overwrite any + // kIgnored tags. + ForEachChunk(ResetTagsCb, nullptr); param->success = true; } -void DoLeakCheck() { - EnsureMainThreadIDIsCorrect(); - BlockingMutexLock l(&global_mutex); - static bool already_done; - if (already_done) return; - already_done = true; +static bool CheckForLeaks() { if (&__lsan_is_turned_off && __lsan_is_turned_off()) - return; - - DoLeakCheckParam param; + return false; + EnsureMainThreadIDIsCorrect(); + CheckForLeaksParam param; param.success = false; LockThreadRegistry(); LockAllocator(); - StopTheWorld(DoLeakCheckCallback, ¶m); + DoStopTheWorld(CheckForLeaksCallback, ¶m); UnlockAllocator(); UnlockThreadRegistry(); @@ -424,25 +430,42 @@ void DoLeakCheck() { PrintMatchedSuppressions(); if (unsuppressed_count > 0) { param.leak_report.PrintSummary(); - if (flags()->exitcode) { - if (common_flags()->coverage) - __sanitizer_cov_dump(); - internal__exit(flags()->exitcode); - } + return true; } + return false; +} + +void DoLeakCheck() { + BlockingMutexLock l(&global_mutex); + static bool already_done; + if (already_done) return; + already_done = true; + bool have_leaks = CheckForLeaks(); + if (!have_leaks) { + return; + } + if (flags()->exitcode) { + if (common_flags()->coverage) + __sanitizer_cov_dump(); + internal__exit(flags()->exitcode); + } +} + +static int DoRecoverableLeakCheck() { + BlockingMutexLock l(&global_mutex); + bool have_leaks = CheckForLeaks(); + return have_leaks ? 1 : 0; } static Suppression *GetSuppressionForAddr(uptr addr) { Suppression *s = nullptr; // Suppress by module name. - const char *module_name; - uptr module_offset; SuppressionContext *suppressions = GetSuppressionContext(); - if (Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(addr, &module_name, - &module_offset) && - suppressions->Match(module_name, kSuppressionLeak, &s)) - return s; + if (const char *module_name = + Symbolizer::GetOrInit()->GetModuleNameForPc(addr)) + if (suppressions->Match(module_name, kSuppressionLeak, &s)) + return s; // Suppress by file or function name. SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); @@ -679,6 +702,15 @@ void __lsan_do_leak_check() { #endif // CAN_SANITIZE_LEAKS } +SANITIZER_INTERFACE_ATTRIBUTE +int __lsan_do_recoverable_leak_check() { +#if CAN_SANITIZE_LEAKS + if (common_flags()->detect_leaks) + return __lsan::DoRecoverableLeakCheck(); +#endif // CAN_SANITIZE_LEAKS + return 0; +} + #if !SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE int __lsan_is_turned_off() { diff --git a/lib/lsan/lsan_common.h b/lib/lsan/lsan_common.h index 2c3a12ab6bd8..4f9d24fb3ab9 100644 --- a/lib/lsan/lsan_common.h +++ b/lib/lsan/lsan_common.h @@ -19,6 +19,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_platform.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_symbolizer.h" #if SANITIZER_LINUX && (defined(__x86_64__) || defined(__mips64)) \ @@ -99,6 +100,8 @@ typedef InternalMmapVector<uptr> Frontier; void InitializePlatformSpecificModules(); void ProcessGlobalRegions(Frontier *frontier); void ProcessPlatformSpecificAllocations(Frontier *frontier); +// Run stoptheworld while holding any platform-specific locks. +void DoStopTheWorld(StopTheWorldCallback callback, void* argument); void ScanRangeForPointers(uptr begin, uptr end, Frontier *frontier, diff --git a/lib/lsan/lsan_common_linux.cc b/lib/lsan/lsan_common_linux.cc index ba51868c76e8..2955343e1f0b 100644 --- a/lib/lsan/lsan_common_linux.cc +++ b/lib/lsan/lsan_common_linux.cc @@ -85,10 +85,6 @@ static int ProcessGlobalRegionsCallback(struct dl_phdr_info *info, size_t size, // Scans global variables for heap pointers. void ProcessGlobalRegions(Frontier *frontier) { if (!flags()->use_globals) return; - // FIXME: dl_iterate_phdr acquires a linker lock, so we run a risk of - // deadlocking by running this under StopTheWorld. However, the lock is - // reentrant, so we should be able to fix this by acquiring the lock before - // suspending threads. dl_iterate_phdr(ProcessGlobalRegionsCallback, frontier); } @@ -114,7 +110,7 @@ static void ProcessPlatformSpecificAllocationsCb(uptr chunk, void *arg) { reinterpret_cast<ProcessPlatformAllocParam *>(arg); chunk = GetUserBegin(chunk); LsanMetadata m(chunk); - if (m.allocated() && m.tag() != kReachable) { + if (m.allocated() && m.tag() != kReachable && m.tag() != kIgnored) { u32 stack_id = m.stack_trace_id(); uptr caller_pc = 0; if (stack_id > 0) @@ -153,5 +149,30 @@ void ProcessPlatformSpecificAllocations(Frontier *frontier) { ForEachChunk(ProcessPlatformSpecificAllocationsCb, &arg); } +struct DoStopTheWorldParam { + StopTheWorldCallback callback; + void *argument; +}; + +static int DoStopTheWorldCallback(struct dl_phdr_info *info, size_t size, + void *data) { + DoStopTheWorldParam *param = reinterpret_cast<DoStopTheWorldParam *>(data); + StopTheWorld(param->callback, param->argument); + return 1; +} + +// LSan calls dl_iterate_phdr() from the tracer task. This may deadlock: if one +// of the threads is frozen while holding the libdl lock, the tracer will hang +// in dl_iterate_phdr() forever. +// Luckily, (a) the lock is reentrant and (b) libc can't distinguish between the +// tracer task and the thread that spawned it. Thus, if we run the tracer task +// while holding the libdl lock in the parent thread, we can safely reenter it +// in the tracer. The solution is to run stoptheworld from a dl_iterate_phdr() +// callback in the parent thread. +void DoStopTheWorld(StopTheWorldCallback callback, void *argument) { + DoStopTheWorldParam param = {callback, argument}; + dl_iterate_phdr(DoStopTheWorldCallback, ¶m); +} + } // namespace __lsan #endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX diff --git a/lib/lsan/lsan_interceptors.cc b/lib/lsan/lsan_interceptors.cc index ba2519dcb25d..61a92154d95e 100644 --- a/lib/lsan/lsan_interceptors.cc +++ b/lib/lsan/lsan_interceptors.cc @@ -208,7 +208,7 @@ extern "C" void *__lsan_thread_start_func(void *arg) { // Wait until the last iteration to maximize the chance that we are the last // destructor to run. if (pthread_setspecific(g_thread_finalize_key, - (void*)kPthreadDestructorIterations)) { + (void*)GetPthreadDestructorIterations())) { Report("LeakSanitizer: failed to set thread key.\n"); Die(); } diff --git a/lib/lsan/lsan_thread.h b/lib/lsan/lsan_thread.h index 4641b32ed6e6..99e2c1d5e64b 100644 --- a/lib/lsan/lsan_thread.h +++ b/lib/lsan/lsan_thread.h @@ -22,8 +22,8 @@ namespace __lsan { class ThreadContext : public ThreadContextBase { public: explicit ThreadContext(int tid); - void OnStarted(void *arg); - void OnFinished(); + void OnStarted(void *arg) override; + void OnFinished() override; uptr stack_begin() { return stack_begin_; } uptr stack_end() { return stack_end_; } uptr tls_begin() { return tls_begin_; } diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt index ccf47fc45cf3..de5980e5644b 100644 --- a/lib/msan/CMakeLists.txt +++ b/lib/msan/CMakeLists.txt @@ -7,12 +7,15 @@ set(MSAN_RTL_SOURCES msan_chained_origin_depot.cc msan_interceptors.cc msan_linux.cc - msan_new_delete.cc msan_report.cc msan_thread.cc msan_poisoning.cc ) +set(MSAN_RTL_CXX_SOURCES + msan_new_delete.cc) + + set(MSAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(MSAN_RTL_CFLAGS) append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE MSAN_RTL_CFLAGS) @@ -29,12 +32,21 @@ foreach(arch ${MSAN_SUPPORTED_ARCH}) $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}> + CFLAGS ${MSAN_RTL_CFLAGS}) + add_compiler_rt_runtime(clang_rt.msan_cxx-${arch} ${arch} STATIC + SOURCES ${MSAN_RTL_CXX_SOURCES} + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}> CFLAGS ${MSAN_RTL_CFLAGS}) - add_dependencies(msan clang_rt.msan-${arch}) - list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}) + add_dependencies(msan clang_rt.msan-${arch} + clang_rt.msan_cxx-${arch}) + list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch} + clang_rt.msan_cxx-${arch}) if(UNIX) add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra) - add_dependencies(msan clang_rt.msan-${arch}-symbols) + add_sanitizer_rt_symbols(clang_rt.msan_cxx-${arch} msan.syms.extra) + add_dependencies(msan clang_rt.msan-${arch}-symbols + clang_rt.msan_cxx-${arch}-symbols) endif() endforeach() diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index ed6efbdd682f..163d59dabfa8 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -26,6 +26,8 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "sanitizer_common/sanitizer_stackdepot.h" +#include "ubsan/ubsan_flags.h" +#include "ubsan/ubsan_init.h" // ACHTUNG! No system header includes in this file. @@ -133,11 +135,6 @@ static void RegisterMsanFlags(FlagParser *parser, Flags *f) { } static void InitializeFlags() { - Flags *f = flags(); - FlagParser parser; - RegisterMsanFlags(&parser, f); - RegisterCommonFlags(&parser); - SetCommonFlagsDefaults(); { CommonFlags cf; @@ -151,14 +148,35 @@ static void InitializeFlags() { OverrideCommonFlags(cf); } + Flags *f = flags(); f->SetDefaults(); + FlagParser parser; + RegisterMsanFlags(&parser, f); + RegisterCommonFlags(&parser); + +#if MSAN_CONTAINS_UBSAN + __ubsan::Flags *uf = __ubsan::flags(); + uf->SetDefaults(); + + FlagParser ubsan_parser; + __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); + RegisterCommonFlags(&ubsan_parser); +#endif + // Override from user-specified string. if (__msan_default_options) parser.ParseString(__msan_default_options()); +#if MSAN_CONTAINS_UBSAN + const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); + ubsan_parser.ParseString(ubsan_default_options); +#endif const char *msan_options = GetEnv("MSAN_OPTIONS"); parser.ParseString(msan_options); +#if MSAN_CONTAINS_UBSAN + ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); +#endif VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); SetVerbosity(common_flags()->verbosity); @@ -355,13 +373,12 @@ void __msan_init() { InitTlsSize(); InitializeFlags(); + CacheBinaryName(); __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); InstallAtExitHandler(); // Needs __cxa_atexit interceptor. - if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE) - ReplaceOperatorsNewAndDelete(); DisableCoreDumperIfNecessary(); if (StackSizeIsUnlimited()) { VPrintf(1, "Unlimited stack, doing reexec\n"); @@ -374,7 +391,7 @@ void __msan_init() { __msan_clear_on_return(); if (__msan_get_track_origins()) VPrintf(1, "msan_track_origins\n"); - if (!InitShadow(/* map_shadow */ true, __msan_get_track_origins())) { + if (!InitShadow(__msan_get_track_origins())) { Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n"); Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n"); Printf("FATAL: Disabling ASLR is known to cause this error.\n"); @@ -394,6 +411,10 @@ void __msan_init() { SetCurrentThread(main_thread); main_thread->ThreadStart(); +#if MSAN_CONTAINS_UBSAN + __ubsan::InitAsPlugin(); +#endif + VPrintf(1, "MemorySanitizer init done\n"); msan_init_is_running = 0; @@ -543,6 +564,13 @@ u32 __msan_get_origin(const void *a) { return *(u32*)origin_ptr; } +int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id) { + Origin o = Origin::FromRawId(this_id); + while (o.raw_id() != prev_id && o.isChainedOrigin()) + o = o.getNextChainedOrigin(nullptr); + return o.raw_id() == prev_id; +} + u32 __msan_get_umr_origin() { return __msan_origin_tls; } diff --git a/lib/msan/msan.h b/lib/msan/msan.h index ed18f21d0282..cd8bc19f51ef 100644 --- a/lib/msan/msan.h +++ b/lib/msan/msan.h @@ -20,11 +20,16 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "msan_interface_internal.h" #include "msan_flags.h" +#include "ubsan/ubsan_platform.h" #ifndef MSAN_REPLACE_OPERATORS_NEW_AND_DELETE # define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 #endif +#ifndef MSAN_CONTAINS_UBSAN +# define MSAN_CONTAINS_UBSAN CAN_SANITIZE_UB +#endif + struct MappingDesc { uptr start; uptr end; @@ -47,6 +52,25 @@ const MappingDesc kMemoryLayout[] = { #define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) #define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x002000000000) +#elif SANITIZER_LINUX && defined(__powerpc64__) + +const MappingDesc kMemoryLayout[] = { + {0x000000000000ULL, 0x000100000000ULL, MappingDesc::APP, "low memory"}, + {0x000100000000ULL, 0x080000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x080000000000ULL, 0x180100000000ULL, MappingDesc::SHADOW, "shadow"}, + {0x180100000000ULL, 0x1C0000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x1C0000000000ULL, 0x2C0100000000ULL, MappingDesc::ORIGIN, "origin"}, + {0x2C0100000000ULL, 0x300000000000ULL, MappingDesc::INVALID, "invalid"}, + {0x300000000000ULL, 0x400000000000ULL, MappingDesc::APP, "high memory"}}; + +// Maps low and high app ranges to contiguous space with zero base: +// Low: 0000 0000 0000 - 0000 ffff ffff -> 1000 0000 0000 - 1000 ffff ffff +// High: 3000 0000 0000 - 3fff ffff ffff -> 0000 0000 0000 - 0fff ffff ffff +#define LINEARIZE_MEM(mem) \ + (((uptr)(mem) & ~0x200000000000ULL) ^ 0x100000000000ULL) +#define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x080000000000ULL) +#define SHADOW_TO_ORIGIN(shadow) (((uptr)(shadow)) + 0x140000000000ULL) + #elif SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 64 // Low memory: main binary, MAP_32BIT mappings and modules @@ -120,7 +144,7 @@ extern bool msan_init_is_running; extern int msan_report_count; bool ProtectRange(uptr beg, uptr end); -bool InitShadow(bool map_shadow, bool init_origins); +bool InitShadow(bool init_origins); char *GetProcSelfMaps(); void InitializeInterceptors(); @@ -131,7 +155,6 @@ void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, void MsanDeallocate(StackTrace *stack, void *ptr); void InstallTrapHandler(); void InstallAtExitHandler(); -void ReplaceOperatorsNewAndDelete(); const char *GetStackOriginDescr(u32 id, uptr *pc); diff --git a/lib/msan/msan.syms.extra b/lib/msan/msan.syms.extra index aad41cf1124e..950e6f4ad5ad 100644 --- a/lib/msan/msan.syms.extra +++ b/lib/msan/msan.syms.extra @@ -1 +1,2 @@ __msan_* +__ubsan_* diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc index 698b6cddd30b..6df35664279f 100644 --- a/lib/msan/msan_allocator.cc +++ b/lib/msan/msan_allocator.cc @@ -58,6 +58,15 @@ struct MsanMapUnmapCallback { typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, DefaultSizeClassMap, MsanMapUnmapCallback> PrimaryAllocator; +#elif defined(__powerpc64__) + static const uptr kAllocatorSpace = 0x300000000000; + static const uptr kAllocatorSize = 0x020000000000; // 2T + static const uptr kMetadataSize = sizeof(Metadata); + static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G + + typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, + DefaultSizeClassMap, + MsanMapUnmapCallback> PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; typedef LargeMmapAllocator<MsanMapUnmapCallback> SecondaryAllocator; diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index 4a243941b8a3..6d5a056a3bb3 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -94,6 +94,13 @@ bool IsInInterceptorScope() { if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ } while (0); +#define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \ + CHECK_UNPOISONED((x), \ + common_flags()->strict_string_checks ? (len) + 1 : (n) ) + +#define CHECK_UNPOISONED_STRING(x, n) \ + CHECK_UNPOISONED_STRING_OF_LEN((x), internal_strlen(x), (n)) + INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(fread)(ptr, size, nmemb, file); @@ -118,6 +125,7 @@ INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { ENSURE_MSAN_INITED(); + CHECK_UNPOISONED_STRING(path, 0) SSIZE_T res = REAL(readlink)(path, buf, bufsiz); if (res > 0) __msan_unpoison(buf, res); @@ -283,13 +291,11 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) { return res; } -// FIXME: Add stricter shadow checks in str* interceptors (ex.: strcpy should -// check the shadow of the terminating \0 byte). - INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); + CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(strcpy)(dest, src); // NOLINT CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; @@ -311,6 +317,7 @@ INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); + CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(stpcpy)(dest, src); // NOLINT CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; @@ -322,6 +329,7 @@ INTERCEPTOR(char *, strdup, char *src) { // On FreeBSD strdup() leverages strlen(). InterceptorScope interceptor_scope; SIZE_T n = REAL(strlen)(src); + CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(strdup)(src); CopyShadowAndOrigin(res, src, n + 1, &stack); return res; @@ -332,6 +340,7 @@ INTERCEPTOR(char *, __strdup, char *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); + CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(__strdup)(src); CopyShadowAndOrigin(res, src, n + 1, &stack); return res; @@ -381,6 +390,8 @@ INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT GET_STORE_STACK_TRACE; SIZE_T src_size = REAL(strlen)(src); SIZE_T dest_size = REAL(strlen)(dest); + CHECK_UNPOISONED_STRING(src + src_size, 0); + CHECK_UNPOISONED_STRING(dest + dest_size, 0); char *res = REAL(strcat)(dest, src); // NOLINT CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack); return res; @@ -391,6 +402,7 @@ INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT GET_STORE_STACK_TRACE; SIZE_T dest_size = REAL(strlen)(dest); SIZE_T copy_size = REAL(strnlen)(src, n); + CHECK_UNPOISONED_STRING(dest + dest_size, 0); char *res = REAL(strncat)(dest, src, n); // NOLINT CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack); __msan_unpoison(dest + dest_size + copy_size, 1); // \0 @@ -667,6 +679,7 @@ static void UnpoisonEnviron() { INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { ENSURE_MSAN_INITED(); + CHECK_UNPOISONED_STRING(name, 0) int res = REAL(setenv)(name, value, overwrite); if (!res) UnpoisonEnviron(); return res; @@ -1384,6 +1397,14 @@ int OnExit() { if (map) ForEachMappedRegion(map, __msan_unpoison); \ } while (false) +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ + if (MsanThread *t = GetCurrentThread()) { \ + *begin = t->tls_begin(); \ + *end = t->tls_end(); \ + } else { \ + *begin = *end = 0; \ + } + #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) @@ -1420,7 +1441,8 @@ void __msan_clear_and_unpoison(void *a, uptr size) { void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { if (!msan_inited) return internal_memcpy(dest, src, n); - if (msan_init_is_running) return REAL(memcpy)(dest, src, n); + if (msan_init_is_running || __msan::IsInSymbolizer()) + return REAL(memcpy)(dest, src, n); ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memcpy)(dest, src, n); diff --git a/lib/msan/msan_interface_internal.h b/lib/msan/msan_interface_internal.h index 8641f814bc5f..f4d37d96c5b5 100644 --- a/lib/msan/msan_interface_internal.h +++ b/lib/msan/msan_interface_internal.h @@ -96,6 +96,13 @@ u32 __msan_chain_origin(u32 id); SANITIZER_INTERFACE_ATTRIBUTE u32 __msan_get_origin(const void *a); +// Test that this_id is a descendant of prev_id (or they are simply equal). +// "descendant" here means that are part of the same chain, created with +// __msan_chain_origin. +SANITIZER_INTERFACE_ATTRIBUTE +int __msan_origin_is_descendant_or_same(u32 this_id, u32 prev_id); + + SANITIZER_INTERFACE_ATTRIBUTE void __msan_clear_on_return(); diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc index 6c185165fc50..7025ef6c812d 100644 --- a/lib/msan/msan_linux.cc +++ b/lib/msan/msan_linux.cc @@ -53,10 +53,19 @@ static bool CheckMemoryRangeAvailability(uptr beg, uptr size) { return true; } -static bool ProtectMemoryRange(uptr beg, uptr size) { +static bool ProtectMemoryRange(uptr beg, uptr size, const char *name) { if (size > 0) { - uptr end = beg + size - 1; - if (!Mprotect(beg, size)) { + void *addr = MmapNoAccess(beg, size, name); + if (beg == 0 && addr != 0) { + // Depending on the kernel configuration, we may not be able to protect + // the page at address zero. + uptr gap = 16 * GetPageSizeCached(); + beg += gap; + size -= gap; + addr = MmapNoAccess(beg, size, name); + } + if ((uptr)addr != beg) { + uptr end = beg + size - 1; Printf("FATAL: Cannot protect memory range %p - %p.\n", beg, end); return false; } @@ -95,7 +104,7 @@ static void CheckMemoryLayoutSanity() { } } -bool InitShadow(bool map_shadow, bool init_origins) { +bool InitShadow(bool init_origins) { // Let user know mapping parameters first. VPrintf(1, "__msan_init %p\n", &__msan_init); for (unsigned i = 0; i < kMemoryLayoutSize; ++i) @@ -115,15 +124,27 @@ bool InitShadow(bool map_shadow, bool init_origins) { uptr end = kMemoryLayout[i].end; uptr size= end - start; MappingDesc::Type type = kMemoryLayout[i].type; - if ((map_shadow && type == MappingDesc::SHADOW) || - (init_origins && type == MappingDesc::ORIGIN)) { - if (!CheckMemoryRangeAvailability(start, size)) return false; - if ((uptr)MmapFixedNoReserve(start, size) != start) return false; + + bool map = type == MappingDesc::SHADOW || + (init_origins && type == MappingDesc::ORIGIN); + bool protect = type == MappingDesc::INVALID || + (!init_origins && type == MappingDesc::ORIGIN); + CHECK(!(map && protect)); + if (!map && !protect) + CHECK(type == MappingDesc::APP); + if (map) { + if (!CheckMemoryRangeAvailability(start, size)) + return false; + if ((uptr)MmapFixedNoReserve(start, size, kMemoryLayout[i].name) != start) + return false; if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(start, size); - } else if (type == MappingDesc::INVALID) { - if (!CheckMemoryRangeAvailability(start, size)) return false; - if (!ProtectMemoryRange(start, size)) return false; + } + if (protect) { + if (!CheckMemoryRangeAvailability(start, size)) + return false; + if (!ProtectMemoryRange(start, size, kMemoryLayout[i].name)) + return false; } } diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc index 9a8e56e4a28a..c8bc0651b507 100644 --- a/lib/msan/msan_new_delete.cc +++ b/lib/msan/msan_new_delete.cc @@ -19,13 +19,6 @@ #include <stddef.h> -namespace __msan { -// This function is a no-op. We need it to make sure that object file -// with our replacements will actually be loaded from static MSan -// run-time library at link-time. -void ReplaceOperatorsNewAndDelete() { } -} - using namespace __msan; // NOLINT // Fake std::nothrow_t to avoid including <new>. diff --git a/lib/msan/msan_origin.h b/lib/msan/msan_origin.h index 1284c01bf2af..36c168b8521c 100644 --- a/lib/msan/msan_origin.h +++ b/lib/msan/msan_origin.h @@ -87,7 +87,7 @@ class Origin { CHECK(isChainedOrigin()); u32 prev_id; u32 stack_id = ChainedOriginDepotGet(getChainedId(), &prev_id); - *stack = StackDepotGet(stack_id); + if (stack) *stack = StackDepotGet(stack_id); return Origin(prev_id); } diff --git a/lib/msan/msan_poisoning.cc b/lib/msan/msan_poisoning.cc index 96411fdbc31b..92134f6a15b8 100644 --- a/lib/msan/msan_poisoning.cc +++ b/lib/msan/msan_poisoning.cc @@ -122,7 +122,7 @@ void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) { void SetShadow(const void *ptr, uptr size, u8 value) { uptr PageSize = GetPageSizeCached(); uptr shadow_beg = MEM_TO_SHADOW(ptr); - uptr shadow_end = MEM_TO_SHADOW((uptr)ptr + size); + uptr shadow_end = shadow_beg + size; if (value || shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg); diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc index 33c28b2fba0e..ddb8070282a9 100644 --- a/lib/msan/msan_report.cc +++ b/lib/msan/msan_report.cc @@ -103,7 +103,7 @@ void ReportUMR(StackTrace *stack, u32 origin) { Decorator d; Printf("%s", d.Warning()); - Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n"); + Report("WARNING: MemorySanitizer: use-of-uninitialized-value\n"); Printf("%s", d.End()); stack->Print(); if (origin) { @@ -115,7 +115,7 @@ void ReportUMR(StackTrace *stack, u32 origin) { void ReportExpectedUMRNotFound(StackTrace *stack) { SpinMutexLock l(&CommonSanitizerReportMutex); - Printf(" WARNING: Expected use of uninitialized value not found\n"); + Printf("WARNING: Expected use of uninitialized value not found\n"); stack->Print(); } diff --git a/lib/msan/msan_thread.cc b/lib/msan/msan_thread.cc index e15a247c6bb8..0ba499350064 100644 --- a/lib/msan/msan_thread.cc +++ b/lib/msan/msan_thread.cc @@ -14,7 +14,7 @@ MsanThread *MsanThread::Create(thread_callback_t start_routine, MsanThread *thread = (MsanThread*)MmapOrDie(size, __func__); thread->start_routine_ = start_routine; thread->arg_ = arg; - thread->destructor_iterations_ = kPthreadDestructorIterations; + thread->destructor_iterations_ = GetPthreadDestructorIterations(); return thread; } diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt index e008bd329cb4..bf16a16bcf20 100644 --- a/lib/msan/tests/CMakeLists.txt +++ b/lib/msan/tests/CMakeLists.txt @@ -33,6 +33,7 @@ set(MSAN_UNITTEST_COMMON_CFLAGS -Wno-deprecated-declarations -Wno-unused-variable -Wno-zero-length-array + -Wno-uninitialized -Werror=sign-compare ) set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS @@ -70,7 +71,7 @@ macro(msan_compile obj_list source arch kind) endmacro() macro(msan_link_shared so_list so_name arch kind) - parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN}) + cmake_parse_arguments(SOURCE "" "" "OBJECTS;LINKFLAGS;DEPS" ${ARGN}) set(output_so "${CMAKE_CURRENT_BINARY_DIR}/${so_name}.${arch}${kind}.so") get_target_flags_for_arch(${arch} TARGET_LINKFLAGS) if(NOT COMPILER_RT_STANDALONE_BUILD) diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index 317f70cbc32e..00dd20a3d775 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -134,9 +134,12 @@ static bool TrackingOrigins() { __msan_set_origin(&x, sizeof(x), 0x1234); U4 origin = __msan_get_origin(&x); __msan_set_origin(&x, sizeof(x), 0); - return origin == 0x1234; + return __msan_origin_is_descendant_or_same(origin, 0x1234); } +#define EXPECT_ORIGIN(expected, origin) \ + EXPECT_TRUE(__msan_origin_is_descendant_or_same((origin), (expected))) + #define EXPECT_UMR(action) \ do { \ __msan_set_expect_umr(1); \ @@ -144,14 +147,13 @@ static bool TrackingOrigins() { __msan_set_expect_umr(0); \ } while (0) -#define EXPECT_UMR_O(action, origin) \ - do { \ - __msan_set_expect_umr(1); \ - action; \ - __msan_set_expect_umr(0); \ - if (TrackingOrigins()) \ - EXPECT_EQ(origin, __msan_get_umr_origin()); \ - } while (0) +#define EXPECT_UMR_O(action, origin) \ + do { \ + __msan_set_expect_umr(1); \ + action; \ + __msan_set_expect_umr(0); \ + if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_umr_origin()); \ + } while (0) #define EXPECT_POISONED(x) ExpectPoisoned(x) @@ -166,8 +168,7 @@ void ExpectPoisoned(const T& t) { template<typename T> void ExpectPoisonedWithOrigin(const T& t, unsigned origin) { EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); - if (TrackingOrigins()) - EXPECT_EQ(origin, __msan_get_origin((void*)&t)); + if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_origin((void *)&t)); } #define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x))) @@ -3902,15 +3903,15 @@ TEST(VectorMaddTest, mmx_pmadd_wd) { #endif // defined(__clang__) TEST(MemorySanitizerOrigins, SetGet) { - EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins()); + EXPECT_EQ(TrackingOrigins(), !!__msan_get_track_origins()); if (!TrackingOrigins()) return; int x; __msan_set_origin(&x, sizeof(x), 1234); - EXPECT_EQ(1234U, __msan_get_origin(&x)); + EXPECT_ORIGIN(1234U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 5678); - EXPECT_EQ(5678U, __msan_get_origin(&x)); + EXPECT_ORIGIN(5678U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 0); - EXPECT_EQ(0U, __msan_get_origin(&x)); + EXPECT_ORIGIN(0U, __msan_get_origin(&x)); } namespace { @@ -3926,12 +3927,12 @@ TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) { S s; U4 origin = rand(); // NOLINT s.a = *GetPoisonedO<U2>(0, origin); - EXPECT_EQ(origin, __msan_get_origin(&s.a)); - EXPECT_EQ(origin, __msan_get_origin(&s.b)); + EXPECT_ORIGIN(origin, __msan_get_origin(&s.a)); + EXPECT_ORIGIN(origin, __msan_get_origin(&s.b)); s.b = 42; - EXPECT_EQ(origin, __msan_get_origin(&s.a)); - EXPECT_EQ(origin, __msan_get_origin(&s.b)); + EXPECT_ORIGIN(origin, __msan_get_origin(&s.a)); + EXPECT_ORIGIN(origin, __msan_get_origin(&s.b)); } } // namespace @@ -3947,7 +3948,8 @@ void BinaryOpOriginTest(BinaryOp op) { *z = op(*x, *y); U4 origin = __msan_get_origin(z); EXPECT_POISONED_O(*z, origin); - EXPECT_EQ(true, origin == ox || origin == oy); + EXPECT_EQ(true, __msan_origin_is_descendant_or_same(origin, ox) || + __msan_origin_is_descendant_or_same(origin, oy)); // y is poisoned, x is not. *x = 10101; @@ -3956,7 +3958,7 @@ void BinaryOpOriginTest(BinaryOp op) { __msan_set_origin(z, sizeof(*z), 0); *z = op(*x, *y); EXPECT_POISONED_O(*z, oy); - EXPECT_EQ(__msan_get_origin(z), oy); + EXPECT_ORIGIN(oy, __msan_get_origin(z)); // x is poisoned, y is not. *x = *GetPoisonedO<T>(0, ox); @@ -3965,7 +3967,7 @@ void BinaryOpOriginTest(BinaryOp op) { __msan_set_origin(z, sizeof(*z), 0); *z = op(*x, *y); EXPECT_POISONED_O(*z, ox); - EXPECT_EQ(__msan_get_origin(z), ox); + EXPECT_ORIGIN(ox, __msan_get_origin(z)); } template<class T> INLINE T XOR(const T &a, const T&b) { return a ^ b; } diff --git a/lib/profile/CMakeLists.txt b/lib/profile/CMakeLists.txt index 420d7660ee36..d03409fc45b7 100644 --- a/lib/profile/CMakeLists.txt +++ b/lib/profile/CMakeLists.txt @@ -7,11 +7,12 @@ set(PROFILE_SOURCES InstrProfilingFile.c InstrProfilingPlatformDarwin.c InstrProfilingPlatformOther.c - InstrProfilingRuntime.cc) + InstrProfilingRuntime.cc + InstrProfilingUtil.c) if(APPLE) add_compiler_rt_osx_static_runtime(clang_rt.profile_osx - ARCH ${PROFILE_SUPPORTED_ARCH} + ARCHS ${PROFILE_SUPPORTED_ARCH} SOURCES ${PROFILE_SOURCES}) add_dependencies(profile clang_rt.profile_osx) else() diff --git a/lib/profile/GCDAProfiling.c b/lib/profile/GCDAProfiling.c index 45fbd07e544b..aec232856e74 100644 --- a/lib/profile/GCDAProfiling.c +++ b/lib/profile/GCDAProfiling.c @@ -20,23 +20,18 @@ |* \*===----------------------------------------------------------------------===*/ +#include "InstrProfilingUtil.h" + #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> -#ifdef _WIN32 -#include <direct.h> -#endif +#include <sys/file.h> #define I386_FREEBSD (defined(__FreeBSD__) && defined(__i386__)) -#if !I386_FREEBSD -#include <sys/stat.h> -#include <sys/types.h> -#endif - #if !defined(_MSC_VER) && !I386_FREEBSD #include <stdint.h> #endif @@ -51,7 +46,6 @@ typedef unsigned long long uint64_t; typedef unsigned char uint8_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; -int mkdir(const char*, unsigned short); #endif /* #define DEBUG_GCDAPROFILING */ @@ -208,21 +202,6 @@ static char *mangle_filename(const char *orig_filename) { return new_filename; } -static void recursive_mkdir(char *path) { - int i; - - for (i = 1; path[i] != '\0'; ++i) { - if (path[i] != '/') continue; - path[i] = '\0'; -#ifdef _WIN32 - _mkdir(path); -#else - mkdir(path, 0755); /* Some of these will fail, ignore it. */ -#endif - path[i] = '/'; - } -} - static int map_file() { fseek(output_file, 0L, SEEK_END); file_size = ftell(output_file); @@ -282,7 +261,7 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4], fd = open(filename, O_RDWR | O_CREAT, 0644); if (fd == -1) { /* Try creating the directories first then opening the file. */ - recursive_mkdir(filename); + __llvm_profile_recursive_mkdir(filename); fd = open(filename, O_RDWR | O_CREAT, 0644); if (fd == -1) { /* Bah! It's hopeless. */ @@ -294,6 +273,11 @@ void llvm_gcda_start_file(const char *orig_filename, const char version[4], } } + /* Try to flock the file to serialize concurrent processes writing out to the + * same GCDA. This can fail if the filesystem doesn't support it, but in that + * case we'll just carry on with the old racy behaviour and hope for the best. + */ + flock(fd, LOCK_EX); output_file = fdopen(fd, mode); /* Initialize the write buffer. */ @@ -493,6 +477,7 @@ void llvm_gcda_end_file() { } fclose(output_file); + flock(fd, LOCK_UN); output_file = NULL; write_buffer = NULL; } diff --git a/lib/profile/InstrProfiling.h b/lib/profile/InstrProfiling.h index 2b1bd003668e..3778a88893e6 100644 --- a/lib/profile/InstrProfiling.h +++ b/lib/profile/InstrProfiling.h @@ -62,7 +62,9 @@ uint64_t *__llvm_profile_end_counters(void); * * Writes to the file with the last name given to \a __llvm_profile_set_filename(), * or if it hasn't been called, the \c LLVM_PROFILE_FILE environment variable, - * or if that's not set, \c "default.profdata". + * or if that's not set, the last name given to + * \a __llvm_profile_override_default_filename(), or if that's not set, + * \c "default.profraw". */ int __llvm_profile_write_file(void); @@ -77,6 +79,19 @@ int __llvm_profile_write_file(void); */ void __llvm_profile_set_filename(const char *Name); +/*! + * \brief Set the filename for writing instrumentation data, unless the + * \c LLVM_PROFILE_FILE environment variable was set. + * + * Unless overridden, sets the filename to be used for subsequent calls to + * \a __llvm_profile_write_file(). + * + * \c Name is not copied, so it must remain valid. Passing NULL resets the + * filename logic to the default behaviour (unless the \c LLVM_PROFILE_FILE + * was set in which case it has no effect). + */ +void __llvm_profile_override_default_filename(const char *Name); + /*! \brief Register to write instrumentation data to file at exit. */ int __llvm_profile_register_write_file_atexit(void); diff --git a/lib/profile/InstrProfilingFile.c b/lib/profile/InstrProfilingFile.c index 346665fd5b3e..68e8c7b07871 100644 --- a/lib/profile/InstrProfilingFile.c +++ b/lib/profile/InstrProfilingFile.c @@ -8,10 +8,11 @@ \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" +#include "InstrProfilingUtil.h" +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/errno.h> #define UNCONST(ptr) ((void *)(uintptr_t)(ptr)) @@ -76,40 +77,61 @@ static int writeFileWithName(const char *OutputName) { __attribute__((weak)) int __llvm_profile_OwnsFilename = 0; __attribute__((weak)) const char *__llvm_profile_CurrentFilename = NULL; -static void setFilename(const char *Filename, int OwnsFilename) { - if (__llvm_profile_OwnsFilename) - free(UNCONST(__llvm_profile_CurrentFilename)); - - __llvm_profile_CurrentFilename = Filename; - __llvm_profile_OwnsFilename = OwnsFilename; -} - static void truncateCurrentFile(void) { - const char *Filename = __llvm_profile_CurrentFilename; + const char *Filename; + FILE *File; + + Filename = __llvm_profile_CurrentFilename; if (!Filename || !Filename[0]) return; + /* Create the directory holding the file, if needed. */ + if (strchr(Filename, '/')) { + char *Copy = malloc(strlen(Filename) + 1); + strcpy(Copy, Filename); + __llvm_profile_recursive_mkdir(Copy); + free(Copy); + } + /* Truncate the file. Later we'll reopen and append. */ - FILE *File = fopen(Filename, "w"); + File = fopen(Filename, "w"); if (!File) return; fclose(File); } -static void setDefaultFilename(void) { setFilename("default.profraw", 0); } +static void setFilename(const char *Filename, int OwnsFilename) { + /* Check if this is a new filename and therefore needs truncation. */ + int NewFile = !__llvm_profile_CurrentFilename || + (Filename && strcmp(Filename, __llvm_profile_CurrentFilename)); + if (__llvm_profile_OwnsFilename) + free(UNCONST(__llvm_profile_CurrentFilename)); -int getpid(void); -static int setFilenameFromEnvironment(void) { - const char *Filename = getenv("LLVM_PROFILE_FILE"); - if (!Filename || !Filename[0]) - return -1; + __llvm_profile_CurrentFilename = Filename; + __llvm_profile_OwnsFilename = OwnsFilename; - /* Check the filename for "%p", which indicates a pid-substitution. */ + /* If not a new file, append to support profiling multiple shared objects. */ + if (NewFile) + truncateCurrentFile(); +} + +static void resetFilenameToDefault(void) { setFilename("default.profraw", 0); } + +int getpid(void); +static int setFilenamePossiblyWithPid(const char *Filename) { #define MAX_PID_SIZE 16 char PidChars[MAX_PID_SIZE] = {0}; - int NumPids = 0; - int PidLength = 0; - int I; + int NumPids = 0, PidLength = 0; + char *Allocated; + int I, J; + + /* Reset filename on NULL, except with env var which is checked by caller. */ + if (!Filename) { + resetFilenameToDefault(); + return 0; + } + + /* Check the filename for "%p", which indicates a pid-substitution. */ for (I = 0; Filename[I]; ++I) if (Filename[I] == '%' && Filename[++I] == 'p') if (!NumPids++) { @@ -123,12 +145,11 @@ static int setFilenameFromEnvironment(void) { } /* Allocate enough space for the substituted filename. */ - char *Allocated = (char*)malloc(I + NumPids*(PidLength - 2) + 1); + Allocated = malloc(I + NumPids*(PidLength - 2) + 1); if (!Allocated) return -1; /* Construct the new filename. */ - int J; for (I = 0, J = 0; Filename[I]; ++I) if (Filename[I] == '%') { if (Filename[++I] == 'p') { @@ -145,11 +166,20 @@ static int setFilenameFromEnvironment(void) { return 0; } +static int setFilenameFromEnvironment(void) { + const char *Filename = getenv("LLVM_PROFILE_FILE"); + + if (!Filename || !Filename[0]) + return -1; + + return setFilenamePossiblyWithPid(Filename); +} + static void setFilenameAutomatically(void) { if (!setFilenameFromEnvironment()) return; - setDefaultFilename(); + resetFilenameToDefault(); } __attribute__((visibility("hidden"))) @@ -160,23 +190,32 @@ void __llvm_profile_initialize_file(void) { /* Detect the filename and truncate. */ setFilenameAutomatically(); - truncateCurrentFile(); } __attribute__((visibility("hidden"))) void __llvm_profile_set_filename(const char *Filename) { - setFilename(Filename, 0); - truncateCurrentFile(); + setFilenamePossiblyWithPid(Filename); +} + +__attribute__((visibility("hidden"))) +void __llvm_profile_override_default_filename(const char *Filename) { + /* If the env var is set, skip setting filename from argument. */ + const char *Env_Filename = getenv("LLVM_PROFILE_FILE"); + if (Env_Filename && Env_Filename[0]) + return; + setFilenamePossiblyWithPid(Filename); } __attribute__((visibility("hidden"))) int __llvm_profile_write_file(void) { + int rc; + /* Check the filename. */ if (!__llvm_profile_CurrentFilename) return -1; /* Write the file. */ - int rc = writeFileWithName(__llvm_profile_CurrentFilename); + rc = writeFileWithName(__llvm_profile_CurrentFilename); if (rc && getenv("LLVM_PROFILE_VERBOSE_ERRORS")) fprintf(stderr, "LLVM Profile: Failed to write file \"%s\": %s\n", __llvm_profile_CurrentFilename, strerror(errno)); diff --git a/lib/profile/InstrProfilingUtil.c b/lib/profile/InstrProfilingUtil.c new file mode 100644 index 000000000000..e146dfca83c8 --- /dev/null +++ b/lib/profile/InstrProfilingUtil.c @@ -0,0 +1,35 @@ +/*===- InstrProfilingUtil.c - Support library for PGO instrumentation -----===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#include "InstrProfilingUtil.h" + +#ifdef _WIN32 +#include <direct.h> +#elif I386_FREEBSD +int mkdir(const char*, unsigned short); +#else +#include <sys/stat.h> +#include <sys/types.h> +#endif + +__attribute__((visibility("hidden"))) +void __llvm_profile_recursive_mkdir(char *path) { + int i; + + for (i = 1; path[i] != '\0'; ++i) { + if (path[i] != '/') continue; + path[i] = '\0'; +#ifdef _WIN32 + _mkdir(path); +#else + mkdir(path, 0755); /* Some of these will fail, ignore it. */ +#endif + path[i] = '/'; + } +} diff --git a/lib/profile/InstrProfilingUtil.h b/lib/profile/InstrProfilingUtil.h new file mode 100644 index 000000000000..756b18e7c56d --- /dev/null +++ b/lib/profile/InstrProfilingUtil.h @@ -0,0 +1,16 @@ +/*===- InstrProfilingUtil.h - Support library for PGO instrumentation -----===*\ +|* +|* The LLVM Compiler Infrastructure +|* +|* This file is distributed under the University of Illinois Open Source +|* License. See LICENSE.TXT for details. +|* +\*===----------------------------------------------------------------------===*/ + +#ifndef PROFILE_INSTRPROFILINGUTIL_H +#define PROFILE_INSTRPROFILINGUTIL_H + +/*! \brief Create a directory tree. */ +void __llvm_profile_recursive_mkdir(char *Pathname); + +#endif /* PROFILE_INSTRPROFILINGUTIL_H */ diff --git a/lib/safestack/CMakeLists.txt b/lib/safestack/CMakeLists.txt new file mode 100644 index 000000000000..1c15d079dbb5 --- /dev/null +++ b/lib/safestack/CMakeLists.txt @@ -0,0 +1,28 @@ +add_custom_target(safestack) + +set(SAFESTACK_SOURCES safestack.cc) + +include_directories(..) + +set(SAFESTACK_CFLAGS ${SANITIZER_COMMON_CFLAGS}) + +if(APPLE) + # Build universal binary on APPLE. + add_compiler_rt_osx_static_runtime(clang_rt.safestack_osx + ARCH ${SAFESTACK_SUPPORTED_ARCH} + SOURCES ${SAFESTACK_SOURCES} + $<TARGET_OBJECTS:RTInterception.osx> + $<TARGET_OBJECTS:RTSanitizerCommon.osx> + CFLAGS ${SAFESTACK_CFLAGS}) + add_dependencies(safestack clang_rt.safestack_osx) +else() + # Otherwise, build separate libraries for each target. + foreach(arch ${SAFESTACK_SUPPORTED_ARCH}) + add_compiler_rt_runtime(clang_rt.safestack-${arch} ${arch} STATIC + SOURCES ${SAFESTACK_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + CFLAGS ${SAFESTACK_CFLAGS}) + add_dependencies(safestack clang_rt.safestack-${arch}) + endforeach() +endif() diff --git a/lib/safestack/safestack.cc b/lib/safestack/safestack.cc new file mode 100644 index 000000000000..504bd3cd0d99 --- /dev/null +++ b/lib/safestack/safestack.cc @@ -0,0 +1,246 @@ +//===-- safestack.cc ------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the runtime support for the safe stack protection +// mechanism. The runtime manages allocation/deallocation of the unsafe stack +// for the main thread, as well as all pthreads that are created/destroyed +// during program execution. +// +//===----------------------------------------------------------------------===// + +#include <limits.h> +#include <pthread.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/user.h> + +#include "interception/interception.h" +#include "sanitizer_common/sanitizer_common.h" + +// TODO: The runtime library does not currently protect the safe stack beyond +// relying on the system-enforced ASLR. The protection of the (safe) stack can +// be provided by three alternative features: +// +// 1) Protection via hardware segmentation on x86-32 and some x86-64 +// architectures: the (safe) stack segment (implicitly accessed via the %ss +// segment register) can be separated from the data segment (implicitly +// accessed via the %ds segment register). Dereferencing a pointer to the safe +// segment would result in a segmentation fault. +// +// 2) Protection via software fault isolation: memory writes that are not meant +// to access the safe stack can be prevented from doing so through runtime +// instrumentation. One way to do it is to allocate the safe stack(s) in the +// upper half of the userspace and bitmask the corresponding upper bit of the +// memory addresses of memory writes that are not meant to access the safe +// stack. +// +// 3) Protection via information hiding on 64 bit architectures: the location +// of the safe stack(s) can be randomized through secure mechanisms, and the +// leakage of the stack pointer can be prevented. Currently, libc can leak the +// stack pointer in several ways (e.g. in longjmp, signal handling, user-level +// context switching related functions, etc.). These can be fixed in libc and +// in other low-level libraries, by either eliminating the escaping/dumping of +// the stack pointer (i.e., %rsp) when that's possible, or by using +// encryption/PTR_MANGLE (XOR-ing the dumped stack pointer with another secret +// we control and protect better, as is already done for setjmp in glibc.) +// Furthermore, a static machine code level verifier can be ran after code +// generation to make sure that the stack pointer is never written to memory, +// or if it is, its written on the safe stack. +// +// Finally, while the Unsafe Stack pointer is currently stored in a thread +// local variable, with libc support it could be stored in the TCB (thread +// control block) as well, eliminating another level of indirection and making +// such accesses faster. Alternatively, dedicating a separate register for +// storing it would also be possible. + +/// Minimum stack alignment for the unsafe stack. +const unsigned kStackAlign = 16; + +/// Default size of the unsafe stack. This value is only used if the stack +/// size rlimit is set to infinity. +const unsigned kDefaultUnsafeStackSize = 0x2800000; + +// TODO: To make accessing the unsafe stack pointer faster, we plan to +// eventually store it directly in the thread control block data structure on +// platforms where this structure is pointed to by %fs or %gs. This is exactly +// the same mechanism as currently being used by the traditional stack +// protector pass to store the stack guard (see getStackCookieLocation() +// function above). Doing so requires changing the tcbhead_t struct in glibc +// on Linux and tcb struct in libc on FreeBSD. +// +// For now, store it in a thread-local variable. +extern "C" { +__attribute__((visibility( + "default"))) __thread void *__safestack_unsafe_stack_ptr = nullptr; +} + +// Per-thread unsafe stack information. It's not frequently accessed, so there +// it can be kept out of the tcb in normal thread-local variables. +static __thread void *unsafe_stack_start = nullptr; +static __thread size_t unsafe_stack_size = 0; +static __thread size_t unsafe_stack_guard = 0; + +static inline void *unsafe_stack_alloc(size_t size, size_t guard) { + CHECK_GE(size + guard, size); + void *addr = MmapOrDie(size + guard, "unsafe_stack_alloc"); + MprotectNoAccess((uptr)addr, (uptr)guard); + return (char *)addr + guard; +} + +static inline void unsafe_stack_setup(void *start, size_t size, size_t guard) { + CHECK_GE((char *)start + size, (char *)start); + CHECK_GE((char *)start + guard, (char *)start); + void *stack_ptr = (char *)start + size; + CHECK_EQ((((size_t)stack_ptr) & (kStackAlign - 1)), 0); + + __safestack_unsafe_stack_ptr = stack_ptr; + unsafe_stack_start = start; + unsafe_stack_size = size; + unsafe_stack_guard = guard; +} + +static void unsafe_stack_free() { + if (unsafe_stack_start) { + UnmapOrDie((char *)unsafe_stack_start - unsafe_stack_guard, + unsafe_stack_size + unsafe_stack_guard); + } + unsafe_stack_start = nullptr; +} + +/// Thread data for the cleanup handler +static pthread_key_t thread_cleanup_key; + +/// Safe stack per-thread information passed to the thread_start function +struct tinfo { + void *(*start_routine)(void *); + void *start_routine_arg; + + void *unsafe_stack_start; + size_t unsafe_stack_size; + size_t unsafe_stack_guard; +}; + +/// Wrap the thread function in order to deallocate the unsafe stack when the +/// thread terminates by returning from its main function. +static void *thread_start(void *arg) { + struct tinfo *tinfo = (struct tinfo *)arg; + + void *(*start_routine)(void *) = tinfo->start_routine; + void *start_routine_arg = tinfo->start_routine_arg; + + // Setup the unsafe stack; this will destroy tinfo content + unsafe_stack_setup(tinfo->unsafe_stack_start, tinfo->unsafe_stack_size, + tinfo->unsafe_stack_guard); + + // Make sure out thread-specific destructor will be called + // FIXME: we can do this only any other specific key is set by + // intercepting the pthread_setspecific function itself + pthread_setspecific(thread_cleanup_key, (void *)1); + + return start_routine(start_routine_arg); +} + +/// Thread-specific data destructor +static void thread_cleanup_handler(void *_iter) { + // We want to free the unsafe stack only after all other destructors + // have already run. We force this function to be called multiple times. + // User destructors that might run more then PTHREAD_DESTRUCTOR_ITERATIONS-1 + // times might still end up executing after the unsafe stack is deallocated. + size_t iter = (size_t)_iter; + if (iter < PTHREAD_DESTRUCTOR_ITERATIONS) { + pthread_setspecific(thread_cleanup_key, (void *)(iter + 1)); + } else { + // This is the last iteration + unsafe_stack_free(); + } +} + +/// Intercept thread creation operation to allocate and setup the unsafe stack +INTERCEPTOR(int, pthread_create, pthread_t *thread, + const pthread_attr_t *attr, + void *(*start_routine)(void*), void *arg) { + + size_t size = 0; + size_t guard = 0; + + if (attr != NULL) { + pthread_attr_getstacksize(attr, &size); + pthread_attr_getguardsize(attr, &guard); + } else { + // get pthread default stack size + pthread_attr_t tmpattr; + pthread_attr_init(&tmpattr); + pthread_attr_getstacksize(&tmpattr, &size); + pthread_attr_getguardsize(&tmpattr, &guard); + pthread_attr_destroy(&tmpattr); + } + + CHECK_NE(size, 0); + CHECK_EQ((size & (kStackAlign - 1)), 0); + CHECK_EQ((guard & (PAGE_SIZE - 1)), 0); + + void *addr = unsafe_stack_alloc(size, guard); + struct tinfo *tinfo = + (struct tinfo *)(((char *)addr) + size - sizeof(struct tinfo)); + tinfo->start_routine = start_routine; + tinfo->start_routine_arg = arg; + tinfo->unsafe_stack_start = addr; + tinfo->unsafe_stack_size = size; + tinfo->unsafe_stack_guard = guard; + + return REAL(pthread_create)(thread, attr, thread_start, tinfo); +} + +extern "C" __attribute__((visibility("default"))) +#if !SANITIZER_CAN_USE_PREINIT_ARRAY +// On ELF platforms, the constructor is invoked using .preinit_array (see below) +__attribute__((constructor(0))) +#endif +void __safestack_init() { + // Determine the stack size for the main thread. + size_t size = kDefaultUnsafeStackSize; + size_t guard = 4096; + + struct rlimit limit; + if (getrlimit(RLIMIT_STACK, &limit) == 0 && limit.rlim_cur != RLIM_INFINITY) + size = limit.rlim_cur; + + // Allocate unsafe stack for main thread + void *addr = unsafe_stack_alloc(size, guard); + + unsafe_stack_setup(addr, size, guard); + + // Initialize pthread interceptors for thread allocation + INTERCEPT_FUNCTION(pthread_create); + + // Setup the cleanup handler + pthread_key_create(&thread_cleanup_key, thread_cleanup_handler); +} + +#if SANITIZER_CAN_USE_PREINIT_ARRAY +// On ELF platforms, run safestack initialization before any other constructors. +// On other platforms we use the constructor attribute to arrange to run our +// initialization early. +extern "C" { +__attribute__((section(".preinit_array"), + used)) void (*__safestack_preinit)(void) = __safestack_init; +} +#endif + +extern "C" + __attribute__((visibility("default"))) void *__get_unsafe_stack_start() { + return unsafe_stack_start; +} + +extern "C" + __attribute__((visibility("default"))) void *__get_unsafe_stack_ptr() { + return __safestack_unsafe_stack_ptr; +} diff --git a/lib/sanitizer_common/CMakeLists.txt b/lib/sanitizer_common/CMakeLists.txt index 6eb6ca8fc900..f604c9f201d4 100644 --- a/lib/sanitizer_common/CMakeLists.txt +++ b/lib/sanitizer_common/CMakeLists.txt @@ -27,6 +27,7 @@ set(SANITIZER_SOURCES sanitizer_suppressions.cc sanitizer_symbolizer.cc sanitizer_symbolizer_libbacktrace.cc + sanitizer_symbolizer_mac.cc sanitizer_symbolizer_win.cc sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc @@ -42,7 +43,8 @@ set(SANITIZER_LIBCDEP_SOURCES sanitizer_stoptheworld_linux_libcdep.cc sanitizer_symbolizer_libcdep.cc sanitizer_symbolizer_posix_libcdep.cc - sanitizer_unwind_posix_libcdep.cc) + sanitizer_symbolizer_process_libcdep.cc + sanitizer_unwind_linux_libcdep.cc) # Explicitly list all sanitizer_common headers. Not all of these are # included in sanitizer_common source files, but we need to depend on @@ -81,6 +83,7 @@ set(SANITIZER_HEADERS sanitizer_platform.h sanitizer_platform_interceptors.h sanitizer_platform_limits_posix.h + sanitizer_posix.h sanitizer_procmaps.h sanitizer_quarantine.h sanitizer_report_decorator.h @@ -91,7 +94,10 @@ set(SANITIZER_HEADERS sanitizer_stoptheworld.h sanitizer_suppressions.h sanitizer_symbolizer.h + sanitizer_symbolizer_internal.h sanitizer_symbolizer_libbacktrace.h + sanitizer_symbolizer_mac.h + sanitizer_symbolizer_win.h sanitizer_syscall_generic.inc sanitizer_syscall_linux_x86_64.inc sanitizer_thread_registry.h) @@ -106,10 +112,14 @@ else() SANITIZER_NEEDS_SEGV=1) endif() +include(CheckIncludeFile) +append_have_file_definition(rpc/xdr.h HAVE_RPC_XDR_H SANITIZER_COMMON_DEFINITIONS) +append_have_file_definition(tirpc/rpc/xdr.h HAVE_TIRPC_RPC_XDR_H SANITIZER_COMMON_DEFINITIONS) + set(SANITIZER_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(SANITIZER_CFLAGS) -append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=512 +append_list_if(SANITIZER_LIMIT_FRAME_SIZE -Wframe-larger-than=570 SANITIZER_CFLAGS) append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors SANITIZER_CFLAGS) @@ -118,31 +128,30 @@ add_custom_target(sanitizer_common) set(SANITIZER_RUNTIME_LIBRARIES) if(APPLE) # Build universal binary on APPLE. - foreach(os ${SANITIZER_COMMON_SUPPORTED_DARWIN_OS}) - add_compiler_rt_darwin_object_library(RTSanitizerCommon ${os} - ARCH ${SANITIZER_COMMON_SUPPORTED_ARCH} - SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES} - CFLAGS ${SANITIZER_CFLAGS} - DEFS ${SANITIZER_COMMON_DEFINITIONS}) + + add_compiler_rt_object_libraries(RTSanitizerCommon + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${SANITIZER_SOURCES} ${SANITIZER_LIBCDEP_SOURCES} + CFLAGS ${SANITIZER_CFLAGS} + DEFS ${SANITIZER_COMMON_DEFINITIONS}) + foreach(os ${SANITIZER_COMMON_SUPPORTED_OS}) list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${os}) endforeach() else() # Otherwise, build separate libraries for each target. + + add_compiler_rt_object_libraries(RTSanitizerCommon + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${SANITIZER_SOURCES} CFLAGS ${SANITIZER_CFLAGS} + DEFS ${SANITIZER_COMMON_DEFINITIONS}) + add_compiler_rt_object_libraries(RTSanitizerCommonLibc + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${SANITIZER_LIBCDEP_SOURCES} CFLAGS ${SANITIZER_CFLAGS} + DEFS ${SANITIZER_COMMON_DEFINITIONS}) foreach(arch ${SANITIZER_COMMON_SUPPORTED_ARCH}) - add_compiler_rt_object_library(RTSanitizerCommon ${arch} - SOURCES ${SANITIZER_SOURCES} CFLAGS ${SANITIZER_CFLAGS} - DEFS ${SANITIZER_COMMON_DEFINITIONS}) - add_compiler_rt_object_library(RTSanitizerCommonLibc ${arch} - SOURCES ${SANITIZER_LIBCDEP_SOURCES} CFLAGS ${SANITIZER_CFLAGS} - DEFS ${SANITIZER_COMMON_DEFINITIONS}) list(APPEND SANITIZER_RUNTIME_LIBRARIES RTSanitizerCommon.${arch} RTSanitizerCommonLibc.${arch}) - add_compiler_rt_runtime(clang_rt.san-${arch} ${arch} STATIC - SOURCES $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${SANITIZER_CFLAGS} - DEFS ${SANITIZER_COMMON_DEFINITIONS}) - add_dependencies(sanitizer_common clang_rt.san-${arch}) endforeach() endif() diff --git a/lib/sanitizer_common/sanitizer_allocator.h b/lib/sanitizer_common/sanitizer_allocator.h index b5105f8c2555..deaffef7150d 100644 --- a/lib/sanitizer_common/sanitizer_allocator.h +++ b/lib/sanitizer_common/sanitizer_allocator.h @@ -323,7 +323,7 @@ class SizeClassAllocator64 { void Init() { CHECK_EQ(kSpaceBeg, - reinterpret_cast<uptr>(Mprotect(kSpaceBeg, kSpaceSize))); + reinterpret_cast<uptr>(MmapNoAccess(kSpaceBeg, kSpaceSize))); MapWithCallback(kSpaceEnd, AdditionalSize()); } diff --git a/lib/sanitizer_common/sanitizer_atomic.h b/lib/sanitizer_common/sanitizer_atomic.h index 6643c5494393..7e3374aadd0c 100644 --- a/lib/sanitizer_common/sanitizer_atomic.h +++ b/lib/sanitizer_common/sanitizer_atomic.h @@ -55,7 +55,7 @@ struct atomic_uintptr_t { } // namespace __sanitizer -#if defined(__GNUC__) +#if defined(__clang__) || defined(__GNUC__) # include "sanitizer_atomic_clang.h" #elif defined(_MSC_VER) # include "sanitizer_atomic_msvc.h" diff --git a/lib/sanitizer_common/sanitizer_atomic_msvc.h b/lib/sanitizer_common/sanitizer_atomic_msvc.h index 12ffef3ba105..24d6f0f34424 100644 --- a/lib/sanitizer_common/sanitizer_atomic_msvc.h +++ b/lib/sanitizer_common/sanitizer_atomic_msvc.h @@ -21,6 +21,15 @@ extern "C" void _mm_mfence(); #pragma intrinsic(_mm_mfence) extern "C" void _mm_pause(); #pragma intrinsic(_mm_pause) +extern "C" char _InterlockedExchange8( // NOLINT + char volatile *Addend, char Value); // NOLINT +#pragma intrinsic(_InterlockedExchange8) +extern "C" short _InterlockedExchange16( // NOLINT + short volatile *Addend, short Value); // NOLINT +#pragma intrinsic(_InterlockedExchange16) +extern "C" long _InterlockedExchange( // NOLINT + long volatile *Addend, long Value); // NOLINT +#pragma intrinsic(_InterlockedExchange) extern "C" long _InterlockedExchangeAdd( // NOLINT long volatile * Addend, long Value); // NOLINT #pragma intrinsic(_InterlockedExchangeAdd) @@ -145,28 +154,25 @@ INLINE u8 atomic_exchange(volatile atomic_uint8_t *a, u8 v, memory_order mo) { (void)mo; DCHECK(!((uptr)a % sizeof(*a))); - __asm { - mov eax, a - mov cl, v - xchg [eax], cl // NOLINT - mov v, cl - } - return v; + return (u8)_InterlockedExchange8((volatile char*)&a->val_dont_use, v); } INLINE u16 atomic_exchange(volatile atomic_uint16_t *a, u16 v, memory_order mo) { (void)mo; DCHECK(!((uptr)a % sizeof(*a))); - __asm { - mov eax, a - mov cx, v - xchg [eax], cx // NOLINT - mov v, cx - } - return v; + return (u16)_InterlockedExchange16((volatile short*)&a->val_dont_use, v); +} + +INLINE u32 atomic_exchange(volatile atomic_uint32_t *a, + u32 v, memory_order mo) { + (void)mo; + DCHECK(!((uptr)a % sizeof(*a))); + return (u32)_InterlockedExchange((volatile long*)&a->val_dont_use, v); } +#ifndef _WIN64 + INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a, u8 *cmp, u8 xchgv, @@ -188,6 +194,8 @@ INLINE bool atomic_compare_exchange_strong(volatile atomic_uint8_t *a, return false; } +#endif + INLINE bool atomic_compare_exchange_strong(volatile atomic_uintptr_t *a, uptr *cmp, uptr xchg, diff --git a/lib/sanitizer_common/sanitizer_common.cc b/lib/sanitizer_common/sanitizer_common.cc index 4be3c7abf756..d14e98824d99 100644 --- a/lib/sanitizer_common/sanitizer_common.cc +++ b/lib/sanitizer_common/sanitizer_common.cc @@ -16,6 +16,8 @@ #include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" +#include "sanitizer_stacktrace_printer.h" +#include "sanitizer_symbolizer.h" namespace __sanitizer { @@ -52,18 +54,23 @@ void ReportFile::ReopenIfNecessary() { if (fd_pid == pid) return; else - internal_close(fd); + CloseFile(fd); } - internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); - uptr openrv = OpenFile(full_path, true); - if (internal_iserror(openrv)) { + const char *exe_name = GetBinaryBasename(); + if (common_flags()->log_exe_name && exe_name) { + internal_snprintf(full_path, kMaxPathLength, "%s.%s.%zu", path_prefix, + exe_name, pid); + } else { + internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); + } + fd = OpenFile(full_path, WrOnly); + if (fd == kInvalidFd) { const char *ErrorMsgPrefix = "ERROR: Can't open file: "; - internal_write(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); - internal_write(kStderrFd, full_path, internal_strlen(full_path)); + WriteToFile(kStderrFd, ErrorMsgPrefix, internal_strlen(ErrorMsgPrefix)); + WriteToFile(kStderrFd, full_path, internal_strlen(full_path)); Die(); } - fd = openrv; fd_pid = pid; } @@ -80,7 +87,7 @@ void ReportFile::SetReportPath(const char *path) { SpinMutexLock l(mu); if (fd != kStdoutFd && fd != kStderrFd && fd != kInvalidFd) - internal_close(fd); + CloseFile(fd); fd = kInvalidFd; if (internal_strcmp(path, "stdout") == 0) { fd = kStdoutFd; @@ -134,7 +141,7 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, } uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr max_len, int *errno_p) { + uptr max_len, error_t *errno_p) { uptr PageSize = GetPageSizeCached(); uptr kMinFileLen = PageSize; uptr read_len = 0; @@ -142,9 +149,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, *buff_size = 0; // The files we usually open are not seekable, so try different buffer sizes. for (uptr size = kMinFileLen; size <= max_len; size *= 2) { - uptr openrv = OpenFile(file_name, /*write*/ false); - if (internal_iserror(openrv, errno_p)) return 0; - fd_t fd = openrv; + fd_t fd = OpenFile(file_name, RdOnly, errno_p); + if (fd == kInvalidFd) return 0; UnmapOrDie(*buff, *buff_size); *buff = (char*)MmapOrDie(size, __func__); *buff_size = size; @@ -152,8 +158,8 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, read_len = 0; bool reached_eof = false; while (read_len + PageSize <= size) { - uptr just_read = internal_read(fd, *buff + read_len, PageSize); - if (internal_iserror(just_read, errno_p)) { + uptr just_read; + if (!ReadFromFile(fd, *buff + read_len, PageSize, &just_read, errno_p)) { UnmapOrDie(*buff, *buff_size); return 0; } @@ -163,7 +169,7 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, } read_len += just_read; } - internal_close(fd); + CloseFile(fd); if (reached_eof) // We've read the whole file. break; } @@ -206,19 +212,26 @@ const char *StripPathPrefix(const char *filepath, const char *strip_path_prefix) { if (filepath == 0) return 0; if (strip_path_prefix == 0) return filepath; - const char *pos = internal_strstr(filepath, strip_path_prefix); - if (pos == 0) return filepath; - pos += internal_strlen(strip_path_prefix); - if (pos[0] == '.' && pos[1] == '/') - pos += 2; - return pos; + const char *res = filepath; + if (const char *pos = internal_strstr(filepath, strip_path_prefix)) + res = pos + internal_strlen(strip_path_prefix); + if (res[0] == '.' && res[1] == '/') + res += 2; + return res; } const char *StripModuleName(const char *module) { if (module == 0) return 0; - if (const char *slash_pos = internal_strrchr(module, '/')) + if (SANITIZER_WINDOWS) { + // On Windows, both slash and backslash are possible. + // Pick the one that goes last. + if (const char *bslash_pos = internal_strrchr(module, '\\')) + return StripModuleName(bslash_pos + 1); + } + if (const char *slash_pos = internal_strrchr(module, '/')) { return slash_pos + 1; + } return module; } @@ -230,26 +243,27 @@ void ReportErrorSummary(const char *error_message) { __sanitizer_report_error_summary(buff.data()); } -void ReportErrorSummary(const char *error_type, const char *file, - int line, const char *function) { +#ifndef SANITIZER_GO +void ReportErrorSummary(const char *error_type, const AddressInfo &info) { if (!common_flags()->print_summary) return; InternalScopedString buff(kMaxSummaryLength); - buff.append("%s %s:%d %s", error_type, - file ? StripPathPrefix(file, common_flags()->strip_path_prefix) - : "??", - line, function ? function : "??"); + buff.append("%s ", error_type); + RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); ReportErrorSummary(buff.data()); } +#endif -LoadedModule::LoadedModule(const char *module_name, uptr base_address) { +void LoadedModule::set(const char *module_name, uptr base_address) { + clear(); full_name_ = internal_strdup(module_name); base_address_ = base_address; - ranges_.clear(); } void LoadedModule::clear() { InternalFree(full_name_); + full_name_ = nullptr; while (!ranges_.empty()) { AddressRange *r = ranges_.front(); ranges_.pop_front(); @@ -330,6 +344,32 @@ bool TemplateMatch(const char *templ, const char *str) { return true; } +static char binary_name_cache_str[kMaxPathLength]; +static const char *binary_basename_cache_str; + +const char *GetBinaryBasename() { + return binary_basename_cache_str; +} + +// Call once to make sure that binary_name_cache_str is initialized +void CacheBinaryName() { + if (binary_name_cache_str[0] != '\0') + return; + ReadBinaryName(binary_name_cache_str, sizeof(binary_name_cache_str)); + binary_basename_cache_str = StripModuleName(binary_name_cache_str); +} + +uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { + CacheBinaryName(); + uptr name_len = internal_strlen(binary_name_cache_str); + name_len = (name_len < buf_len - 1) ? name_len : buf_len - 1; + if (buf_len == 0) + return 0; + internal_memcpy(buf, binary_name_cache_str, name_len); + buf[name_len] = '\0'; + return name_len; +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h index ff13ef164045..2c5a8dbe1238 100644 --- a/lib/sanitizer_common/sanitizer_common.h +++ b/lib/sanitizer_common/sanitizer_common.h @@ -23,8 +23,14 @@ #include "sanitizer_list.h" #include "sanitizer_mutex.h" +#ifdef _MSC_VER +extern "C" void _ReadWriteBarrier(); +#pragma intrinsic(_ReadWriteBarrier) +#endif + namespace __sanitizer { struct StackTrace; +struct AddressInfo; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; @@ -38,8 +44,15 @@ const uptr kWordSizeInBits = 8 * kWordSize; const uptr kMaxPathLength = 4096; +// 16K loaded modules should be enough for everyone. +static const uptr kMaxNumberOfModules = 1 << 14; + const uptr kMaxThreadStackSize = 1 << 30; // 1Gb +// Denotes fake PC values that come from JIT/JAVA/etc. +// For such PC values __tsan_symbolize_external() will be called. +const u64 kExternalPCBit = 1ULL << 60; + extern const char *SanitizerToolName; // Can be changed by the tool. extern atomic_uint32_t current_verbosity; @@ -65,12 +78,17 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, // Memory management void *MmapOrDie(uptr size, const char *mem_type); void UnmapOrDie(void *addr, uptr size); -void *MmapFixedNoReserve(uptr fixed_addr, uptr size); +void *MmapFixedNoReserve(uptr fixed_addr, uptr size, + const char *name = nullptr); void *MmapNoReserveOrDie(uptr size, const char *mem_type); void *MmapFixedOrDie(uptr fixed_addr, uptr size); -void *Mprotect(uptr fixed_addr, uptr size); +void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr); // Map aligned chunk of address space; size and alignment are powers of two. void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type); +// Disallow access to a memory range. Use MmapNoAccess to allocate an +// unaccessible memory. +bool MprotectNoAccess(uptr addr, uptr size); + // Used to check if we can map shadow memory to a fixed location. bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); void FlushUnneededShadowMemory(uptr addr, uptr size); @@ -159,7 +177,7 @@ extern StaticSpinMutex CommonSanitizerReportMutex; struct ReportFile { void Write(const char *buffer, uptr length); - bool PrintsToTty(); + bool SupportsColors(); void SetReportPath(const char *path); // Don't use fields directly. They are only declared public to allow @@ -186,18 +204,39 @@ extern ReportFile report_file; extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; -uptr OpenFile(const char *filename, bool write); +enum FileAccessMode { + RdOnly, + WrOnly, + RdWr +}; + +// Returns kInvalidFd on error. +fd_t OpenFile(const char *filename, FileAccessMode mode, + error_t *errno_p = nullptr); +void CloseFile(fd_t); + +// Return true on success, false on error. +bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, + uptr *bytes_read = nullptr, error_t *error_p = nullptr); +bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, + uptr *bytes_written = nullptr, error_t *error_p = nullptr); + +bool RenameFile(const char *oldpath, const char *newpath, + error_t *error_p = nullptr); + +bool SupportsColoredOutput(fd_t fd); + // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. // The size of the mmaped region is stored in '*buff_size', // Returns the number of read bytes or 0 if file can not be opened. uptr ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, - uptr max_len, int *errno_p = nullptr); + uptr max_len, error_t *errno_p = nullptr); // Maps given file to virtual memory, and returns pointer to it -// (or NULL if the mapping failes). Stores the size of mmaped region +// (or NULL if mapping fails). Stores the size of mmaped region // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); -void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset); +void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset); bool IsAccessibleMemoryRange(uptr beg, uptr size); @@ -208,6 +247,10 @@ const char *StripPathPrefix(const char *filepath, const char *StripModuleName(const char *module); // OS +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); +uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); +const char *GetBinaryBasename(); +void CacheBinaryName(); void DisableCoreDumperIfNecessary(); void DumpProcessMap(); bool FileExists(const char *filename); @@ -215,6 +258,9 @@ const char *GetEnv(const char *name); bool SetEnv(const char *name, const char *value); const char *GetPwd(); char *FindPathToBinary(const char *name); +bool IsPathSeparator(const char c); +bool IsAbsolutePath(const char *path); + u32 GetUid(); void ReExec(); bool StackSizeIsUnlimited(); @@ -288,9 +334,9 @@ const int kMaxSummaryLength = 1024; // and pass it to __sanitizer_report_error_summary. void ReportErrorSummary(const char *error_message); // Same as above, but construct error_message as: -// error_type file:line function -void ReportErrorSummary(const char *error_type, const char *file, - int line, const char *function); +// error_type file:line[:column][ function] +void ReportErrorSummary(const char *error_type, const AddressInfo &info); +// Same as above, but obtains AddressInfo by symbolizing top stack trace frame. void ReportErrorSummary(const char *error_type, StackTrace *trace); // Math @@ -309,7 +355,11 @@ INLINE uptr MostSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) +# ifdef _WIN64 + up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x); +# else up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); +# endif #elif defined(_WIN64) _BitScanReverse64(&up, x); #else @@ -322,7 +372,11 @@ INLINE uptr LeastSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) +# ifdef _WIN64 + up = __builtin_ctzll(x); +# else up = __builtin_ctzl(x); +# endif #elif defined(_WIN64) _BitScanForward64(&up, x); #else @@ -342,7 +396,7 @@ INLINE uptr RoundUpToPowerOfTwo(uptr size) { uptr up = MostSignificantSetBitIndex(size); CHECK(size < (1ULL << (up + 1))); CHECK(size > (1ULL << up)); - return 1UL << (up + 1); + return 1ULL << (up + 1); } INLINE uptr RoundUpTo(uptr size, uptr boundary) { @@ -360,17 +414,7 @@ INLINE bool IsAligned(uptr a, uptr alignment) { INLINE uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); -#if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) - return __builtin_ctzl(x); -#elif defined(_WIN64) - unsigned long ret; // NOLINT - _BitScanForward64(&ret, x); - return ret; -#else - unsigned long ret; // NOLINT - _BitScanForward(&ret, x); - return ret; -#endif + return LeastSignificantSetBitIndex(x); } // Don't use std::min, std::max or std::swap, to minimize dependency @@ -439,11 +483,15 @@ class InternalMmapVectorNoCtor { const T *data() const { return data_; } + T *data() { + return data_; + } uptr capacity() const { return capacity_; } void clear() { size_ = 0; } + bool empty() const { return size() == 0; } private: void Resize(uptr new_capacity) { @@ -532,7 +580,8 @@ uptr InternalBinarySearch(const Container &v, uptr first, uptr last, // executable or a shared object). class LoadedModule { public: - LoadedModule(const char *module_name, uptr base_address); + LoadedModule() : full_name_(nullptr), base_address_(0) { ranges_.clear(); } + void set(const char *module_name, uptr base_address); void clear(); void addAddressRange(uptr beg, uptr end, bool executable); bool containsAddress(uptr address) const; @@ -567,28 +616,41 @@ typedef bool (*string_predicate_t)(const char *); uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter); -#if SANITIZER_POSIX -const uptr kPthreadDestructorIterations = 4; -#else -// Unused on Windows. -const uptr kPthreadDestructorIterations = 0; -#endif - // Callback type for iterating over a set of memory ranges. typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); +enum AndroidApiLevel { + ANDROID_NOT_ANDROID = 0, + ANDROID_KITKAT = 19, + ANDROID_LOLLIPOP_MR1 = 22, + ANDROID_POST_LOLLIPOP = 23 +}; + #if SANITIZER_ANDROID // Initialize Android logging. Any writes before this are silently lost. void AndroidLogInit(); void AndroidLogWrite(const char *buffer); void GetExtraActivationFlags(char *buf, uptr size); void SanitizerInitializeUnwinder(); +AndroidApiLevel AndroidGetApiLevel(); #else INLINE void AndroidLogInit() {} INLINE void AndroidLogWrite(const char *buffer_unused) {} INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; } INLINE void SanitizerInitializeUnwinder() {} +INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; } +#endif + +INLINE uptr GetPthreadDestructorIterations() { +#if SANITIZER_ANDROID + return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4; +#elif SANITIZER_POSIX + return 4; +#else +// Unused on Windows. + return 0; #endif +} void *internal_start_thread(void(*func)(void*), void *arg); void internal_join_thread(void *th); @@ -599,14 +661,30 @@ void MaybeStartBackgroudThread(); // compiler from recognising it and turning it into an actual call to // memset/memcpy/etc. static inline void SanitizerBreakOptimization(void *arg) { -#if _MSC_VER - // FIXME: make sure this is actually enough. - __asm; +#if _MSC_VER && !defined(__clang__) + _ReadWriteBarrier(); #else __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } +struct SignalContext { + void *context; + uptr addr; + uptr pc; + uptr sp; + uptr bp; + + SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp) : + context(context), addr(addr), pc(pc), sp(sp), bp(bp) { + } + + // Creates signal context in a platform-specific manner. + static SignalContext Create(void *siginfo, void *context); +}; + +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); + } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, diff --git a/lib/sanitizer_common/sanitizer_common_interceptors.inc b/lib/sanitizer_common/sanitizer_common_interceptors.inc index 87c33e186320..a7772b7394a5 100644 --- a/lib/sanitizer_common/sanitizer_common_interceptors.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -22,6 +22,7 @@ // COMMON_INTERCEPTOR_FD_RELEASE // COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME +// COMMON_INTERCEPTOR_ON_DLOPEN // COMMON_INTERCEPTOR_ON_EXIT // COMMON_INTERCEPTOR_MUTEX_LOCK // COMMON_INTERCEPTOR_MUTEX_UNLOCK @@ -46,6 +47,7 @@ #define pthread_setname_np pthread_set_name_np #define inet_aton __inet_aton #define inet_pton __inet_pton +#define iconv __bsd_iconv #endif #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE @@ -101,6 +103,21 @@ #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0) #endif +#define COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s, len, n) \ + COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \ + common_flags()->strict_string_checks ? (len) + 1 : (n) ) + +#define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \ + COMMON_INTERCEPTOR_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) + +#ifndef COMMON_INTERCEPTOR_ON_DLOPEN +#define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) {} +#endif + +#ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0; +#endif + struct FileMetadata { // For open_memstream(). char **addr; @@ -154,7 +171,8 @@ UNUSED static void DeleteInterceptorMetadata(void *addr) { INTERCEPTOR(char*, textdomain, const char *domainname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname); - char* domain = REAL(textdomain)(domainname); + COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0); + char *domain = REAL(textdomain)(domainname); if (domain) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1); } @@ -180,8 +198,8 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } - COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); - COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); return CharCmpX(c1, c2); } @@ -226,8 +244,8 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } - COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); - COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); return CharCaseCmp(c1, c2); } @@ -253,6 +271,97 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { #define INIT_STRNCASECMP #endif +#if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR +static inline void StrstrCheck(void *ctx, char *r, const char *s1, + const char *s2) { + uptr len1 = REAL(strlen)(s1); + uptr len2 = REAL(strlen)(s2); + COMMON_INTERCEPTOR_READ_STRING_OF_LEN(ctx, s1, len1, + r ? r - s1 + len2 : len1 + 1); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1); +} +#endif + +#if SANITIZER_INTERCEPT_STRSTR +INTERCEPTOR(char*, strstr, const char *s1, const char *s2) { + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return internal_strstr(s1, s2); + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2); + char *r = REAL(strstr)(s1, s2); + if (common_flags()->intercept_strstr) + StrstrCheck(ctx, r, s1, s2); + return r; +} + +#define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr); +#else +#define INIT_STRSTR +#endif + +#if SANITIZER_INTERCEPT_STRCASESTR +INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2); + char *r = REAL(strcasestr)(s1, s2); + if (common_flags()->intercept_strstr) + StrstrCheck(ctx, r, s1, s2); + return r; +} + +#define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr); +#else +#define INIT_STRCASESTR +#endif + +#if SANITIZER_INTERCEPT_STRSPN +INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2); + SIZE_T r = REAL(strspn)(s1, s2); + if (common_flags()->intercept_strspn) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); + } + return r; +} + +INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2); + SIZE_T r = REAL(strcspn)(s1, s2); + if (common_flags()->intercept_strspn) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); + } + return r; +} + +#define INIT_STRSPN \ + COMMON_INTERCEPT_FUNCTION(strspn); \ + COMMON_INTERCEPT_FUNCTION(strcspn); +#else +#define INIT_STRSPN +#endif + +#if SANITIZER_INTERCEPT_STRPBRK +INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2); + char *r = REAL(strpbrk)(s1, s2); + if (common_flags()->intercept_strpbrk) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); + COMMON_INTERCEPTOR_READ_STRING(ctx, s1, + r ? r - s1 + 1 : REAL(strlen)(s1) + 1); + } + return r; +} + +#define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk); +#else +#define INIT_STRPBRK +#endif + #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { void *ctx; @@ -722,12 +831,12 @@ INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. char *res = REAL(strptime)(s, format, tm); - if (res) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s); + COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0); + if (res && tm) { // Do not call unpoison_tm here, because strptime does not, in fact, // initialize the entire struct tm. For example, tm_zone pointer is left // uninitialized. - if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); } return res; } @@ -1029,6 +1138,12 @@ FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) { + // We need a frame pointer, because we call into ioctl_common_[pre|post] which + // can trigger a report and we need to be able to unwind through this + // function. On Mac in debug mode we might not have a frame pointer, because + // ioctl_common_[pre|post] doesn't get inlined here. + ENABLE_FRAME_POINTER; + void *ctx; va_list ap; va_start(ap, request); @@ -1502,6 +1617,7 @@ INTERCEPTOR(int, glob, const char *pattern, int flags, __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); + COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, @@ -1532,6 +1648,7 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags, __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); + COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, @@ -1678,6 +1795,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst); + COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0); // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See @@ -2348,6 +2466,37 @@ INTERCEPTOR(char *, get_current_dir_name, int fake) { #define INIT_GET_CURRENT_DIR_NAME #endif +UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { + CHECK(endptr); + if (nptr == *endptr) { + // No digits were found at strtol call, we need to find out the last + // symbol accessed by strtoll on our own. + // We get this symbol by skipping leading blanks and optional +/- sign. + while (IsSpace(*nptr)) nptr++; + if (*nptr == '+' || *nptr == '-') nptr++; + *endptr = const_cast<char *>(nptr); + } + CHECK(*endptr >= nptr); +} + +UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr, + char **endptr, char *real_endptr, int base) { + if (endptr != 0) { + *endptr = real_endptr; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + } + // If base has unsupported value, strtol can exit with EINVAL + // without reading any characters. So do additional checks only + // if base is valid. + bool is_valid_base = (base == 0) || (2 <= base && base <= 36); + if (is_valid_base) { + FixRealStrtolEndptr(nptr, &real_endptr); + } + COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ? + (real_endptr - nptr) + 1 : 0); +} + + #if SANITIZER_INTERCEPT_STRTOIMAX INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { void *ctx; @@ -2355,8 +2504,9 @@ INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - INTMAX_T res = REAL(strtoimax)(nptr, endptr, base); - if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + char *real_endptr; + INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base); + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } @@ -2366,8 +2516,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - INTMAX_T res = REAL(strtoumax)(nptr, endptr, base); - if (endptr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); + char *real_endptr; + INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base); + StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } @@ -3631,6 +3782,7 @@ INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); + COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); return REAL(pthread_setname_np)(thread, name); } @@ -3847,25 +3999,33 @@ INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { } return res; } -INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim, - void *stream) { - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, __getdelim, lineptr, n, delim, stream); - // FIXME: under ASan the call below may write to freed memory and corrupt - // its metadata. See - // https://code.google.com/p/address-sanitizer/issues/detail?id=321. - SSIZE_T res = REAL(__getdelim)(lineptr, n, delim, stream); - if (res > 0) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + +// FIXME: under ASan the call below may write to freed memory and corrupt its +// metadata. See +// https://code.google.com/p/address-sanitizer/issues/detail?id=321. +#define GETDELIM_INTERCEPTOR_IMPL(vname) \ + { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \ + SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \ + if (res > 0) { \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \ + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \ + } \ + return res; \ } - return res; -} + +INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim, + void *stream) +GETDELIM_INTERCEPTOR_IMPL(__getdelim) + +// There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor +// with its own body. INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, - void *stream) { - return __getdelim(lineptr, n, delim, stream); -} + void *stream) +GETDELIM_INTERCEPTOR_IMPL(getdelim) + #define INIT_GETLINE \ COMMON_INTERCEPT_FUNCTION(getline); \ COMMON_INTERCEPT_FUNCTION(__getdelim); \ @@ -3931,7 +4091,9 @@ INTERCEPTOR(void *, __tls_get_addr, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg); void *res = REAL(__tls_get_addr)(arg); - DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res); + uptr tls_begin, tls_end; + COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); + DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); @@ -4688,6 +4850,8 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { INTERCEPTOR(void*, dlopen, const char *filename, int flag) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag); + if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0); + COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; @@ -4792,6 +4956,63 @@ INTERCEPTOR(int, munlockall, void) { #define INIT_MLOCKX #endif // SANITIZER_INTERCEPT_MLOCKX +#if SANITIZER_INTERCEPT_FOPENCOOKIE +struct WrappedCookie { + void *real_cookie; + __sanitizer_cookie_io_functions_t real_io_funcs; +}; + +static uptr wrapped_read(void *cookie, char *buf, uptr size) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(3); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read; + return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0; +} + +static uptr wrapped_write(void *cookie, const char *buf, uptr size) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(3); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write; + return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size; +} + +static int wrapped_seek(void *cookie, u64 *offset, int whence) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(3); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset)); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek; + return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence) + : -1; +} + +static int wrapped_close(void *cookie) { + COMMON_INTERCEPTOR_UNPOISON_PARAM(1); + WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; + __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close; + int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0; + InternalFree(wrapped_cookie); + return res; +} + +INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode, + __sanitizer_cookie_io_functions_t io_funcs) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs); + WrappedCookie *wrapped_cookie = + (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie)); + wrapped_cookie->real_cookie = cookie; + wrapped_cookie->real_io_funcs = io_funcs; + __sanitizer_FILE *res = + REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write, + wrapped_seek, wrapped_close}); + return res; +} + +#define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie); +#else +#define INIT_FOPENCOOKIE +#endif // SANITIZER_INTERCEPT_FOPENCOOKIE + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); @@ -4801,6 +5022,10 @@ static void InitializeCommonInterceptors() { INIT_STRNCMP; INIT_STRCASECMP; INIT_STRNCASECMP; + INIT_STRSTR; + INIT_STRCASESTR; + INIT_STRSPN; + INIT_STRPBRK; INIT_MEMCHR; INIT_MEMRCHR; INIT_READ; @@ -4955,4 +5180,5 @@ static void InitializeCommonInterceptors() { INIT_GETPASS; INIT_TIMERFD; INIT_MLOCKX; + INIT_FOPENCOOKIE; } diff --git a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc index 69b7ca9eaa2e..b94c21c96aa0 100755 --- a/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc +++ b/lib/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -578,14 +578,10 @@ static void ioctl_common_pre(void *ctx, const ioctl_desc *desc, int d, } if (desc->type != ioctl_desc::CUSTOM) return; - switch (request) { - case 0x00008912: { // SIOCGIFCONF - struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; - COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len)); - break; - } + if (request == IOCTL_SIOCGIFCONF) { + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_READ_RANGE(ctx, &ifc->ifc_len, sizeof(ifc->ifc_len)); } - return; } static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d, @@ -597,12 +593,8 @@ static void ioctl_common_post(void *ctx, const ioctl_desc *desc, int res, int d, } if (desc->type != ioctl_desc::CUSTOM) return; - switch (request) { - case 0x00008912: { // SIOCGIFCONF - struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len); - break; - } + if (request == IOCTL_SIOCGIFCONF) { + struct __sanitizer_ifconf *ifc = (__sanitizer_ifconf *)arg; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifc->ifc_ifcu.ifcu_req, ifc->ifc_len); } - return; } diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc index 17ef6897ba26..1b65bced75d5 100644 --- a/lib/sanitizer_common/sanitizer_common_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -17,12 +17,16 @@ #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" +#if SANITIZER_POSIX +#include "sanitizer_posix.h" +#endif + namespace __sanitizer { -bool ReportFile::PrintsToTty() { +bool ReportFile::SupportsColors() { SpinMutexLock l(mu); ReopenIfNecessary(); - return internal_isatty(fd) != 0; + return SupportsColoredOutput(fd); } bool ColorizeReports() { @@ -33,7 +37,7 @@ bool ColorizeReports() { const char *flag = common_flags()->color; return internal_strcmp(flag, "always") == 0 || - (internal_strcmp(flag, "auto") == 0 && report_file.PrintsToTty()); + (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors()); } static void (*sandboxing_callback)(); @@ -44,20 +48,16 @@ void SetSandboxingCallback(void (*f)()) { void ReportErrorSummary(const char *error_type, StackTrace *stack) { if (!common_flags()->print_summary) return; -#if !SANITIZER_GO - if (stack->size > 0 && Symbolizer::GetOrInit()->CanReturnFileLineInfo()) { - // Currently, we include the first stack frame into the report summary. - // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); - const AddressInfo &ai = frame->info; - ReportErrorSummary(error_type, ai.file, ai.line, ai.function); - frame->ClearAll(); + if (stack->size == 0) { + ReportErrorSummary(error_type); + return; } -#else - AddressInfo ai; - ReportErrorSummary(error_type, ai.file, ai.line, ai.function); -#endif + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); + ReportErrorSummary(error_type, frame->info); + frame->ClearAll(); } static void (*SoftRssLimitExceededCallback)(bool exceeded); @@ -117,12 +117,13 @@ void BackgroundThread(void *arg) { } void MaybeStartBackgroudThread() { - if (!SANITIZER_LINUX) return; // Need to implement/test on other platforms. +#if SANITIZER_LINUX // Need to implement/test on other platforms. // Start the background thread if one of the rss limits is given. if (!common_flags()->hard_rss_limit_mb && !common_flags()->soft_rss_limit_mb) return; if (!&real_pthread_create) return; // Can't spawn the thread anyway. internal_start_thread(BackgroundThread, nullptr); +#endif } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_common_syscalls.inc b/lib/sanitizer_common/sanitizer_common_syscalls.inc index 7e15d51ff35b..5d4840f961ef 100644 --- a/lib/sanitizer_common/sanitizer_common_syscalls.inc +++ b/lib/sanitizer_common/sanitizer_common_syscalls.inc @@ -1443,6 +1443,7 @@ PRE_SYSCALL(fchown)(long fd, long user, long group) {} POST_SYSCALL(fchown)(long res, long fd, long user, long group) {} +#if SANITIZER_USES_UID16_SYSCALLS PRE_SYSCALL(chown16)(const void *filename, long user, long group) { if (filename) PRE_READ(filename, @@ -1552,6 +1553,7 @@ POST_SYSCALL(getgid16)(long res) {} PRE_SYSCALL(getegid16)() {} POST_SYSCALL(getegid16)(long res) {} +#endif // SANITIZER_USES_UID16_SYSCALLS PRE_SYSCALL(utime)(void *filename, void *times) {} @@ -2298,7 +2300,8 @@ POST_SYSCALL(ni_syscall)(long res) {} PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { #if !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) if (data) { if (request == ptrace_setregs) { PRE_READ((void *)data, struct_user_regs_struct_sz); @@ -2318,7 +2321,8 @@ PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { #if !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) if (res >= 0 && data) { // Note that this is different from the interceptor in // sanitizer_common_interceptors.inc. diff --git a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc index 49887b1e91a9..f511c996d8c2 100644 --- a/lib/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -24,8 +24,12 @@ // and atomically set Guard to -Guard. // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data -// this will create a file module_name.PID.sancov. The file format is simple: -// it's just a sorted sequence of 4-byte offsets in the module. +// this will create a file module_name.PID.sancov. +// +// The file format is simple: the first 8 bytes is the magic, +// one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the +// magic defines the size of the following offsets. +// The rest of the data is the offsets in the module. // // Eventually, this coverage implementation should be obsoleted by a more // powerful general purpose Clang/LLVM coverage instrumentation. @@ -43,6 +47,9 @@ #include "sanitizer_symbolizer.h" #include "sanitizer_flags.h" +static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL; +static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL; + static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uintptr_t coverage_counter; @@ -56,7 +63,7 @@ static atomic_uintptr_t coverage_counter; // dump current memory layout to another file. static bool cov_sandboxed = false; -static int cov_fd = kInvalidFd; +static fd_t cov_fd = kInvalidFd; static unsigned int cov_max_block_size = 0; static bool coverage_enabled = false; static const char *coverage_dir; @@ -77,21 +84,34 @@ class CoverageData { uptr cache_size); void DumpCallerCalleePairs(); void DumpTrace(); + void DumpAsBitSet(); + void DumpCounters(); + void DumpOffsets(); + void DumpAll(); ALWAYS_INLINE void TraceBasicBlock(s32 *id); void InitializeGuardArray(s32 *guards); - void InitializeGuards(s32 *guards, uptr n, const char *module_name); + void InitializeGuards(s32 *guards, uptr n, const char *module_name, + uptr caller_pc); + void InitializeCounters(u8 *counters, uptr n); void ReinitializeGuards(); + uptr GetNumberOf8bitCounters(); + uptr Update8bitCounterBitsetAndClearCounters(u8 *bitset); uptr *data(); uptr size(); private: + void DirectOpen(); + void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end); + // Maximal size pc array may ever grow. // We MmapNoReserve this space to ensure that the array is contiguous. - static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << 24, 1 << 27); + static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64( + 1 << (SANITIZER_ANDROID ? 24 : (SANITIZER_WINDOWS ? 27 : 26)), + 1 << 27); // The amount file mapping for the pc array is grown by. static const uptr kPcArrayMmapSize = 64 * 1024; @@ -105,13 +125,27 @@ class CoverageData { // Current file mapped size of the pc array. uptr pc_array_mapped_size; // Descriptor of the file mapped pc array. - int pc_fd; + fd_t pc_fd; // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor<s32*> guard_array_vec; - // Vector of module (compilation unit) names. - InternalMmapVectorNoCtor<const char*> comp_unit_name_vec; + struct NamedPcRange { + const char *copied_module_name; + uptr beg, end; // elements [beg,end) in pc_array. + }; + + // Vector of module and compilation unit pc ranges. + InternalMmapVectorNoCtor<NamedPcRange> comp_unit_name_vec; + InternalMmapVectorNoCtor<NamedPcRange> module_name_vec; + + struct CounterAndSize { + u8 *counters; + uptr n; + }; + + InternalMmapVectorNoCtor<CounterAndSize> counters_vec; + uptr num_8bit_counters; // Caller-Callee (cc) array, size and current index. static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24); @@ -133,8 +167,6 @@ class CoverageData { static const uptr kTrPcArrayMaxSize = FIRST_32_SECOND_64(1 << 22, 1 << 27); StaticSpinMutex mu; - - void DirectOpen(); }; static CoverageData coverage_data; @@ -145,9 +177,9 @@ void CoverageData::DirectOpen() { InternalScopedString path(kMaxPathLength); internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", coverage_dir, internal_getpid()); - pc_fd = OpenFile(path.data(), true); - if (internal_iserror(pc_fd)) { - Report(" Coverage: failed to open %s for writing\n", path.data()); + pc_fd = OpenFile(path.data(), RdWr); + if (pc_fd == kInvalidFd) { + Report("Coverage: failed to open %s for reading/writing\n", path.data()); Die(); } @@ -180,10 +212,13 @@ void CoverageData::Enable() { tr_event_array = reinterpret_cast<u32 *>(MmapNoReserveOrDie( sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + GetMmapGranularity(), "CovInit::tr_event_array")); - Mprotect(reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]), - GetMmapGranularity()); + MprotectNoAccess( + reinterpret_cast<uptr>(&tr_event_array[kTrEventArrayMaxSize]), + GetMmapGranularity()); tr_event_array_size = kTrEventArrayMaxSize; tr_event_pointer = tr_event_array; + + num_8bit_counters = 0; } void CoverageData::InitializeGuardArray(s32 *guards) { @@ -197,22 +232,22 @@ void CoverageData::InitializeGuardArray(s32 *guards) { void CoverageData::Disable() { if (pc_array) { - internal_munmap(pc_array, sizeof(uptr) * kPcArrayMaxSize); + UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize); pc_array = nullptr; } if (cc_array) { - internal_munmap(cc_array, sizeof(uptr *) * kCcArrayMaxSize); + UnmapOrDie(cc_array, sizeof(uptr *) * kCcArrayMaxSize); cc_array = nullptr; } if (tr_event_array) { - internal_munmap(tr_event_array, - sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + - GetMmapGranularity()); + UnmapOrDie(tr_event_array, + sizeof(tr_event_array[0]) * kTrEventArrayMaxSize + + GetMmapGranularity()); tr_event_array = nullptr; tr_event_pointer = nullptr; } if (pc_fd != kInvalidFd) { - internal_close(pc_fd); + CloseFile(pc_fd); pc_fd = kInvalidFd; } } @@ -231,8 +266,9 @@ void CoverageData::ReInit() { // In memory-mapped mode we must extend the new file to the known array // size. uptr size = atomic_load(&pc_array_size, memory_order_relaxed); + uptr npcs = size / sizeof(uptr); Enable(); - if (size) Extend(size); + if (size) Extend(npcs); if (coverage_enabled) CovUpdateMapping(coverage_dir); } else { Enable(); @@ -289,16 +325,69 @@ void CoverageData::Extend(uptr npcs) { atomic_store(&pc_array_size, size, memory_order_release); } +void CoverageData::InitializeCounters(u8 *counters, uptr n) { + if (!counters) return; + CHECK_EQ(reinterpret_cast<uptr>(counters) % 16, 0); + n = RoundUpTo(n, 16); // The compiler must ensure that counters is 16-aligned. + SpinMutexLock l(&mu); + counters_vec.push_back({counters, n}); + num_8bit_counters += n; +} + +void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg, + uptr range_end) { + auto sym = Symbolizer::GetOrInit(); + if (!sym) + return; + const char *module_name = sym->GetModuleNameForPc(caller_pc); + if (!module_name) return; + if (module_name_vec.empty() || + module_name_vec.back().copied_module_name != module_name) + module_name_vec.push_back({module_name, range_beg, range_end}); + else + module_name_vec.back().end = range_end; +} + void CoverageData::InitializeGuards(s32 *guards, uptr n, - const char *module_name) { + const char *comp_unit_name, + uptr caller_pc) { // The array 'guards' has n+1 elements, we use the element zero // to store 'n'. CHECK_LT(n, 1 << 30); guards[0] = static_cast<s32>(n); InitializeGuardArray(guards); SpinMutexLock l(&mu); - comp_unit_name_vec.push_back(module_name); + uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed); + uptr range_beg = range_end - n; + comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end}); guard_array_vec.push_back(guards); + UpdateModuleNameVec(caller_pc, range_beg, range_end); +} + +static const uptr kBundleCounterBits = 16; + +// When coverage_order_pcs==true and SANITIZER_WORDSIZE==64 +// we insert the global counter into the first 16 bits of the PC. +uptr BundlePcAndCounter(uptr pc, uptr counter) { + if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) + return pc; + static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1; + if (counter > kMaxCounter) + counter = kMaxCounter; + CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits)); + return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits)); +} + +uptr UnbundlePc(uptr bundle) { + if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) + return bundle; + return (bundle << kBundleCounterBits) >> kBundleCounterBits; +} + +uptr UnbundleCounter(uptr bundle) { + if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) + return 0; + return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits); } // If guard is negative, atomically set it to -guard and store the PC in @@ -316,8 +405,8 @@ void CoverageData::Add(uptr pc, u32 *guard) { return; // May happen after fork when pc_array_index becomes 0. CHECK_LT(idx * sizeof(uptr), atomic_load(&pc_array_size, memory_order_acquire)); - pc_array[idx] = pc; - atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); + uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); + pc_array[idx] = BundlePcAndCounter(pc, counter); } // Registers a pair caller=>callee. @@ -354,6 +443,64 @@ void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[], } } +uptr CoverageData::GetNumberOf8bitCounters() { + return num_8bit_counters; +} + +// Map every 8bit counter to a 8-bit bitset and clear the counter. +uptr CoverageData::Update8bitCounterBitsetAndClearCounters(u8 *bitset) { + uptr num_new_bits = 0; + uptr cur = 0; + // For better speed we map 8 counters to 8 bytes of bitset at once. + static const uptr kBatchSize = 8; + CHECK_EQ(reinterpret_cast<uptr>(bitset) % kBatchSize, 0); + for (uptr i = 0, len = counters_vec.size(); i < len; i++) { + u8 *c = counters_vec[i].counters; + uptr n = counters_vec[i].n; + CHECK_EQ(n % 16, 0); + CHECK_EQ(cur % kBatchSize, 0); + CHECK_EQ(reinterpret_cast<uptr>(c) % kBatchSize, 0); + if (!bitset) { + internal_bzero_aligned16(c, n); + cur += n; + continue; + } + for (uptr j = 0; j < n; j += kBatchSize, cur += kBatchSize) { + CHECK_LT(cur, num_8bit_counters); + u64 *pc64 = reinterpret_cast<u64*>(c + j); + u64 *pb64 = reinterpret_cast<u64*>(bitset + cur); + u64 c64 = *pc64; + u64 old_bits_64 = *pb64; + u64 new_bits_64 = old_bits_64; + if (c64) { + *pc64 = 0; + for (uptr k = 0; k < kBatchSize; k++) { + u64 x = (c64 >> (8 * k)) & 0xff; + if (x) { + u64 bit = 0; + /**/ if (x >= 128) bit = 128; + else if (x >= 32) bit = 64; + else if (x >= 16) bit = 32; + else if (x >= 8) bit = 16; + else if (x >= 4) bit = 8; + else if (x >= 3) bit = 4; + else if (x >= 2) bit = 2; + else if (x >= 1) bit = 1; + u64 mask = bit << (8 * k); + if (!(new_bits_64 & mask)) { + num_new_bits++; + new_bits_64 |= mask; + } + } + } + *pb64 = new_bits_64; + } + } + } + CHECK_EQ(cur, num_8bit_counters); + return num_new_bits; +} + uptr *CoverageData::data() { return pc_array; } @@ -372,15 +519,15 @@ struct CovHeader { static void CovWritePacked(int pid, const char *module, const void *blob, unsigned int blob_size) { - if (cov_fd < 0) return; + if (cov_fd == kInvalidFd) return; unsigned module_name_length = internal_strlen(module); CovHeader header = {pid, module_name_length, blob_size}; if (cov_max_block_size == 0) { // Writing to a file. Just go ahead. - internal_write(cov_fd, &header, sizeof(header)); - internal_write(cov_fd, module, module_name_length); - internal_write(cov_fd, blob, blob_size); + WriteToFile(cov_fd, &header, sizeof(header)); + WriteToFile(cov_fd, module, module_name_length); + WriteToFile(cov_fd, blob, blob_size); } else { // Writing to a socket. We want to split the data into appropriately sized // blocks. @@ -403,8 +550,7 @@ static void CovWritePacked(int pid, const char *module, const void *blob, internal_memcpy(block_data_begin, blob_pos, payload_size); blob_pos += payload_size; ((CovHeader *)block.data())->data_length = payload_size; - internal_write(cov_fd, block.data(), - header_size_with_module + payload_size); + WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size); } } } @@ -413,25 +559,25 @@ static void CovWritePacked(int pid, const char *module, const void *blob, // If packed = true and name == 0: <pid>.<sancov>.<packed>. // If packed = true and name != 0: <name>.<sancov>.<packed> (name is // user-supplied). -static int CovOpenFile(bool packed, const char *name, - const char *extension = "sancov") { - InternalScopedString path(kMaxPathLength); +static fd_t CovOpenFile(InternalScopedString *path, bool packed, + const char *name, const char *extension = "sancov") { + path->clear(); if (!packed) { CHECK(name); - path.append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), + path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), extension); } else { if (!name) - path.append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), + path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), extension); else - path.append("%s/%s.%s.packed", coverage_dir, name, extension); - } - uptr fd = OpenFile(path.data(), true); - if (internal_iserror(fd)) { - Report(" SanitizerCoverage: failed to open %s for writing\n", path.data()); - return -1; + path->append("%s/%s.%s.packed", coverage_dir, name, extension); } + error_t err; + fd_t fd = OpenFile(path->data(), WrOnly, &err); + if (fd == kInvalidFd) + Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", + path->data(), err); return fd; } @@ -446,38 +592,40 @@ void CoverageData::DumpTrace() { for (uptr i = 0, n = size(); i < n; i++) { const char *module_name = "<unknown>"; uptr module_address = 0; - sym->GetModuleNameAndOffsetForPC(pc_array[i], &module_name, + sym->GetModuleNameAndOffsetForPC(UnbundlePc(pc_array[i]), &module_name, &module_address); out.append("%s 0x%zx\n", module_name, module_address); } - int fd = CovOpenFile(false, "trace-points"); - if (fd < 0) return; - internal_write(fd, out.data(), out.length()); - internal_close(fd); + InternalScopedString path(kMaxPathLength); + fd_t fd = CovOpenFile(&path, false, "trace-points"); + if (fd == kInvalidFd) return; + WriteToFile(fd, out.data(), out.length()); + CloseFile(fd); - fd = CovOpenFile(false, "trace-compunits"); - if (fd < 0) return; + fd = CovOpenFile(&path, false, "trace-compunits"); + if (fd == kInvalidFd) return; out.clear(); for (uptr i = 0; i < comp_unit_name_vec.size(); i++) - out.append("%s\n", comp_unit_name_vec[i]); - internal_write(fd, out.data(), out.length()); - internal_close(fd); + out.append("%s\n", comp_unit_name_vec[i].copied_module_name); + WriteToFile(fd, out.data(), out.length()); + CloseFile(fd); - fd = CovOpenFile(false, "trace-events"); - if (fd < 0) return; + fd = CovOpenFile(&path, false, "trace-events"); + if (fd == kInvalidFd) return; uptr bytes_to_write = max_idx * sizeof(tr_event_array[0]); u8 *event_bytes = reinterpret_cast<u8*>(tr_event_array); // The trace file could be huge, and may not be written with a single syscall. while (bytes_to_write) { - uptr actually_written = internal_write(fd, event_bytes, bytes_to_write); - if (actually_written <= bytes_to_write) { + uptr actually_written; + if (WriteToFile(fd, event_bytes, bytes_to_write, &actually_written) && + actually_written <= bytes_to_write) { bytes_to_write -= actually_written; event_bytes += actually_written; } else { break; } } - internal_close(fd); + CloseFile(fd); VReport(1, " CovDump: Trace: %zd PCs written\n", size()); VReport(1, " CovDump: Trace: %zd Events written\n", max_idx); } @@ -514,10 +662,11 @@ void CoverageData::DumpCallerCalleePairs() { callee_module_address); } } - int fd = CovOpenFile(false, "caller-callee"); - if (fd < 0) return; - internal_write(fd, out.data(), out.length()); - internal_close(fd); + InternalScopedString path(kMaxPathLength); + fd_t fd = CovOpenFile(&path, false, "caller-callee"); + if (fd == kInvalidFd) return; + WriteToFile(fd, out.data(), out.length()); + CloseFile(fd); VReport(1, " CovDump: %zd caller-callee pairs written\n", total); } @@ -534,86 +683,123 @@ void CoverageData::TraceBasicBlock(s32 *id) { tr_event_pointer++; } -static void CovDumpAsBitSet() { +void CoverageData::DumpCounters() { + if (!common_flags()->coverage_counters) return; + uptr n = coverage_data.GetNumberOf8bitCounters(); + if (!n) return; + InternalScopedBuffer<u8> bitset(n); + coverage_data.Update8bitCounterBitsetAndClearCounters(bitset.data()); + InternalScopedString path(kMaxPathLength); + + for (uptr m = 0; m < module_name_vec.size(); m++) { + auto r = module_name_vec[m]; + CHECK(r.copied_module_name); + CHECK_LE(r.beg, r.end); + CHECK_LE(r.end, size()); + const char *base_name = StripModuleName(r.copied_module_name); + fd_t fd = + CovOpenFile(&path, /* packed */ false, base_name, "counters-sancov"); + if (fd == kInvalidFd) return; + WriteToFile(fd, bitset.data() + r.beg, r.end - r.beg); + CloseFile(fd); + VReport(1, " CovDump: %zd counters written for '%s'\n", r.end - r.beg, + base_name); + } +} + +void CoverageData::DumpAsBitSet() { if (!common_flags()->coverage_bitset) return; - if (!coverage_data.size()) return; - int fd = CovOpenFile(/* packed */false, "combined", "bitset-sancov"); - if (fd < 0) return; - uptr n = coverage_data.size(); - uptr n_set_bits = 0; - InternalScopedBuffer<char> out(n); - for (uptr i = 0; i < n; i++) { - uptr pc = coverage_data.data()[i]; - out[i] = pc ? '1' : '0'; - if (pc) - n_set_bits++; + if (!size()) return; + InternalScopedBuffer<char> out(size()); + InternalScopedString path(kMaxPathLength); + for (uptr m = 0; m < module_name_vec.size(); m++) { + uptr n_set_bits = 0; + auto r = module_name_vec[m]; + CHECK(r.copied_module_name); + CHECK_LE(r.beg, r.end); + CHECK_LE(r.end, size()); + for (uptr i = r.beg; i < r.end; i++) { + uptr pc = UnbundlePc(pc_array[i]); + out[i] = pc ? '1' : '0'; + if (pc) + n_set_bits++; + } + const char *base_name = StripModuleName(r.copied_module_name); + fd_t fd = CovOpenFile(&path, /* packed */false, base_name, "bitset-sancov"); + if (fd == kInvalidFd) return; + WriteToFile(fd, out.data() + r.beg, r.end - r.beg); + CloseFile(fd); + VReport(1, + " CovDump: bitset of %zd bits written for '%s', %zd bits are set\n", + r.end - r.beg, base_name, n_set_bits); } - internal_write(fd, out.data(), n); - internal_close(fd); - VReport(1, " CovDump: bitset of %zd bits written, %zd bits are set\n", n, - n_set_bits); } -// Dump the coverage on disk. -static void CovDump() { - if (!coverage_enabled || common_flags()->coverage_direct) return; -#if !SANITIZER_WINDOWS - if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) - return; - CovDumpAsBitSet(); - coverage_data.DumpTrace(); +void CoverageData::DumpOffsets() { + auto sym = Symbolizer::GetOrInit(); if (!common_flags()->coverage_pcs) return; - uptr size = coverage_data.size(); - InternalMmapVector<u32> offsets(size); - uptr *vb = coverage_data.data(); - uptr *ve = vb + size; - SortArray(vb, size); - MemoryMappingLayout proc_maps(/*cache_enabled*/true); - uptr mb, me, off, prot; - InternalScopedString module(kMaxPathLength); + CHECK_NE(sym, nullptr); + InternalMmapVector<uptr> offsets(0); InternalScopedString path(kMaxPathLength); - for (int i = 0; - proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); - i++) { - if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) - continue; - while (vb < ve && *vb < mb) vb++; - if (vb >= ve) break; - if (*vb < me) { - offsets.clear(); - const uptr *old_vb = vb; - CHECK_LE(off, *vb); - for (; vb < ve && *vb < me; vb++) { - uptr diff = *vb - (i ? mb : 0) + off; - CHECK_LE(diff, 0xffffffffU); - offsets.push_back(static_cast<u32>(diff)); - } - const char *module_name = StripModuleName(module.data()); - if (cov_sandboxed) { - if (cov_fd >= 0) { - CovWritePacked(internal_getpid(), module_name, offsets.data(), - offsets.size() * sizeof(u32)); - VReport(1, " CovDump: %zd PCs written to packed file\n", vb - old_vb); - } - } else { - // One file per module per process. - path.clear(); - path.append("%s/%s.%zd.sancov", coverage_dir, module_name, - internal_getpid()); - int fd = CovOpenFile(false /* packed */, module_name); - if (fd > 0) { - internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); - internal_close(fd); - VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), - vb - old_vb); - } + for (uptr m = 0; m < module_name_vec.size(); m++) { + offsets.clear(); + uptr num_words_for_magic = SANITIZER_WORDSIZE == 64 ? 1 : 2; + for (uptr i = 0; i < num_words_for_magic; i++) + offsets.push_back(0); + auto r = module_name_vec[m]; + CHECK(r.copied_module_name); + CHECK_LE(r.beg, r.end); + CHECK_LE(r.end, size()); + for (uptr i = r.beg; i < r.end; i++) { + uptr pc = UnbundlePc(pc_array[i]); + uptr counter = UnbundleCounter(pc_array[i]); + if (!pc) continue; // Not visited. + uptr offset = 0; + sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset); + offsets.push_back(BundlePcAndCounter(offset, counter)); + } + + CHECK_GE(offsets.size(), num_words_for_magic); + SortArray(offsets.data(), offsets.size()); + for (uptr i = 0; i < offsets.size(); i++) + offsets[i] = UnbundlePc(offsets[i]); + + uptr num_offsets = offsets.size() - num_words_for_magic; + u64 *magic_p = reinterpret_cast<u64*>(offsets.data()); + CHECK_EQ(*magic_p, 0ULL); + // FIXME: we may want to write 32-bit offsets even in 64-mode + // if all the offsets are small enough. + *magic_p = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32; + + const char *module_name = StripModuleName(r.copied_module_name); + if (cov_sandboxed) { + if (cov_fd != kInvalidFd) { + CovWritePacked(internal_getpid(), module_name, offsets.data(), + offsets.size() * sizeof(offsets[0])); + VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets); } + } else { + // One file per module per process. + fd_t fd = CovOpenFile(&path, false /* packed */, module_name); + if (fd == kInvalidFd) continue; + WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0])); + CloseFile(fd); + VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets); } } - if (cov_fd >= 0) - internal_close(cov_fd); - coverage_data.DumpCallerCalleePairs(); -#endif // !SANITIZER_WINDOWS + if (cov_fd != kInvalidFd) + CloseFile(cov_fd); +} + +void CoverageData::DumpAll() { + if (!coverage_enabled || common_flags()->coverage_direct) return; + if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) + return; + DumpAsBitSet(); + DumpCounters(); + DumpTrace(); + DumpOffsets(); + DumpCallerCalleePairs(); } void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { @@ -621,17 +807,21 @@ void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { if (!coverage_enabled) return; cov_sandboxed = args->coverage_sandboxed; if (!cov_sandboxed) return; - cov_fd = args->coverage_fd; cov_max_block_size = args->coverage_max_block_size; - if (cov_fd < 0) + if (args->coverage_fd >= 0) { + cov_fd = (fd_t)args->coverage_fd; + } else { + InternalScopedString path(kMaxPathLength); // Pre-open the file now. The sandbox won't allow us to do it later. - cov_fd = CovOpenFile(true /* packed */, 0); + cov_fd = CovOpenFile(&path, true /* packed */, 0); + } } -int MaybeOpenCovFile(const char *name) { +fd_t MaybeOpenCovFile(const char *name) { CHECK(name); - if (!coverage_enabled) return -1; - return CovOpenFile(true /* packed */, name); + if (!coverage_enabled) return kInvalidFd; + InternalScopedString path(kMaxPathLength); + return CovOpenFile(&path, true /* packed */, name); } void CovBeforeFork() { @@ -649,9 +839,7 @@ void InitializeCoverage(bool enabled, const char *dir) { coverage_dir = dir; coverage_data.Init(); if (enabled) coverage_data.Enable(); -#if !SANITIZER_WINDOWS if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); -#endif } void ReInitializeCoverage(bool enabled, const char *dir) { @@ -674,7 +862,8 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) { atomic_uint32_t *atomic_guard = reinterpret_cast<atomic_uint32_t*>(guard); - if (__sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) + if (static_cast<s32>( + __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0) __sanitizer_cov(guard); } SANITIZER_INTERFACE_ATTRIBUTE void @@ -687,10 +876,14 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { coverage_dir = common_flags()->coverage_dir; coverage_data.Init(); } -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { + coverage_data.DumpAll(); +} SANITIZER_INTERFACE_ATTRIBUTE void -__sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) { - coverage_data.InitializeGuards(guards, npcs, module_name); +__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters, + const char *comp_unit_name) { + coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC()); + coverage_data.InitializeCounters(counters, npcs); if (!common_flags()->coverage_direct) return; if (SANITIZER_ANDROID && coverage_enabled) { // dlopen/dlclose interceptors do not work on Android, so we rely on @@ -701,7 +894,7 @@ __sanitizer_cov_module_init(s32 *guards, uptr npcs, const char *module_name) { } SANITIZER_INTERFACE_ATTRIBUTE sptr __sanitizer_maybe_open_cov_file(const char *name) { - return MaybeOpenCovFile(name); + return (sptr)MaybeOpenCovFile(name); } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_total_unique_coverage() { @@ -728,4 +921,17 @@ uptr __sanitizer_get_coverage_guards(uptr **data) { *data = coverage_data.data(); return coverage_data.size(); } + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_get_number_of_counters() { + return coverage_data.GetNumberOf8bitCounters(); +} + +SANITIZER_INTERFACE_ATTRIBUTE +uptr __sanitizer_update_counter_bitset_and_clear_counters(u8 *bitset) { + return coverage_data.Update8bitCounterBitsetAndClearCounters(bitset); +} +// Default empty implementation (weak). Users should redefine it. +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void __sanitizer_cov_trace_cmp() {} } // extern "C" diff --git a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index 6b5e91fbc018..a3d75abc0476 100644 --- a/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -35,7 +35,6 @@ namespace __sanitizer { -static const uptr kMaxNumberOfModules = 1 << 14; static const uptr kMaxTextSize = 64 * 1024; struct CachedMapping { @@ -96,32 +95,30 @@ void CovUpdateMapping(const char *coverage_dir, uptr caller_pc) { } } - int err; + error_t err; InternalScopedString tmp_path(64 + internal_strlen(coverage_dir)); uptr res = internal_snprintf((char *)tmp_path.data(), tmp_path.size(), "%s/%zd.sancov.map.tmp", coverage_dir, internal_getpid()); CHECK_LE(res, tmp_path.size()); - uptr map_fd = OpenFile(tmp_path.data(), true); - if (internal_iserror(map_fd, &err)) { - Report(" Coverage: failed to open %s for writing: %d\n", tmp_path.data(), + fd_t map_fd = OpenFile(tmp_path.data(), WrOnly, &err); + if (map_fd == kInvalidFd) { + Report("Coverage: failed to open %s for writing: %d\n", tmp_path.data(), err); Die(); } - res = internal_write(map_fd, text.data(), text.length()); - if (internal_iserror(res, &err)) { + if (!WriteToFile(map_fd, text.data(), text.length(), nullptr, &err)) { Printf("sancov.map write failed: %d\n", err); Die(); } - internal_close(map_fd); + CloseFile(map_fd); InternalScopedString path(64 + internal_strlen(coverage_dir)); res = internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.map", coverage_dir, internal_getpid()); CHECK_LE(res, path.size()); - res = internal_rename(tmp_path.data(), path.data()); - if (internal_iserror(res, &err)) { + if (!RenameFile(tmp_path.data(), path.data(), &err)) { Printf("sancov.map rename failed: %d\n", err); Die(); } diff --git a/lib/sanitizer_common/sanitizer_deadlock_detector1.cc b/lib/sanitizer_common/sanitizer_deadlock_detector1.cc index b931edc7eae5..5890b54b933b 100644 --- a/lib/sanitizer_common/sanitizer_deadlock_detector1.cc +++ b/lib/sanitizer_common/sanitizer_deadlock_detector1.cc @@ -40,19 +40,20 @@ struct DD : public DDetector { explicit DD(const DDFlags *flags); - DDPhysicalThread* CreatePhysicalThread(); - void DestroyPhysicalThread(DDPhysicalThread *pt); + DDPhysicalThread *CreatePhysicalThread() override; + void DestroyPhysicalThread(DDPhysicalThread *pt) override; - DDLogicalThread* CreateLogicalThread(u64 ctx); - void DestroyLogicalThread(DDLogicalThread *lt); + DDLogicalThread *CreateLogicalThread(u64 ctx) override; + void DestroyLogicalThread(DDLogicalThread *lt) override; - void MutexInit(DDCallback *cb, DDMutex *m); - void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock); - void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, bool trylock); - void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock); - void MutexDestroy(DDCallback *cb, DDMutex *m); + void MutexInit(DDCallback *cb, DDMutex *m) override; + void MutexBeforeLock(DDCallback *cb, DDMutex *m, bool wlock) override; + void MutexAfterLock(DDCallback *cb, DDMutex *m, bool wlock, + bool trylock) override; + void MutexBeforeUnlock(DDCallback *cb, DDMutex *m, bool wlock) override; + void MutexDestroy(DDCallback *cb, DDMutex *m) override; - DDReport *GetReport(DDCallback *cb); + DDReport *GetReport(DDCallback *cb) override; void MutexEnsureID(DDLogicalThread *lt, DDMutex *m); void ReportDeadlock(DDCallback *cb, DDMutex *m); diff --git a/lib/sanitizer_common/sanitizer_flags.cc b/lib/sanitizer_common/sanitizer_flags.cc index e835b46a24fc..01098a3d6b87 100644 --- a/lib/sanitizer_common/sanitizer_flags.cc +++ b/lib/sanitizer_common/sanitizer_flags.cc @@ -54,7 +54,7 @@ class FlagHandlerInclude : public FlagHandlerBase { bool Parse(const char *value) final { char *data; uptr data_mapped_size; - int err; + error_t err; uptr len = ReadFileToBuffer(value, &data, &data_mapped_size, Max(kMaxIncludeSize, GetPageSizeCached()), &err); diff --git a/lib/sanitizer_common/sanitizer_flags.inc b/lib/sanitizer_common/sanitizer_flags.inc index 58f7f372228f..bbb39c73e2d0 100644 --- a/lib/sanitizer_common/sanitizer_flags.inc +++ b/lib/sanitizer_common/sanitizer_flags.inc @@ -51,6 +51,10 @@ COMMON_FLAG( "Write logs to \"log_path.pid\". The special values are \"stdout\" and " "\"stderr\". The default is \"stderr\".") COMMON_FLAG( + bool, log_exe_name, false, + "Mention name of executable when reporting error and " + "append executable name to logs (as in \"log_path.exe_name.pid\").") +COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") COMMON_FLAG(bool, detect_leaks, true, "Enable memory leak detection.") @@ -67,8 +71,9 @@ COMMON_FLAG(bool, print_summary, true, "reports.") COMMON_FLAG(bool, check_printf, true, "Check printf arguments.") COMMON_FLAG(bool, handle_segv, SANITIZER_NEEDS_SEGV, - "If set, registers the tool's custom SEGV handler (both SIGBUS and " - "SIGSEGV on OSX).") + "If set, registers the tool's custom SIGSEGV/SIGBUS handler.") +COMMON_FLAG(bool, handle_abort, false, + "If set, registers the tool's custom SIGABRT handler.") COMMON_FLAG(bool, allow_user_segv_handler, false, "If set, allows user to register a SEGV handler even if the tool " "registers one.") @@ -111,13 +116,18 @@ COMMON_FLAG( bool, coverage, false, "If set, coverage information will be dumped at program shutdown (if the " "coverage instrumentation was enabled at compile time).") -// On by default, but works only if coverage == true. COMMON_FLAG(bool, coverage_pcs, true, "If set (and if 'coverage' is set too), the coverage information " "will be dumped as a set of PC offsets for every module.") +COMMON_FLAG(bool, coverage_order_pcs, false, + "If true, the PCs will be dumped in the order they've" + " appeared during the execution.") COMMON_FLAG(bool, coverage_bitset, false, "If set (and if 'coverage' is set too), the coverage information " "will also be dumped as a bitset to a separate file.") +COMMON_FLAG(bool, coverage_counters, false, + "If set (and if 'coverage' is set too), the bitmap that corresponds" + " to coverage counters will be dumped.") COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, "If set, coverage information will be dumped directly to a memory " "mapped file. This way data is not lost even if the process is " @@ -140,9 +150,26 @@ COMMON_FLAG(bool, use_madv_dontdump, true, "in core file.") COMMON_FLAG(bool, symbolize_inline_frames, true, "Print inlined frames in stacktraces. Defaults to true.") +COMMON_FLAG(bool, symbolize_vs_style, false, + "Print file locations in Visual Studio style (e.g: " + " file(10,42): ...") COMMON_FLAG(const char *, stack_trace_format, "DEFAULT", "Format string used to render stack frames. " "See sanitizer_stacktrace_printer.h for the format description. " "Use DEFAULT to get default format.") COMMON_FLAG(bool, no_huge_pages_for_shadow, true, "If true, the shadow is not allowed to use huge pages. ") +COMMON_FLAG(bool, strict_string_checks, false, + "If set check that string arguments are properly null-terminated") +COMMON_FLAG(bool, intercept_strstr, true, + "If set, uses custom wrappers for strstr and strcasestr functions " + "to find more errors.") +COMMON_FLAG(bool, intercept_strspn, true, + "If set, uses custom wrappers for strspn and strcspn function " + "to find more errors.") +COMMON_FLAG(bool, intercept_strpbrk, true, + "If set, uses custom wrappers for strpbrk function " + "to find more errors.") +COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer " + "mappings in /proc/self/maps with " + "user-readable names") diff --git a/lib/sanitizer_common/sanitizer_internal_defs.h b/lib/sanitizer_common/sanitizer_internal_defs.h index a969f305cd1a..b76c6023aba2 100644 --- a/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/lib/sanitizer_common/sanitizer_internal_defs.h @@ -32,7 +32,7 @@ # define SANITIZER_WEAK_ATTRIBUTE __attribute__((weak)) #endif -#if SANITIZER_LINUX && !defined(SANITIZER_GO) +#if (SANITIZER_LINUX || SANITIZER_WINDOWS) && !defined(SANITIZER_GO) # define SANITIZER_SUPPORTS_WEAK_HOOKS 1 #else # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 @@ -80,7 +80,15 @@ typedef signed char s8; typedef signed short s16; // NOLINT typedef signed int s32; typedef signed long long s64; // NOLINT +#if SANITIZER_WINDOWS +// On Windows, files are HANDLE, which is a synonim of void*. +// Use void* to avoid including <windows.h> everywhere. +typedef void* fd_t; +typedef unsigned error_t; +#else typedef int fd_t; +typedef int error_t; +#endif // WARNING: OFF_T may be different from OS type off_t, depending on the value of // _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls @@ -295,6 +303,7 @@ extern "C" void* _ReturnAddress(void); do { \ volatile uptr enable_fp; \ enable_fp = GET_CURRENT_FRAME(); \ + (void)enable_fp; \ } while (0) #endif // SANITIZER_DEFS_H diff --git a/lib/sanitizer_common/sanitizer_libc.h b/lib/sanitizer_common/sanitizer_libc.h index c086b8a9139e..ae4e938f4af9 100644 --- a/lib/sanitizer_common/sanitizer_libc.h +++ b/lib/sanitizer_common/sanitizer_libc.h @@ -11,6 +11,7 @@ // run-time libraries. // These tools can not use some of the libc functions directly because those // functions are intercepted. Instead, we implement a tiny subset of libc here. +// FIXME: Some of functions declared in this file are in fact POSIX, not libc. //===----------------------------------------------------------------------===// #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H @@ -56,74 +57,26 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...); // Optimized for the case when the result is true. bool mem_is_zero(const char *mem, uptr size); - -// Memory -uptr internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset); -uptr internal_munmap(void *addr, uptr length); - // I/O -const fd_t kInvalidFd = -1; +const fd_t kInvalidFd = (fd_t)-1; const fd_t kStdinFd = 0; -const fd_t kStdoutFd = 1; -const fd_t kStderrFd = 2; -uptr internal_close(fd_t fd); -int internal_isatty(fd_t fd); +const fd_t kStdoutFd = (fd_t)1; +const fd_t kStderrFd = (fd_t)2; -// Use __sanitizer::OpenFile() instead. -uptr internal_open(const char *filename, int flags); -uptr internal_open(const char *filename, int flags, u32 mode); - -uptr internal_read(fd_t fd, void *buf, uptr count); -uptr internal_write(fd_t fd, const void *buf, uptr count); uptr internal_ftruncate(fd_t fd, uptr size); // OS -uptr internal_filesize(fd_t fd); // -1 on error. -uptr internal_stat(const char *path, void *buf); -uptr internal_lstat(const char *path, void *buf); -uptr internal_fstat(fd_t fd, void *buf); -uptr internal_dup2(int oldfd, int newfd); -uptr internal_readlink(const char *path, char *buf, uptr bufsize); -uptr internal_unlink(const char *path); -uptr internal_rename(const char *oldpath, const char *newpath); void NORETURN internal__exit(int exitcode); -uptr internal_lseek(fd_t fd, OFF_T offset, int whence); -uptr internal_ptrace(int request, int pid, void *addr, void *data); -uptr internal_waitpid(int pid, int *status, int options); uptr internal_getpid(); uptr internal_getppid(); -int internal_fork(); - // Threading uptr internal_sched_yield(); -// These functions call appropriate pthread_ functions directly, bypassing -// the interceptor. They are weak and may not be present in some tools. -SANITIZER_WEAK_ATTRIBUTE -int real_pthread_create(void *th, void *attr, void *(*callback)(void *), - void *param); -SANITIZER_WEAK_ATTRIBUTE -int real_pthread_join(void *th, void **ret); - -#define DEFINE_REAL_PTHREAD_FUNCTIONS \ - namespace __sanitizer { \ - int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ - void *param) { \ - return REAL(pthread_create)(th, attr, callback, param); \ - } \ - int real_pthread_join(void *th, void **ret) { \ - return REAL(pthread_join(th, ret)); \ - } \ - } // namespace __sanitizer - // Error handling bool internal_iserror(uptr retval, int *rverrno = 0); -int internal_sigaction(int signum, const void *act, void *oldact); - } // namespace __sanitizer #endif // SANITIZER_LIBC_H diff --git a/lib/sanitizer_common/sanitizer_libignore.cc b/lib/sanitizer_common/sanitizer_libignore.cc index cefb1dc97a17..8c4aeffda45e 100644 --- a/lib/sanitizer_common/sanitizer_libignore.cc +++ b/lib/sanitizer_common/sanitizer_libignore.cc @@ -12,6 +12,7 @@ #include "sanitizer_libignore.h" #include "sanitizer_flags.h" +#include "sanitizer_posix.h" #include "sanitizer_procmaps.h" namespace __sanitizer { diff --git a/lib/sanitizer_common/sanitizer_linux.cc b/lib/sanitizer_common/sanitizer_linux.cc index 8029181a5173..98e5d122a0f9 100644 --- a/lib/sanitizer_common/sanitizer_linux.cc +++ b/lib/sanitizer_common/sanitizer_linux.cc @@ -36,6 +36,7 @@ // access stat from asm/stat.h, without conflicting with definition in // sys/stat.h, we use this trick. #if defined(__mips64) +#include <asm/unistd.h> #include <sys/types.h> #define stat kernel_stat #include <asm/stat.h> @@ -45,9 +46,7 @@ #include <dlfcn.h> #include <errno.h> #include <fcntl.h> -#if !SANITIZER_ANDROID #include <link.h> -#endif #include <pthread.h> #include <sched.h> #include <sys/mman.h> @@ -57,6 +56,7 @@ #include <sys/syscall.h> #include <sys/time.h> #include <sys/types.h> +#include <ucontext.h> #include <unistd.h> #if SANITIZER_FREEBSD @@ -110,7 +110,7 @@ namespace __sanitizer { // --------------- sanitizer_libc.h uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, - u64 offset) { + OFF_T offset) { #if SANITIZER_FREEBSD || SANITIZER_LINUX_USES_64BIT_SYSCALLS return internal_syscall(SYSCALL(mmap), (uptr)addr, length, prot, flags, fd, offset); @@ -126,6 +126,10 @@ uptr internal_munmap(void *addr, uptr length) { return internal_syscall(SYSCALL(munmap), (uptr)addr, length); } +int internal_mprotect(void *addr, uptr length, int prot) { + return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot); +} + uptr internal_close(fd_t fd) { return internal_syscall(SYSCALL(close), fd); } @@ -147,11 +151,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) { #endif } -uptr OpenFile(const char *filename, bool write) { - return internal_open(filename, - write ? O_RDWR | O_CREAT /*| O_CLOEXEC*/ : O_RDONLY, 0660); -} - uptr internal_read(fd_t fd, void *buf, uptr count) { sptr res; HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(read), fd, (uptr)buf, @@ -168,7 +167,8 @@ uptr internal_write(fd_t fd, const void *buf, uptr count) { uptr internal_ftruncate(fd_t fd, uptr size) { sptr res; - HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, size)); + HANDLE_EINTR(res, (sptr)internal_syscall(SYSCALL(ftruncate), fd, + (OFF_T)size)); return res; } @@ -558,6 +558,7 @@ int internal_fork() { } #if SANITIZER_LINUX +#define SA_RESTORER 0x04000000 // Doesn't set sa_restorer, use with caution (see below). int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { __sanitizer_kernel_sigaction_t k_act, k_oldact; @@ -570,7 +571,8 @@ int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { k_act.sigaction = u_act->sigaction; internal_memcpy(&k_act.sa_mask, &u_act->sa_mask, sizeof(__sanitizer_kernel_sigset_t)); - k_act.sa_flags = u_act->sa_flags; + // Without SA_RESTORER kernel ignores the calls (probably returns EINVAL). + k_act.sa_flags = u_act->sa_flags | SA_RESTORER; // FIXME: most often sa_restorer is unset, however the kernel requires it // to point to a valid signal restorer that calls the rt_sigreturn syscall. // If sa_restorer passed to the kernel is NULL, the program may crash upon @@ -704,47 +706,32 @@ uptr GetPageSize() { #endif } -static char proc_self_exe_cache_str[kMaxPathLength]; -static uptr proc_self_exe_cache_len = 0; - uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { - if (proc_self_exe_cache_len > 0) { - // If available, use the cached module name. - uptr module_name_len = - internal_snprintf(buf, buf_len, "%s", proc_self_exe_cache_str); - CHECK_LT(module_name_len, buf_len); - return module_name_len; - } #if SANITIZER_FREEBSD - const int Mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + const int Mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + const char *default_module_name = "kern.proc.pathname"; size_t Size = buf_len; - bool IsErr = (sysctl(Mib, 4, buf, &Size, NULL, 0) != 0); + bool IsErr = (sysctl(Mib, ARRAY_SIZE(Mib), buf, &Size, NULL, 0) != 0); int readlink_error = IsErr ? errno : 0; uptr module_name_len = Size; #else + const char *default_module_name = "/proc/self/exe"; uptr module_name_len = internal_readlink( - "/proc/self/exe", buf, buf_len); + default_module_name, buf, buf_len); int readlink_error; bool IsErr = internal_iserror(module_name_len, &readlink_error); #endif if (IsErr) { - // We can't read /proc/self/exe for some reason, assume the name of the - // binary is unknown. - Report("WARNING: readlink(\"/proc/self/exe\") failed with errno %d, " + // We can't read binary name for some reason, assume it's unknown. + Report("WARNING: reading executable name failed with errno %d, " "some stack frames may not be symbolized\n", readlink_error); - module_name_len = internal_snprintf(buf, buf_len, "/proc/self/exe"); + module_name_len = internal_snprintf(buf, buf_len, "%s", + default_module_name); CHECK_LT(module_name_len, buf_len); } return module_name_len; } -void CacheBinaryName() { - if (!proc_self_exe_cache_len) { - proc_self_exe_cache_len = - ReadBinaryName(proc_self_exe_cache_str, kMaxPathLength); - } -} - // Match full names of the form /path/to/base_name{-,.}* bool LibraryNameIs(const char *full_name, const char *base_name) { const char *name = full_name; @@ -861,11 +848,70 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, return res; } #elif defined(__mips__) -// TODO(sagarthakur): clone function is to be rewritten in assembly. uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { - return clone(fn, child_stack, flags, arg, parent_tidptr, - newtls, child_tidptr); + long long res; + if (!fn || !child_stack) + return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); + child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); + ((unsigned long long *)child_stack)[0] = (uptr)fn; + ((unsigned long long *)child_stack)[1] = (uptr)arg; + register void *a3 __asm__("$7") = newtls; + register int *a4 __asm__("$8") = child_tidptr; + // We don't have proper CFI directives here because it requires alot of code + // for very marginal benefits. + __asm__ __volatile__( + /* $v0 = syscall($v0 = __NR_clone, + * $a0 = flags, + * $a1 = child_stack, + * $a2 = parent_tidptr, + * $a3 = new_tls, + * $a4 = child_tidptr) + */ + ".cprestore 16;\n" + "move $4,%1;\n" + "move $5,%2;\n" + "move $6,%3;\n" + "move $7,%4;\n" + /* Store the fifth argument on stack + * if we are using 32-bit abi. + */ +#if SANITIZER_WORDSIZE == 32 + "lw %5,16($29);\n" +#else + "move $8,%5;\n" +#endif + "li $2,%6;\n" + "syscall;\n" + + /* if ($v0 != 0) + * return; + */ + "bnez $2,1f;\n" + + /* Call "fn(arg)". */ + "ld $25,0($29);\n" + "ld $4,8($29);\n" + "jal $25;\n" + + /* Call _exit($v0). */ + "move $4,$2;\n" + "li $2,%7;\n" + "syscall;\n" + + /* Return to parent. */ + "1:\n" + : "=r" (res) + : "r"(flags), + "r"(child_stack), + "r"(parent_tidptr), + "r"(a3), + "r"(a4), + "i"(__NR_clone), + "i"(__NR_exit) + : "memory", "$29" ); + return res; } #endif // defined(__x86_64__) && SANITIZER_LINUX @@ -901,9 +947,52 @@ void GetExtraActivationFlags(char *buf, uptr size) { CHECK(size > PROP_VALUE_MAX); __system_property_get("asan.options", buf); } + +#if __ANDROID_API__ < 21 +extern "C" __attribute__((weak)) int dl_iterate_phdr( + int (*)(struct dl_phdr_info *, size_t, void *), void *); +#endif + +static int dl_iterate_phdr_test_cb(struct dl_phdr_info *info, size_t size, + void *data) { + // Any name starting with "lib" indicates a bug in L where library base names + // are returned instead of paths. + if (info->dlpi_name && info->dlpi_name[0] == 'l' && + info->dlpi_name[1] == 'i' && info->dlpi_name[2] == 'b') { + *(bool *)data = true; + return 1; + } + return 0; +} + +static atomic_uint32_t android_api_level; + +static AndroidApiLevel AndroidDetectApiLevel() { + if (!&dl_iterate_phdr) + return ANDROID_KITKAT; // K or lower + bool base_name_seen = false; + dl_iterate_phdr(dl_iterate_phdr_test_cb, &base_name_seen); + if (base_name_seen) + return ANDROID_LOLLIPOP_MR1; // L MR1 + return ANDROID_POST_LOLLIPOP; // post-L + // Plain L (API level 21) is completely broken wrt ASan and not very + // interesting to detect. +} + +AndroidApiLevel AndroidGetApiLevel() { + AndroidApiLevel level = + (AndroidApiLevel)atomic_load(&android_api_level, memory_order_relaxed); + if (level) return level; + level = AndroidDetectApiLevel(); + atomic_store(&android_api_level, level, memory_order_relaxed); + return level; +} + #endif bool IsDeadlySignal(int signum) { + if (common_flags()->handle_abort && signum == SIGABRT) + return true; return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; } @@ -912,6 +1001,11 @@ void *internal_start_thread(void(*func)(void *arg), void *arg) { // Start the thread with signals blocked, otherwise it can steal user signals. __sanitizer_sigset_t set, old; internal_sigfillset(&set); +#if SANITIZER_LINUX && !SANITIZER_ANDROID + // Glibc uses SIGSETXID signal during setuid call. If this signal is blocked + // on any thread, setuid call hangs (see test/tsan/setuid.c). + internal_sigdelset(&set, 33); +#endif internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; real_pthread_create(&th, 0, (void*(*)(void *arg))func, arg); @@ -928,6 +1022,78 @@ void *internal_start_thread(void (*func)(void *), void *arg) { return 0; } void internal_join_thread(void *th) {} #endif +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { +#if defined(__arm__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.arm_pc; + *bp = ucontext->uc_mcontext.arm_fp; + *sp = ucontext->uc_mcontext.arm_sp; +#elif defined(__aarch64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.pc; + *bp = ucontext->uc_mcontext.regs[29]; + *sp = ucontext->uc_mcontext.sp; +#elif defined(__hppa__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.sc_iaoq[0]; + /* GCC uses %r3 whenever a frame pointer is needed. */ + *bp = ucontext->uc_mcontext.sc_gr[3]; + *sp = ucontext->uc_mcontext.sc_gr[30]; +#elif defined(__x86_64__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.mc_rip; + *bp = ucontext->uc_mcontext.mc_rbp; + *sp = ucontext->uc_mcontext.mc_rsp; +# else + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_RIP]; + *bp = ucontext->uc_mcontext.gregs[REG_RBP]; + *sp = ucontext->uc_mcontext.gregs[REG_RSP]; +# endif +#elif defined(__i386__) +# if SANITIZER_FREEBSD + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.mc_eip; + *bp = ucontext->uc_mcontext.mc_ebp; + *sp = ucontext->uc_mcontext.mc_esp; +# else + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.gregs[REG_EIP]; + *bp = ucontext->uc_mcontext.gregs[REG_EBP]; + *sp = ucontext->uc_mcontext.gregs[REG_ESP]; +# endif +#elif defined(__powerpc__) || defined(__powerpc64__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.regs->nip; + *sp = ucontext->uc_mcontext.regs->gpr[PT_R1]; + // The powerpc{,64}-linux ABIs do not specify r31 as the frame + // pointer, but GCC always uses r31 when we need a frame pointer. + *bp = ucontext->uc_mcontext.regs->gpr[PT_R31]; +#elif defined(__sparc__) + ucontext_t *ucontext = (ucontext_t*)context; + uptr *stk_ptr; +# if defined (__arch64__) + *pc = ucontext->uc_mcontext.mc_gregs[MC_PC]; + *sp = ucontext->uc_mcontext.mc_gregs[MC_O6]; + stk_ptr = (uptr *) (*sp + 2047); + *bp = stk_ptr[15]; +# else + *pc = ucontext->uc_mcontext.gregs[REG_PC]; + *sp = ucontext->uc_mcontext.gregs[REG_O6]; + stk_ptr = (uptr *) *sp; + *bp = stk_ptr[15]; +# endif +#elif defined(__mips__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.pc; + *bp = ucontext->uc_mcontext.gregs[30]; + *sp = ucontext->uc_mcontext.gregs[29]; +#else +# error "Unsupported arch" +#endif +} + } // namespace __sanitizer #endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_linux.h b/lib/sanitizer_common/sanitizer_linux.h index b2e603d3a23e..e9fc4ad448d8 100644 --- a/lib/sanitizer_common/sanitizer_linux.h +++ b/lib/sanitizer_common/sanitizer_linux.h @@ -17,6 +17,7 @@ #if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" +#include "sanitizer_posix.h" #include "sanitizer_platform_limits_posix.h" struct link_map; // Opaque type returned by dlopen(). @@ -80,11 +81,6 @@ uptr ThreadSelfOffset(); // information). bool LibraryNameIs(const char *full_name, const char *base_name); -// Read the name of the current binary from /proc/self/exe. -uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); -// Cache the value of /proc/self/exe. -void CacheBinaryName(); - // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)); } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_linux_libcdep.cc index c71b6257ebc3..39eb1d216b2e 100644 --- a/lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -15,6 +15,7 @@ #include "sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_LINUX +#include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_freebsd.h" @@ -22,13 +23,12 @@ #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" -#include "sanitizer_atomic.h" -#include "sanitizer_symbolizer.h" #if SANITIZER_ANDROID || SANITIZER_FREEBSD #include <dlfcn.h> // for dlsym() #endif +#include <link.h> #include <pthread.h> #include <signal.h> #include <sys/resource.h> @@ -43,9 +43,12 @@ #include <sys/prctl.h> #endif +#if SANITIZER_ANDROID +#include <android/api-level.h> +#endif + #if !SANITIZER_ANDROID #include <elf.h> -#include <link.h> #include <unistd.h> #endif @@ -398,13 +401,6 @@ void AdjustStackSize(void *attr_) { } } -#if SANITIZER_ANDROID -uptr GetListOfModules(LoadedModule *modules, uptr max_modules, - string_predicate_t filter) { - MemoryMappingLayout memory_mapping(false); - return memory_mapping.DumpListOfModules(modules, max_modules, filter); -} -#else // SANITIZER_ANDROID # if !SANITIZER_FREEBSD typedef ElfW(Phdr) Elf_Phdr; # elif SANITIZER_WORDSIZE == 32 && __FreeBSD_version <= 902001 // v9.2 @@ -429,7 +425,7 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { if (data->first) { data->first = false; // First module is the binary itself. - ReadBinaryName(module_name.data(), module_name.size()); + ReadBinaryNameCached(module_name.data(), module_name.size()); } else if (info->dlpi_name) { module_name.append("%s", info->dlpi_name); } @@ -437,9 +433,8 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { return 0; if (data->filter && !data->filter(module_name.data())) return 0; - void *mem = &data->modules[data->current_n]; - LoadedModule *cur_module = new(mem) LoadedModule(module_name.data(), - info->dlpi_addr); + LoadedModule *cur_module = &data->modules[data->current_n]; + cur_module->set(module_name.data(), info->dlpi_addr); data->current_n++; for (int i = 0; i < info->dlpi_phnum; i++) { const Elf_Phdr *phdr = &info->dlpi_phdr[i]; @@ -453,27 +448,28 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { return 0; } +#if SANITIZER_ANDROID && __ANDROID_API__ < 21 +extern "C" __attribute__((weak)) int dl_iterate_phdr( + int (*)(struct dl_phdr_info *, size_t, void *), void *); +#endif + uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { +#if SANITIZER_ANDROID && __ANDROID_API__ < 21 + u32 api_level = AndroidGetApiLevel(); + // Fall back to /proc/maps if dl_iterate_phdr is unavailable or broken. + // The runtime check allows the same library to work with + // both K and L (and future) Android releases. + if (api_level <= ANDROID_LOLLIPOP_MR1) { // L or earlier + MemoryMappingLayout memory_mapping(false); + return memory_mapping.DumpListOfModules(modules, max_modules, filter); + } +#endif CHECK(modules); DlIteratePhdrData data = {modules, 0, true, max_modules, filter}; dl_iterate_phdr(dl_iterate_phdr_cb, &data); return data.current_n; } -#endif // SANITIZER_ANDROID - -void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { - // Some kinds of sandboxes may forbid filesystem access, so we won't be able - // to read the file mappings from /proc/self/maps. Luckily, neither the - // process will be able to load additional libraries, so it's fine to use the - // cached mappings. - MemoryMappingLayout::CacheMemoryMappings(); - // Same for /proc/self/exe in the symbolizer. -#if !SANITIZER_GO - Symbolizer::GetOrInit()->PrepareForSandboxing(); - CovPrepareForSandboxing(args); -#endif -} // getrusage does not give us the current RSS, only the max RSS. // Still, this is better than nothing if /proc/self/statm is not available @@ -488,8 +484,8 @@ static uptr GetRSSFromGetrusage() { uptr GetRSS() { if (!common_flags()->can_use_proc_maps_statm) return GetRSSFromGetrusage(); - uptr fd = OpenFile("/proc/self/statm", false); - if ((sptr)fd < 0) + fd_t fd = OpenFile("/proc/self/statm", RdOnly); + if (fd == kInvalidFd) return GetRSSFromGetrusage(); char buf[64]; uptr len = internal_read(fd, buf, sizeof(buf) - 1); diff --git a/lib/sanitizer_common/sanitizer_mac.cc b/lib/sanitizer_common/sanitizer_mac.cc index 39a5c7e8d24f..dddce1c1583c 100644 --- a/lib/sanitizer_common/sanitizer_mac.cc +++ b/lib/sanitizer_common/sanitizer_mac.cc @@ -27,21 +27,30 @@ #include "sanitizer_libc.h" #include "sanitizer_mac.h" #include "sanitizer_placement_new.h" +#include "sanitizer_platform_limits_posix.h" #include "sanitizer_procmaps.h" +#if !SANITIZER_IOS #include <crt_externs.h> // for _NSGetEnviron +#else +extern char **environ; +#endif + +#include <errno.h> #include <fcntl.h> +#include <libkern/OSAtomic.h> +#include <mach-o/dyld.h> +#include <mach/mach.h> #include <pthread.h> #include <sched.h> #include <signal.h> +#include <stdlib.h> #include <sys/mman.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/sysctl.h> #include <sys/types.h> #include <unistd.h> -#include <libkern/OSAtomic.h> -#include <errno.h> namespace __sanitizer { @@ -57,6 +66,10 @@ uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); } +int internal_mprotect(void *addr, uptr length, int prot) { + return mprotect(addr, length, prot); +} + uptr internal_close(fd_t fd) { return close(fd); } @@ -69,11 +82,6 @@ uptr internal_open(const char *filename, int flags, u32 mode) { return open(filename, flags, mode); } -uptr OpenFile(const char *filename, bool write) { - return internal_open(filename, - write ? O_WRONLY | O_CREAT : O_RDONLY, 0660); -} - uptr internal_read(fd_t fd, void *buf, uptr count) { return read(fd, buf, count); } @@ -130,6 +138,13 @@ int internal_sigaction(int signum, const void *act, void *oldact) { (struct sigaction *)act, (struct sigaction *)oldact); } +void internal_sigfillset(__sanitizer_sigset_t *set) { sigfillset(set); } + +uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, + __sanitizer_sigset_t *oldset) { + return sigprocmask(how, set, oldset); +} + int internal_fork() { // TODO(glider): this may call user's pthread_atfork() handlers which is bad. return fork(); @@ -180,7 +195,8 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, *stack_bottom = *stack_top - stacksize; } -const char *GetEnv(const char *name) { +char **GetEnviron() { +#if !SANITIZER_IOS char ***env_ptr = _NSGetEnviron(); if (!env_ptr) { Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " @@ -188,29 +204,45 @@ const char *GetEnv(const char *name) { CHECK(env_ptr); } char **environ = *env_ptr; +#endif CHECK(environ); + return environ; +} + +const char *GetEnv(const char *name) { + char **env = GetEnviron(); uptr name_len = internal_strlen(name); - while (*environ != 0) { - uptr len = internal_strlen(*environ); + while (*env != 0) { + uptr len = internal_strlen(*env); if (len > name_len) { - const char *p = *environ; + const char *p = *env; if (!internal_memcmp(p, name, name_len) && p[name_len] == '=') { // Match. - return *environ + name_len + 1; // String starting after =. + return *env + name_len + 1; // String starting after =. } } - environ++; + env++; } return 0; } -void ReExec() { - UNIMPLEMENTED(); +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + CHECK_LE(kMaxPathLength, buf_len); + + // On OS X the executable path is saved to the stack by dyld. Reading it + // from there is much faster than calling dladdr, especially for large + // binaries with symbols. + InternalScopedString exe_path(kMaxPathLength); + uint32_t size = exe_path.size(); + if (_NSGetExecutablePath(exe_path.data(), &size) == 0 && + realpath(exe_path.data(), buf) != 0) { + return internal_strlen(buf); + } + return 0; } -void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { - (void)args; - // Nothing here for now. +void ReExec() { + UNIMPLEMENTED(); } uptr GetPageSize() { @@ -322,12 +354,47 @@ MacosVersion GetMacosVersion() { } uptr GetRSS() { - return 0; + struct task_basic_info info; + unsigned count = TASK_BASIC_INFO_COUNT; + kern_return_t result = + task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &count); + if (UNLIKELY(result != KERN_SUCCESS)) { + Report("Cannot get task info. Error: %d\n", result); + Die(); + } + return info.resident_size; } void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } void internal_join_thread(void *th) { } +void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { + ucontext_t *ucontext = (ucontext_t*)context; +# if defined(__aarch64__) + *pc = ucontext->uc_mcontext->__ss.__pc; +# if defined(__IPHONE_8_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0 + *bp = ucontext->uc_mcontext->__ss.__fp; +# else + *bp = ucontext->uc_mcontext->__ss.__lr; +# endif + *sp = ucontext->uc_mcontext->__ss.__sp; +# elif defined(__x86_64__) + *pc = ucontext->uc_mcontext->__ss.__rip; + *bp = ucontext->uc_mcontext->__ss.__rbp; + *sp = ucontext->uc_mcontext->__ss.__rsp; +# elif defined(__arm__) + *pc = ucontext->uc_mcontext->__ss.__pc; + *bp = ucontext->uc_mcontext->__ss.__r[7]; + *sp = ucontext->uc_mcontext->__ss.__sp; +# elif defined(__i386__) + *pc = ucontext->uc_mcontext->__ss.__eip; + *bp = ucontext->uc_mcontext->__ss.__ebp; + *sp = ucontext->uc_mcontext->__ss.__esp; +# else +# error "Unknown architecture" +# endif +} + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_mac.h b/lib/sanitizer_common/sanitizer_mac.h index 9eed905187ec..50dbe93226c2 100644 --- a/lib/sanitizer_common/sanitizer_mac.h +++ b/lib/sanitizer_common/sanitizer_mac.h @@ -15,6 +15,7 @@ #include "sanitizer_platform.h" #if SANITIZER_MAC +#include "sanitizer_posix.h" namespace __sanitizer { @@ -32,6 +33,8 @@ enum MacosVersion { MacosVersion GetMacosVersion(); +char **GetEnviron(); + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_platform.h b/lib/sanitizer_common/sanitizer_platform.h index 6f8cd30bf4d9..b47281b589e9 100644 --- a/lib/sanitizer_common/sanitizer_platform.h +++ b/lib/sanitizer_common/sanitizer_platform.h @@ -38,9 +38,15 @@ # else # define SANITIZER_IOS 0 # endif +# if TARGET_IPHONE_SIMULATOR +# define SANITIZER_IOSSIM 1 +# else +# define SANITIZER_IOSSIM 0 +# endif #else # define SANITIZER_MAC 0 # define SANITIZER_IOS 0 +# define SANITIZER_IOSSIM 0 #endif #if defined(_WIN32) @@ -111,10 +117,29 @@ # endif #endif +// udi16 syscalls can only be used when the following conditions are +// met: +// * target is one of arm32, x86-32, sparc32, sh or m68k +// * libc version is libc5, glibc-2.0, glibc-2.1 or glibc-2.2 to 2.15 +// built against > linux-2.2 kernel headers +// Since we don't want to include libc headers here, we check the +// target only. +#if defined(__arm__) || SANITIZER_X32 || defined(__sparc__) +#define SANITIZER_USES_UID16_SYSCALLS 1 +#else +#define SANITIZER_USES_UID16_SYSCALLS 0 +#endif + #ifdef __mips__ # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10) #else # define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12) #endif +// Assume obsolete RPC headers are available by default +#if !defined(HAVE_RPC_XDR_H) && !defined(HAVE_TIRPC_RPC_XDR_H) +# define HAVE_RPC_XDR_H (SANITIZER_LINUX && !SANITIZER_ANDROID) +# define HAVE_TIRPC_RPC_XDR_H 0 +#endif + #endif // SANITIZER_PLATFORM_H diff --git a/lib/sanitizer_common/sanitizer_platform_interceptors.h b/lib/sanitizer_common/sanitizer_platform_interceptors.h index 438ecbaa2ec8..77cc84cd03af 100644 --- a/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -54,6 +54,10 @@ #endif #define SANITIZER_INTERCEPT_STRCMP 1 +#define SANITIZER_INTERCEPT_STRSTR 1 +#define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_STRSPN 1 +#define SANITIZER_INTERCEPT_STRPBRK 1 #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMCHR 1 @@ -127,7 +131,8 @@ #define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTRACE SI_LINUX_NOT_ANDROID && \ - (defined(__i386) || defined (__x86_64) || defined (__mips64)) // NOLINT + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID @@ -147,7 +152,8 @@ #define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_SCANDIR \ + SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS @@ -161,7 +167,7 @@ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID @@ -203,12 +209,13 @@ #define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_RAND_R SI_MAC || SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_RAND_R \ + SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS // FIXME: getline seems to be available on OSX 10.7 -#define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD @@ -222,12 +229,14 @@ #define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETRESID SI_LINUX -#define SANITIZER_INTERCEPT_GETIFADDRS SI_LINUX_NOT_ANDROID || SI_MAC -#define SANITIZER_INTERCEPT_IF_INDEXTONAME SI_LINUX_NOT_ANDROID || SI_MAC +#define SANITIZER_INTERCEPT_GETIFADDRS \ + SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC +#define SANITIZER_INTERCEPT_IF_INDEXTONAME \ + SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_AEABI_MEM SI_LINUX && defined(__arm__) #define SANITIZER_INTERCEPT___BZERO SI_MAC -#define SANITIZER_INTERCEPT_FTIME SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID @@ -243,5 +252,6 @@ #define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS +#define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc index 8824c8088f2d..aaa37ed02ebd 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -34,8 +34,6 @@ #include <grp.h> #include <limits.h> #include <net/if.h> -#include <net/if_arp.h> -#include <net/route.h> #include <netdb.h> #include <poll.h> #include <pthread.h> @@ -54,6 +52,10 @@ #include <time.h> #include <wchar.h> +#if !SANITIZER_IOS +#include <net/route.h> +#endif + #if !SANITIZER_ANDROID #include <sys/mount.h> #include <sys/timeb.h> @@ -75,6 +77,7 @@ #include <linux/sysctl.h> #include <linux/utsname.h> #include <linux/posix_types.h> +#include <net/if_arp.h> #endif #if SANITIZER_FREEBSD @@ -135,7 +138,11 @@ #include <netax25/ax25.h> #include <netipx/ipx.h> #include <netrom/netrom.h> -#include <rpc/xdr.h> +#if HAVE_RPC_XDR_H +# include <rpc/xdr.h> +#elif HAVE_TIRPC_RPC_XDR_H +# include <tirpc/rpc/xdr.h> +#endif #include <scsi/scsi.h> #include <sys/mtio.h> #include <sys/kd.h> @@ -289,19 +296,20 @@ namespace __sanitizer { #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) -#if defined(__mips64) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) +#if defined(__mips64) || defined(__powerpc64__) unsigned struct_user_regs_struct_sz = sizeof(struct pt_regs); unsigned struct_user_fpregs_struct_sz = sizeof(elf_fpregset_t); #else unsigned struct_user_regs_struct_sz = sizeof(struct user_regs_struct); unsigned struct_user_fpregs_struct_sz = sizeof(struct user_fpregs_struct); -#endif // __mips64 -#if (defined(__x86_64) || defined(__mips64)) +#endif // __mips64 || __powerpc64__ +#if defined(__x86_64) || defined(__mips64) || defined(__powerpc64__) unsigned struct_user_fpxregs_struct_sz = 0; #else unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); -#endif // __x86_64 || __mips64 +#endif // __x86_64 || __mips64 || __powerpc64__ int ptrace_peektext = PTRACE_PEEKTEXT; int ptrace_peekdata = PTRACE_PEEKDATA; @@ -310,8 +318,13 @@ namespace __sanitizer { int ptrace_setregs = PTRACE_SETREGS; int ptrace_getfpregs = PTRACE_GETFPREGS; int ptrace_setfpregs = PTRACE_SETFPREGS; +#if defined(PTRACE_GETFPXREGS) && defined(PTRACE_SETFPXREGS) int ptrace_getfpxregs = PTRACE_GETFPXREGS; int ptrace_setfpxregs = PTRACE_SETFPXREGS; +#else + int ptrace_getfpxregs = -1; + int ptrace_setfpxregs = -1; +#endif // PTRACE_GETFPXREGS/PTRACE_SETFPXREGS int ptrace_geteventmsg = PTRACE_GETEVENTMSG; #if (defined(PTRACE_GETSIGINFO) && defined(PTRACE_SETSIGINFO)) || \ (defined(PT_GETSIGINFO) && defined(PT_SETSIGINFO)) @@ -333,12 +346,12 @@ namespace __sanitizer { unsigned path_max = PATH_MAX; // ioctl arguments - unsigned struct_arpreq_sz = sizeof(struct arpreq); unsigned struct_ifreq_sz = sizeof(struct ifreq); unsigned struct_termios_sz = sizeof(struct termios); unsigned struct_winsize_sz = sizeof(struct winsize); #if SANITIZER_LINUX + unsigned struct_arpreq_sz = sizeof(struct arpreq); unsigned struct_cdrom_msf_sz = sizeof(struct cdrom_msf); unsigned struct_cdrom_multisession_sz = sizeof(struct cdrom_multisession); unsigned struct_cdrom_read_audio_sz = sizeof(struct cdrom_read_audio); @@ -1016,7 +1029,7 @@ CHECK_SIZE_AND_OFFSET(__sysctl_args, newlen); CHECK_TYPE_SIZE(__kernel_uid_t); CHECK_TYPE_SIZE(__kernel_gid_t); -#if !defined(__aarch64__) +#if SANITIZER_USES_UID16_SYSCALLS CHECK_TYPE_SIZE(__kernel_old_uid_t); CHECK_TYPE_SIZE(__kernel_old_gid_t); #endif @@ -1159,7 +1172,7 @@ CHECK_SIZE_AND_OFFSET(group, gr_passwd); CHECK_SIZE_AND_OFFSET(group, gr_gid); CHECK_SIZE_AND_OFFSET(group, gr_mem); -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if HAVE_RPC_XDR_H || HAVE_TIRPC_RPC_XDR_H CHECK_TYPE_SIZE(XDR); CHECK_SIZE_AND_OFFSET(XDR, x_op); CHECK_SIZE_AND_OFFSET(XDR, x_ops); @@ -1200,6 +1213,12 @@ CHECK_SIZE_AND_OFFSET(obstack, chunk_size); CHECK_SIZE_AND_OFFSET(obstack, chunk); CHECK_SIZE_AND_OFFSET(obstack, object_base); CHECK_SIZE_AND_OFFSET(obstack, next_free); + +CHECK_TYPE_SIZE(cookie_io_functions_t); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek); +CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close); #endif #endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_platform_limits_posix.h b/lib/sanitizer_common/sanitizer_platform_limits_posix.h index bd20bea94e93..4da7c70da0a6 100644 --- a/lib/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/lib/sanitizer_common/sanitizer_platform_limits_posix.h @@ -321,7 +321,7 @@ namespace __sanitizer { long pw_change; char *pw_class; #endif -#if !SANITIZER_ANDROID +#if !(SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32)) char *pw_gecos; #endif char *pw_dir; @@ -383,7 +383,7 @@ namespace __sanitizer { }; #endif -#if SANITIZER_ANDROID || SANITIZER_MAC || SANITIZER_FREEBSD +#if SANITIZER_MAC || SANITIZER_FREEBSD struct __sanitizer_msghdr { void *msg_name; unsigned msg_namelen; @@ -520,6 +520,27 @@ namespace __sanitizer { #endif // Linux system headers define the 'sa_handler' and 'sa_sigaction' macros. +#if SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 64) + struct __sanitizer_sigaction { + unsigned sa_flags; + union { + void (*sigaction)(int sig, void *siginfo, void *uctx); + void (*handler)(int sig); + }; + __sanitizer_sigset_t sa_mask; + void (*sa_restorer)(); + }; +#elif SANITIZER_ANDROID && (SANITIZER_WORDSIZE == 32) + struct __sanitizer_sigaction { + union { + void (*sigaction)(int sig, void *siginfo, void *uctx); + void (*handler)(int sig); + }; + __sanitizer_sigset_t sa_mask; + uptr sa_flags; + void (*sa_restorer)(); + }; +#else // !SANITIZER_ANDROID struct __sanitizer_sigaction { #if defined(__mips__) && !SANITIZER_FREEBSD unsigned int sa_flags; @@ -544,6 +565,7 @@ namespace __sanitizer { int sa_resv[1]; #endif }; +#endif // !SANITIZER_ANDROID #if SANITIZER_FREEBSD typedef __sanitizer_sigset_t __sanitizer_kernel_sigset_t; @@ -699,7 +721,8 @@ namespace __sanitizer { #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID && \ - (defined(__i386) || defined(__x86_64) || defined(__mips64)) + (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ + defined(__powerpc64__)) extern unsigned struct_user_regs_struct_sz; extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; @@ -756,6 +779,20 @@ struct __sanitizer_obstack { char *next_free; uptr more_fields[7]; }; + +typedef uptr (*__sanitizer_cookie_io_read)(void *cookie, char *buf, uptr size); +typedef uptr (*__sanitizer_cookie_io_write)(void *cookie, const char *buf, + uptr size); +typedef int (*__sanitizer_cookie_io_seek)(void *cookie, u64 *offset, + int whence); +typedef int (*__sanitizer_cookie_io_close)(void *cookie); + +struct __sanitizer_cookie_io_functions_t { + __sanitizer_cookie_io_read read; + __sanitizer_cookie_io_write write; + __sanitizer_cookie_io_seek seek; + __sanitizer_cookie_io_close close; +}; #endif #define IOC_NRBITS 8 @@ -792,12 +829,12 @@ struct __sanitizer_obstack { #define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK) #define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK) - extern unsigned struct_arpreq_sz; extern unsigned struct_ifreq_sz; extern unsigned struct_termios_sz; extern unsigned struct_winsize_sz; #if SANITIZER_LINUX + extern unsigned struct_arpreq_sz; extern unsigned struct_cdrom_msf_sz; extern unsigned struct_cdrom_multisession_sz; extern unsigned struct_cdrom_read_audio_sz; diff --git a/lib/sanitizer_common/sanitizer_posix.cc b/lib/sanitizer_common/sanitizer_posix.cc index 5bc41c2580fb..abf6738a8daa 100644 --- a/lib/sanitizer_common/sanitizer_posix.cc +++ b/lib/sanitizer_common/sanitizer_posix.cc @@ -9,7 +9,7 @@ // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements POSIX-specific functions from -// sanitizer_libc.h. +// sanitizer_posix.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" @@ -17,9 +17,12 @@ #include "sanitizer_common.h" #include "sanitizer_libc.h" +#include "sanitizer_posix.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" +#include <fcntl.h> +#include <signal.h> #include <sys/mman.h> #if SANITIZER_LINUX @@ -47,7 +50,7 @@ uptr GetMmapGranularity() { #if SANITIZER_WORDSIZE == 32 // Take care of unusable kernel area in top gigabyte. static uptr GetKernelAreaSize() { -#if SANITIZER_LINUX +#if SANITIZER_LINUX && !SANITIZER_X32 const uptr gbyte = 1UL << 30; // Firstly check if there are writable segments @@ -79,7 +82,7 @@ static uptr GetKernelAreaSize() { return gbyte; #else return 0; -#endif // SANITIZER_LINUX +#endif // SANITIZER_LINUX && !SANITIZER_X32 } #endif // SANITIZER_WORDSIZE == 32 @@ -162,22 +165,6 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) { return (void *)p; } -void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { - uptr PageSize = GetPageSizeCached(); - uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), - RoundUpTo(size, PageSize), - PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, - -1, 0); - int reserrno; - if (internal_iserror(p, &reserrno)) - Report("ERROR: %s failed to " - "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", - SanitizerToolName, size, size, fixed_addr, reserrno); - IncreaseTotalMmap(size); - return (void *)p; -} - void *MmapFixedOrDie(uptr fixed_addr, uptr size) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), @@ -196,17 +183,55 @@ void *MmapFixedOrDie(uptr fixed_addr, uptr size) { return (void *)p; } -void *Mprotect(uptr fixed_addr, uptr size) { - return (void *)internal_mmap((void*)fixed_addr, size, - PROT_NONE, - MAP_PRIVATE | MAP_ANON | MAP_FIXED | - MAP_NORESERVE, -1, 0); +bool MprotectNoAccess(uptr addr, uptr size) { + return 0 == internal_mprotect((void*)addr, size, PROT_NONE); +} + +fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) { + int flags; + switch (mode) { + case RdOnly: flags = O_RDONLY; break; + case WrOnly: flags = O_WRONLY | O_CREAT; break; + case RdWr: flags = O_RDWR | O_CREAT; break; + } + fd_t res = internal_open(filename, flags, 0660); + if (internal_iserror(res, errno_p)) + return kInvalidFd; + return res; +} + +void CloseFile(fd_t fd) { + internal_close(fd); +} + +bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, + error_t *error_p) { + uptr res = internal_read(fd, buff, buff_size); + if (internal_iserror(res, error_p)) + return false; + if (bytes_read) + *bytes_read = res; + return true; +} + +bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, + error_t *error_p) { + uptr res = internal_write(fd, buff, buff_size); + if (internal_iserror(res, error_p)) + return false; + if (bytes_written) + *bytes_written = res; + return true; +} + +bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { + uptr res = internal_rename(oldpath, newpath); + return !internal_iserror(res, error_p); } void *MapFileToMemory(const char *file_name, uptr *buff_size) { - uptr openrv = OpenFile(file_name, false); - CHECK(!internal_iserror(openrv)); - fd_t fd = openrv; + fd_t fd = OpenFile(file_name, RdOnly); + CHECK(fd != kInvalidFd); uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); CHECK_GT(fsize, 0); @@ -215,13 +240,14 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { return internal_iserror(map) ? 0 : (void *)map; } -void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) { +void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { uptr flags = MAP_SHARED; if (addr) flags |= MAP_FIXED; uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset); - if (internal_iserror(p)) { - Printf("could not map writable file (%zd, %zu, %zu): %zd\n", fd, offset, - size, p); + int mmap_errno = 0; + if (internal_iserror(p, &mmap_errno)) { + Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n", + fd, (long long)offset, size, p, mmap_errno); return 0; } return (void *)p; @@ -244,6 +270,7 @@ bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { while (proc_maps.Next(&start, &end, /*offset*/0, /*filename*/0, /*filename_size*/0, /*protection*/0)) { + if (start == end) continue; // Empty range. CHECK_NE(0, end); if (!IntervalsAreSeparate(start, end - 1, range_start, range_end)) return false; @@ -293,6 +320,14 @@ char *FindPathToBinary(const char *name) { return 0; } +bool IsPathSeparator(const char c) { + return c == '/'; +} + +bool IsAbsolutePath(const char *path) { + return path != nullptr && IsPathSeparator(path[0]); +} + void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); static const char *kWriteError = @@ -319,6 +354,13 @@ bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { return false; } +SignalContext SignalContext::Create(void *siginfo, void *context) { + uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr; + uptr pc, sp, bp; + GetPcSpBp(context, &pc, &sp, &bp); + return SignalContext(context, addr, pc, sp, bp); +} + } // namespace __sanitizer #endif // SANITIZER_POSIX diff --git a/lib/sanitizer_common/sanitizer_posix.h b/lib/sanitizer_common/sanitizer_posix.h new file mode 100644 index 000000000000..5a9e97d5b5a9 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_posix.h @@ -0,0 +1,81 @@ +//===-- sanitizer_posix.h -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries and declares some useful POSIX-specific functions. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_POSIX_H +#define SANITIZER_POSIX_H + +// ----------- ATTENTION ------------- +// This header should NOT include any other headers from sanitizer runtime. +#include "sanitizer_internal_defs.h" + +#if !SANITIZER_POSIX +// Make it hard to accidentally use any of functions declared in this file: +#error This file should only be included on POSIX +#endif + +namespace __sanitizer { + +// I/O +// Don't use directly, use __sanitizer::OpenFile() instead. +uptr internal_open(const char *filename, int flags); +uptr internal_open(const char *filename, int flags, u32 mode); +uptr internal_close(fd_t fd); + +uptr internal_read(fd_t fd, void *buf, uptr count); +uptr internal_write(fd_t fd, const void *buf, uptr count); + +// Memory +uptr internal_mmap(void *addr, uptr length, int prot, int flags, + int fd, OFF_T offset); +uptr internal_munmap(void *addr, uptr length); +int internal_mprotect(void *addr, uptr length, int prot); + +// OS +uptr internal_filesize(fd_t fd); // -1 on error. +uptr internal_stat(const char *path, void *buf); +uptr internal_lstat(const char *path, void *buf); +uptr internal_fstat(fd_t fd, void *buf); +uptr internal_dup2(int oldfd, int newfd); +uptr internal_readlink(const char *path, char *buf, uptr bufsize); +uptr internal_unlink(const char *path); +uptr internal_rename(const char *oldpath, const char *newpath); +uptr internal_lseek(fd_t fd, OFF_T offset, int whence); + +uptr internal_ptrace(int request, int pid, void *addr, void *data); +uptr internal_waitpid(int pid, int *status, int options); + +int internal_fork(); + +// These functions call appropriate pthread_ functions directly, bypassing +// the interceptor. They are weak and may not be present in some tools. +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_create(void *th, void *attr, void *(*callback)(void *), + void *param); +SANITIZER_WEAK_ATTRIBUTE +int real_pthread_join(void *th, void **ret); + +#define DEFINE_REAL_PTHREAD_FUNCTIONS \ + namespace __sanitizer { \ + int real_pthread_create(void *th, void *attr, void *(*callback)(void *), \ + void *param) { \ + return REAL(pthread_create)(th, attr, callback, param); \ + } \ + int real_pthread_join(void *th, void **ret) { \ + return REAL(pthread_join(th, ret)); \ + } \ + } // namespace __sanitizer + +int internal_sigaction(int signum, const void *act, void *oldact); + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX_H diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc index 11828e6cdf51..3f0a4f453cb4 100644 --- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc @@ -18,9 +18,13 @@ #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_platform_limits_posix.h" +#include "sanitizer_posix.h" +#include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" #include <errno.h> +#include <fcntl.h> #include <pthread.h> #include <signal.h> #include <stdlib.h> @@ -28,8 +32,16 @@ #include <sys/resource.h> #include <sys/time.h> #include <sys/types.h> +#include <sys/stat.h> #include <unistd.h> +#if SANITIZER_FREEBSD +// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before +// that, it was never implemented. So just define it to zero. +#undef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif + namespace __sanitizer { u32 GetUid() { @@ -119,8 +131,8 @@ int Atexit(void (*function)(void)) { #endif } -int internal_isatty(fd_t fd) { - return isatty(fd); +bool SupportsColoredOutput(fd_t fd) { + return isatty(fd) != 0; } #ifndef SANITIZER_GO @@ -175,6 +187,7 @@ void InstallDeadlySignalHandlers(SignalHandlerType handler) { if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); MaybeInstallSigaction(SIGSEGV, handler); MaybeInstallSigaction(SIGBUS, handler); + MaybeInstallSigaction(SIGABRT, handler); } #endif // SANITIZER_GO @@ -200,6 +213,68 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size) { return result; } +void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { + // Some kinds of sandboxes may forbid filesystem access, so we won't be able + // to read the file mappings from /proc/self/maps. Luckily, neither the + // process will be able to load additional libraries, so it's fine to use the + // cached mappings. + MemoryMappingLayout::CacheMemoryMappings(); + // Same for /proc/self/exe in the symbolizer. +#if !SANITIZER_GO + Symbolizer::GetOrInit()->PrepareForSandboxing(); + CovPrepareForSandboxing(args); +#endif +} + +#if SANITIZER_ANDROID +int GetNamedMappingFd(const char *name, uptr size) { + return -1; +} +#else +int GetNamedMappingFd(const char *name, uptr size) { + if (!common_flags()->decorate_proc_maps) + return -1; + char shmname[200]; + CHECK(internal_strlen(name) < sizeof(shmname) - 10); + internal_snprintf(shmname, sizeof(shmname), "%zu [%s]", internal_getpid(), + name); + int fd = shm_open(shmname, O_RDWR | O_CREAT | O_TRUNC, S_IRWXU); + CHECK_GE(fd, 0); + int res = internal_ftruncate(fd, size); + CHECK_EQ(0, res); + res = shm_unlink(shmname); + CHECK_EQ(0, res); + return fd; +} +#endif + +void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { + int fd = name ? GetNamedMappingFd(name, size) : -1; + unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE; + if (fd == -1) flags |= MAP_ANON; + + uptr PageSize = GetPageSizeCached(); + uptr p = internal_mmap((void *)(fixed_addr & ~(PageSize - 1)), + RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, + flags, fd, 0); + int reserrno; + if (internal_iserror(p, &reserrno)) + Report("ERROR: %s failed to " + "allocate 0x%zx (%zd) bytes at address %zx (errno: %d)\n", + SanitizerToolName, size, size, fixed_addr, reserrno); + IncreaseTotalMmap(size); + return (void *)p; +} + +void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) { + int fd = name ? GetNamedMappingFd(name, size) : -1; + unsigned flags = MAP_PRIVATE | MAP_FIXED | MAP_NORESERVE; + if (fd == -1) flags |= MAP_ANON; + + return (void *)internal_mmap((void *)fixed_addr, size, PROT_NONE, flags, fd, + 0); +} + } // namespace __sanitizer #endif // SANITIZER_POSIX diff --git a/lib/sanitizer_common/sanitizer_printf.cc b/lib/sanitizer_common/sanitizer_printf.cc index 3be6723cd660..e4f67f5e0db5 100644 --- a/lib/sanitizer_common/sanitizer_printf.cc +++ b/lib/sanitizer_common/sanitizer_printf.cc @@ -251,26 +251,32 @@ static void SharedPrintfCode(bool append_pid, const char *format, buffer_size = kLen; } needed_length = 0; + // Check that data fits into the current buffer. +# define CHECK_NEEDED_LENGTH \ + if (needed_length >= buffer_size) { \ + if (!use_mmap) continue; \ + RAW_CHECK_MSG(needed_length < kLen, \ + "Buffer in Report is too short!\n"); \ + } if (append_pid) { int pid = internal_getpid(); - needed_length += internal_snprintf(buffer, buffer_size, "==%d==", pid); - if (needed_length >= buffer_size) { - // The pid doesn't fit into the current buffer. - if (!use_mmap) - continue; - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); + const char *exe_name = GetBinaryBasename(); + if (common_flags()->log_exe_name && exe_name) { + needed_length += internal_snprintf(buffer, buffer_size, + "==%s", exe_name); + CHECK_NEEDED_LENGTH } + needed_length += internal_snprintf(buffer + needed_length, + buffer_size - needed_length, + "==%d==", pid); + CHECK_NEEDED_LENGTH } needed_length += VSNPrintf(buffer + needed_length, buffer_size - needed_length, format, args); - if (needed_length >= buffer_size) { - // The message doesn't fit into the current buffer. - if (!use_mmap) - continue; - RAW_CHECK_MSG(needed_length < kLen, "Buffer in Report is too short!\n"); - } + CHECK_NEEDED_LENGTH // If the message fit into the buffer, print it and exit. break; +# undef CHECK_NEEDED_LENGTH } RawWrite(buffer); AndroidLogWrite(buffer); diff --git a/lib/sanitizer_common/sanitizer_procmaps_common.cc b/lib/sanitizer_common/sanitizer_procmaps_common.cc index 2ec08d7f127b..2c6ce8e2b211 100644 --- a/lib/sanitizer_common/sanitizer_procmaps_common.cc +++ b/lib/sanitizer_common/sanitizer_procmaps_common.cc @@ -130,7 +130,6 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, continue; if (filter && !filter(cur_name)) continue; - void *mem = &modules[n_modules]; // Don't subtract 'cur_beg' from the first entry: // * If a binary is compiled w/o -pie, then the first entry in // process maps is likely the binary itself (all dynamic libs @@ -143,7 +142,8 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, // shadow memory of the tool), so the module can't be the // first entry. uptr base_address = (i ? cur_beg : 0) - cur_offset; - LoadedModule *cur_module = new(mem) LoadedModule(cur_name, base_address); + LoadedModule *cur_module = &modules[n_modules]; + cur_module->set(cur_name, base_address); cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); n_modules++; } diff --git a/lib/sanitizer_common/sanitizer_procmaps_mac.cc b/lib/sanitizer_common/sanitizer_procmaps_mac.cc index c0a86149788c..29d699609a6e 100644 --- a/lib/sanitizer_common/sanitizer_procmaps_mac.cc +++ b/lib/sanitizer_common/sanitizer_procmaps_mac.cc @@ -171,13 +171,13 @@ uptr MemoryMappingLayout::DumpListOfModules(LoadedModule *modules, continue; if (filter && !filter(cur_name)) continue; - LoadedModule *cur_module = 0; + LoadedModule *cur_module = nullptr; if (n_modules > 0 && 0 == internal_strcmp(cur_name, modules[n_modules - 1].full_name())) { cur_module = &modules[n_modules - 1]; } else { - void *mem = &modules[n_modules]; - cur_module = new(mem) LoadedModule(cur_name, cur_beg); + cur_module = &modules[n_modules]; + cur_module->set(cur_name, cur_beg); n_modules++; } cur_module->addAddressRange(cur_beg, cur_end, prot & kProtectionExecute); diff --git a/lib/sanitizer_common/sanitizer_stacktrace.cc b/lib/sanitizer_common/sanitizer_stacktrace.cc index 2deadb6e3560..84ff9d9d9e3b 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace.cc +++ b/lib/sanitizer_common/sanitizer_stacktrace.cc @@ -20,6 +20,8 @@ namespace __sanitizer { uptr StackTrace::GetNextInstructionPc(uptr pc) { #if defined(__mips__) return pc + 8; +#elif defined(__powerpc__) + return pc + 4; #else return pc + 1; #endif diff --git a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc index 0f98c7d5af4c..f66fa79f19a5 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -35,7 +35,8 @@ void StackTrace::Print() const { for (SymbolizedStack *cur = frames; cur; cur = cur->next) { frame_desc.clear(); RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, - cur->info, common_flags()->strip_path_prefix); + cur->info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); Printf("%s\n", frame_desc.data()); } frames->ClearAll(); @@ -59,10 +60,14 @@ void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context, return; } if (!WillUseFastUnwind(request_fast_unwind)) { +#if SANITIZER_CAN_SLOW_UNWIND if (context) SlowUnwindStackWithContext(pc, context, max_depth); else SlowUnwindStack(pc, max_depth); +#else + UNREACHABLE("slow unwind requested but not available"); +#endif } else { FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); } diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc index 7b37dbcf3860..3574fa3782c6 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace_printer.cc +++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -26,8 +26,8 @@ static const char *StripFunctionName(const char *function, const char *prefix) { static const char kDefaultFormat[] = " #%n %p %F %L"; void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, - const AddressInfo &info, const char *strip_path_prefix, - const char *strip_func_prefix) { + const AddressInfo &info, bool vs_style, + const char *strip_path_prefix, const char *strip_func_prefix) { if (0 == internal_strcmp(format, "DEFAULT")) format = kDefaultFormat; for (const char *p = format; *p != '\0'; p++) { @@ -82,14 +82,14 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, break; case 'S': // File/line information. - RenderSourceLocation(buffer, info.file, info.line, info.column, + RenderSourceLocation(buffer, info.file, info.line, info.column, vs_style, strip_path_prefix); break; case 'L': // Source location, or module location. if (info.file) { RenderSourceLocation(buffer, info.file, info.line, info.column, - strip_path_prefix); + vs_style, strip_path_prefix); } else if (info.module) { RenderModuleLocation(buffer, info.module, info.module_offset, strip_path_prefix); @@ -99,22 +99,33 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, break; case 'M': // Module basename and offset, or PC. - if (info.module) + if (info.address & kExternalPCBit) + {} // There PCs are not meaningful. + else if (info.module) buffer->append("(%s+%p)", StripModuleName(info.module), (void *)info.module_offset); else buffer->append("(%p)", (void *)info.address); break; default: - Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", - *p, *p); + Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n", *p, + *p); Die(); } } } void RenderSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column, const char *strip_path_prefix) { + int line, int column, bool vs_style, + const char *strip_path_prefix) { + if (vs_style && line > 0) { + buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line); + if (column > 0) + buffer->append(",%d", column); + buffer->append(")"); + return; + } + buffer->append("%s", StripPathPrefix(file, strip_path_prefix)); if (line > 0) { buffer->append(":%d", line); diff --git a/lib/sanitizer_common/sanitizer_stacktrace_printer.h b/lib/sanitizer_common/sanitizer_stacktrace_printer.h index 93569882ba9d..7f6c5c73b85d 100644 --- a/lib/sanitizer_common/sanitizer_stacktrace_printer.h +++ b/lib/sanitizer_common/sanitizer_stacktrace_printer.h @@ -48,11 +48,13 @@ namespace __sanitizer { // module+offset if it is known, or (<unknown module>) string. // %M - prints module basename and offset, if it is known, or PC. void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, - const AddressInfo &info, const char *strip_path_prefix = "", + const AddressInfo &info, bool vs_style, + const char *strip_path_prefix = "", const char *strip_func_prefix = ""); void RenderSourceLocation(InternalScopedString *buffer, const char *file, - int line, int column, const char *strip_path_prefix); + int line, int column, bool vs_style, + const char *strip_path_prefix); void RenderModuleLocation(InternalScopedString *buffer, const char *module, uptr offset, const char *strip_path_prefix); diff --git a/lib/sanitizer_common/sanitizer_stoptheworld.h b/lib/sanitizer_common/sanitizer_stoptheworld.h index a32646708d45..aa6f5d833a4d 100644 --- a/lib/sanitizer_common/sanitizer_stoptheworld.h +++ b/lib/sanitizer_common/sanitizer_stoptheworld.h @@ -59,7 +59,8 @@ typedef void (*StopTheWorldCallback)( // Suspend all threads in the current process and run the callback on the list // of suspended threads. This function will resume the threads before returning. -// The callback should not call any libc functions. +// The callback should not call any libc functions. The callback must not call +// exit() nor _exit() and instead return to the caller. // This function should NOT be called from multiple threads simultaneously. void StopTheWorld(StopTheWorldCallback callback, void *argument); diff --git a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index ad20e39556d8..47b27e7e5c75 100644 --- a/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -19,6 +19,7 @@ #include "sanitizer_stoptheworld.h" #include "sanitizer_platform_limits_posix.h" +#include "sanitizer_atomic.h" #include <errno.h> #include <sched.h> // for CLONE_* definitions @@ -70,11 +71,25 @@ COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); namespace __sanitizer { + +// Structure for passing arguments into the tracer thread. +struct TracerThreadArgument { + StopTheWorldCallback callback; + void *callback_argument; + // The tracer thread waits on this mutex while the parent finishes its + // preparations. + BlockingMutex mutex; + // Tracer thread signals its completion by setting done. + atomic_uintptr_t done; + uptr parent_pid; +}; + // This class handles thread suspending/unsuspending in the tracer thread. class ThreadSuspender { public: - explicit ThreadSuspender(pid_t pid) - : pid_(pid) { + explicit ThreadSuspender(pid_t pid, TracerThreadArgument *arg) + : arg(arg) + , pid_(pid) { CHECK_GE(pid, 0); } bool SuspendAllThreads(); @@ -83,6 +98,7 @@ class ThreadSuspender { SuspendedThreadsList &suspended_threads_list() { return suspended_threads_list_; } + TracerThreadArgument *arg; private: SuspendedThreadsList suspended_threads_list_; pid_t pid_; @@ -103,7 +119,7 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID tid) { VReport(1, "Could not attach to thread %d (errno %d).\n", tid, pterrno); return false; } else { - VReport(1, "Attached to thread %d.\n", tid); + VReport(2, "Attached to thread %d.\n", tid); // The thread is not guaranteed to stop before ptrace returns, so we must // wait on it. Note: if the thread receives a signal concurrently, // we can get notification about the signal before notification about stop. @@ -143,7 +159,7 @@ void ThreadSuspender::ResumeAllThreads() { int pterrno; if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), &pterrno)) { - VReport(1, "Detached from thread %d.\n", tid); + VReport(2, "Detached from thread %d.\n", tid); } else { // Either the thread is dead, or we are already detached. // The latter case is possible, for instance, if this function was called @@ -188,25 +204,23 @@ static ThreadSuspender *thread_suspender_instance = NULL; static const int kSyncSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGXCPU, SIGXFSZ }; -// Structure for passing arguments into the tracer thread. -struct TracerThreadArgument { - StopTheWorldCallback callback; - void *callback_argument; - // The tracer thread waits on this mutex while the parent finishes its - // preparations. - BlockingMutex mutex; - uptr parent_pid; -}; - static DieCallbackType old_die_callback; // Signal handler to wake up suspended threads when the tracer thread dies. -static void TracerThreadSignalHandler(int signum, void *siginfo, void *) { - if (thread_suspender_instance != NULL) { +static void TracerThreadSignalHandler(int signum, void *siginfo, void *uctx) { + SignalContext ctx = SignalContext::Create(siginfo, uctx); + VPrintf(1, "Tracer caught signal %d: addr=0x%zx pc=0x%zx sp=0x%zx\n", + signum, ctx.addr, ctx.pc, ctx.sp); + ThreadSuspender *inst = thread_suspender_instance; + if (inst != NULL) { if (signum == SIGABRT) - thread_suspender_instance->KillAllThreads(); + inst->KillAllThreads(); else - thread_suspender_instance->ResumeAllThreads(); + inst->ResumeAllThreads(); + SetDieCallback(old_die_callback); + old_die_callback = NULL; + thread_suspender_instance = NULL; + atomic_store(&inst->arg->done, 1, memory_order_relaxed); } internal__exit((signum == SIGABRT) ? 1 : 2); } @@ -218,10 +232,15 @@ static void TracerThreadDieCallback() { // point. So we correctly handle calls to Die() from within the callback, but // not those that happen before or after the callback. Hopefully there aren't // a lot of opportunities for that to happen... - if (thread_suspender_instance) - thread_suspender_instance->KillAllThreads(); + ThreadSuspender *inst = thread_suspender_instance; + if (inst != NULL && stoptheworld_tracer_pid == internal_getpid()) { + inst->KillAllThreads(); + thread_suspender_instance = NULL; + } if (old_die_callback) old_die_callback(); + SetDieCallback(old_die_callback); + old_die_callback = NULL; } // Size of alternative stack for signal handlers in the tracer thread. @@ -244,7 +263,7 @@ static int TracerThread(void* argument) { old_die_callback = GetDieCallback(); SetDieCallback(TracerThreadDieCallback); - ThreadSuspender thread_suspender(internal_getppid()); + ThreadSuspender thread_suspender(internal_getppid(), tracer_thread_argument); // Global pointer for the signal handler. thread_suspender_instance = &thread_suspender; @@ -276,11 +295,9 @@ static int TracerThread(void* argument) { thread_suspender.ResumeAllThreads(); exit_code = 0; } - // Note, this is a bad race. If TracerThreadDieCallback is already started - // in another thread and observed that thread_suspender_instance != 0, - // it can call KillAllThreads on the destroyed variable. SetDieCallback(old_die_callback); thread_suspender_instance = NULL; + atomic_store(&tracer_thread_argument->done, 1, memory_order_relaxed); return exit_code; } @@ -293,7 +310,7 @@ class ScopedStackSpaceWithGuard { // in the future. guard_start_ = (uptr)MmapOrDie(stack_size_ + guard_size_, "ScopedStackWithGuard"); - CHECK_EQ(guard_start_, (uptr)Mprotect((uptr)guard_start_, guard_size_)); + CHECK(MprotectNoAccess((uptr)guard_start_, guard_size_)); } ~ScopedStackSpaceWithGuard() { UnmapOrDie((void *)guard_start_, stack_size_ + guard_size_); @@ -354,6 +371,7 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { tracer_thread_argument.callback = callback; tracer_thread_argument.callback_argument = argument; tracer_thread_argument.parent_pid = internal_getpid(); + atomic_store(&tracer_thread_argument.done, 0, memory_order_relaxed); const uptr kTracerStackSize = 2 * 1024 * 1024; ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); // Block the execution of TracerThread until after we have set ptrace @@ -402,14 +420,27 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { #endif // Allow the tracer thread to start. tracer_thread_argument.mutex.Unlock(); - // Since errno is shared between this thread and the tracer thread, we - // must avoid using errno while the tracer thread is running. - // At this point, any signal will either be blocked or kill us, so waitpid - // should never return (and set errno) while the tracer thread is alive. - uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); - if (internal_iserror(waitpid_status, &local_errno)) + // NOTE: errno is shared between this thread and the tracer thread. + // internal_waitpid() may call syscall() which can access/spoil errno, + // so we can't call it now. Instead we for the tracer thread to finish using + // the spin loop below. Man page for sched_yield() says "In the Linux + // implementation, sched_yield() always succeeds", so let's hope it does not + // spoil errno. Note that this spin loop runs only for brief periods before + // the tracer thread has suspended us and when it starts unblocking threads. + while (atomic_load(&tracer_thread_argument.done, memory_order_relaxed) == 0) + sched_yield(); + // Now the tracer thread is about to exit and does not touch errno, + // wait for it. + for (;;) { + uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); + if (!internal_iserror(waitpid_status, &local_errno)) + break; + if (local_errno == EINTR) + continue; VReport(1, "Waiting on the tracer thread failed (errno %d).\n", local_errno); + break; + } } } diff --git a/lib/sanitizer_common/sanitizer_suppressions.cc b/lib/sanitizer_common/sanitizer_suppressions.cc index 2b697e955709..08cb497269bf 100644 --- a/lib/sanitizer_common/sanitizer_suppressions.cc +++ b/lib/sanitizer_common/sanitizer_suppressions.cc @@ -30,18 +30,50 @@ SuppressionContext::SuppressionContext(const char *suppression_types[], internal_memset(has_suppression_type_, 0, suppression_types_num_); } +static bool GetPathAssumingFileIsRelativeToExec(const char *file_path, + /*out*/char *new_file_path, + uptr new_file_path_size) { + InternalScopedString exec(kMaxPathLength); + if (ReadBinaryNameCached(exec.data(), exec.size())) { + const char *file_name_pos = StripModuleName(exec.data()); + uptr path_to_exec_len = file_name_pos - exec.data(); + internal_strncat(new_file_path, exec.data(), + Min(path_to_exec_len, new_file_path_size - 1)); + internal_strncat(new_file_path, file_path, + new_file_path_size - internal_strlen(new_file_path) - 1); + return true; + } + return false; +} + void SuppressionContext::ParseFromFile(const char *filename) { if (filename[0] == '\0') return; + + // If we cannot find the file, check if its location is relative to + // the location of the executable. + InternalScopedString new_file_path(kMaxPathLength); + if (!FileExists(filename) && !IsAbsolutePath(filename) && + GetPathAssumingFileIsRelativeToExec(filename, new_file_path.data(), + new_file_path.size())) { + filename = new_file_path.data(); + } + + // Read the file. char *file_contents; uptr buffer_size; - uptr contents_size = ReadFileToBuffer(filename, &file_contents, &buffer_size, - 1 << 26 /* max_len */); + const uptr max_len = 1 << 26; + uptr contents_size = + ReadFileToBuffer(filename, &file_contents, &buffer_size, max_len); + VPrintf(1, "%s: reading suppressions file at %s\n", + SanitizerToolName, filename); + if (contents_size == 0) { Printf("%s: failed to read suppressions file '%s'\n", SanitizerToolName, filename); Die(); } + Parse(file_contents); } diff --git a/lib/sanitizer_common/sanitizer_symbolizer.cc b/lib/sanitizer_common/sanitizer_symbolizer.cc index 135720ed5eb1..8b2496a6df2e 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer.cc @@ -16,7 +16,7 @@ #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" namespace __sanitizer { @@ -33,9 +33,7 @@ void AddressInfo::Clear() { function_offset = kUnknown; } -void AddressInfo::FillAddressAndModuleInfo(uptr addr, const char *mod_name, - uptr mod_offset) { - address = addr; +void AddressInfo::FillModuleInfo(const char *mod_name, uptr mod_offset) { module = internal_strdup(mod_name); module_offset = mod_offset; } @@ -70,13 +68,6 @@ Symbolizer *Symbolizer::symbolizer_; StaticSpinMutex Symbolizer::init_mu_; LowLevelAllocator Symbolizer::symbolizer_allocator_; -Symbolizer *Symbolizer::Disable() { - CHECK_EQ(0, symbolizer_); - // Initialize a dummy symbolizer. - symbolizer_ = new(symbolizer_allocator_) Symbolizer; - return symbolizer_; -} - void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, Symbolizer::EndSymbolizationHook end_hook) { CHECK(start_hook_ == 0 && end_hook_ == 0); @@ -84,7 +75,29 @@ void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, end_hook_ = end_hook; } -Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {} +const char *Symbolizer::ModuleNameOwner::GetOwnedCopy(const char *str) { + mu_->CheckLocked(); + + // 'str' will be the same string multiple times in a row, optimize this case. + if (last_match_ && !internal_strcmp(last_match_, str)) + return last_match_; + + // FIXME: this is linear search. + // We should optimize this further if this turns out to be a bottleneck later. + for (uptr i = 0; i < storage_.size(); ++i) { + if (!internal_strcmp(storage_[i], str)) { + last_match_ = storage_[i]; + return last_match_; + } + } + last_match_ = internal_strdup(str); + storage_.push_back(last_match_); + return last_match_; +} + +Symbolizer::Symbolizer(IntrusiveList<SymbolizerTool> tools) + : module_names_(&mu_), n_modules_(0), modules_fresh_(false), tools_(tools), + start_hook_(0), end_hook_(0) {} Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym) : sym_(sym) { diff --git a/lib/sanitizer_common/sanitizer_symbolizer.h b/lib/sanitizer_common/sanitizer_symbolizer.h index 3ccfce9995be..92332230f80a 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer.h +++ b/lib/sanitizer_common/sanitizer_symbolizer.h @@ -43,8 +43,7 @@ struct AddressInfo { AddressInfo(); // Deletes all strings and resets all fields. void Clear(); - void FillAddressAndModuleInfo(uptr addr, const char *mod_name, - uptr mod_offset); + void FillModuleInfo(const char *mod_name, uptr mod_offset); }; // Linked list of symbolized frames (each frame is described by AddressInfo). @@ -74,33 +73,35 @@ struct DataInfo { void Clear(); }; -class Symbolizer { +class SymbolizerTool; + +class Symbolizer final { public: /// Initialize and return platform-specific implementation of symbolizer /// (if it wasn't already initialized). static Symbolizer *GetOrInit(); // Returns a list of symbolized frames for a given address (containing // all inlined functions, if necessary). - virtual SymbolizedStack *SymbolizePC(uptr address) { - return SymbolizedStack::New(address); - } - virtual bool SymbolizeData(uptr address, DataInfo *info) { - return false; - } - virtual bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, - uptr *module_address) { - return false; - } - virtual bool CanReturnFileLineInfo() { - return false; + SymbolizedStack *SymbolizePC(uptr address); + bool SymbolizeData(uptr address, DataInfo *info); + + // The module names Symbolizer returns are stable and unique for every given + // module. It is safe to store and compare them as pointers. + bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address); + const char *GetModuleNameForPc(uptr pc) { + const char *module_name = nullptr; + uptr unused; + if (GetModuleNameAndOffsetForPC(pc, &module_name, &unused)) + return module_name; + return nullptr; } + // Release internal caches (if any). - virtual void Flush() {} + void Flush(); // Attempts to demangle the provided C++ mangled name. - virtual const char *Demangle(const char *name) { - return name; - } - virtual void PrepareForSandboxing() {} + const char *Demangle(const char *name); + void PrepareForSandboxing(); // Allow user to install hooks that would be called before/after Symbolizer // does the actual file/line info fetching. Specific sanitizers may need this @@ -113,16 +114,53 @@ class Symbolizer { EndSymbolizationHook end_hook); private: + // GetModuleNameAndOffsetForPC has to return a string to the caller. + // Since the corresponding module might get unloaded later, we should create + // our owned copies of the strings that we can safely return. + // ModuleNameOwner does not provide any synchronization, thus calls to + // its method should be protected by |mu_|. + class ModuleNameOwner { + public: + explicit ModuleNameOwner(BlockingMutex *synchronized_by) + : storage_(kInitialCapacity), last_match_(nullptr), + mu_(synchronized_by) {} + const char *GetOwnedCopy(const char *str); + + private: + static const uptr kInitialCapacity = 1000; + InternalMmapVector<const char*> storage_; + const char *last_match_; + + BlockingMutex *mu_; + } module_names_; + /// Platform-specific function for creating a Symbolizer object. static Symbolizer *PlatformInit(); - /// Initialize the symbolizer in a disabled state. Not thread safe. - static Symbolizer *Disable(); + + bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name, + uptr *module_offset); + LoadedModule *FindModuleForAddress(uptr address); + LoadedModule modules_[kMaxNumberOfModules]; + uptr n_modules_; + // If stale, need to reload the modules before looking up addresses. + bool modules_fresh_; + + // Platform-specific default demangler, must not return nullptr. + const char *PlatformDemangle(const char *name); + void PlatformPrepareForSandboxing(); static Symbolizer *symbolizer_; static StaticSpinMutex init_mu_; - protected: - Symbolizer(); + // Mutex locked from public methods of |Symbolizer|, so that the internals + // (including individual symbolizer tools and platform-specific methods) are + // always synchronized. + BlockingMutex mu_; + + typedef IntrusiveList<SymbolizerTool>::Iterator Iterator; + IntrusiveList<SymbolizerTool> tools_; + + explicit Symbolizer(IntrusiveList<SymbolizerTool> tools); static LowLevelAllocator symbolizer_allocator_; diff --git a/lib/sanitizer_common/sanitizer_symbolizer_internal.h b/lib/sanitizer_common/sanitizer_symbolizer_internal.h new file mode 100644 index 000000000000..66ae809ed53e --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer_internal.h @@ -0,0 +1,109 @@ +//===-- sanitizer_symbolizer_internal.h -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Header for internal classes and functions to be used by implementations of +// symbolizers. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_INTERNAL_H +#define SANITIZER_SYMBOLIZER_INTERNAL_H + +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +// Parsing helpers, 'str' is searched for delimiter(s) and a string or uptr +// is extracted. When extracting a string, a newly allocated (using +// InternalAlloc) and null-terminataed buffer is returned. They return a pointer +// to the next characted after the found delimiter. +const char *ExtractToken(const char *str, const char *delims, char **result); +const char *ExtractInt(const char *str, const char *delims, int *result); +const char *ExtractUptr(const char *str, const char *delims, uptr *result); +const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter, + char **result); + +const char *DemangleCXXABI(const char *name); + +// SymbolizerTool is an interface that is implemented by individual "tools" +// that can perform symbolication (external llvm-symbolizer, libbacktrace, +// Windows DbgHelp symbolizer, etc.). +class SymbolizerTool { + public: + // The main |Symbolizer| class implements a "fallback chain" of symbolizer + // tools. In a request to symbolize an address, if one tool returns false, + // the next tool in the chain will be tried. + SymbolizerTool *next; + + SymbolizerTool() : next(nullptr) { } + + // Can't declare pure virtual functions in sanitizer runtimes: + // __cxa_pure_virtual might be unavailable. + + // The |stack| parameter is inout. It is pre-filled with the address, + // module base and module offset values and is to be used to construct + // other stack frames. + virtual bool SymbolizePC(uptr addr, SymbolizedStack *stack) { + UNIMPLEMENTED(); + } + + // The |info| parameter is inout. It is pre-filled with the module base + // and module offset values. + virtual bool SymbolizeData(uptr addr, DataInfo *info) { + UNIMPLEMENTED(); + } + + virtual void Flush() {} + + // Return nullptr to fallback to the default platform-specific demangler. + virtual const char *Demangle(const char *name) { + return nullptr; + } +}; + +// SymbolizerProcess encapsulates communication between the tool and +// external symbolizer program, running in a different subprocess. +// SymbolizerProcess may not be used from two threads simultaneously. +class SymbolizerProcess { + public: + explicit SymbolizerProcess(const char *path, bool use_forkpty = false); + const char *SendCommand(const char *command); + + private: + bool Restart(); + const char *SendCommandImpl(const char *command); + bool ReadFromSymbolizer(char *buffer, uptr max_length); + bool WriteToSymbolizer(const char *buffer, uptr length); + bool StartSymbolizerSubprocess(); + + virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { + UNIMPLEMENTED(); + } + + virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const { + UNIMPLEMENTED(); + } + + const char *path_; + int input_fd_; + int output_fd_; + + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; + + static const uptr kMaxTimesRestarted = 5; + static const int kSymbolizerStartupTimeMillis = 10; + uptr times_restarted_; + bool failed_to_start_; + bool reported_invalid_path_; + bool use_forkpty_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_INTERNAL_H diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc index 9317a78eef13..57354668bfbb 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc @@ -33,6 +33,8 @@ namespace __sanitizer { +static char *DemangleAlloc(const char *name, bool always_alloc); + #if SANITIZER_LIBBACKTRACE namespace { @@ -86,17 +88,20 @@ char *CplusV3Demangle(const char *name) { struct SymbolizeCodeCallbackArg { SymbolizedStack *first; SymbolizedStack *last; - const char *module_name; - uptr module_offset; - - void append(SymbolizedStack *f) { - if (last != nullptr) { - last->next = f; - last = f; - } else { - first = f; - last = f; + uptr frames_symbolized; + + AddressInfo *get_new_frame(uintptr_t addr) { + CHECK(last); + if (frames_symbolized > 0) { + SymbolizedStack *cur = SymbolizedStack::New(addr); + AddressInfo *info = &cur->info; + info->FillModuleInfo(first->info.module, first->info.module_offset); + last->next = cur; + last = cur; } + CHECK_EQ(addr, first->info.address); + CHECK_EQ(addr, last->info.address); + return &last->info; } }; @@ -106,15 +111,12 @@ static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr, const char *function) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (function) { - SymbolizedStack *cur = SymbolizedStack::New(addr); - cdata->append(cur); - AddressInfo *info = &cur->info; - info->FillAddressAndModuleInfo(addr, cdata->module_name, - cdata->module_offset); - info->function = LibbacktraceSymbolizer::Demangle(function, true); + AddressInfo *info = cdata->get_new_frame(addr); + info->function = DemangleAlloc(function, /*always_alloc*/ true); if (filename) info->file = internal_strdup(filename); info->line = lineno; + cdata->frames_symbolized++; } return 0; } @@ -123,12 +125,9 @@ static void SymbolizeCodeCallback(void *vdata, uintptr_t addr, const char *symname, uintptr_t, uintptr_t) { SymbolizeCodeCallbackArg *cdata = (SymbolizeCodeCallbackArg *)vdata; if (symname) { - SymbolizedStack *cur = SymbolizedStack::New(addr); - cdata->append(cur); - AddressInfo *info = &cur->info; - info->FillAddressAndModuleInfo(addr, cdata->module_name, - cdata->module_offset); - info->function = LibbacktraceSymbolizer::Demangle(symname, true); + AddressInfo *info = cdata->get_new_frame(addr); + info->function = DemangleAlloc(symname, /*always_alloc*/ true); + cdata->frames_symbolized++; } } @@ -136,7 +135,7 @@ static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname, uintptr_t symval, uintptr_t symsize) { DataInfo *info = (DataInfo *)vdata; if (symname && symval) { - info->name = LibbacktraceSymbolizer::Demangle(symname, true); + info->name = DemangleAlloc(symname, /*always_alloc*/ true); info->start = symval; info->size = symsize; } @@ -156,21 +155,18 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { return new(*alloc) LibbacktraceSymbolizer(state); } -SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, - const char *module_name, - uptr module_offset) { +bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { SymbolizeCodeCallbackArg data; - data.first = nullptr; - data.last = nullptr; - data.module_name = module_name; - data.module_offset = module_offset; + data.first = stack; + data.last = stack; + data.frames_symbolized = 0; backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback, ErrorCallback, &data); - if (data.first) - return data.first; + if (data.frames_symbolized > 0) + return true; backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback, ErrorCallback, &data); - return data.first; + return (data.frames_symbolized > 0); } bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { @@ -185,11 +181,9 @@ LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { return 0; } -SymbolizedStack *LibbacktraceSymbolizer::SymbolizeCode(uptr addr, - const char *module_name, - uptr module_offset) { +bool LibbacktraceSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { (void)state_; - return nullptr; + return false; } bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { @@ -198,7 +192,7 @@ bool LibbacktraceSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { #endif // SANITIZER_LIBBACKTRACE -char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) { +static char *DemangleAlloc(const char *name, bool always_alloc) { #if SANITIZER_LIBBACKTRACE && SANITIZER_CP_DEMANGLE if (char *demangled = CplusV3Demangle(name)) return demangled; @@ -208,4 +202,8 @@ char *LibbacktraceSymbolizer::Demangle(const char *name, bool always_alloc) { return 0; } +const char *LibbacktraceSymbolizer::Demangle(const char *name) { + return DemangleAlloc(name, /*always_alloc*/ false); +} + } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h index 1ff005042a11..00b465a72774 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h +++ b/lib/sanitizer_common/sanitizer_symbolizer_libbacktrace.h @@ -16,7 +16,7 @@ #include "sanitizer_platform.h" #include "sanitizer_common.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" #ifndef SANITIZER_LIBBACKTRACE # define SANITIZER_LIBBACKTRACE 0 @@ -28,17 +28,16 @@ namespace __sanitizer { -class LibbacktraceSymbolizer { +class LibbacktraceSymbolizer : public SymbolizerTool { public: static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc); - SymbolizedStack *SymbolizeCode(uptr addr, const char *module_name, - uptr module_offset); + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; - bool SymbolizeData(uptr addr, DataInfo *info); + bool SymbolizeData(uptr addr, DataInfo *info) override; // May return NULL if demangling failed. - static char *Demangle(const char *name, bool always_alloc = false); + const char *Demangle(const char *name) override; private: explicit LibbacktraceSymbolizer(void *state) : state_(state) {} diff --git a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc index 63c9356a521f..160f55d422ca 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -11,18 +11,177 @@ // run-time libraries. //===----------------------------------------------------------------------===// +#include "sanitizer_allocator_internal.h" #include "sanitizer_internal_defs.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" namespace __sanitizer { +const char *ExtractToken(const char *str, const char *delims, char **result) { + uptr prefix_len = internal_strcspn(str, delims); + *result = (char*)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') prefix_end++; + return prefix_end; +} + +const char *ExtractInt(const char *str, const char *delims, int *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (int)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +const char *ExtractUptr(const char *str, const char *delims, uptr *result) { + char *buff; + const char *ret = ExtractToken(str, delims, &buff); + if (buff != 0) { + *result = (uptr)internal_atoll(buff); + } + InternalFree(buff); + return ret; +} + +const char *ExtractTokenUpToDelimiter(const char *str, const char *delimiter, + char **result) { + const char *found_delimiter = internal_strstr(str, delimiter); + uptr prefix_len = + found_delimiter ? found_delimiter - str : internal_strlen(str); + *result = (char *)InternalAlloc(prefix_len + 1); + internal_memcpy(*result, str, prefix_len); + (*result)[prefix_len] = '\0'; + const char *prefix_end = str + prefix_len; + if (*prefix_end != '\0') prefix_end += internal_strlen(delimiter); + return prefix_end; +} + +SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) { + BlockingMutexLock l(&mu_); + const char *module_name; + uptr module_offset; + SymbolizedStack *res = SymbolizedStack::New(addr); + if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) + return res; + // Always fill data about module name and offset. + res->info.FillModuleInfo(module_name, module_offset); + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + if (tool->SymbolizePC(addr, res)) { + return res; + } + } + return res; +} + +bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); + const char *module_name; + uptr module_offset; + if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) + return false; + info->Clear(); + info->module = internal_strdup(module_name); + info->module_offset = module_offset; + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + if (tool->SymbolizeData(addr, info)) { + return true; + } + } + return true; +} + +bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address) { + BlockingMutexLock l(&mu_); + const char *internal_module_name = nullptr; + if (!FindModuleNameAndOffsetForAddress(pc, &internal_module_name, + module_address)) + return false; + + if (module_name) + *module_name = module_names_.GetOwnedCopy(internal_module_name); + return true; +} + +void Symbolizer::Flush() { + BlockingMutexLock l(&mu_); + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + tool->Flush(); + } +} + +const char *Symbolizer::Demangle(const char *name) { + BlockingMutexLock l(&mu_); + for (auto iter = Iterator(&tools_); iter.hasNext();) { + auto *tool = iter.next(); + SymbolizerScope sym_scope(this); + if (const char *demangled = tool->Demangle(name)) + return demangled; + } + return PlatformDemangle(name); +} + +void Symbolizer::PrepareForSandboxing() { + BlockingMutexLock l(&mu_); + PlatformPrepareForSandboxing(); +} + +bool Symbolizer::FindModuleNameAndOffsetForAddress(uptr address, + const char **module_name, + uptr *module_offset) { + LoadedModule *module = FindModuleForAddress(address); + if (module == 0) + return false; + *module_name = module->full_name(); + *module_offset = address - module->base_address(); + return true; +} + +LoadedModule *Symbolizer::FindModuleForAddress(uptr address) { + bool modules_were_reloaded = false; + if (!modules_fresh_) { + for (uptr i = 0; i < n_modules_; i++) + modules_[i].clear(); + n_modules_ = + GetListOfModules(modules_, kMaxNumberOfModules, /* filter */ nullptr); + CHECK_GT(n_modules_, 0); + CHECK_LT(n_modules_, kMaxNumberOfModules); + modules_fresh_ = true; + modules_were_reloaded = true; + } + for (uptr i = 0; i < n_modules_; i++) { + if (modules_[i].containsAddress(address)) { + return &modules_[i]; + } + } + // Reload the modules and look up again, if we haven't tried it yet. + if (!modules_were_reloaded) { + // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. + // It's too aggressive to reload the list of modules each time we fail + // to find a module for a given address. + modules_fresh_ = false; + return FindModuleForAddress(address); + } + return 0; +} + Symbolizer *Symbolizer::GetOrInit() { SpinMutexLock l(&init_mu_); if (symbolizer_) return symbolizer_; - if ((symbolizer_ = PlatformInit())) - return symbolizer_; - return Disable(); + symbolizer_ = PlatformInit(); + CHECK(symbolizer_); + return symbolizer_; } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.cc b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc new file mode 100644 index 000000000000..9a64192b0353 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.cc @@ -0,0 +1,148 @@ +//===-- sanitizer_symbolizer_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 shared between various sanitizers' runtime libraries. +// +// Implementation of Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_mac.h" +#include "sanitizer_symbolizer_mac.h" + +namespace __sanitizer { + +#include <dlfcn.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> +#include <util.h> + +bool DlAddrSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + Dl_info info; + int result = dladdr((const void *)addr, &info); + if (!result) return false; + const char *demangled = DemangleCXXABI(info.dli_sname); + stack->info.function = internal_strdup(demangled); + return true; +} + +bool DlAddrSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { + return false; +} + +class AtosSymbolizerProcess : public SymbolizerProcess { + public: + explicit AtosSymbolizerProcess(const char *path, pid_t parent_pid) + : SymbolizerProcess(path, /*use_forkpty*/ true), + parent_pid_(parent_pid) {} + + private: + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { + return (length >= 1 && buffer[length - 1] == '\n'); + } + + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { + char pid_str[16]; + internal_snprintf(pid_str, sizeof(pid_str), "%d", parent_pid_); + if (GetMacosVersion() == MACOS_VERSION_MAVERICKS) { + // On Mavericks atos prints a deprecation warning which we suppress by + // passing -d. The warning isn't present on other OSX versions, even the + // newer ones. + execl(path_to_binary, path_to_binary, "-p", pid_str, "-d", (char *)0); + } else { + execl(path_to_binary, path_to_binary, "-p", pid_str, (char *)0); + } + } + + pid_t parent_pid_; +}; + +static const char *kAtosErrorMessages[] = { + "atos cannot examine process", + "unable to get permission to examine process", + "An admin user name and password is required", + "could not load inserted library", + "architecture mismatch between analysis process", +}; + +static bool IsAtosErrorMessage(const char *str) { + for (uptr i = 0; i < ARRAY_SIZE(kAtosErrorMessages); i++) { + if (internal_strstr(str, kAtosErrorMessages[i])) { + return true; + } + } + return false; +} + +static bool ParseCommandOutput(const char *str, SymbolizedStack *res) { + // Trim ending newlines. + char *trim; + ExtractTokenUpToDelimiter(str, "\n", &trim); + + // The line from `atos` is in one of these formats: + // myfunction (in library.dylib) (sourcefile.c:17) + // myfunction (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + 0x1fe + // 0xdeadbeef (in library.dylib) + // 0xdeadbeef + + if (IsAtosErrorMessage(trim)) { + Report("atos returned an error: %s\n", trim); + InternalFree(trim); + return false; + } + + const char *rest = trim; + char *function_name; + rest = ExtractTokenUpToDelimiter(rest, " (in ", &function_name); + if (internal_strncmp(function_name, "0x", 2) != 0) + res->info.function = function_name; + else + InternalFree(function_name); + rest = ExtractTokenUpToDelimiter(rest, ") ", &res->info.module); + + if (rest[0] == '(') { + rest++; + rest = ExtractTokenUpToDelimiter(rest, ":", &res->info.file); + char *extracted_line_number; + rest = ExtractTokenUpToDelimiter(rest, ")", &extracted_line_number); + res->info.line = internal_atoll(extracted_line_number); + InternalFree(extracted_line_number); + } + + InternalFree(trim); + return true; +} + +AtosSymbolizer::AtosSymbolizer(const char *path, LowLevelAllocator *allocator) + : process_(new(*allocator) AtosSymbolizerProcess(path, getpid())) {} + +bool AtosSymbolizer::SymbolizePC(uptr addr, SymbolizedStack *stack) { + if (!process_) return false; + char command[32]; + internal_snprintf(command, sizeof(command), "0x%zx\n", addr); + const char *buf = process_->SendCommand(command); + if (!buf) return false; + if (!ParseCommandOutput(buf, stack)) { + process_ = nullptr; + return false; + } + return true; +} + +bool AtosSymbolizer::SymbolizeData(uptr addr, DataInfo *info) { return false; } + +} // namespace __sanitizer + +#endif // SANITIZER_MAC diff --git a/lib/sanitizer_common/sanitizer_symbolizer_mac.h b/lib/sanitizer_common/sanitizer_symbolizer_mac.h new file mode 100644 index 000000000000..068644de3762 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer_mac.h @@ -0,0 +1,48 @@ +//===-- sanitizer_symbolizer_mac.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 shared between various sanitizers' runtime libraries. +// +// Header for Mac-specific "atos" symbolizer. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_SYMBOLIZER_MAC_H +#define SANITIZER_SYMBOLIZER_MAC_H + +#include "sanitizer_platform.h" +#if SANITIZER_MAC + +#include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +class DlAddrSymbolizer : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override; +}; + +class AtosSymbolizerProcess; + +class AtosSymbolizer : public SymbolizerTool { + public: + explicit AtosSymbolizer(const char *path, LowLevelAllocator *allocator); + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override; + + private: + AtosSymbolizerProcess *process_; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_MAC + +#endif // SANITIZER_SYMBOLIZER_MAC_H diff --git a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 69ac18e8426f..cb8455a21ae2 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -21,12 +21,10 @@ #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_internal.h" #include "sanitizer_symbolizer_libbacktrace.h" +#include "sanitizer_symbolizer_mac.h" -#include <errno.h> -#include <stdlib.h> -#include <sys/wait.h> #include <unistd.h> // C++ demangling function, as required by Itanium C++ ABI. This is weak, @@ -41,7 +39,7 @@ namespace __cxxabiv1 { namespace __sanitizer { // Attempts to demangle the name via __cxa_demangle from __cxxabiv1. -static const char *DemangleCXXABI(const char *name) { +const char *DemangleCXXABI(const char *name) { // FIXME: __cxa_demangle aggressively insists on allocating memory. // There's not much we can do about that, short of providing our // own demangler (libc++abi's implementation could be adapted so that @@ -55,258 +53,67 @@ static const char *DemangleCXXABI(const char *name) { return name; } -// Extracts the prefix of "str" that consists of any characters not -// present in "delims" string, and copies this prefix to "result", allocating -// space for it. -// Returns a pointer to "str" after skipping extracted prefix and first -// delimiter char. -static const char *ExtractToken(const char *str, const char *delims, - char **result) { - uptr prefix_len = internal_strcspn(str, delims); - *result = (char*)InternalAlloc(prefix_len + 1); - internal_memcpy(*result, str, prefix_len); - (*result)[prefix_len] = '\0'; - const char *prefix_end = str + prefix_len; - if (*prefix_end != '\0') prefix_end++; - return prefix_end; -} - -// Same as ExtractToken, but converts extracted token to integer. -static const char *ExtractInt(const char *str, const char *delims, - int *result) { - char *buff; - const char *ret = ExtractToken(str, delims, &buff); - if (buff != 0) { - *result = (int)internal_atoll(buff); - } - InternalFree(buff); - return ret; -} - -static const char *ExtractUptr(const char *str, const char *delims, - uptr *result) { - char *buff; - const char *ret = ExtractToken(str, delims, &buff); - if (buff != 0) { - *result = (uptr)internal_atoll(buff); - } - InternalFree(buff); - return ret; -} - -class ExternalSymbolizerInterface { - public: - // Can't declare pure virtual functions in sanitizer runtimes: - // __cxa_pure_virtual might be unavailable. - virtual char *SendCommand(bool is_data, const char *module_name, - uptr module_offset) { - UNIMPLEMENTED(); - } -}; - -// SymbolizerProcess encapsulates communication between the tool and -// external symbolizer program, running in a different subprocess. -// SymbolizerProcess may not be used from two threads simultaneously. -class SymbolizerProcess : public ExternalSymbolizerInterface { - public: - explicit SymbolizerProcess(const char *path) - : path_(path), - input_fd_(kInvalidFd), - output_fd_(kInvalidFd), - times_restarted_(0), - failed_to_start_(false), - reported_invalid_path_(false) { - CHECK(path_); - CHECK_NE(path_[0], '\0'); - } - - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { - // Start or restart symbolizer if we failed to send command to it. - if (char *res = SendCommandImpl(is_data, module_name, module_offset)) - return res; - Restart(); - } - if (!failed_to_start_) { - Report("WARNING: Failed to use and restart external symbolizer!\n"); - failed_to_start_ = true; - } - return 0; - } - - private: - bool Restart() { - if (input_fd_ != kInvalidFd) - internal_close(input_fd_); - if (output_fd_ != kInvalidFd) - internal_close(output_fd_); - return StartSymbolizerSubprocess(); - } - - char *SendCommandImpl(bool is_data, const char *module_name, - uptr module_offset) { - if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) - return 0; - CHECK(module_name); - if (!RenderInputCommand(buffer_, kBufferSize, is_data, module_name, - module_offset)) - return 0; - if (!writeToSymbolizer(buffer_, internal_strlen(buffer_))) - return 0; - if (!readFromSymbolizer(buffer_, kBufferSize)) - return 0; - return buffer_; - } - - bool readFromSymbolizer(char *buffer, uptr max_length) { - if (max_length == 0) - return true; - uptr read_len = 0; - while (true) { - uptr just_read = internal_read(input_fd_, buffer + read_len, - max_length - read_len - 1); - // We can't read 0 bytes, as we don't expect external symbolizer to close - // its stdout. - if (just_read == 0 || just_read == (uptr)-1) { - Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); - return false; - } - read_len += just_read; - if (ReachedEndOfOutput(buffer, read_len)) - break; - } - buffer[read_len] = '\0'; - return true; - } - - bool writeToSymbolizer(const char *buffer, uptr length) { - if (length == 0) - return true; - uptr write_len = internal_write(output_fd_, buffer, length); - if (write_len == 0 || write_len == (uptr)-1) { - Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); - return false; +// Parses one or more two-line strings in the following format: +// <function_name> +// <file_name>:<line_number>[:<column_number>] +// Used by LLVMSymbolizer, Addr2LinePool and InternalSymbolizer, since all of +// them use the same output format. +static void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { + bool top_frame = true; + SymbolizedStack *last = res; + while (true) { + char *function_name = 0; + str = ExtractToken(str, "\n", &function_name); + CHECK(function_name); + if (function_name[0] == '\0') { + // There are no more frames. + InternalFree(function_name); + break; } - return true; - } - - bool StartSymbolizerSubprocess() { - if (!FileExists(path_)) { - if (!reported_invalid_path_) { - Report("WARNING: invalid path to external symbolizer!\n"); - reported_invalid_path_ = true; - } - return false; + SymbolizedStack *cur; + if (top_frame) { + cur = res; + top_frame = false; + } else { + cur = SymbolizedStack::New(res->info.address); + cur->info.FillModuleInfo(res->info.module, res->info.module_offset); + last->next = cur; + last = cur; } - int *infd = NULL; - int *outfd = NULL; - // The client program may close its stdin and/or stdout and/or stderr - // thus allowing socketpair to reuse file descriptors 0, 1 or 2. - // In this case the communication between the forked processes may be - // broken if either the parent or the child tries to close or duplicate - // these descriptors. The loop below produces two pairs of file - // descriptors, each greater than 2 (stderr). - int sock_pair[5][2]; - for (int i = 0; i < 5; i++) { - if (pipe(sock_pair[i]) == -1) { - for (int j = 0; j < i; j++) { - internal_close(sock_pair[j][0]); - internal_close(sock_pair[j][1]); - } - Report("WARNING: Can't create a socket pair to start " - "external symbolizer (errno: %d)\n", errno); - return false; - } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { - if (infd == NULL) { - infd = sock_pair[i]; - } else { - outfd = sock_pair[i]; - for (int j = 0; j < i; j++) { - if (sock_pair[j] == infd) continue; - internal_close(sock_pair[j][0]); - internal_close(sock_pair[j][1]); - } - break; - } - } - } - CHECK(infd); - CHECK(outfd); - - // Real fork() may call user callbacks registered with pthread_atfork(). - int pid = internal_fork(); - if (pid == -1) { - // Fork() failed. - internal_close(infd[0]); - internal_close(infd[1]); - internal_close(outfd[0]); - internal_close(outfd[1]); - Report("WARNING: failed to fork external symbolizer " - " (errno: %d)\n", errno); - return false; - } else if (pid == 0) { - // Child subprocess. - internal_close(STDOUT_FILENO); - internal_close(STDIN_FILENO); - internal_dup2(outfd[0], STDIN_FILENO); - internal_dup2(infd[1], STDOUT_FILENO); - internal_close(outfd[0]); - internal_close(outfd[1]); - internal_close(infd[0]); - internal_close(infd[1]); - for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) - internal_close(fd); - ExecuteWithDefaultArgs(path_); - internal__exit(1); + AddressInfo *info = &cur->info; + info->function = function_name; + // Parse <file>:<line>:<column> buffer. + char *file_line_info = 0; + str = ExtractToken(str, "\n", &file_line_info); + CHECK(file_line_info); + const char *line_info = ExtractToken(file_line_info, ":", &info->file); + line_info = ExtractInt(line_info, ":", &info->line); + line_info = ExtractInt(line_info, "", &info->column); + InternalFree(file_line_info); + + // Functions and filenames can be "??", in which case we write 0 + // to address info to mark that names are unknown. + if (0 == internal_strcmp(info->function, "??")) { + InternalFree(info->function); + info->function = 0; } - - // Continue execution in parent process. - internal_close(outfd[0]); - internal_close(infd[1]); - input_fd_ = infd[0]; - output_fd_ = outfd[1]; - - // Check that symbolizer subprocess started successfully. - int pid_status; - SleepForMillis(kSymbolizerStartupTimeMillis); - int exited_pid = waitpid(pid, &pid_status, WNOHANG); - if (exited_pid != 0) { - // Either waitpid failed, or child has already exited. - Report("WARNING: external symbolizer didn't start up correctly!\n"); - return false; + if (0 == internal_strcmp(info->file, "??")) { + InternalFree(info->file); + info->file = 0; } - - return true; - } - - virtual bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, - const char *module_name, - uptr module_offset) const { - UNIMPLEMENTED(); - } - - virtual bool ReachedEndOfOutput(const char *buffer, uptr length) const { - UNIMPLEMENTED(); - } - - virtual void ExecuteWithDefaultArgs(const char *path_to_binary) const { - UNIMPLEMENTED(); } +} - const char *path_; - int input_fd_; - int output_fd_; - - static const uptr kBufferSize = 16 * 1024; - char buffer_[kBufferSize]; - - static const uptr kMaxTimesRestarted = 5; - static const int kSymbolizerStartupTimeMillis = 10; - uptr times_restarted_; - bool failed_to_start_; - bool reported_invalid_path_; -}; +// Parses a two-line string in the following format: +// <symbol_name> +// <start_address> <size> +// Used by LLVMSymbolizer and InternalSymbolizer. +static void ParseSymbolizeDataOutput(const char *str, DataInfo *info) { + str = ExtractToken(str, "\n", &info->name); + str = ExtractUptr(str, " ", &info->start); + str = ExtractUptr(str, "\n", &info->size); +} // For now we assume the following protocol: // For each request of the form @@ -323,21 +130,16 @@ class LLVMSymbolizerProcess : public SymbolizerProcess { explicit LLVMSymbolizerProcess(const char *path) : SymbolizerProcess(path) {} private: - bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, - const char *module_name, uptr module_offset) const { - internal_snprintf(buffer, max_length, "%s\"%s\" 0x%zx\n", - is_data ? "DATA " : "", module_name, module_offset); - return true; - } - - bool ReachedEndOfOutput(const char *buffer, uptr length) const { + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { // Empty line marks the end of llvm-symbolizer output. return length >= 2 && buffer[length - 1] == '\n' && buffer[length - 2] == '\n'; } - void ExecuteWithDefaultArgs(const char *path_to_binary) const { -#if defined(__x86_64__) + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { +#if defined(__x86_64h__) + const char* const kSymbolizerArch = "--default-arch=x86_64h"; +#elif defined(__x86_64__) const char* const kSymbolizerArch = "--default-arch=x86_64"; #elif defined(__i386__) const char* const kSymbolizerArch = "--default-arch=i386"; @@ -357,6 +159,44 @@ class LLVMSymbolizerProcess : public SymbolizerProcess { } }; +class LLVMSymbolizer : public SymbolizerTool { + public: + explicit LLVMSymbolizer(const char *path, LowLevelAllocator *allocator) + : symbolizer_process_(new(*allocator) LLVMSymbolizerProcess(path)) {} + + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { + if (const char *buf = SendCommand(/*is_data*/ false, stack->info.module, + stack->info.module_offset)) { + ParseSymbolizePCOutput(buf, stack); + return true; + } + return false; + } + + bool SymbolizeData(uptr addr, DataInfo *info) override { + if (const char *buf = + SendCommand(/*is_data*/ true, info->module, info->module_offset)) { + ParseSymbolizeDataOutput(buf, info); + info->start += (addr - info->module_offset); // Add the base address. + return true; + } + return false; + } + + private: + const char *SendCommand(bool is_data, const char *module_name, + uptr module_offset) { + CHECK(module_name); + internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", + is_data ? "DATA " : "", module_name, module_offset); + return symbolizer_process_->SendCommand(buffer_); + } + + LLVMSymbolizerProcess *symbolizer_process_; + static const uptr kBufferSize = 16 * 1024; + char buffer_[kBufferSize]; +}; + class Addr2LineProcess : public SymbolizerProcess { public: Addr2LineProcess(const char *path, const char *module_name) @@ -365,16 +205,7 @@ class Addr2LineProcess : public SymbolizerProcess { const char *module_name() const { return module_name_; } private: - bool RenderInputCommand(char *buffer, uptr max_length, bool is_data, - const char *module_name, uptr module_offset) const { - if (is_data) - return false; - CHECK_EQ(0, internal_strcmp(module_name, module_name_)); - internal_snprintf(buffer, max_length, "0x%zx\n", module_offset); - return true; - } - - bool ReachedEndOfOutput(const char *buffer, uptr length) const { + bool ReachedEndOfOutput(const char *buffer, uptr length) const override { // Output should consist of two lines. int num_lines = 0; for (uptr i = 0; i < length; ++i) { @@ -386,23 +217,35 @@ class Addr2LineProcess : public SymbolizerProcess { return false; } - void ExecuteWithDefaultArgs(const char *path_to_binary) const { + void ExecuteWithDefaultArgs(const char *path_to_binary) const override { execl(path_to_binary, path_to_binary, "-Cfe", module_name_, (char *)0); } const char *module_name_; // Owned, leaked. }; -class Addr2LinePool : public ExternalSymbolizerInterface { +class Addr2LinePool : public SymbolizerTool { public: explicit Addr2LinePool(const char *addr2line_path, LowLevelAllocator *allocator) : addr2line_path_(addr2line_path), allocator_(allocator), addr2line_pool_(16) {} - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - if (is_data) - return 0; + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { + if (const char *buf = + SendCommand(stack->info.module, stack->info.module_offset)) { + ParseSymbolizePCOutput(buf, stack); + return true; + } + return false; + } + + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + + private: + const char *SendCommand(const char *module_name, uptr module_offset) { Addr2LineProcess *addr2line = 0; for (uptr i = 0; i < addr2line_pool_.size(); ++i) { if (0 == @@ -416,10 +259,13 @@ class Addr2LinePool : public ExternalSymbolizerInterface { new(*allocator_) Addr2LineProcess(addr2line_path_, module_name); addr2line_pool_.push_back(addr2line); } - return addr2line->SendCommand(is_data, module_name, module_offset); + CHECK_EQ(0, internal_strcmp(module_name, addr2line->module_name())); + char buffer_[kBufferSize]; + internal_snprintf(buffer_, kBufferSize, "0x%zx\n", module_offset); + return addr2line->SendCommand(buffer_); } - private: + static const uptr kBufferSize = 32; const char *addr2line_path_; LowLevelAllocator *allocator_; InternalMmapVector<Addr2LineProcess*> addr2line_pool_; @@ -440,10 +286,8 @@ int __sanitizer_symbolize_demangle(const char *Name, char *Buffer, int MaxLength); } // extern "C" -class InternalSymbolizer { +class InternalSymbolizer : public SymbolizerTool { public: - typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); - static InternalSymbolizer *get(LowLevelAllocator *alloc) { if (__sanitizer_symbolize_code != 0 && __sanitizer_symbolize_data != 0) { @@ -452,20 +296,29 @@ class InternalSymbolizer { return 0; } - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - SanitizerSymbolizeFn symbolize_fn = is_data ? __sanitizer_symbolize_data - : __sanitizer_symbolize_code; - if (symbolize_fn(module_name, module_offset, buffer_, kBufferSize)) - return buffer_; - return 0; + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override { + bool result = __sanitizer_symbolize_code( + stack->info.module, stack->info.module_offset, buffer_, kBufferSize); + if (result) ParseSymbolizePCOutput(buffer_, stack); + return result; } - void Flush() { + bool SymbolizeData(uptr addr, DataInfo *info) override { + bool result = __sanitizer_symbolize_data(info->module, info->module_offset, + buffer_, kBufferSize); + if (result) { + ParseSymbolizeDataOutput(buffer_, info); + info->start += (addr - info->module_offset); // Add the base address. + } + return result; + } + + void Flush() override { if (__sanitizer_symbolize_flush) __sanitizer_symbolize_flush(); } - const char *Demangle(const char *name) { + const char *Demangle(const char *name) override { if (__sanitizer_symbolize_demangle) { for (uptr res_length = 1024; res_length <= InternalSizeClassMap::kMaxSize;) { @@ -492,273 +345,101 @@ class InternalSymbolizer { }; #else // SANITIZER_SUPPORTS_WEAK_HOOKS -class InternalSymbolizer { +class InternalSymbolizer : public SymbolizerTool { public: static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - return 0; - } - void Flush() { } - const char *Demangle(const char *name) { return name; } }; #endif // SANITIZER_SUPPORTS_WEAK_HOOKS -class POSIXSymbolizer : public Symbolizer { - public: - POSIXSymbolizer(ExternalSymbolizerInterface *external_symbolizer, - InternalSymbolizer *internal_symbolizer, - LibbacktraceSymbolizer *libbacktrace_symbolizer) - : Symbolizer(), - external_symbolizer_(external_symbolizer), - internal_symbolizer_(internal_symbolizer), - libbacktrace_symbolizer_(libbacktrace_symbolizer) {} - - SymbolizedStack *SymbolizePC(uptr addr) override { - BlockingMutexLock l(&mu_); - const char *module_name; - uptr module_offset; - if (!FindModuleNameAndOffsetForAddress(addr, &module_name, &module_offset)) - return SymbolizedStack::New(addr); - // First, try to use libbacktrace symbolizer (if it's available). - if (libbacktrace_symbolizer_ != 0) { - mu_.CheckLocked(); - if (SymbolizedStack *res = libbacktrace_symbolizer_->SymbolizeCode( - addr, module_name, module_offset)) - return res; - } - // Always fill data about module name and offset. - SymbolizedStack *res = SymbolizedStack::New(addr); - res->info.FillAddressAndModuleInfo(addr, module_name, module_offset); - - const char *str = SendCommand(false, module_name, module_offset); - if (str == 0) { - // Symbolizer was not initialized or failed. - return res; - } - - bool top_frame = true; - SymbolizedStack *last = res; - while (true) { - char *function_name = 0; - str = ExtractToken(str, "\n", &function_name); - CHECK(function_name); - if (function_name[0] == '\0') { - // There are no more frames. - break; - } - SymbolizedStack *cur; - if (top_frame) { - cur = res; - top_frame = false; - } else { - cur = SymbolizedStack::New(addr); - cur->info.FillAddressAndModuleInfo(addr, module_name, module_offset); - last->next = cur; - last = cur; - } - - AddressInfo *info = &cur->info; - info->function = function_name; - // Parse <file>:<line>:<column> buffer. - char *file_line_info = 0; - str = ExtractToken(str, "\n", &file_line_info); - CHECK(file_line_info); - const char *line_info = ExtractToken(file_line_info, ":", &info->file); - line_info = ExtractInt(line_info, ":", &info->line); - line_info = ExtractInt(line_info, "", &info->column); - InternalFree(file_line_info); - - // Functions and filenames can be "??", in which case we write 0 - // to address info to mark that names are unknown. - if (0 == internal_strcmp(info->function, "??")) { - InternalFree(info->function); - info->function = 0; - } - if (0 == internal_strcmp(info->file, "??")) { - InternalFree(info->file); - info->file = 0; - } - } - return res; - } - - bool SymbolizeData(uptr addr, DataInfo *info) override { - BlockingMutexLock l(&mu_); - LoadedModule *module = FindModuleForAddress(addr); - if (module == 0) - return false; - const char *module_name = module->full_name(); - uptr module_offset = addr - module->base_address(); - info->Clear(); - info->module = internal_strdup(module_name); - info->module_offset = module_offset; - // First, try to use libbacktrace symbolizer (if it's available). - if (libbacktrace_symbolizer_ != 0) { - mu_.CheckLocked(); - if (libbacktrace_symbolizer_->SymbolizeData(addr, info)) - return true; - } - const char *str = SendCommand(true, module_name, module_offset); - if (str == 0) - return true; - str = ExtractToken(str, "\n", &info->name); - str = ExtractUptr(str, " ", &info->start); - str = ExtractUptr(str, "\n", &info->size); - info->start += module->base_address(); - return true; - } - - bool GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, - uptr *module_address) override { - BlockingMutexLock l(&mu_); - return FindModuleNameAndOffsetForAddress(pc, module_name, module_address); - } - - bool CanReturnFileLineInfo() override { - return internal_symbolizer_ != 0 || external_symbolizer_ != 0 || - libbacktrace_symbolizer_ != 0; - } - - void Flush() override { - BlockingMutexLock l(&mu_); - if (internal_symbolizer_ != 0) { - SymbolizerScope sym_scope(this); - internal_symbolizer_->Flush(); - } - } +const char *Symbolizer::PlatformDemangle(const char *name) { + return DemangleCXXABI(name); +} - const char *Demangle(const char *name) override { - BlockingMutexLock l(&mu_); - // Run hooks even if we don't use internal symbolizer, as cxxabi - // demangle may call system functions. - SymbolizerScope sym_scope(this); - // Try to use libbacktrace demangler (if available). - if (libbacktrace_symbolizer_ != 0) { - if (const char *demangled = libbacktrace_symbolizer_->Demangle(name)) - return demangled; +void Symbolizer::PlatformPrepareForSandboxing() {} + +static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) { + const char *path = common_flags()->external_symbolizer_path; + const char *binary_name = path ? StripModuleName(path) : ""; + if (path && path[0] == '\0') { + VReport(2, "External symbolizer is explicitly disabled.\n"); + return nullptr; + } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) { + VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path); + return new(*allocator) LLVMSymbolizer(path, allocator); + } else if (!internal_strcmp(binary_name, "atos")) { +#if SANITIZER_MAC + VReport(2, "Using atos at user-specified path: %s\n", path); + return new(*allocator) AtosSymbolizer(path, allocator); +#else // SANITIZER_MAC + Report("ERROR: Using `atos` is only supported on Darwin.\n"); + Die(); +#endif // SANITIZER_MAC + } else if (!internal_strcmp(binary_name, "addr2line")) { + VReport(2, "Using addr2line at user-specified path: %s\n", path); + return new(*allocator) Addr2LinePool(path, allocator); + } else if (path) { + Report("ERROR: External symbolizer path is set to '%s' which isn't " + "a known symbolizer. Please set the path to the llvm-symbolizer " + "binary or other known tool.\n", path); + Die(); + } + + // Otherwise symbolizer program is unknown, let's search $PATH + CHECK(path == nullptr); + if (const char *found_path = FindPathToBinary("llvm-symbolizer")) { + VReport(2, "Using llvm-symbolizer found at: %s\n", found_path); + return new(*allocator) LLVMSymbolizer(found_path, allocator); + } +#if SANITIZER_MAC + if (const char *found_path = FindPathToBinary("atos")) { + VReport(2, "Using atos found at: %s\n", found_path); + return new(*allocator) AtosSymbolizer(found_path, allocator); + } +#endif // SANITIZER_MAC + if (common_flags()->allow_addr2line) { + if (const char *found_path = FindPathToBinary("addr2line")) { + VReport(2, "Using addr2line found at: %s\n", found_path); + return new(*allocator) Addr2LinePool(found_path, allocator); } - if (internal_symbolizer_ != 0) - return internal_symbolizer_->Demangle(name); - return DemangleCXXABI(name); } + return nullptr; +} - void PrepareForSandboxing() override { -#if SANITIZER_LINUX && !SANITIZER_ANDROID - BlockingMutexLock l(&mu_); - // Cache /proc/self/exe on Linux. - CacheBinaryName(); -#endif +static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list, + LowLevelAllocator *allocator) { + if (!common_flags()->symbolize) { + VReport(2, "Symbolizer is disabled.\n"); + return; } - - private: - char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { - mu_.CheckLocked(); - // First, try to use internal symbolizer. - if (internal_symbolizer_) { - SymbolizerScope sym_scope(this); - return internal_symbolizer_->SendCommand(is_data, module_name, - module_offset); - } - // Otherwise, fall back to external symbolizer. - if (external_symbolizer_) { - SymbolizerScope sym_scope(this); - return external_symbolizer_->SendCommand(is_data, module_name, - module_offset); - } - return 0; + if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) { + VReport(2, "Using internal symbolizer.\n"); + list->push_back(tool); + return; } - - LoadedModule *FindModuleForAddress(uptr address) { - mu_.CheckLocked(); - bool modules_were_reloaded = false; - if (modules_ == 0 || !modules_fresh_) { - modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate( - kMaxNumberOfModuleContexts * sizeof(LoadedModule))); - CHECK(modules_); - n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, - /* filter */ 0); - CHECK_GT(n_modules_, 0); - CHECK_LT(n_modules_, kMaxNumberOfModuleContexts); - modules_fresh_ = true; - modules_were_reloaded = true; - } - for (uptr i = 0; i < n_modules_; i++) { - if (modules_[i].containsAddress(address)) { - return &modules_[i]; - } - } - // Reload the modules and look up again, if we haven't tried it yet. - if (!modules_were_reloaded) { - // FIXME: set modules_fresh_ from dlopen()/dlclose() interceptors. - // It's too aggressive to reload the list of modules each time we fail - // to find a module for a given address. - modules_fresh_ = false; - return FindModuleForAddress(address); - } - return 0; + if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) { + VReport(2, "Using libbacktrace symbolizer.\n"); + list->push_back(tool); + return; } - bool FindModuleNameAndOffsetForAddress(uptr address, const char **module_name, - uptr *module_offset) { - mu_.CheckLocked(); - LoadedModule *module = FindModuleForAddress(address); - if (module == 0) - return false; - *module_name = module->full_name(); - *module_offset = address - module->base_address(); - return true; + if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) { + list->push_back(tool); + } else { + VReport(2, "No internal or external symbolizer found.\n"); } - // 16K loaded modules should be enough for everyone. - static const uptr kMaxNumberOfModuleContexts = 1 << 14; - LoadedModule *modules_; // Array of module descriptions is leaked. - uptr n_modules_; - // If stale, need to reload the modules before looking up addresses. - bool modules_fresh_; - BlockingMutex mu_; - - ExternalSymbolizerInterface *external_symbolizer_; // Leaked. - InternalSymbolizer *const internal_symbolizer_; // Leaked. - LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked. -}; +#if SANITIZER_MAC + VReport(2, "Using dladdr symbolizer.\n"); + list->push_back(new(*allocator) DlAddrSymbolizer()); +#endif // SANITIZER_MAC +} Symbolizer *Symbolizer::PlatformInit() { - if (!common_flags()->symbolize) { - return new(symbolizer_allocator_) POSIXSymbolizer(0, 0, 0); - } - InternalSymbolizer* internal_symbolizer = - InternalSymbolizer::get(&symbolizer_allocator_); - ExternalSymbolizerInterface *external_symbolizer = 0; - LibbacktraceSymbolizer *libbacktrace_symbolizer = 0; - - if (!internal_symbolizer) { - libbacktrace_symbolizer = - LibbacktraceSymbolizer::get(&symbolizer_allocator_); - if (!libbacktrace_symbolizer) { - const char *path_to_external = common_flags()->external_symbolizer_path; - if (path_to_external && path_to_external[0] == '\0') { - // External symbolizer is explicitly disabled. Do nothing. - } else { - // Find path to llvm-symbolizer if it's not provided. - if (!path_to_external) - path_to_external = FindPathToBinary("llvm-symbolizer"); - if (path_to_external) { - external_symbolizer = new(symbolizer_allocator_) - LLVMSymbolizerProcess(path_to_external); - } else if (common_flags()->allow_addr2line) { - // If llvm-symbolizer is not found, try to use addr2line. - if (const char *addr2line_path = FindPathToBinary("addr2line")) { - external_symbolizer = new(symbolizer_allocator_) - Addr2LinePool(addr2line_path, &symbolizer_allocator_); - } - } - } - } - } - - return new(symbolizer_allocator_) POSIXSymbolizer( - external_symbolizer, internal_symbolizer, libbacktrace_symbolizer); + IntrusiveList<SymbolizerTool> list; + list.clear(); + ChooseSymbolizerTools(&list, &symbolizer_allocator_); + return new(symbolizer_allocator_) Symbolizer(list); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc b/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc new file mode 100644 index 000000000000..f1c01a332499 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer_process_libcdep.cc @@ -0,0 +1,229 @@ +//===-- sanitizer_symbolizer_process_libcdep.cc ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of SymbolizerProcess used by external symbolizers. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_POSIX +#include "sanitizer_posix.h" +#include "sanitizer_symbolizer_internal.h" + +#include <errno.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <unistd.h> + +#if SANITIZER_MAC +#include <util.h> // for forkpty() +#endif // SANITIZER_MAC + +namespace __sanitizer { + +SymbolizerProcess::SymbolizerProcess(const char *path, bool use_forkpty) + : path_(path), + input_fd_(kInvalidFd), + output_fd_(kInvalidFd), + times_restarted_(0), + failed_to_start_(false), + reported_invalid_path_(false), + use_forkpty_(use_forkpty) { + CHECK(path_); + CHECK_NE(path_[0], '\0'); +} + +const char *SymbolizerProcess::SendCommand(const char *command) { + for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { + // Start or restart symbolizer if we failed to send command to it. + if (const char *res = SendCommandImpl(command)) + return res; + Restart(); + } + if (!failed_to_start_) { + Report("WARNING: Failed to use and restart external symbolizer!\n"); + failed_to_start_ = true; + } + return 0; +} + +bool SymbolizerProcess::Restart() { + if (input_fd_ != kInvalidFd) + internal_close(input_fd_); + if (output_fd_ != kInvalidFd) + internal_close(output_fd_); + return StartSymbolizerSubprocess(); +} + +const char *SymbolizerProcess::SendCommandImpl(const char *command) { + if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) + return 0; + if (!WriteToSymbolizer(command, internal_strlen(command))) + return 0; + if (!ReadFromSymbolizer(buffer_, kBufferSize)) + return 0; + return buffer_; +} + +bool SymbolizerProcess::ReadFromSymbolizer(char *buffer, uptr max_length) { + if (max_length == 0) + return true; + uptr read_len = 0; + while (true) { + uptr just_read = internal_read(input_fd_, buffer + read_len, + max_length - read_len - 1); + // We can't read 0 bytes, as we don't expect external symbolizer to close + // its stdout. + if (just_read == 0 || just_read == (uptr)-1) { + Report("WARNING: Can't read from symbolizer at fd %d\n", input_fd_); + return false; + } + read_len += just_read; + if (ReachedEndOfOutput(buffer, read_len)) + break; + } + buffer[read_len] = '\0'; + return true; +} + +bool SymbolizerProcess::WriteToSymbolizer(const char *buffer, uptr length) { + if (length == 0) + return true; + uptr write_len = internal_write(output_fd_, buffer, length); + if (write_len == 0 || write_len == (uptr)-1) { + Report("WARNING: Can't write to symbolizer at fd %d\n", output_fd_); + return false; + } + return true; +} + +bool SymbolizerProcess::StartSymbolizerSubprocess() { + if (!FileExists(path_)) { + if (!reported_invalid_path_) { + Report("WARNING: invalid path to external symbolizer!\n"); + reported_invalid_path_ = true; + } + return false; + } + + int pid; + if (use_forkpty_) { +#if SANITIZER_MAC + fd_t fd = kInvalidFd; + // Use forkpty to disable buffering in the new terminal. + pid = forkpty(&fd, 0, 0, 0); + if (pid == -1) { + // forkpty() failed. + Report("WARNING: failed to fork external symbolizer (errno: %d)\n", + errno); + return false; + } else if (pid == 0) { + // Child subprocess. + ExecuteWithDefaultArgs(path_); + internal__exit(1); + } + + // Continue execution in parent process. + input_fd_ = output_fd_ = fd; + + // Disable echo in the new terminal, disable CR. + struct termios termflags; + tcgetattr(fd, &termflags); + termflags.c_oflag &= ~ONLCR; + termflags.c_lflag &= ~ECHO; + tcsetattr(fd, TCSANOW, &termflags); +#else // SANITIZER_MAC + UNIMPLEMENTED(); +#endif // SANITIZER_MAC + } else { + int *infd = NULL; + int *outfd = NULL; + // The client program may close its stdin and/or stdout and/or stderr + // thus allowing socketpair to reuse file descriptors 0, 1 or 2. + // In this case the communication between the forked processes may be + // broken if either the parent or the child tries to close or duplicate + // these descriptors. The loop below produces two pairs of file + // descriptors, each greater than 2 (stderr). + int sock_pair[5][2]; + for (int i = 0; i < 5; i++) { + if (pipe(sock_pair[i]) == -1) { + for (int j = 0; j < i; j++) { + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + Report("WARNING: Can't create a socket pair to start " + "external symbolizer (errno: %d)\n", errno); + return false; + } else if (sock_pair[i][0] > 2 && sock_pair[i][1] > 2) { + if (infd == NULL) { + infd = sock_pair[i]; + } else { + outfd = sock_pair[i]; + for (int j = 0; j < i; j++) { + if (sock_pair[j] == infd) continue; + internal_close(sock_pair[j][0]); + internal_close(sock_pair[j][1]); + } + break; + } + } + } + CHECK(infd); + CHECK(outfd); + + // Real fork() may call user callbacks registered with pthread_atfork(). + pid = internal_fork(); + if (pid == -1) { + // Fork() failed. + internal_close(infd[0]); + internal_close(infd[1]); + internal_close(outfd[0]); + internal_close(outfd[1]); + Report("WARNING: failed to fork external symbolizer " + " (errno: %d)\n", errno); + return false; + } else if (pid == 0) { + // Child subprocess. + internal_close(STDOUT_FILENO); + internal_close(STDIN_FILENO); + internal_dup2(outfd[0], STDIN_FILENO); + internal_dup2(infd[1], STDOUT_FILENO); + internal_close(outfd[0]); + internal_close(outfd[1]); + internal_close(infd[0]); + internal_close(infd[1]); + for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) + internal_close(fd); + ExecuteWithDefaultArgs(path_); + internal__exit(1); + } + + // Continue execution in parent process. + internal_close(outfd[0]); + internal_close(infd[1]); + input_fd_ = infd[0]; + output_fd_ = outfd[1]; + } + + // Check that symbolizer subprocess started successfully. + int pid_status; + SleepForMillis(kSymbolizerStartupTimeMillis); + int exited_pid = waitpid(pid, &pid_status, WNOHANG); + if (exited_pid != 0) { + // Either waitpid failed, or child has already exited. + Report("WARNING: external symbolizer didn't start up correctly!\n"); + return false; + } + + return true; +} + +} // namespace __sanitizer + +#endif // SANITIZER_POSIX diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.cc b/lib/sanitizer_common/sanitizer_symbolizer_win.cc index ed96a3a895a8..31f374687e96 100644 --- a/lib/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/lib/sanitizer_common/sanitizer_symbolizer_win.cc @@ -18,139 +18,130 @@ #include <dbghelp.h> #pragma comment(lib, "dbghelp.lib") -#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_win.h" +#include "sanitizer_symbolizer_internal.h" namespace __sanitizer { -class WinSymbolizer : public Symbolizer { - public: - WinSymbolizer() : initialized_(false) {} - - SymbolizedStack *SymbolizePC(uptr addr) override { - SymbolizedStack *frame = SymbolizedStack::New(addr); - - BlockingMutexLock l(&dbghelp_mu_); - InitializeIfNeeded(); - - // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; - PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - symbol->MaxNameLen = MAX_SYM_NAME; - DWORD64 offset = 0; - BOOL got_objname = SymFromAddr(GetCurrentProcess(), - (DWORD64)addr, &offset, symbol); - if (!got_objname) - return frame; - - DWORD unused; - IMAGEHLP_LINE64 line_info; - line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr, - &unused, &line_info); - frame->info.function = internal_strdup(symbol->Name); - frame->info.function_offset = (uptr)offset; - if (got_fileline) { - frame->info.file = internal_strdup(line_info.FileName); - frame->info.line = line_info.LineNumber; - } - - IMAGEHLP_MODULE64 mod_info; - internal_memset(&mod_info, 0, sizeof(mod_info)); - mod_info.SizeOfStruct = sizeof(mod_info); - if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info)) - frame->info.FillAddressAndModuleInfo(addr, mod_info.ImageName, - addr - (uptr)mod_info.BaseOfImage); - return frame; - } +namespace { - bool CanReturnFileLineInfo() override { - return true; - } +bool is_dbghelp_initialized = false; - const char *Demangle(const char *name) override { - CHECK(initialized_); - static char demangle_buffer[1000]; - if (name[0] == '\01' && - UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer), - UNDNAME_NAME_ONLY)) - return demangle_buffer; - else - return name; - } +bool TrySymInitialize() { + SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); + return SymInitialize(GetCurrentProcess(), 0, TRUE); + // FIXME: We don't call SymCleanup() on exit yet - should we? +} - // FIXME: Implement GetModuleNameAndOffsetForPC(). - - private: - void InitializeIfNeeded() { - if (initialized_) - return; - if (!TrySymInitialize()) { - // OK, maybe the client app has called SymInitialize already. - // That's a bit unfortunate for us as all the DbgHelp functions are - // single-threaded and we can't coordinate with the app. - // FIXME: Can we stop the other threads at this point? - // Anyways, we have to reconfigure stuff to make sure that SymInitialize - // has all the appropriate options set. - // Cross our fingers and reinitialize DbgHelp. - Report("*** WARNING: Failed to initialize DbgHelp! ***\n"); - Report("*** Most likely this means that the app is already ***\n"); - Report("*** using DbgHelp, possibly with incompatible flags. ***\n"); - Report("*** Due to technical reasons, symbolization might crash ***\n"); - Report("*** or produce wrong results. ***\n"); - SymCleanup(GetCurrentProcess()); - TrySymInitialize(); - } - initialized_ = true; - - // When an executable is run from a location different from the one where it - // was originally built, we may not see the nearby PDB files. - // To work around this, let's append the directory of the main module - // to the symbol search path. All the failures below are not fatal. - const size_t kSymPathSize = 2048; - static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH]; - if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) { - Report("*** WARNING: Failed to SymGetSearchPathW ***\n"); - return; - } - size_t sz = wcslen(path_buffer); - if (sz) { - CHECK_EQ(0, wcscat_s(path_buffer, L";")); - sz++; - } - DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH); - if (res == 0 || res == MAX_PATH) { - Report("*** WARNING: Failed to getting the EXE directory ***\n"); - return; - } - // Write the zero character in place of the last backslash to get the - // directory of the main module at the end of path_buffer. - wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\'); - CHECK_NE(last_bslash, 0); - *last_bslash = L'\0'; - if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) { - Report("*** WARNING: Failed to SymSetSearchPathW\n"); - return; - } +// Initializes DbgHelp library, if it's not yet initialized. Calls to this +// function should be synchronized with respect to other calls to DbgHelp API +// (e.g. from WinSymbolizerTool). +void InitializeDbgHelpIfNeeded() { + if (is_dbghelp_initialized) + return; + if (!TrySymInitialize()) { + // OK, maybe the client app has called SymInitialize already. + // That's a bit unfortunate for us as all the DbgHelp functions are + // single-threaded and we can't coordinate with the app. + // FIXME: Can we stop the other threads at this point? + // Anyways, we have to reconfigure stuff to make sure that SymInitialize + // has all the appropriate options set. + // Cross our fingers and reinitialize DbgHelp. + Report("*** WARNING: Failed to initialize DbgHelp! ***\n"); + Report("*** Most likely this means that the app is already ***\n"); + Report("*** using DbgHelp, possibly with incompatible flags. ***\n"); + Report("*** Due to technical reasons, symbolization might crash ***\n"); + Report("*** or produce wrong results. ***\n"); + SymCleanup(GetCurrentProcess()); + TrySymInitialize(); + } + is_dbghelp_initialized = true; + + // When an executable is run from a location different from the one where it + // was originally built, we may not see the nearby PDB files. + // To work around this, let's append the directory of the main module + // to the symbol search path. All the failures below are not fatal. + const size_t kSymPathSize = 2048; + static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH]; + if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) { + Report("*** WARNING: Failed to SymGetSearchPathW ***\n"); + return; + } + size_t sz = wcslen(path_buffer); + if (sz) { + CHECK_EQ(0, wcscat_s(path_buffer, L";")); + sz++; + } + DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH); + if (res == 0 || res == MAX_PATH) { + Report("*** WARNING: Failed to getting the EXE directory ***\n"); + return; + } + // Write the zero character in place of the last backslash to get the + // directory of the main module at the end of path_buffer. + wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\'); + CHECK_NE(last_bslash, 0); + *last_bslash = L'\0'; + if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) { + Report("*** WARNING: Failed to SymSetSearchPathW\n"); + return; } +} - bool TrySymInitialize() { - SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES); - return SymInitialize(GetCurrentProcess(), 0, TRUE); - // FIXME: We don't call SymCleanup() on exit yet - should we? +} // namespace + +bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) { + InitializeDbgHelpIfNeeded(); + + // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; + PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + DWORD64 offset = 0; + BOOL got_objname = SymFromAddr(GetCurrentProcess(), + (DWORD64)addr, &offset, symbol); + if (!got_objname) + return false; + + DWORD unused; + IMAGEHLP_LINE64 line_info; + line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr, + &unused, &line_info); + frame->info.function = internal_strdup(symbol->Name); + frame->info.function_offset = (uptr)offset; + if (got_fileline) { + frame->info.file = internal_strdup(line_info.FileName); + frame->info.line = line_info.LineNumber; } + return true; +} + +const char *WinSymbolizerTool::Demangle(const char *name) { + CHECK(is_dbghelp_initialized); + static char demangle_buffer[1000]; + if (name[0] == '\01' && + UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer), + UNDNAME_NAME_ONLY)) + return demangle_buffer; + else + return name; +} - // All DbgHelp functions are single threaded, so we should use a mutex to - // serialize accesses. - BlockingMutex dbghelp_mu_; - bool initialized_; -}; +const char *Symbolizer::PlatformDemangle(const char *name) { + return name; +} + +void Symbolizer::PlatformPrepareForSandboxing() { + // Do nothing. +} Symbolizer *Symbolizer::PlatformInit() { - static bool called_once = false; - CHECK(!called_once && "Shouldn't create more than one symbolizer"); - called_once = true; - return new(symbolizer_allocator_) WinSymbolizer(); + IntrusiveList<SymbolizerTool> list; + list.clear(); + list.push_back(new(symbolizer_allocator_) WinSymbolizerTool()); + return new(symbolizer_allocator_) Symbolizer(list); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/sanitizer_symbolizer_win.h b/lib/sanitizer_common/sanitizer_symbolizer_win.h new file mode 100644 index 000000000000..72ac5e5ee104 --- /dev/null +++ b/lib/sanitizer_common/sanitizer_symbolizer_win.h @@ -0,0 +1,31 @@ +//===-- sanitizer_symbolizer_win.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Header file for the Windows symbolizer tool. +// +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_SYMBOLIZER_WIN_H +#define SANITIZER_SYMBOLIZER_WIN_H + +#include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +class WinSymbolizerTool : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + const char *Demangle(const char *name) override; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_SYMBOLIZER_WIN_H diff --git a/lib/sanitizer_common/sanitizer_tls_get_addr.cc b/lib/sanitizer_common/sanitizer_tls_get_addr.cc index 6142ce517db5..ea037159d00b 100644 --- a/lib/sanitizer_common/sanitizer_tls_get_addr.cc +++ b/lib/sanitizer_common/sanitizer_tls_get_addr.cc @@ -78,7 +78,8 @@ void DTLS_Destroy() { DTLS_Deallocate(dtls.dtv, s); } -DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) { +DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, + uptr static_tls_begin, uptr static_tls_end) { if (!common_flags()->intercept_tls_get_addr) return 0; TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void); uptr dso_id = arg->dso_id; @@ -95,6 +96,11 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res) { tls_size = dtls.last_memalign_size; VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n", tls_beg, tls_size); + } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) { + // This is the static TLS block which was initialized / unpoisoned at thread + // creation. + VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg); + tls_size = 0; } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) { // We may want to check gnu_get_libc_version(). Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1; diff --git a/lib/sanitizer_common/sanitizer_tls_get_addr.h b/lib/sanitizer_common/sanitizer_tls_get_addr.h index 0fc9a2257c68..58d47634d382 100644 --- a/lib/sanitizer_common/sanitizer_tls_get_addr.h +++ b/lib/sanitizer_common/sanitizer_tls_get_addr.h @@ -50,7 +50,8 @@ struct DTLS { // Returns pointer and size of a linker-allocated TLS block. // Each block is returned exactly once. -DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res); +DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin, + uptr static_tls_end); void DTLS_on_libc_memalign(void *ptr, uptr size); DTLS *DTLS_Get(); void DTLS_Destroy(); // Make sure to call this before the thread is destroyed. diff --git a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc index 7ab2efbd75bd..1082ccfd6a88 100644 --- a/lib/sanitizer_common/sanitizer_unwind_posix_libcdep.cc +++ b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc @@ -1,4 +1,4 @@ -//===-- sanitizer_unwind_posix.cc ----------------------------------------===// +//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===// // // The LLVM Compiler Infrastructure // @@ -8,11 +8,11 @@ //===----------------------------------------------------------------------===// // // This file contains the unwind.h-based (aka "slow") stack unwinding routines -// available to the tools on Linux, Android, FreeBSD and OS X. +// available to the tools on Linux, Android, and FreeBSD. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" -#if SANITIZER_POSIX +#if SANITIZER_FREEBSD || SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_stacktrace.h" @@ -82,7 +82,7 @@ void SanitizerInitializeUnwinder() { #endif uptr Unwind_GetIP(struct _Unwind_Context *ctx) { -#ifdef __arm__ +#if defined(__arm__) && !SANITIZER_MAC uptr val; _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, 15 /* r15 = PC */, _UVRSD_UINT32, &val); @@ -155,4 +155,4 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, } // namespace __sanitizer -#endif // SANITIZER_POSIX +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/lib/sanitizer_common/sanitizer_win.cc b/lib/sanitizer_common/sanitizer_win.cc index 335cecabe118..d5bbe4565068 100644 --- a/lib/sanitizer_common/sanitizer_win.cc +++ b/lib/sanitizer_common/sanitizer_win.cc @@ -35,7 +35,9 @@ namespace __sanitizer { // --------------------- sanitizer_common.h uptr GetPageSize() { - return 1U << 14; // FIXME: is this configurable? + // FIXME: there is an API for getting the system page size (GetSystemInfo or + // GetNativeSystemInfo), but if we use it here we get test failures elsewhere. + return 1U << 14; } uptr GetMmapGranularity() { @@ -93,6 +95,9 @@ void *MmapOrDie(uptr size, const char *mem_type) { } void UnmapOrDie(void *addr, uptr size) { + if (!size || !addr) + return; + if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { Report("ERROR: %s failed to " "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", @@ -101,9 +106,10 @@ void UnmapOrDie(void *addr, uptr size) { } } -void *MmapFixedNoReserve(uptr fixed_addr, uptr size) { +void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { // FIXME: is this really "NoReserve"? On Win32 this does not matter much, // but on Win64 it does. + (void)name; // unsupported void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (p == 0) @@ -122,7 +128,8 @@ void *MmapNoReserveOrDie(uptr size, const char *mem_type) { return MmapOrDie(size, mem_type); } -void *Mprotect(uptr fixed_addr, uptr size) { +void *MmapNoAccess(uptr fixed_addr, uptr size, const char *name) { + (void)name; // unsupported void *res = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS); if (res == 0) @@ -132,6 +139,12 @@ void *Mprotect(uptr fixed_addr, uptr size) { return res; } +bool MprotectNoAccess(uptr addr, uptr size) { + DWORD old_protection; + return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection); +} + + void FlushUnneededShadowMemory(uptr addr, uptr size) { // This is almost useless on 32-bits. // FIXME: add madvise-analog when we move to 64-bits. @@ -157,7 +170,7 @@ void *MapFileToMemory(const char *file_name, uptr *buff_size) { UNIMPLEMENTED(); } -void *MapWritableFileToMemory(void *addr, uptr size, uptr fd, uptr offset) { +void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { UNIMPLEMENTED(); } @@ -206,76 +219,46 @@ u32 GetUid() { namespace { struct ModuleInfo { - HMODULE handle; + const char *filepath; uptr base_address; uptr end_address; }; int CompareModulesBase(const void *pl, const void *pr) { - const ModuleInfo &l = *(ModuleInfo *)pl, &r = *(ModuleInfo *)pr; - if (l.base_address < r.base_address) + const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; + if (l->base_address < r->base_address) return -1; - return l.base_address > r.base_address; + return l->base_address > r->base_address; } } // namespace #ifndef SANITIZER_GO void DumpProcessMap() { Report("Dumping process modules:\n"); - HANDLE cur_process = GetCurrentProcess(); + InternalScopedBuffer<LoadedModule> modules(kMaxNumberOfModules); + uptr num_modules = + GetListOfModules(modules.data(), kMaxNumberOfModules, nullptr); - // Query the list of modules. Start by assuming there are no more than 256 - // modules and retry if that's not sufficient. - ModuleInfo *modules; - size_t num_modules; - { - HMODULE *hmodules = 0; - uptr modules_buffer_size = sizeof(HMODULE) * 256; - DWORD bytes_required; - while (!hmodules) { - hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); - CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, - &bytes_required)); - if (bytes_required > modules_buffer_size) { - // Either there turned out to be more than 256 hmodules, or new hmodules - // could have loaded since the last try. Retry. - UnmapOrDie(hmodules, modules_buffer_size); - hmodules = 0; - modules_buffer_size = bytes_required; - } - } - - num_modules = bytes_required / sizeof(HMODULE); - modules = - (ModuleInfo *)MmapOrDie(num_modules * sizeof(ModuleInfo), __FUNCTION__); - for (size_t i = 0; i < num_modules; ++i) { - modules[i].handle = hmodules[i]; - MODULEINFO mi; - if (!GetModuleInformation(cur_process, hmodules[i], &mi, sizeof(mi))) - continue; - modules[i].base_address = (uptr)mi.lpBaseOfDll; - modules[i].end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; - } - UnmapOrDie(hmodules, modules_buffer_size); + InternalScopedBuffer<ModuleInfo> module_infos(num_modules); + for (size_t i = 0; i < num_modules; ++i) { + module_infos[i].filepath = modules[i].full_name(); + module_infos[i].base_address = modules[i].base_address(); + module_infos[i].end_address = modules[i].ranges().next()->end; } - - qsort(modules, num_modules, sizeof(ModuleInfo), CompareModulesBase); + qsort(module_infos.data(), num_modules, sizeof(ModuleInfo), + CompareModulesBase); for (size_t i = 0; i < num_modules; ++i) { - const ModuleInfo &mi = modules[i]; - char module_name[MAX_PATH]; - bool got_module_name = GetModuleFileNameA( - mi.handle, module_name, sizeof(module_name)); + const ModuleInfo &mi = module_infos[i]; if (mi.end_address != 0) { Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, - got_module_name ? module_name : "[no name]"); - } else if (got_module_name) { - Printf("\t??\?-??? %s\n", module_name); + mi.filepath[0] ? mi.filepath : "[no name]"); + } else if (mi.filepath[0]) { + Printf("\t??\?-??? %s\n", mi.filepath); } else { Printf("\t???\n"); } } - UnmapOrDie(modules, num_modules * sizeof(ModuleInfo)); } #endif @@ -288,8 +271,9 @@ void ReExec() { } void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { - (void)args; - // Nothing here for now. +#if !SANITIZER_GO + CovPrepareForSandboxing(args); +#endif } bool StackSizeIsUnlimited() { @@ -313,6 +297,14 @@ char *FindPathToBinary(const char *name) { return 0; } +bool IsPathSeparator(const char c) { + return c == '\\' || c == '/'; +} + +bool IsAbsolutePath(const char *path) { + UNIMPLEMENTED(); +} + void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } @@ -333,117 +325,132 @@ void Abort() { uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter) { - UNIMPLEMENTED(); -}; - -#ifndef SANITIZER_GO -int Atexit(void (*function)(void)) { - return atexit(function); -} -#endif + HANDLE cur_process = GetCurrentProcess(); -// ------------------ sanitizer_libc.h -uptr internal_mmap(void *addr, uptr length, int prot, int flags, - int fd, u64 offset) { - UNIMPLEMENTED(); -} + // Query the list of modules. Start by assuming there are no more than 256 + // modules and retry if that's not sufficient. + HMODULE *hmodules = 0; + uptr modules_buffer_size = sizeof(HMODULE) * 256; + DWORD bytes_required; + while (!hmodules) { + hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); + CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, + &bytes_required)); + if (bytes_required > modules_buffer_size) { + // Either there turned out to be more than 256 hmodules, or new hmodules + // could have loaded since the last try. Retry. + UnmapOrDie(hmodules, modules_buffer_size); + hmodules = 0; + modules_buffer_size = bytes_required; + } + } -uptr internal_munmap(void *addr, uptr length) { - UNIMPLEMENTED(); -} + // |num_modules| is the number of modules actually present, + // |count| is the number of modules we return. + size_t nun_modules = bytes_required / sizeof(HMODULE), + count = 0; + for (size_t i = 0; i < nun_modules && count < max_modules; ++i) { + HMODULE handle = hmodules[i]; + MODULEINFO mi; + if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi))) + continue; -uptr internal_close(fd_t fd) { - UNIMPLEMENTED(); -} + char module_name[MAX_PATH]; + bool got_module_name = + GetModuleFileNameA(handle, module_name, sizeof(module_name)); + if (!got_module_name) + module_name[0] = '\0'; + + if (filter && !filter(module_name)) + continue; + + uptr base_address = (uptr)mi.lpBaseOfDll; + uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; + LoadedModule *cur_module = &modules[count]; + cur_module->set(module_name, base_address); + // We add the whole module as one single address range. + cur_module->addAddressRange(base_address, end_address, /*executable*/ true); + count++; + } + UnmapOrDie(hmodules, modules_buffer_size); -int internal_isatty(fd_t fd) { - return _isatty(fd); -} + return count; +}; -uptr internal_open(const char *filename, int flags) { - UNIMPLEMENTED(); -} +#ifndef SANITIZER_GO +// We can't use atexit() directly at __asan_init time as the CRT is not fully +// initialized at this point. Place the functions into a vector and use +// atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers). +InternalMmapVectorNoCtor<void (*)(void)> atexit_functions; -uptr internal_open(const char *filename, int flags, u32 mode) { - UNIMPLEMENTED(); +int Atexit(void (*function)(void)) { + atexit_functions.push_back(function); + return 0; } -uptr OpenFile(const char *filename, bool write) { - UNIMPLEMENTED(); +static int RunAtexit() { + int ret = 0; + for (uptr i = 0; i < atexit_functions.size(); ++i) { + ret |= atexit(atexit_functions[i]); + } + return ret; } -uptr internal_read(fd_t fd, void *buf, uptr count) { - UNIMPLEMENTED(); -} +#pragma section(".CRT$XID", long, read) // NOLINT +static __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; +#endif -uptr internal_write(fd_t fd, const void *buf, uptr count) { - if (fd != kStderrFd) +// ------------------ sanitizer_libc.h +fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) { + if (mode != WrOnly) UNIMPLEMENTED(); - - static HANDLE output_stream = 0; - // Abort immediately if we know printing is not possible. - if (output_stream == INVALID_HANDLE_VALUE) - return 0; - - // If called for the first time, try to use stderr to output stuff, - // falling back to stdout if anything goes wrong. - bool fallback_to_stdout = false; - if (output_stream == 0) { - output_stream = GetStdHandle(STD_ERROR_HANDLE); - // We don't distinguish "no such handle" from error. - if (output_stream == 0) - output_stream = INVALID_HANDLE_VALUE; - - if (output_stream == INVALID_HANDLE_VALUE) { - // Retry with stdout? - output_stream = GetStdHandle(STD_OUTPUT_HANDLE); - if (output_stream == 0) - output_stream = INVALID_HANDLE_VALUE; - if (output_stream == INVALID_HANDLE_VALUE) - return 0; - } else { - // Successfully got an stderr handle. However, if WriteFile() fails, - // we can still try to fallback to stdout. - fallback_to_stdout = true; - } - } - - DWORD ret; - if (WriteFile(output_stream, buf, count, &ret, 0)) - return ret; - - // Re-try with stdout if using a valid stderr handle fails. - if (fallback_to_stdout) { - output_stream = GetStdHandle(STD_OUTPUT_HANDLE); - if (output_stream == 0) - output_stream = INVALID_HANDLE_VALUE; - if (output_stream != INVALID_HANDLE_VALUE) - return internal_write(fd, buf, count); - } - return 0; + fd_t res = CreateFile(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd); + CHECK(res != kStderrFd || kStderrFd == kInvalidFd); + if (res == kInvalidFd && last_error) + *last_error = GetLastError(); + return res; } -uptr internal_stat(const char *path, void *buf) { - UNIMPLEMENTED(); +void CloseFile(fd_t fd) { + CloseHandle(fd); } -uptr internal_lstat(const char *path, void *buf) { +bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, + error_t *error_p) { UNIMPLEMENTED(); } -uptr internal_fstat(fd_t fd, void *buf) { - UNIMPLEMENTED(); +bool SupportsColoredOutput(fd_t fd) { + // FIXME: support colored output. + return false; } -uptr internal_filesize(fd_t fd) { - UNIMPLEMENTED(); -} +bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, + error_t *error_p) { + CHECK(fd != kInvalidFd); -uptr internal_dup2(int oldfd, int newfd) { - UNIMPLEMENTED(); + if (fd == kStdoutFd) { + fd = GetStdHandle(STD_OUTPUT_HANDLE); + if (fd == 0) fd = kInvalidFd; + } else if (fd == kStderrFd) { + fd = GetStdHandle(STD_ERROR_HANDLE); + if (fd == 0) fd = kInvalidFd; + } + + DWORD internal_bytes_written; + if (fd == kInvalidFd || + WriteFile(fd, buff, buff_size, &internal_bytes_written, 0)) { + if (error_p) *error_p = GetLastError(); + return false; + } else { + if (bytes_written) *bytes_written = internal_bytes_written; + return true; + } } -uptr internal_readlink(const char *path, char *buf, uptr bufsize) { +bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { UNIMPLEMENTED(); } @@ -460,10 +467,6 @@ uptr internal_ftruncate(fd_t fd, uptr size) { UNIMPLEMENTED(); } -uptr internal_rename(const char *oldpath, const char *newpath) { - UNIMPLEMENTED(); -} - uptr GetRSS() { return 0; } @@ -587,7 +590,7 @@ void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); ReopenIfNecessary(); - if (length != internal_write(fd, buffer, length)) { + if (!WriteToFile(fd, buffer, length)) { // stderr may be closed, but we may be able to print to the debugger // instead. This is the case when launching a program from Visual Studio, // and the following routine should write to its console. @@ -614,10 +617,54 @@ bool IsDeadlySignal(int signum) { } bool IsAccessibleMemoryRange(uptr beg, uptr size) { - // FIXME: Actually implement this function. + SYSTEM_INFO si; + GetNativeSystemInfo(&si); + uptr page_size = si.dwPageSize; + uptr page_mask = ~(page_size - 1); + + for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask; + page <= end;) { + MEMORY_BASIC_INFORMATION info; + if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info)) + return false; + + if (info.Protect == 0 || info.Protect == PAGE_NOACCESS || + info.Protect == PAGE_EXECUTE) + return false; + + if (info.RegionSize == 0) + return false; + + page += info.RegionSize; + } + return true; } +SignalContext SignalContext::Create(void *siginfo, void *context) { + EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD*)siginfo; + CONTEXT *context_record = (CONTEXT*)context; + + uptr pc = (uptr)exception_record->ExceptionAddress; +#ifdef _WIN64 + uptr bp = (uptr)context_record->Rbp; + uptr sp = (uptr)context_record->Rsp; +#else + uptr bp = (uptr)context_record->Ebp; + uptr sp = (uptr)context_record->Esp; +#endif + uptr access_addr = exception_record->ExceptionInformation[1]; + + return SignalContext(context, access_addr, pc, sp, bp); +} + +uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { + // FIXME: Actually implement this function. + CHECK_GT(buf_len, 0); + buf[0] = 0; + return 0; +} + } // namespace __sanitizer #endif // _WIN32 diff --git a/lib/sanitizer_common/scripts/check_lint.sh b/lib/sanitizer_common/scripts/check_lint.sh index 7ed05d73563a..9108a81e26c8 100755 --- a/lib/sanitizer_common/scripts/check_lint.sh +++ b/lib/sanitizer_common/scripts/check_lint.sh @@ -17,7 +17,6 @@ fi # Filters # TODO: remove some of these filters -LLVM_LINT_FILTER=-,+whitespace COMMON_LINT_FILTER=-build/include,-build/header_guard,-legal/copyright,-whitespace/comments,-readability/casting,\ -build/namespaces ASAN_RTL_LINT_FILTER=${COMMON_LINT_FILTER},-runtime/int @@ -60,9 +59,6 @@ run_lint() { ${LITLINT} "$@" 2>>$ERROR_LOG } -run_lint ${LLVM_LINT_FILTER} --filter=${LLVM_LINT_FILTER} \ - lib/Transforms/Instrumentation/*Sanitizer.cpp & - if [ "${COMPILER_RT}" = "" ]; then COMPILER_RT=projects/compiler-rt fi diff --git a/lib/sanitizer_common/scripts/cpplint.py b/lib/sanitizer_common/scripts/cpplint.py index 742459af172f..d45c47f7ed0c 100755 --- a/lib/sanitizer_common/scripts/cpplint.py +++ b/lib/sanitizer_common/scripts/cpplint.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # Copyright (c) 2009 Google Inc. All rights reserved. # diff --git a/lib/sanitizer_common/scripts/gen_dynamic_list.py b/lib/sanitizer_common/scripts/gen_dynamic_list.py index 7bab230650cb..f055bb44ba21 100755 --- a/lib/sanitizer_common/scripts/gen_dynamic_list.py +++ b/lib/sanitizer_common/scripts/gen_dynamic_list.py @@ -14,15 +14,24 @@ # gen_dynamic_list.py libclang_rt.*san*.a [ files ... ] # #===------------------------------------------------------------------------===# +import argparse import os import re import subprocess import sys -new_delete = set(['_ZdaPv', '_ZdaPvRKSt9nothrow_t', - '_ZdlPv', '_ZdlPvRKSt9nothrow_t', - '_Znam', '_ZnamRKSt9nothrow_t', - '_Znwm', '_ZnwmRKSt9nothrow_t']) +new_delete = set([ + '_Znam', '_ZnamRKSt9nothrow_t', # operator new[](unsigned long) + '_Znwm', '_ZnwmRKSt9nothrow_t', # operator new(unsigned long) + '_Znaj', '_ZnajRKSt9nothrow_t', # operator new[](unsigned int) + '_Znwj', '_ZnwjRKSt9nothrow_t', # operator new(unsigned int) + '_ZdaPv', '_ZdaPvRKSt9nothrow_t', # operator delete[](void *) + '_ZdlPv', '_ZdlPvRKSt9nothrow_t', # operator delete(void *) + '_ZdaPvm', # operator delete[](void*, unsigned long) + '_ZdlPvm', # operator delete(void*, unsigned long) + '_ZdaPvj', # operator delete[](void*, unsigned int) + '_ZdlPvj', # operator delete(void*, unsigned int) + ]) versioned_functions = set(['memcpy', 'pthread_attr_getaffinity_np', 'pthread_cond_broadcast', @@ -49,10 +58,17 @@ def get_global_functions(library): return functions def main(argv): + parser = argparse.ArgumentParser() + parser.add_argument('--version-list', action='store_true') + parser.add_argument('--extra', default=[], action='append') + parser.add_argument('libraries', default=[], nargs='+') + args = parser.parse_args() + result = [] - library = argv[1] - all_functions = get_global_functions(library) + all_functions = [] + for library in args.libraries: + all_functions.extend(get_global_functions(library)) function_set = set(all_functions) for func in all_functions: # Export new/delete operators. @@ -66,7 +82,7 @@ def main(argv): # We have to avoid exporting the interceptors for versioned library # functions due to gold internal error. orig_name = match.group(1) - if orig_name in function_set and orig_name not in versioned_functions: + if orig_name in function_set and (args.version_list or orig_name not in versioned_functions): result.append(orig_name) continue # Export sanitizer interface functions. @@ -74,15 +90,20 @@ def main(argv): result.append(func) # Additional exported functions from files. - for fname in argv[2:]: + for fname in args.extra: f = open(fname, 'r') for line in f: result.append(line.rstrip()) # Print the resulting list in the format recognized by ld. print('{') + if args.version_list: + print('global:') result.sort() for f in result: - print(' ' + f + ';') + print(' ' + f.encode('utf-8') + ';') + if args.version_list: + print('local:') + print(' *;') print('};') if __name__ == '__main__': diff --git a/lib/sanitizer_common/scripts/litlint.py b/lib/sanitizer_common/scripts/litlint.py index 1e78448b63d4..81b89c214438 100755 --- a/lib/sanitizer_common/scripts/litlint.py +++ b/lib/sanitizer_common/scripts/litlint.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # # litlint # diff --git a/lib/sanitizer_common/scripts/sancov.py b/lib/sanitizer_common/scripts/sancov.py index 566116eb2334..a5ae9574a26a 100755 --- a/lib/sanitizer_common/scripts/sancov.py +++ b/lib/sanitizer_common/scripts/sancov.py @@ -4,40 +4,85 @@ # We need to merge these integers into a set and then # either print them (as hex) or dump them into another file. import array -import struct -import sys import bisect +import glob import os.path +import struct +import subprocess +import sys -prog_name = ""; +prog_name = "" def Usage(): print >> sys.stderr, "Usage: \n" + \ - " " + prog_name + " merge file1 [file2 ...] > output\n" \ - " " + prog_name + " print file1 [file2 ...]\n" \ - " " + prog_name + " unpack file1 [file2 ...]\n" \ - " " + prog_name + " rawunpack file1 [file2 ...]\n" + " " + prog_name + " merge FILE [FILE...] > OUTPUT\n" \ + " " + prog_name + " print FILE [FILE...]\n" \ + " " + prog_name + " unpack FILE [FILE...]\n" \ + " " + prog_name + " rawunpack FILE [FILE ...]\n" \ + " " + prog_name + " missing BINARY < LIST_OF_PCS\n" exit(1) +def CheckBits(bits): + if bits != 32 and bits != 64: + raise Exception("Wrong bitness: %d" % bits) + +def TypeCodeForBits(bits): + CheckBits(bits) + return 'L' if bits == 64 else 'I' + +kMagic32SecondHalf = 0xFFFFFF32; +kMagic64SecondHalf = 0xFFFFFF64; +kMagicFirstHalf = 0xC0BFFFFF; + +def MagicForBits(bits): + CheckBits(bits) + if sys.byteorder == 'little': + return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf] + else: + return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf] + +def ReadMagicAndReturnBitness(f, path): + magic_bytes = f.read(8) + magic_words = struct.unpack('II', magic_bytes); + bits = 0 + idx = 1 if sys.byteorder == 'little' else 0 + if magic_words[idx] == kMagicFirstHalf: + if magic_words[1-idx] == kMagic64SecondHalf: + bits = 64 + elif magic_words[1-idx] == kMagic32SecondHalf: + bits = 32 + if bits == 0: + raise Exception('Bad magic word in %s' % path) + return bits + def ReadOneFile(path): with open(path, mode="rb") as f: f.seek(0, 2) size = f.tell() f.seek(0, 0) - s = set(array.array('I', f.read(size))) - print >>sys.stderr, "%s: read %d PCs from %s" % (prog_name, size / 4, path) + if size < 8: + raise Exception('File %s is short (< 8 bytes)' % path) + bits = ReadMagicAndReturnBitness(f, path) + size -= 8 + s = array.array(TypeCodeForBits(bits), f.read(size)) + print >>sys.stderr, "%s: read %d %d-bit PCs from %s" % (prog_name, size * 8 / bits, bits, path) return s def Merge(files): s = set() for f in files: - s = s.union(ReadOneFile(f)) + s = s.union(set(ReadOneFile(f))) print >> sys.stderr, "%s: %d files merged; %d PCs total" % \ (prog_name, len(files), len(s)) return sorted(s) def PrintFiles(files): - s = Merge(files) + if len(files) > 1: + s = Merge(files) + else: # If there is just on file, print the PCs in order. + s = ReadOneFile(files[0]) + print >> sys.stderr, "%s: 1 file merged; %d PCs total" % \ + (prog_name, len(s)) for i in s: print "0x%x" % i @@ -45,7 +90,11 @@ def MergeAndPrint(files): if sys.stdout.isatty(): Usage() s = Merge(files) - a = array.array('I', s) + bits = 32 + if max(s) > 0xFFFFFFFF: + bits = 64 + array.array('I', MagicForBits(bits)).tofile(sys.stdout) + a = array.array(TypeCodeForBits(bits), s) a.tofile(sys.stdout) @@ -82,6 +131,8 @@ def UnpackOneRawFile(path, map_path): with open(map_path, mode="rt") as f_map: print >> sys.stderr, "%s: reading map %s" % (prog_name, map_path) bits = int(f_map.readline()) + if bits != 32 and bits != 64: + raise Exception('Wrong bits size in the map') for line in f_map: parts = line.rstrip().split() mem_map.append((int(parts[0], 16), @@ -97,11 +148,7 @@ def UnpackOneRawFile(path, map_path): f.seek(0, 2) size = f.tell() f.seek(0, 0) - if bits == 64: - typecode = 'L' - else: - typecode = 'I' - pcs = array.array(typecode, f.read(size)) + pcs = array.array(TypeCodeForBits(bits), f.read(size)) mem_map_pcs = [[] for i in range(0, len(mem_map))] for pc in pcs: @@ -119,9 +166,10 @@ def UnpackOneRawFile(path, map_path): assert path.endswith('.sancov.raw') dst_path = module_path + '.' + os.path.basename(path)[:-4] print >> sys.stderr, "%s: writing %d PCs to %s" % (prog_name, len(pc_list), dst_path) - arr = array.array('I') + arr = array.array(TypeCodeForBits(bits)) arr.fromlist(sorted(pc_list)) with open(dst_path, 'ab') as f2: + array.array('I', MagicForBits(bits)).tofile(f2) arr.tofile(f2) def RawUnpack(files): @@ -131,17 +179,63 @@ def RawUnpack(files): f_map = f[:-3] + 'map' UnpackOneRawFile(f, f_map) +def GetInstrumentedPCs(binary): + # This looks scary, but all it does is extract all offsets where we call: + # - __sanitizer_cov() or __sanitizer_cov_with_check(), + # - with call or callq, + # - directly or via PLT. + cmd = "objdump -d %s | " \ + "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(_with_check\|\)\(@plt\|\)>' | " \ + "grep '^\s\+[0-9a-f]\+' -o" % binary + proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, + shell=True) + proc.stdin.close() + # The PCs we get from objdump are off by 4 bytes, as they point to the + # beginning of the callq instruction. Empirically this is true on x86 and + # x86_64. + return set(int(line.strip(), 16) + 4 for line in proc.stdout) + +def PrintMissing(binary): + if not os.path.isfile(binary): + raise Exception('File not found: %s' % binary) + instrumented = GetInstrumentedPCs(binary) + print >> sys.stderr, "%s: found %d instrumented PCs in %s" % (prog_name, + len(instrumented), + binary) + covered = set(int(line, 16) for line in sys.stdin) + print >> sys.stderr, "%s: read %d PCs from stdin" % (prog_name, len(covered)) + missing = instrumented - covered + print >> sys.stderr, "%s: %d PCs missing from coverage" % (prog_name, len(missing)) + if (len(missing) > len(instrumented) - len(covered)): + print >> sys.stderr, \ + "%s: WARNING: stdin contains PCs not found in binary" % prog_name + for pc in sorted(missing): + print "0x%x" % pc + if __name__ == '__main__': prog_name = sys.argv[0] if len(sys.argv) <= 2: Usage(); + + if sys.argv[1] == "missing": + if len(sys.argv) != 3: + Usage() + PrintMissing(sys.argv[2]) + exit(0) + + file_list = [] + for f in sys.argv[2:]: + file_list += glob.glob(f) + if not file_list: + Usage() + if sys.argv[1] == "print": - PrintFiles(sys.argv[2:]) + PrintFiles(file_list) elif sys.argv[1] == "merge": - MergeAndPrint(sys.argv[2:]) + MergeAndPrint(file_list) elif sys.argv[1] == "unpack": - Unpack(sys.argv[2:]) + Unpack(file_list) elif sys.argv[1] == "rawunpack": - RawUnpack(sys.argv[2:]) + RawUnpack(file_list) else: Usage() diff --git a/lib/sanitizer_common/tests/CMakeLists.txt b/lib/sanitizer_common/tests/CMakeLists.txt index 75008db3b6c9..b0165eac25f0 100644 --- a/lib/sanitizer_common/tests/CMakeLists.txt +++ b/lib/sanitizer_common/tests/CMakeLists.txt @@ -28,6 +28,7 @@ set(SANITIZER_UNITTESTS sanitizer_stacktrace_test.cc sanitizer_stoptheworld_test.cc sanitizer_suppressions_test.cc + sanitizer_symbolizer_test.cc sanitizer_test_main.cc sanitizer_thread_registry_test.cc) @@ -50,23 +51,33 @@ set(SANITIZER_TEST_CFLAGS_COMMON -Werror=sign-compare -Wno-non-virtual-dtor) +if(MSVC) + # Disable exceptions on Windows until they work reliably. + list(APPEND SANITIZER_TEST_CFLAGS_COMMON -fno-exceptions -DGTEST_HAS_SEH=0) +endif() + # -gline-tables-only must be enough for these tests, so use it if possible. if(COMPILER_RT_TEST_COMPILER_ID MATCHES "Clang") - list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -gline-tables-only) + list(APPEND SANITIZER_TEST_CFLAGS_COMMON -gline-tables-only) else() - list(APPEND ASAN_UNITTEST_COMMON_CFLAGS -g) + list(APPEND SANITIZER_TEST_CFLAGS_COMMON -g) endif() if(NOT MSVC) list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON --driver-mode=g++) endif() +if(ANDROID) + list(APPEND SANITIZER_TEST_LINK_FLAGS_COMMON -pie) +endif() + set(SANITIZER_TEST_LINK_LIBS) append_list_if(ANDROID log SANITIZER_TEST_LINK_LIBS) # NDK r10 requires -latomic almost always. append_list_if(ANDROID atomic SANITIZER_TEST_LINK_LIBS) append_list_if(COMPILER_RT_HAS_LIBDL -ldl SANITIZER_TEST_LINK_FLAGS_COMMON) +append_list_if(COMPILER_RT_HAS_LIBRT -lrt SANITIZER_TEST_LINK_FLAGS_COMMON) append_list_if(COMPILER_RT_HAS_LIBPTHREAD -pthread SANITIZER_TEST_LINK_FLAGS_COMMON) # x86_64 FreeBSD 9.2 additionally requires libc++ to build the tests. Also, # 'libm' shall be specified explicitly to build i386 tests. diff --git a/lib/sanitizer_common/tests/sanitizer_libc_test.cc b/lib/sanitizer_common/tests/sanitizer_libc_test.cc index 8712d2c1b2a5..3252db77653d 100644 --- a/lib/sanitizer_common/tests/sanitizer_libc_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_libc_test.cc @@ -17,6 +17,7 @@ #if SANITIZER_LINUX || SANITIZER_MAC # define SANITIZER_TEST_HAS_STAT_H 1 # include <sys/stat.h> +# include "sanitizer_common/sanitizer_posix.h" #else # define SANITIZER_TEST_HAS_STAT_H 0 #endif @@ -78,16 +79,14 @@ TEST(SanitizerCommon, FileOps) { char tmpfile[128]; temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.fileops.tmp."); - uptr openrv = OpenFile(tmpfile, true); - EXPECT_FALSE(internal_iserror(openrv)); - fd_t fd = openrv; + fd_t fd = OpenFile(tmpfile, WrOnly); + ASSERT_NE(fd, kInvalidFd); EXPECT_EQ(len1, internal_write(fd, str1, len1)); EXPECT_EQ(len2, internal_write(fd, str2, len2)); - internal_close(fd); + CloseFile(fd); - openrv = OpenFile(tmpfile, false); - EXPECT_FALSE(internal_iserror(openrv)); - fd = openrv; + fd = OpenFile(tmpfile, RdOnly); + ASSERT_NE(fd, kInvalidFd); uptr fsize = internal_filesize(fd); EXPECT_EQ(len1 + len2, fsize); @@ -115,7 +114,7 @@ TEST(SanitizerCommon, FileOps) { internal_memset(buf, 0, len1); EXPECT_EQ(len2, internal_read(fd, buf, len2)); EXPECT_EQ(0, internal_memcmp(buf, str2, len2)); - internal_close(fd); + CloseFile(fd); internal_unlink(tmpfile); } #endif @@ -134,12 +133,11 @@ TEST(SanitizerCommon, InternalMmapWithOffset) { char tmpfile[128]; temp_file_name(tmpfile, sizeof(tmpfile), "sanitizer_common.internalmmapwithoffset.tmp."); - uptr res = OpenFile(tmpfile, true); - ASSERT_FALSE(internal_iserror(res)); - fd_t fd = res; + fd_t fd = OpenFile(tmpfile, RdWr); + ASSERT_NE(fd, kInvalidFd); uptr page_size = GetPageSizeCached(); - res = internal_ftruncate(fd, page_size * 2); + uptr res = internal_ftruncate(fd, page_size * 2); ASSERT_FALSE(internal_iserror(res)); res = internal_lseek(fd, page_size, SEEK_SET); @@ -154,8 +152,8 @@ TEST(SanitizerCommon, InternalMmapWithOffset) { ASSERT_EQ('A', p[0]); ASSERT_EQ('B', p[1]); - internal_close(fd); - internal_munmap(p, page_size); + CloseFile(fd); + UnmapOrDie(p, page_size); internal_unlink(tmpfile); } #endif diff --git a/lib/sanitizer_common/tests/sanitizer_posix_test.cc b/lib/sanitizer_common/tests/sanitizer_posix_test.cc index 56ce416141fa..03ca449d3622 100644 --- a/lib/sanitizer_common/tests/sanitizer_posix_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_posix_test.cc @@ -52,9 +52,9 @@ static void SpawnThread(uptr iteration) { TEST(SanitizerCommon, PthreadDestructorIterations) { ASSERT_EQ(0, pthread_key_create(&key, &destructor)); - SpawnThread(kPthreadDestructorIterations); + SpawnThread(GetPthreadDestructorIterations()); EXPECT_TRUE(destructor_executed); - SpawnThread(kPthreadDestructorIterations + 1); + SpawnThread(GetPthreadDestructorIterations() + 1); EXPECT_FALSE(destructor_executed); } diff --git a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc index abe4ef43093f..12bc9e18193a 100644 --- a/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_procmaps_test.cc @@ -37,8 +37,7 @@ TEST(MemoryMappingLayout, DumpListOfModules) { const char *binary_name = last_slash ? last_slash + 1 : argv0; MemoryMappingLayout memory_mapping(false); const uptr kMaxModules = 100; - LoadedModule *modules = - (LoadedModule *)malloc(kMaxModules * sizeof(LoadedModule)); + LoadedModule modules[kMaxModules]; uptr n_modules = memory_mapping.DumpListOfModules(modules, kMaxModules, 0); EXPECT_GT(n_modules, 0U); bool found = false; @@ -51,7 +50,6 @@ TEST(MemoryMappingLayout, DumpListOfModules) { modules[i].clear(); } EXPECT_TRUE(found); - free(modules); } } // namespace __sanitizer diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc index cc9a9edbbb46..05796fcbff77 100644 --- a/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_printer_test.cc @@ -18,20 +18,36 @@ namespace __sanitizer { TEST(SanitizerStacktracePrinter, RenderSourceLocation) { InternalScopedString str(128); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, ""); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, ""); EXPECT_STREQ("/dir/file.cc:10:5", str.data()); str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 11, 0, ""); + RenderSourceLocation(&str, "/dir/file.cc", 11, 0, false, ""); EXPECT_STREQ("/dir/file.cc:11", str.data()); str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 0, 0, ""); + RenderSourceLocation(&str, "/dir/file.cc", 0, 0, false, ""); EXPECT_STREQ("/dir/file.cc", str.data()); str.clear(); - RenderSourceLocation(&str, "/dir/file.cc", 10, 5, "/dir/"); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, false, "/dir/"); EXPECT_STREQ("file.cc:10:5", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, ""); + EXPECT_STREQ("/dir/file.cc(10,5)", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 11, 0, true, ""); + EXPECT_STREQ("/dir/file.cc(11)", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 0, 0, true, ""); + EXPECT_STREQ("/dir/file.cc", str.data()); + + str.clear(); + RenderSourceLocation(&str, "/dir/file.cc", 10, 5, true, "/dir/"); + EXPECT_STREQ("file.cc(10,5)", str.data()); } TEST(SanitizerStacktracePrinter, RenderModuleLocation) { @@ -62,7 +78,7 @@ TEST(SanitizerStacktracePrinter, RenderFrame) { RenderFrame(&str, "%% Frame:%n PC:%p Module:%m ModuleOffset:%o " "Function:%f FunctionOffset:%q Source:%s Line:%l " "Column:%c", - frame_no, info, "/path/to/", "function_"); + frame_no, info, false, "/path/to/", "function_"); EXPECT_STREQ("% Frame:42 PC:0x400000 Module:my/module ModuleOffset:0x200 " "Function:foo FunctionOffset:0x100 Source:my/source Line:10 " "Column:5", @@ -72,50 +88,64 @@ TEST(SanitizerStacktracePrinter, RenderFrame) { // Test special format specifiers. info.address = 0x400000; - RenderFrame(&str, "%M", frame_no, info); + RenderFrame(&str, "%M", frame_no, info, false); EXPECT_NE(nullptr, internal_strstr(str.data(), "400000")); str.clear(); - RenderFrame(&str, "%L", frame_no, info); + RenderFrame(&str, "%L", frame_no, info, false); EXPECT_STREQ("(<unknown module>)", str.data()); str.clear(); info.module = internal_strdup("/path/to/module"); info.module_offset = 0x200; - RenderFrame(&str, "%M", frame_no, info); + RenderFrame(&str, "%M", frame_no, info, false); EXPECT_NE(nullptr, internal_strstr(str.data(), "(module+0x")); EXPECT_NE(nullptr, internal_strstr(str.data(), "200")); str.clear(); - RenderFrame(&str, "%L", frame_no, info); + RenderFrame(&str, "%L", frame_no, info, false); EXPECT_STREQ("(/path/to/module+0x200)", str.data()); str.clear(); info.function = internal_strdup("my_function"); - RenderFrame(&str, "%F", frame_no, info); + RenderFrame(&str, "%F", frame_no, info, false); EXPECT_STREQ("in my_function", str.data()); str.clear(); info.function_offset = 0x100; - RenderFrame(&str, "%F %S", frame_no, info); + RenderFrame(&str, "%F %S", frame_no, info, false); EXPECT_STREQ("in my_function+0x100 <null>", str.data()); str.clear(); info.file = internal_strdup("my_file"); - RenderFrame(&str, "%F %S", frame_no, info); + RenderFrame(&str, "%F %S", frame_no, info, false); EXPECT_STREQ("in my_function my_file", str.data()); str.clear(); info.line = 10; - RenderFrame(&str, "%F %S", frame_no, info); + RenderFrame(&str, "%F %S", frame_no, info, false); EXPECT_STREQ("in my_function my_file:10", str.data()); str.clear(); info.column = 5; - RenderFrame(&str, "%S %L", frame_no, info); + RenderFrame(&str, "%S %L", frame_no, info, false); EXPECT_STREQ("my_file:10:5 my_file:10:5", str.data()); str.clear(); + RenderFrame(&str, "%S %L", frame_no, info, true); + EXPECT_STREQ("my_file(10,5) my_file(10,5)", str.data()); + str.clear(); + + info.column = 0; + RenderFrame(&str, "%F %S", frame_no, info, true); + EXPECT_STREQ("in my_function my_file(10)", str.data()); + str.clear(); + + info.line = 0; + RenderFrame(&str, "%F %S", frame_no, info, true); + EXPECT_STREQ("in my_function my_file", str.data()); + str.clear(); + info.Clear(); } diff --git a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc index ac820c25a0f8..654ea1db82fb 100644 --- a/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stacktrace_test.cc @@ -30,11 +30,11 @@ class FastUnwindTest : public ::testing::Test { } void *mapping; - uptr *fake_stack; + uhwptr *fake_stack; const uptr fake_stack_size = 10; - uptr start_pc; - uptr fake_top; - uptr fake_bottom; + uhwptr start_pc; + uhwptr fake_top; + uhwptr fake_bottom; BufferedStackTrace trace; }; @@ -45,10 +45,10 @@ static uptr PC(uptr idx) { void FastUnwindTest::SetUp() { size_t ps = GetPageSize(); mapping = MmapOrDie(2 * ps, "FastUnwindTest"); - Mprotect((uptr)mapping, ps); + MprotectNoAccess((uptr)mapping, ps); // Unwinder may peek 1 word down from the starting FP. - fake_stack = (uptr *)((uptr)mapping + ps + sizeof(uptr)); + fake_stack = (uhwptr *)((uptr)mapping + ps + sizeof(uhwptr)); // Fill an array of pointers with fake fp+retaddr pairs. Frame pointers have // even indices. @@ -57,12 +57,12 @@ void FastUnwindTest::SetUp() { fake_stack[i+1] = PC(i + 1); // retaddr } // Mark the last fp point back up to terminate the stack trace. - fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uptr)&fake_stack[0]; + fake_stack[RoundDownTo(fake_stack_size - 1, 2)] = (uhwptr)&fake_stack[0]; // Top is two slots past the end because FastUnwindStack subtracts two. - fake_top = (uptr)&fake_stack[fake_stack_size + 2]; + fake_top = (uhwptr)&fake_stack[fake_stack_size + 2]; // Bottom is one slot before the start because FastUnwindStack uses >. - fake_bottom = (uptr)mapping; + fake_bottom = (uhwptr)mapping; start_pc = PC(0); } @@ -85,7 +85,7 @@ TEST_F(FastUnwindTest, Basic) { // From: http://code.google.com/p/address-sanitizer/issues/detail?id=162 TEST_F(FastUnwindTest, FramePointerLoop) { // Make one fp point to itself. - fake_stack[4] = (uptr)&fake_stack[4]; + fake_stack[4] = (uhwptr)&fake_stack[4]; if (!TryFastUnwind(kStackTraceMax)) return; // Should get all on-stack retaddrs up to the 4th slot and start_pc. @@ -114,7 +114,7 @@ TEST_F(FastUnwindTest, OneFrameStackTrace) { return; EXPECT_EQ(1U, trace.size); EXPECT_EQ(start_pc, trace.trace[0]); - EXPECT_EQ((uptr)&fake_stack[0], trace.top_frame_bp); + EXPECT_EQ((uhwptr)&fake_stack[0], trace.top_frame_bp); } TEST_F(FastUnwindTest, ZeroFramesStackTrace) { @@ -127,7 +127,7 @@ TEST_F(FastUnwindTest, ZeroFramesStackTrace) { TEST_F(FastUnwindTest, FPBelowPrevFP) { // The next FP points to unreadable memory inside the stack limits, but below // current FP. - fake_stack[0] = (uptr)&fake_stack[-50]; + fake_stack[0] = (uhwptr)&fake_stack[-50]; fake_stack[1] = PC(1); if (!TryFastUnwind(3)) return; diff --git a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc index b6786ba8b013..802af392c609 100644 --- a/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc +++ b/lib/sanitizer_common/tests/sanitizer_stoptheworld_test.cc @@ -189,6 +189,16 @@ TEST(StopTheWorld, SuspendThreadsAdvanced) { pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex); } +static void SegvCallback(const SuspendedThreadsList &suspended_threads_list, + void *argument) { + *(volatile int*)0x1234 = 0; +} + +TEST(StopTheWorld, SegvInCallback) { + // Test that tracer thread catches SIGSEGV. + StopTheWorld(&SegvCallback, NULL); +} + } // namespace __sanitizer #endif // SANITIZER_LINUX && defined(__x86_64__) diff --git a/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc b/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc new file mode 100644 index 000000000000..429ac591e502 --- /dev/null +++ b/lib/sanitizer_common/tests/sanitizer_symbolizer_test.cc @@ -0,0 +1,58 @@ +//===-- sanitizer_symbolizer_test.cc --------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests for sanitizer_symbolizer.h and sanitizer_symbolizer_internal.h +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_symbolizer_internal.h" +#include "gtest/gtest.h" + +namespace __sanitizer { + +TEST(Symbolizer, ExtractToken) { + char *token; + const char *rest; + + rest = ExtractToken("a;b;c", ";", &token); + EXPECT_STREQ("a", token); + EXPECT_STREQ("b;c", rest); + InternalFree(token); + + rest = ExtractToken("aaa-bbb.ccc", ";.-*", &token); + EXPECT_STREQ("aaa", token); + EXPECT_STREQ("bbb.ccc", rest); + InternalFree(token); +} + +TEST(Symbolizer, ExtractInt) { + int token; + const char *rest = ExtractInt("123,456;789", ";,", &token); + EXPECT_EQ(123, token); + EXPECT_STREQ("456;789", rest); +} + +TEST(Symbolizer, ExtractUptr) { + uptr token; + const char *rest = ExtractUptr("123,456;789", ";,", &token); + EXPECT_EQ(123U, token); + EXPECT_STREQ("456;789", rest); +} + +TEST(Symbolizer, ExtractTokenUpToDelimiter) { + char *token; + const char *rest = + ExtractTokenUpToDelimiter("aaa-+-bbb-+-ccc", "-+-", &token); + EXPECT_STREQ("aaa", token); + EXPECT_STREQ("bbb-+-ccc", rest); + InternalFree(token); +} + +} // namespace __sanitizer diff --git a/lib/tsan/CMakeLists.txt b/lib/tsan/CMakeLists.txt index 9f6d63041a68..90137800ffac 100644 --- a/lib/tsan/CMakeLists.txt +++ b/lib/tsan/CMakeLists.txt @@ -41,6 +41,9 @@ set(TSAN_SOURCES rtl/tsan_symbolize.cc rtl/tsan_sync.cc) +set(TSAN_CXX_SOURCES + rtl/tsan_new_delete.cc) + if(APPLE) list(APPEND TSAN_SOURCES rtl/tsan_platform_mac.cc) elseif(UNIX) @@ -57,6 +60,7 @@ set(TSAN_HEADERS rtl/tsan_flags.h rtl/tsan_flags.inc rtl/tsan_ignoreset.h + rtl/tsan_interceptors.h rtl/tsan_interface_ann.h rtl/tsan_interface.h rtl/tsan_interface_inl.h @@ -78,45 +82,50 @@ set(TSAN_HEADERS set(TSAN_RUNTIME_LIBRARIES) add_custom_target(tsan) -# TSan is currently supported on 64-bit Linux only. -if(UNIX AND NOT APPLE) - foreach(arch ${TSAN_SUPPORTED_ARCH}) - if(arch STREQUAL "x86_64") - set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) - # Pass ASM file directly to the C++ compiler. - set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES - LANGUAGE C) - # Sanity check for Go runtime. - set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh) - add_custom_target(GotsanRuntimeCheck - COMMAND CC=${CMAKE_C_COMPILER} IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT} - DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go - COMMENT "Checking TSan Go runtime..." - VERBATIM) - else() - set(TSAN_ASM_SOURCES) - endif() - add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC - SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} - $<TARGET_OBJECTS:RTInterception.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> - $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${TSAN_RTL_CFLAGS} - DEFS ${TSAN_COMMON_DEFINITIONS}) - list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch}) - add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) - add_dependencies(tsan clang_rt.tsan-${arch} - clang_rt.tsan-${arch}-symbols) - endforeach() -endif() -add_dependencies(compiler-rt tsan) +foreach(arch ${TSAN_SUPPORTED_ARCH}) + if(arch STREQUAL "x86_64") + set(TSAN_ASM_SOURCES rtl/tsan_rtl_amd64.S) + # Pass ASM file directly to the C++ compiler. + set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES + LANGUAGE C) + # Sanity check for Go runtime. + set(BUILDGO_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/go/buildgo.sh) + add_custom_target(GotsanRuntimeCheck + COMMAND env "CC=${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" + IN_TMPDIR=1 SILENT=1 ${BUILDGO_SCRIPT} + DEPENDS clang_rt.tsan-${arch} ${BUILDGO_SCRIPT} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/go + COMMENT "Checking TSan Go runtime..." + VERBATIM) + else() + set(TSAN_ASM_SOURCES) + endif() + add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC + SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} + $<TARGET_OBJECTS:RTInterception.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}> + CFLAGS ${TSAN_RTL_CFLAGS}) + add_compiler_rt_runtime(clang_rt.tsan_cxx-${arch} ${arch} STATIC + SOURCES ${TSAN_CXX_SOURCES} + $<TARGET_OBJECTS:RTUbsan_cxx.${arch}> + CFLAGS ${TSAN_RTL_CFLAGS}) + list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch} + clang_rt.tsan_cxx-${arch}) + add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) + add_sanitizer_rt_symbols(clang_rt.tsan_cxx-${arch} rtl/tsan.syms.extra) + add_dependencies(tsan clang_rt.tsan-${arch} + clang_rt.tsan_cxx-${arch} + clang_rt.tsan-${arch}-symbols + clang_rt.tsan_cxx-${arch}-symbols) +endforeach() +add_dependencies(compiler-rt tsan) # Build libcxx instrumented with TSan. -if(TSAN_SUPPORTED_ARCH AND - COMPILER_RT_HAS_LIBCXX_SOURCES AND +if(COMPILER_RT_HAS_LIBCXX_SOURCES AND COMPILER_RT_TEST_COMPILER_ID STREQUAL "Clang") set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/libcxx_tsan) add_custom_libcxx(libcxx_tsan ${LIBCXX_PREFIX} diff --git a/lib/tsan/Makefile.old b/lib/tsan/Makefile.old index 9e0693fa04c4..b2ac912d741d 100644 --- a/lib/tsan/Makefile.old +++ b/lib/tsan/Makefile.old @@ -1,7 +1,8 @@ DEBUG=0 LDFLAGS=-ldl -lrt -lpthread -pie CXXFLAGS = -std=c++11 -fPIE -fno-rtti -g -Wall -Werror \ - -DGTEST_HAS_RTTI=0 -DSANITIZER_DEBUG=$(DEBUG) + -DGTEST_HAS_RTTI=0 -DSANITIZER_DEBUG=$(DEBUG) \ + -DTSAN_CONTAINS_UBSAN=0 CLANG=clang FILECHECK=FileCheck # Silence warnings that Clang produces for gtest code. @@ -11,7 +12,9 @@ ifeq ($(DEBUG), 0) CXXFLAGS += -O3 endif ifeq ($(CXX), $(CLANG)++) - CXXFLAGS+= -Wno-unused-private-field -Wno-static-in-inline -Wgnu + CXXFLAGS += -Wno-unused-private-field -Wno-static-in-inline -Wgnu +else + CXXFLAGS += -Wno-maybe-uninitialized endif LIBTSAN=rtl/libtsan.a diff --git a/lib/tsan/dd/CMakeLists.txt b/lib/tsan/dd/CMakeLists.txt index 981c1fb0bb5e..8130e09c27e8 100644 --- a/lib/tsan/dd/CMakeLists.txt +++ b/lib/tsan/dd/CMakeLists.txt @@ -12,6 +12,7 @@ set(DD_SOURCES set(DD_LINKLIBS) append_list_if(COMPILER_RT_HAS_LIBDL dl DD_LINKLIBS) +append_list_if(COMPILER_RT_HAS_LIBRT rt DD_LINKLIBS) append_list_if(COMPILER_RT_HAS_LIBPTHREAD pthread DD_LINKLIBS) add_custom_target(dd) @@ -26,7 +27,8 @@ if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE AND NOT ANDROID) CFLAGS ${DD_CFLAGS}) add_dependencies(dd clang_rt.dd-${arch}) - add_compiler_rt_object_library(RTDD ${arch} + add_compiler_rt_object_libraries(RTDD + ARCHS ${arch} SOURCES ${DD_SOURCES} CFLAGS ${DD_CFLAGS}) add_compiler_rt_runtime(clang_rt.dyndd-${arch} ${arch} SHARED diff --git a/lib/tsan/dd/dd_rtl.h b/lib/tsan/dd/dd_rtl.h index bb1b20205b54..9abf17da48c8 100644 --- a/lib/tsan/dd/dd_rtl.h +++ b/lib/tsan/dd/dd_rtl.h @@ -35,7 +35,7 @@ struct Callback : DDCallback { Thread *thr; Callback(Thread *thr); - virtual u32 Unwind(); + u32 Unwind() override; }; typedef AddrHashMap<Mutex, 31051> MutexHashMap; diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh index 5ac60349e081..7193b57684f2 100755 --- a/lib/tsan/go/buildgo.sh +++ b/lib/tsan/go/buildgo.sh @@ -36,7 +36,7 @@ SRCS=" if [ "`uname -a | grep Linux`" != "" ]; then SUFFIX="linux_amd64" OSCFLAGS="-fPIC -ffreestanding -Wno-maybe-uninitialized -Wno-unused-const-variable -Werror -Wno-unknown-warning-option" - OSLDFLAGS="-lpthread -fPIC -fpie" + OSLDFLAGS="-lpthread -lrt -fPIC -fpie" SRCS=" $SRCS ../rtl/tsan_platform_linux.cc diff --git a/lib/tsan/rtl/Makefile.old b/lib/tsan/rtl/Makefile.old index 150b376343be..ee7095e88d96 100644 --- a/lib/tsan/rtl/Makefile.old +++ b/lib/tsan/rtl/Makefile.old @@ -1,4 +1,4 @@ -CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DSANITIZER_DEBUG=$(DEBUG) +CXXFLAGS = -std=c++11 -fPIE -g -Wall -Werror -fno-builtin -msse3 -DSANITIZER_DEBUG=$(DEBUG) -DTSAN_CONTAINS_UBSAN=0 CLANG=clang ifeq ($(DEBUG), 0) CXXFLAGS += -O3 @@ -15,6 +15,7 @@ CXXFLAGS+=$(CFLAGS) ifeq ($(DEBUG), 0) CXXFLAGS+=-fomit-frame-pointer ifeq ($(CXX), g++) + CXXFLAGS+=-Wno-maybe-uninitialized CXXFLAGS+=-Wframe-larger-than=512 endif # CXX=g++ endif # DEBUG=0 diff --git a/lib/tsan/rtl/tsan.syms.extra b/lib/tsan/rtl/tsan.syms.extra index 49ed6b46629e..1bc1d93298d3 100644 --- a/lib/tsan/rtl/tsan.syms.extra +++ b/lib/tsan/rtl/tsan.syms.extra @@ -8,6 +8,7 @@ __tsan_java* __tsan_unaligned* __tsan_release __tsan_acquire +__ubsan_* Annotate* WTFAnnotate* RunningOnValgrind diff --git a/lib/tsan/rtl/tsan_defs.h b/lib/tsan/rtl/tsan_defs.h index 910a483127d5..d869d95e0878 100644 --- a/lib/tsan/rtl/tsan_defs.h +++ b/lib/tsan/rtl/tsan_defs.h @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_libc.h" #include "tsan_stat.h" +#include "ubsan/ubsan_platform.h" // Setup defaults for compile definitions. #ifndef TSAN_NO_HISTORY @@ -27,6 +28,10 @@ # define TSAN_COLLECT_STATS 0 #endif +#ifndef TSAN_CONTAINS_UBSAN +# define TSAN_CONTAINS_UBSAN (CAN_SANITIZE_UB && !defined(SANITIZER_GO)) +#endif + namespace __tsan { #ifdef SANITIZER_GO diff --git a/lib/tsan/rtl/tsan_fd.cc b/lib/tsan/rtl/tsan_fd.cc index d18502f00540..d84df4a6413c 100644 --- a/lib/tsan/rtl/tsan_fd.cc +++ b/lib/tsan/rtl/tsan_fd.cc @@ -91,7 +91,8 @@ static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { } // pd must be already ref'ed. -static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) { +static void init(ThreadState *thr, uptr pc, int fd, FdSync *s, + bool write = true) { FdDesc *d = fddesc(thr, pc, fd); // As a matter of fact, we don't intercept all close calls. // See e.g. libc __res_iclose(). @@ -109,8 +110,13 @@ static void init(ThreadState *thr, uptr pc, int fd, FdSync *s) { } d->creation_tid = thr->tid; d->creation_stack = CurrentStackId(thr, pc); - // To catch races between fd usage and open. - MemoryRangeImitateWrite(thr, pc, (uptr)d, 8); + if (write) { + // To catch races between fd usage and open. + MemoryRangeImitateWrite(thr, pc, (uptr)d, 8); + } else { + // See the dup-related comment in FdClose. + MemoryRead(thr, pc, (uptr)d, kSizeLog8); + } } void FdInit() { @@ -181,13 +187,25 @@ void FdAccess(ThreadState *thr, uptr pc, int fd) { MemoryRead(thr, pc, (uptr)d, kSizeLog8); } -void FdClose(ThreadState *thr, uptr pc, int fd) { +void FdClose(ThreadState *thr, uptr pc, int fd, bool write) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); if (bogusfd(fd)) return; FdDesc *d = fddesc(thr, pc, fd); - // To catch races between fd usage and close. - MemoryWrite(thr, pc, (uptr)d, kSizeLog8); + if (write) { + // To catch races between fd usage and close. + MemoryWrite(thr, pc, (uptr)d, kSizeLog8); + } else { + // This path is used only by dup2/dup3 calls. + // We do read instead of write because there is a number of legitimate + // cases where write would lead to false positives: + // 1. Some software dups a closed pipe in place of a socket before closing + // the socket (to prevent races actually). + // 2. Some daemons dup /dev/null in place of stdin/stdout. + // On the other hand we have not seen cases when write here catches real + // bugs. + MemoryRead(thr, pc, (uptr)d, kSizeLog8); + } // We need to clear it, because if we do not intercept any call out there // that creates fd, we will hit false postives. MemoryResetRange(thr, pc, (uptr)d, 8); @@ -204,15 +222,15 @@ void FdFileCreate(ThreadState *thr, uptr pc, int fd) { init(thr, pc, fd, &fdctx.filesync); } -void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { +void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); if (bogusfd(oldfd) || bogusfd(newfd)) return; // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); MemoryRead(thr, pc, (uptr)od, kSizeLog8); - FdClose(thr, pc, newfd); - init(thr, pc, newfd, ref(od->sync)); + FdClose(thr, pc, newfd, write); + init(thr, pc, newfd, ref(od->sync), write); } void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { diff --git a/lib/tsan/rtl/tsan_fd.h b/lib/tsan/rtl/tsan_fd.h index 75c616dae0fd..64dc84f6edd6 100644 --- a/lib/tsan/rtl/tsan_fd.h +++ b/lib/tsan/rtl/tsan_fd.h @@ -42,9 +42,9 @@ void FdInit(); void FdAcquire(ThreadState *thr, uptr pc, int fd); void FdRelease(ThreadState *thr, uptr pc, int fd); void FdAccess(ThreadState *thr, uptr pc, int fd); -void FdClose(ThreadState *thr, uptr pc, int fd); +void FdClose(ThreadState *thr, uptr pc, int fd, bool write = true); void FdFileCreate(ThreadState *thr, uptr pc, int fd); -void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd); +void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd, bool write); void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd); void FdEventCreate(ThreadState *thr, uptr pc, int fd); void FdSignalCreate(ThreadState *thr, uptr pc, int fd); diff --git a/lib/tsan/rtl/tsan_flags.cc b/lib/tsan/rtl/tsan_flags.cc index 1e81ef3d0003..5de227a42dee 100644 --- a/lib/tsan/rtl/tsan_flags.cc +++ b/lib/tsan/rtl/tsan_flags.cc @@ -17,6 +17,7 @@ #include "tsan_flags.h" #include "tsan_rtl.h" #include "tsan_mman.h" +#include "ubsan/ubsan_flags.h" namespace __tsan { @@ -47,15 +48,13 @@ void RegisterTsanFlags(FlagParser *parser, Flags *f) { RegisterFlag(parser, #Name, Description, &f->Name); #include "tsan_flags.inc" #undef TSAN_FLAG + // DDFlags + RegisterFlag(parser, "second_deadlock_stack", + "Report where each mutex is locked in deadlock reports", + &f->second_deadlock_stack); } void InitializeFlags(Flags *f, const char *env) { - FlagParser parser; - RegisterTsanFlags(&parser, f); - RegisterCommonFlags(&parser); - - f->SetDefaults(); - SetCommonFlagsDefaults(); { // Override some common flags defaults. @@ -70,10 +69,32 @@ void InitializeFlags(Flags *f, const char *env) { OverrideCommonFlags(cf); } + f->SetDefaults(); + + FlagParser parser; + RegisterTsanFlags(&parser, f); + RegisterCommonFlags(&parser); + +#if TSAN_CONTAINS_UBSAN + __ubsan::Flags *uf = __ubsan::flags(); + uf->SetDefaults(); + + FlagParser ubsan_parser; + __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); + RegisterCommonFlags(&ubsan_parser); +#endif + // Let a frontend override. parser.ParseString(__tsan_default_options()); +#if TSAN_CONTAINS_UBSAN + const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); + ubsan_parser.ParseString(ubsan_default_options); +#endif // Override from command line. parser.ParseString(env); +#if TSAN_CONTAINS_UBSAN + ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); +#endif // Sanity check. if (!f->report_bugs) { diff --git a/lib/tsan/rtl/tsan_interceptors.cc b/lib/tsan/rtl/tsan_interceptors.cc index 31ff7d56aac7..b1a7ae6de328 100644 --- a/lib/tsan/rtl/tsan_interceptors.cc +++ b/lib/tsan/rtl/tsan_interceptors.cc @@ -20,6 +20,7 @@ #include "sanitizer_common/sanitizer_placement_new.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "interception/interception.h" +#include "tsan_interceptors.h" #include "tsan_interface.h" #include "tsan_platform.h" #include "tsan_suppressions.h" @@ -31,10 +32,8 @@ using namespace __tsan; // NOLINT #if SANITIZER_FREEBSD #define __errno_location __error -#define __libc_malloc __malloc #define __libc_realloc __realloc #define __libc_calloc __calloc -#define __libc_free __free #define stdout __stdoutp #define stderr __stderrp #endif @@ -78,10 +77,8 @@ extern "C" void *pthread_self(); extern "C" void _exit(int status); extern "C" int *__errno_location(); extern "C" int fileno_unlocked(void *stream); -extern "C" void *__libc_malloc(uptr size); extern "C" void *__libc_calloc(uptr size, uptr n); extern "C" void *__libc_realloc(void *ptr, uptr size); -extern "C" void __libc_free(void *ptr); extern "C" int dirfd(void *dirp); #if !SANITIZER_FREEBSD extern "C" int mallopt(int param, int value); @@ -145,7 +142,10 @@ struct sigaction_t { const sighandler_t SIG_DFL = (sighandler_t)0; const sighandler_t SIG_IGN = (sighandler_t)1; const sighandler_t SIG_ERR = (sighandler_t)-1; -#ifdef __mips__ +#if SANITIZER_FREEBSD +const int SA_SIGINFO = 0x40; +const int SIG_SETMASK = 3; +#elif defined(__mips__) const int SA_SIGINFO = 8; const int SIG_SETMASK = 3; #else @@ -153,9 +153,8 @@ const int SA_SIGINFO = 4; const int SIG_SETMASK = 2; #endif -namespace std { -struct nothrow_t {}; -} // namespace std +#define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED \ + (!cur_thread()->is_inited) static sigaction_t sigactions[kSigCount]; @@ -167,7 +166,7 @@ struct SignalDesc { ucontext_t ctx; }; -struct SignalContext { +struct ThreadSignalContext { int int_signal_send; atomic_uintptr_t in_blocking_func; atomic_uintptr_t have_pending_signals; @@ -194,10 +193,10 @@ void InitializeLibIgnore() { } // namespace __tsan -static SignalContext *SigCtx(ThreadState *thr) { - SignalContext *ctx = (SignalContext*)thr->signal_ctx; +static ThreadSignalContext *SigCtx(ThreadState *thr) { + ThreadSignalContext *ctx = (ThreadSignalContext*)thr->signal_ctx; if (ctx == 0 && !thr->is_dead) { - ctx = (SignalContext*)MmapOrDie(sizeof(*ctx), "SignalContext"); + ctx = (ThreadSignalContext*)MmapOrDie(sizeof(*ctx), "ThreadSignalContext"); MemoryResetRange(thr, (uptr)&SigCtx, (uptr)ctx, sizeof(*ctx)); thr->signal_ctx = ctx; } @@ -206,16 +205,6 @@ static SignalContext *SigCtx(ThreadState *thr) { static unsigned g_thread_finalize_key; -class ScopedInterceptor { - public: - ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc); - ~ScopedInterceptor(); - private: - ThreadState *const thr_; - const uptr pc_; - bool in_ignored_lib_; -}; - ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) : thr_(thr) @@ -245,14 +234,6 @@ ScopedInterceptor::~ScopedInterceptor() { } } -#define SCOPED_INTERCEPTOR_RAW(func, ...) \ - ThreadState *thr = cur_thread(); \ - const uptr caller_pc = GET_CALLER_PC(); \ - ScopedInterceptor si(thr, #func, caller_pc); \ - const uptr pc = StackTrace::GetCurrentPc(); \ - (void)pc; \ -/**/ - #define SCOPED_TSAN_INTERCEPTOR(func, ...) \ SCOPED_INTERCEPTOR_RAW(func, __VA_ARGS__); \ if (REAL(func) == 0) { \ @@ -271,6 +252,13 @@ ScopedInterceptor::~ScopedInterceptor() { # define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver) #endif +#define READ_STRING_OF_LEN(thr, pc, s, len, n) \ + MemoryAccessRange((thr), (pc), (uptr)(s), \ + common_flags()->strict_string_checks ? (len) + 1 : (n), false) + +#define READ_STRING(thr, pc, s, n) \ + READ_STRING_OF_LEN((thr), (pc), (s), internal_strlen(s), (n)) + #define BLOCK_REAL(name) (BlockingCall(thr), REAL(name)) struct BlockingCall { @@ -298,7 +286,7 @@ struct BlockingCall { } ThreadState *thr; - SignalContext *ctx; + ThreadSignalContext *ctx; }; TSAN_INTERCEPTOR(unsigned, sleep, unsigned sec) { @@ -410,7 +398,7 @@ static void JmpBufGarbageCollect(ThreadState *thr, uptr sp) { } static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { - if (thr->shadow_stack_pos == 0) // called from libc guts during bootstrap + if (!thr->is_inited) // called from libc guts during bootstrap return; // Cleanup old bufs. JmpBufGarbageCollect(thr, sp); @@ -419,7 +407,7 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) { buf->sp = sp; buf->mangled_sp = mangled_sp; buf->shadow_stack_pos = thr->shadow_stack_pos; - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); buf->int_signal_send = sctx ? sctx->int_signal_send : 0; buf->in_blocking_func = sctx ? atomic_load(&sctx->in_blocking_func, memory_order_relaxed) : @@ -442,7 +430,7 @@ static void LongJmp(ThreadState *thr, uptr *env) { // Unwind the stack. while (thr->shadow_stack_pos > buf->shadow_stack_pos) FuncExit(thr); - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); if (sctx) { sctx->int_signal_send = buf->int_signal_send; atomic_store(&sctx->in_blocking_func, buf->in_blocking_func, @@ -585,73 +573,6 @@ TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { return user_alloc_usable_size(p); } -#define OPERATOR_NEW_BODY(mangled_name) \ - if (cur_thread()->in_symbolizer) \ - return __libc_malloc(size); \ - void *p = 0; \ - { \ - SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ - p = user_alloc(thr, pc, size); \ - } \ - invoke_malloc_hook(p, size); \ - return p; - -SANITIZER_INTERFACE_ATTRIBUTE -void *operator new(__sanitizer::uptr size); -void *operator new(__sanitizer::uptr size) { - OPERATOR_NEW_BODY(_Znwm); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *operator new[](__sanitizer::uptr size); -void *operator new[](__sanitizer::uptr size) { - OPERATOR_NEW_BODY(_Znam); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *operator new(__sanitizer::uptr size, std::nothrow_t const&); -void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); -void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); -} - -#define OPERATOR_DELETE_BODY(mangled_name) \ - if (ptr == 0) return; \ - if (cur_thread()->in_symbolizer) \ - return __libc_free(ptr); \ - invoke_free_hook(ptr); \ - SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ - user_free(thr, pc, ptr); - -SANITIZER_INTERFACE_ATTRIBUTE -void operator delete(void *ptr) throw(); -void operator delete(void *ptr) throw() { - OPERATOR_DELETE_BODY(_ZdlPv); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void operator delete[](void *ptr) throw(); -void operator delete[](void *ptr) throw() { - OPERATOR_DELETE_BODY(_ZdaPv); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void operator delete(void *ptr, std::nothrow_t const&); -void operator delete(void *ptr, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); -} - -SANITIZER_INTERFACE_ATTRIBUTE -void operator delete[](void *ptr, std::nothrow_t const&); -void operator delete[](void *ptr, std::nothrow_t const&) { - OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); -} - TSAN_INTERCEPTOR(uptr, strlen, const char *s) { SCOPED_TSAN_INTERCEPTOR(strlen, s); uptr len = internal_strlen(s); @@ -660,15 +581,21 @@ TSAN_INTERCEPTOR(uptr, strlen, const char *s) { } TSAN_INTERCEPTOR(void*, memset, void *dst, int v, uptr size) { - SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size); - MemoryAccessRange(thr, pc, (uptr)dst, size, true); + // On FreeBSD we get here from libthr internals on thread initialization. + if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) { + SCOPED_TSAN_INTERCEPTOR(memset, dst, v, size); + MemoryAccessRange(thr, pc, (uptr)dst, size, true); + } return internal_memset(dst, v, size); } TSAN_INTERCEPTOR(void*, memcpy, void *dst, const void *src, uptr size) { - SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size); - MemoryAccessRange(thr, pc, (uptr)dst, size, true); - MemoryAccessRange(thr, pc, (uptr)src, size, false); + // On FreeBSD we get here from libthr internals on thread initialization. + if (!COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) { + SCOPED_TSAN_INTERCEPTOR(memcpy, dst, src, size); + MemoryAccessRange(thr, pc, (uptr)dst, size, true); + MemoryAccessRange(thr, pc, (uptr)src, size, false); + } return internal_memcpy(dst, src, size); } @@ -696,8 +623,9 @@ TSAN_INTERCEPTOR(void*, memmove, void *dst, void *src, uptr n) { TSAN_INTERCEPTOR(char*, strchr, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strchr, s, c); char *res = REAL(strchr)(s, c); - uptr len = res ? (char*)res - (char*)s + 1 : internal_strlen(s) + 1; - MemoryAccessRange(thr, pc, (uptr)s, len, false); + uptr len = internal_strlen(s); + uptr n = res ? (char*)res - (char*)s + 1 : len + 1; + READ_STRING_OF_LEN(thr, pc, s, len, n); return res; } @@ -705,7 +633,7 @@ TSAN_INTERCEPTOR(char*, strchrnul, char *s, int c) { SCOPED_TSAN_INTERCEPTOR(strchrnul, s, c); char *res = REAL(strchrnul)(s, c); uptr len = (char*)res - (char*)s + 1; - MemoryAccessRange(thr, pc, (uptr)s, len, false); + READ_STRING(thr, pc, s, len); return res; } @@ -731,16 +659,6 @@ TSAN_INTERCEPTOR(char*, strncpy, char *dst, char *src, uptr n) { return REAL(strncpy)(dst, src, n); } -TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { - SCOPED_TSAN_INTERCEPTOR(strstr, s1, s2); - const char *res = REAL(strstr)(s1, s2); - uptr len1 = internal_strlen(s1); - uptr len2 = internal_strlen(s2); - MemoryAccessRange(thr, pc, (uptr)s1, len1 + 1, false); - MemoryAccessRange(thr, pc, (uptr)s2, len2 + 1, false); - return res; -} - TSAN_INTERCEPTOR(char*, strdup, const char *str) { SCOPED_TSAN_INTERCEPTOR(strdup, str); // strdup will call malloc, so no instrumentation is required here. @@ -796,7 +714,11 @@ TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot, TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz); - DontNeedShadowFor((uptr)addr, sz); + if (sz != 0) { + // If sz == 0, munmap will return EINVAL and don't unmap any memory. + DontNeedShadowFor((uptr)addr, sz); + ctx->metamap.ResetRange(thr, pc, (uptr)addr, (uptr)sz); + } int res = REAL(munmap)(addr, sz); return res; } @@ -878,7 +800,7 @@ static void thread_finalize(void *v) { { ThreadState *thr = cur_thread(); ThreadFinish(thr); - SignalContext *sctx = thr->signal_ctx; + ThreadSignalContext *sctx = thr->signal_ctx; if (sctx) { thr->signal_ctx = 0; UnmapOrDie(sctx, sizeof(*sctx)); @@ -904,15 +826,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) { ScopedIgnoreInterceptors ignore; ThreadIgnoreBegin(thr, 0); if (pthread_setspecific(g_thread_finalize_key, - (void *)kPthreadDestructorIterations)) { + (void *)GetPthreadDestructorIterations())) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } ThreadIgnoreEnd(thr, 0); while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) pthread_yield(); - atomic_store(&p->tid, 0, memory_order_release); ThreadStart(thr, tid, GetTid()); + atomic_store(&p->tid, 0, memory_order_release); } void *res = callback(param); // Prevent the callback from being tail called, @@ -960,6 +882,13 @@ TSAN_INTERCEPTOR(int, pthread_create, if (res == 0) { int tid = ThreadCreate(thr, pc, *(uptr*)th, detached); CHECK_NE(tid, 0); + // Synchronization on p.tid serves two purposes: + // 1. ThreadCreate must finish before the new thread starts. + // Otherwise the new thread can call pthread_detach, but the pthread_t + // identifier is not yet registered in ThreadRegistry by ThreadCreate. + // 2. ThreadStart must finish before this thread continues. + // Otherwise, this thread can call pthread_detach and reset thr->sync + // before the new thread got a chance to acquire from it in ThreadStart. atomic_store(&p.tid, tid, memory_order_release); while (atomic_load(&p.tid, memory_order_acquire) != 0) pthread_yield(); @@ -1032,13 +961,25 @@ static void *init_cond(void *c, bool force = false) { } struct CondMutexUnlockCtx { + ScopedInterceptor *si; ThreadState *thr; uptr pc; void *m; }; static void cond_mutex_unlock(CondMutexUnlockCtx *arg) { + // pthread_cond_wait interceptor has enabled async signal delivery + // (see BlockingCall below). Disable async signals since we are running + // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run + // since the thread is cancelled, so we have to manually execute them + // (the thread still can run some user code due to pthread_cleanup_push). + ThreadSignalContext *ctx = SigCtx(arg->thr); + CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1); + atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed); MutexLock(arg->thr, arg->pc, (uptr)arg->m); + // Undo BlockingCall ctor effects. + arg->thr->ignore_interceptors--; + arg->si->~ScopedInterceptor(); } INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { @@ -1051,14 +992,19 @@ INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m); - MutexUnlock(thr, pc, (uptr)m); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); - CondMutexUnlockCtx arg = {thr, pc, m}; + MutexUnlock(thr, pc, (uptr)m); + CondMutexUnlockCtx arg = {&si, thr, pc, m}; + int res = 0; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cc. - int res = call_pthread_cancel_with_cleanup( - (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait), - cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg); + { + // Enable signal delivery while the thread is blocked. + BlockingCall bc(thr); + res = call_pthread_cancel_with_cleanup( + (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait), + cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg); + } if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); MutexLock(thr, pc, (uptr)m); @@ -1068,14 +1014,18 @@ INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime); - MutexUnlock(thr, pc, (uptr)m); MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); - CondMutexUnlockCtx arg = {thr, pc, m}; + MutexUnlock(thr, pc, (uptr)m); + CondMutexUnlockCtx arg = {&si, thr, pc, m}; + int res = 0; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cc. - int res = call_pthread_cancel_with_cleanup( - REAL(pthread_cond_timedwait), cond, m, abstime, - (void(*)(void *arg))cond_mutex_unlock, &arg); + { + BlockingCall bc(thr); + res = call_pthread_cancel_with_cleanup( + REAL(pthread_cond_timedwait), cond, m, abstime, + (void(*)(void *arg))cond_mutex_unlock, &arg); + } if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); MutexLock(thr, pc, (uptr)m); @@ -1382,6 +1332,7 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__xstat)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat) @@ -1392,9 +1343,11 @@ TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(stat, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(stat)(path, buf); #else SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__xstat)(0, path, buf); #endif } @@ -1402,6 +1355,7 @@ TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__xstat64)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64) @@ -1412,6 +1366,7 @@ TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__xstat64)(0, path, buf); } #define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64) @@ -1422,6 +1377,7 @@ TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__lxstat)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat) @@ -1432,9 +1388,11 @@ TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { #if SANITIZER_FREEBSD SCOPED_TSAN_INTERCEPTOR(lstat, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(lstat)(path, buf); #else SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__lxstat)(0, path, buf); #endif } @@ -1442,6 +1400,7 @@ TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__lxstat64)(version, path, buf); } #define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64) @@ -1452,6 +1411,7 @@ TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) { SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf); + READ_STRING(thr, pc, path, 0); return REAL(__lxstat64)(0, path, buf); } #define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64) @@ -1511,6 +1471,7 @@ TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) { TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode); + READ_STRING(thr, pc, name, 0); int fd = REAL(open)(name, flags, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1520,6 +1481,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode); + READ_STRING(thr, pc, name, 0); int fd = REAL(open64)(name, flags, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1532,6 +1494,7 @@ TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) { TSAN_INTERCEPTOR(int, creat, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat, name, mode); + READ_STRING(thr, pc, name, 0); int fd = REAL(creat)(name, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1541,6 +1504,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) { #if !SANITIZER_FREEBSD TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) { SCOPED_TSAN_INTERCEPTOR(creat64, name, mode); + READ_STRING(thr, pc, name, 0); int fd = REAL(creat64)(name, mode); if (fd >= 0) FdFileCreate(thr, pc, fd); @@ -1555,7 +1519,7 @@ TSAN_INTERCEPTOR(int, dup, int oldfd) { SCOPED_TSAN_INTERCEPTOR(dup, oldfd); int newfd = REAL(dup)(oldfd); if (oldfd >= 0 && newfd >= 0 && newfd != oldfd) - FdDup(thr, pc, oldfd, newfd); + FdDup(thr, pc, oldfd, newfd, true); return newfd; } @@ -1563,7 +1527,7 @@ TSAN_INTERCEPTOR(int, dup2, int oldfd, int newfd) { SCOPED_TSAN_INTERCEPTOR(dup2, oldfd, newfd); int newfd2 = REAL(dup2)(oldfd, newfd); if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd) - FdDup(thr, pc, oldfd, newfd2); + FdDup(thr, pc, oldfd, newfd2, false); return newfd2; } @@ -1571,7 +1535,7 @@ TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) { SCOPED_TSAN_INTERCEPTOR(dup3, oldfd, newfd, flags); int newfd2 = REAL(dup3)(oldfd, newfd, flags); if (oldfd >= 0 && newfd2 >= 0 && newfd2 != oldfd) - FdDup(thr, pc, oldfd, newfd2); + FdDup(thr, pc, oldfd, newfd2, false); return newfd2; } @@ -1940,7 +1904,7 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, } void ProcessPendingSignals(ThreadState *thr) { - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); if (sctx == 0 || atomic_load(&sctx->have_pending_signals, memory_order_relaxed) == 0) return; @@ -1948,8 +1912,8 @@ void ProcessPendingSignals(ThreadState *thr) { atomic_fetch_add(&thr->in_signal_handler, 1, memory_order_relaxed); // These are too big for stack. static THREADLOCAL __sanitizer_sigset_t emptyset, oldset; - REAL(sigfillset)(&emptyset); - pthread_sigmask(SIG_SETMASK, &emptyset, &oldset); + CHECK_EQ(0, REAL(sigfillset)(&emptyset)); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &emptyset, &oldset)); for (int sig = 0; sig < kSigCount; sig++) { SignalDesc *signal = &sctx->pending_signals[sig]; if (signal->armed) { @@ -1958,13 +1922,13 @@ void ProcessPendingSignals(ThreadState *thr) { &signal->siginfo, &signal->ctx); } } - pthread_sigmask(SIG_SETMASK, &oldset, 0); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &oldset, 0)); atomic_fetch_add(&thr->in_signal_handler, -1, memory_order_relaxed); } } // namespace __tsan -static bool is_sync_signal(SignalContext *sctx, int sig) { +static bool is_sync_signal(ThreadSignalContext *sctx, int sig) { return sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || sig == SIGABRT || sig == SIGFPE || sig == SIGPIPE || sig == SIGSYS || // If we are sending signal to ourselves, we must process it now. @@ -1974,7 +1938,7 @@ static bool is_sync_signal(SignalContext *sctx, int sig) { void ALWAYS_INLINE rtl_generic_sighandler(bool sigact, int sig, my_siginfo_t *info, void *ctx) { ThreadState *thr = cur_thread(); - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); if (sig < 0 || sig >= kSigCount) { VPrintf(1, "ThreadSanitizer: ignoring signal %d\n", sig); return; @@ -2083,7 +2047,7 @@ TSAN_INTERCEPTOR(int, sigsuspend, const __sanitizer_sigset_t *mask) { TSAN_INTERCEPTOR(int, raise, int sig) { SCOPED_TSAN_INTERCEPTOR(raise, sig); - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; sctx->int_signal_send = sig; @@ -2095,7 +2059,7 @@ TSAN_INTERCEPTOR(int, raise, int sig) { TSAN_INTERCEPTOR(int, kill, int pid, int sig) { SCOPED_TSAN_INTERCEPTOR(kill, pid, sig); - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; if (pid == (int)internal_getpid()) { @@ -2111,7 +2075,7 @@ TSAN_INTERCEPTOR(int, kill, int pid, int sig) { TSAN_INTERCEPTOR(int, pthread_kill, void *tid, int sig) { SCOPED_TSAN_INTERCEPTOR(pthread_kill, tid, sig); - SignalContext *sctx = SigCtx(thr); + ThreadSignalContext *sctx = SigCtx(thr); CHECK_NE(sctx, 0); int prev = sctx->int_signal_send; if (tid == pthread_self()) { @@ -2180,6 +2144,50 @@ TSAN_INTERCEPTOR(int, vfork, int fake) { return WRAP(fork)(fake); } +typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size, + void *data); +struct dl_iterate_phdr_data { + ThreadState *thr; + uptr pc; + dl_iterate_phdr_cb_t cb; + void *data; +}; + +static bool IsAppNotRodata(uptr addr) { + return IsAppMem(addr) && *(u64*)MemToShadow(addr) != kShadowRodata; +} + +static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, + void *data) { + dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; + // dlopen/dlclose allocate/free dynamic-linker-internal memory, which is later + // accessible in dl_iterate_phdr callback. But we don't see synchronization + // inside of dynamic linker, so we "unpoison" it here in order to not + // produce false reports. Ignoring malloc/free in dlopen/dlclose is not enough + // because some libc functions call __libc_dlopen. + if (info && IsAppNotRodata((uptr)info->dlpi_name)) + MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name, + internal_strlen(info->dlpi_name)); + int res = cbdata->cb(info, size, cbdata->data); + // Perform the check one more time in case info->dlpi_name was overwritten + // by user callback. + if (info && IsAppNotRodata((uptr)info->dlpi_name)) + MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name, + internal_strlen(info->dlpi_name)); + return res; +} + +TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) { + SCOPED_TSAN_INTERCEPTOR(dl_iterate_phdr, cb, data); + dl_iterate_phdr_data cbdata; + cbdata.thr = thr; + cbdata.pc = pc; + cbdata.cb = cb; + cbdata.data = data; + int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata); + return res; +} + static int OnExit(ThreadState *thr) { int status = Finalize(thr); FlushStreams(); @@ -2311,6 +2319,14 @@ static void HandleRecvmsg(ThreadState *thr, uptr pc, HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, msg) +#define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ + if (TsanThread *t = GetCurrentThread()) { \ + *begin = t->tls_begin(); \ + *end = t->tls_end(); \ + } else { \ + *begin = *end = 0; \ + } + #include "sanitizer_common/sanitizer_common_interceptors.inc" #define TSAN_SYSCALL() \ @@ -2490,7 +2506,6 @@ void InitializeInterceptors() { TSAN_INTERCEPT(strrchr); TSAN_INTERCEPT(strcpy); // NOLINT TSAN_INTERCEPT(strncpy); - TSAN_INTERCEPT(strstr); TSAN_INTERCEPT(strdup); TSAN_INTERCEPT(pthread_create); @@ -2606,6 +2621,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(fork); TSAN_INTERCEPT(vfork); + TSAN_INTERCEPT(dl_iterate_phdr); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); diff --git a/lib/tsan/rtl/tsan_interceptors.h b/lib/tsan/rtl/tsan_interceptors.h new file mode 100644 index 000000000000..49b79a7c5f9e --- /dev/null +++ b/lib/tsan/rtl/tsan_interceptors.h @@ -0,0 +1,37 @@ +#ifndef TSAN_INTERCEPTORS_H +#define TSAN_INTERCEPTORS_H + +#include "sanitizer_common/sanitizer_stacktrace.h" +#include "tsan_rtl.h" + +namespace __tsan { + +class ScopedInterceptor { + public: + ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc); + ~ScopedInterceptor(); + private: + ThreadState *const thr_; + const uptr pc_; + bool in_ignored_lib_; +}; + +} // namespace __tsan + +#define SCOPED_INTERCEPTOR_RAW(func, ...) \ + ThreadState *thr = cur_thread(); \ + const uptr caller_pc = GET_CALLER_PC(); \ + ScopedInterceptor si(thr, #func, caller_pc); \ + const uptr pc = StackTrace::GetCurrentPc(); \ + (void)pc; \ +/**/ + +#if SANITIZER_FREEBSD +#define __libc_free __free +#define __libc_malloc __malloc +#endif + +extern "C" void __libc_free(void *ptr); +extern "C" void *__libc_malloc(uptr size); + +#endif // TSAN_INTERCEPTORS_H diff --git a/lib/tsan/rtl/tsan_interface.cc b/lib/tsan/rtl/tsan_interface.cc index 9bc9a696363d..809d2abeb0be 100644 --- a/lib/tsan/rtl/tsan_interface.cc +++ b/lib/tsan/rtl/tsan_interface.cc @@ -38,6 +38,16 @@ void __tsan_write16(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr + 8, kSizeLog8); } +void __tsan_read16_pc(void *addr, void *pc) { + MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8); +} + +void __tsan_write16_pc(void *addr, void *pc) { + MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr + 8, kSizeLog8); +} + // __tsan_unaligned_read/write calls are emitted by compiler. void __tsan_unaligned_read2(const void *addr) { diff --git a/lib/tsan/rtl/tsan_interface.h b/lib/tsan/rtl/tsan_interface.h index a05e6f0f6d09..41e084b82ab1 100644 --- a/lib/tsan/rtl/tsan_interface.h +++ b/lib/tsan/rtl/tsan_interface.h @@ -51,6 +51,18 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write4(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write8(void *addr); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_unaligned_write16(void *addr); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read1_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read2_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read4_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read8_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_read16_pc(void *addr, void *pc); + +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write1_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write2_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write4_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write8_pc(void *addr, void *pc); +SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write16_pc(void *addr, void *pc); + SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_read(void **vptr_p); SANITIZER_INTERFACE_ATTRIBUTE void __tsan_vptr_update(void **vptr_p, void *new_val); diff --git a/lib/tsan/rtl/tsan_interface_inl.h b/lib/tsan/rtl/tsan_interface_inl.h index 0187e49d96e5..8852aa348b8e 100644 --- a/lib/tsan/rtl/tsan_interface_inl.h +++ b/lib/tsan/rtl/tsan_interface_inl.h @@ -50,6 +50,38 @@ void __tsan_write8(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); } +void __tsan_read1_pc(void *addr, void *pc) { + MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1); +} + +void __tsan_read2_pc(void *addr, void *pc) { + MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2); +} + +void __tsan_read4_pc(void *addr, void *pc) { + MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4); +} + +void __tsan_read8_pc(void *addr, void *pc) { + MemoryRead(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); +} + +void __tsan_write1_pc(void *addr, void *pc) { + MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog1); +} + +void __tsan_write2_pc(void *addr, void *pc) { + MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog2); +} + +void __tsan_write4_pc(void *addr, void *pc) { + MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog4); +} + +void __tsan_write8_pc(void *addr, void *pc) { + MemoryWrite(cur_thread(), (uptr)pc, (uptr)addr, kSizeLog8); +} + void __tsan_vptr_update(void **vptr_p, void *new_val) { CHECK_EQ(sizeof(vptr_p), 8); if (*vptr_p != new_val) { diff --git a/lib/tsan/rtl/tsan_mman.cc b/lib/tsan/rtl/tsan_mman.cc index ebb3f77fb992..12a616ff5ae8 100644 --- a/lib/tsan/rtl/tsan_mman.cc +++ b/lib/tsan/rtl/tsan_mman.cc @@ -36,6 +36,23 @@ struct MapUnmapCallback { // We are about to unmap a chunk of user memory. // Mark the corresponding shadow memory as not needed. DontNeedShadowFor(p, size); + // Mark the corresponding meta shadow memory as not needed. + // Note the block does not contain any meta info at this point + // (this happens after free). + const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize; + const uptr kPageSize = GetPageSizeCached() * kMetaRatio; + // Block came from LargeMmapAllocator, so must be large. + // We rely on this in the calculations below. + CHECK_GE(size, 2 * kPageSize); + uptr diff = RoundUp(p, kPageSize) - p; + if (diff != 0) { + p += diff; + size -= diff; + } + diff = p + size - RoundDown(p + size, kPageSize); + if (diff != 0) + size -= diff; + FlushUnneededShadowMemory((uptr)MemToMeta(p), size / kMetaRatio); } }; diff --git a/lib/tsan/rtl/tsan_new_delete.cc b/lib/tsan/rtl/tsan_new_delete.cc new file mode 100644 index 000000000000..2d9d044e42fa --- /dev/null +++ b/lib/tsan/rtl/tsan_new_delete.cc @@ -0,0 +1,88 @@ +//===-- tsan_new_delete.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 ThreadSanitizer (TSan), a race detector. +// +// Interceptors for operators new and delete. +//===----------------------------------------------------------------------===// +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "tsan_interceptors.h" + +using namespace __tsan; // NOLINT + +namespace std { +struct nothrow_t {}; +} // namespace std + +#define OPERATOR_NEW_BODY(mangled_name) \ + if (cur_thread()->in_symbolizer) \ + return __libc_malloc(size); \ + void *p = 0; \ + { \ + SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ + p = user_alloc(thr, pc, size); \ + } \ + invoke_malloc_hook(p, size); \ + return p; + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size); +void *operator new(__sanitizer::uptr size) { + OPERATOR_NEW_BODY(_Znwm); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size); +void *operator new[](__sanitizer::uptr size) { + OPERATOR_NEW_BODY(_Znam); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new(__sanitizer::uptr size, std::nothrow_t const&); +void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); +void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); +} + +#define OPERATOR_DELETE_BODY(mangled_name) \ + if (ptr == 0) return; \ + if (cur_thread()->in_symbolizer) \ + return __libc_free(ptr); \ + invoke_free_hook(ptr); \ + SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ + user_free(thr, pc, ptr); + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr) throw(); +void operator delete(void *ptr) throw() { + OPERATOR_DELETE_BODY(_ZdlPv); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr) throw(); +void operator delete[](void *ptr) throw() { + OPERATOR_DELETE_BODY(_ZdaPv); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete(void *ptr, std::nothrow_t const&); +void operator delete(void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); +} + +SANITIZER_INTERFACE_ATTRIBUTE +void operator delete[](void *ptr, std::nothrow_t const&); +void operator delete[](void *ptr, std::nothrow_t const&) { + OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); +} diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index 659e8d8a8345..1309058210ce 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -18,6 +18,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_stackdepot.h" @@ -135,7 +136,7 @@ static void ProtectRange(uptr beg, uptr end) { CHECK_LE(beg, end); if (beg == end) return; - if (beg != (uptr)Mprotect(beg, end - beg)) { + if (beg != (uptr)MmapNoAccess(beg, end - beg)) { Printf("FATAL: ThreadSanitizer can not protect [%zx,%zx]\n", beg, end); Printf("FATAL: Make sure you are not using unlimited stack\n"); Die(); @@ -201,8 +202,8 @@ static void MapRodata() { void InitializeShadowMemory() { // Map memory shadow. - uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg, - kShadowEnd - kShadowBeg); + uptr shadow = + (uptr)MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg, "shadow"); if (shadow != kShadowBeg) { Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); Printf("FATAL: Make sure to compile with -fPIE and " @@ -223,6 +224,10 @@ void InitializeShadowMemory() { #endif NoHugePagesInRegion(MemToShadow(kMadviseRangeBeg), kMadviseRangeSize * kShadowMultiplier); + // Meta shadow is compressing and we don't flush it, + // so it makes sense to mark it as NOHUGEPAGE to not over-allocate memory. + // On one program it reduces memory consumption from 5GB to 2.5GB. + NoHugePagesInRegion(kMetaShadowBeg, kMetaShadowEnd - kMetaShadowBeg); if (common_flags()->use_madv_dontdump) DontDumpShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg); DPrintf("memory shadow: %zx-%zx (%zuGB)\n", @@ -231,7 +236,8 @@ void InitializeShadowMemory() { // Map meta shadow. uptr meta_size = kMetaShadowEnd - kMetaShadowBeg; - uptr meta = (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size); + uptr meta = + (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size, "meta shadow"); if (meta != kMetaShadowBeg) { Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n"); Printf("FATAL: Make sure to compile with -fPIE and " @@ -394,6 +400,8 @@ int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) { return res; } +// Note: this function runs with async signals enabled, +// so it must not touch any tsan state. int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg) { diff --git a/lib/tsan/rtl/tsan_platform_mac.cc b/lib/tsan/rtl/tsan_platform_mac.cc index 63f1748e13ce..b72d9b07ef35 100644 --- a/lib/tsan/rtl/tsan_platform_mac.cc +++ b/lib/tsan/rtl/tsan_platform_mac.cc @@ -76,6 +76,8 @@ void InitializePlatform() { } #ifndef SANITIZER_GO +// Note: this function runs with async signals enabled, +// so it must not touch any tsan state. int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg) { diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc index 7e69cb4ecfbc..f4b06878a58e 100644 --- a/lib/tsan/rtl/tsan_report.cc +++ b/lib/tsan/rtl/tsan_report.cc @@ -120,6 +120,7 @@ void PrintStack(const ReportStack *ent) { for (int i = 0; frame && frame->info.address; frame = frame->next, i++) { InternalScopedString res(2 * GetPageSizeCached()); RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info, + common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix, "__interceptor_"); Printf("%s\n", res.data()); } @@ -335,10 +336,8 @@ void PrintReport(const ReportDesc *rep) { Printf(" And %d more similar thread leaks.\n\n", rep->count - 1); if (ReportStack *stack = ChooseSummaryStack(rep)) { - if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) { - const AddressInfo &info = frame->info; - ReportErrorSummary(rep_typ_str, info.file, info.line, info.function); - } + if (SymbolizedStack *frame = SkipTsanInternalFrames(stack->frames)) + ReportErrorSummary(rep_typ_str, frame->info); } Printf("==================\n"); diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index b76f3e05dde6..63c356b228a4 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -24,6 +24,7 @@ #include "tsan_mman.h" #include "tsan_suppressions.h" #include "tsan_symbolize.h" +#include "ubsan/ubsan_init.h" #ifdef __SSE3__ // <emmintrin.h> transitively includes <stdlib.h>, @@ -66,9 +67,12 @@ static char thread_registry_placeholder[sizeof(ThreadRegistry)]; static ThreadContextBase *CreateThreadContext(u32 tid) { // Map thread trace when context is created. - MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event)); + char name[50]; + internal_snprintf(name, sizeof(name), "trace %u", tid); + MapThreadTrace(GetThreadTrace(tid), TraceSize() * sizeof(Event), name); const uptr hdr = GetThreadTraceHeader(tid); - MapThreadTrace(hdr, sizeof(Trace)); + internal_snprintf(name, sizeof(name), "trace header %u", tid); + MapThreadTrace(hdr, sizeof(Trace), name); new((void*)hdr) Trace(); // We are going to use only a small part of the trace with the default // value of history_size. However, the constructor writes to the whole trace. @@ -133,7 +137,7 @@ static void MemoryProfiler(Context *ctx, fd_t fd, int i) { ctx->thread_registry->GetNumberOfThreads(&n_threads, &n_running_threads); InternalScopedBuffer<char> buf(4096); WriteMemoryProfile(buf.data(), buf.size(), n_threads, n_running_threads); - internal_write(fd, buf.data(), internal_strlen(buf.data())); + WriteToFile(fd, buf.data(), internal_strlen(buf.data())); } static void BackgroundThread(void *arg) { @@ -153,12 +157,12 @@ static void BackgroundThread(void *arg) { } else { InternalScopedString filename(kMaxPathLength); filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid()); - uptr openrv = OpenFile(filename.data(), true); - if (internal_iserror(openrv)) { + fd_t fd = OpenFile(filename.data(), WrOnly); + if (fd == kInvalidFd) { Printf("ThreadSanitizer: failed to open memory profile file '%s'\n", &filename[0]); } else { - mprof_fd = openrv; + mprof_fd = fd; } } } @@ -236,7 +240,7 @@ void MapShadow(uptr addr, uptr size) { // Global data is not 64K aligned, but there are no adjacent mappings, // so we can get away with unaligned mapping. // CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment - MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier); + MmapFixedNoReserve(MemToShadow(addr), size * kShadowMultiplier, "shadow"); // Meta shadow is 2:1, so tread carefully. static bool data_mapped = false; @@ -248,7 +252,7 @@ void MapShadow(uptr addr, uptr size) { if (!data_mapped) { // First call maps data+bss. data_mapped = true; - MmapFixedNoReserve(meta_begin, meta_end - meta_begin); + MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow"); } else { // Mapping continous heap. // Windows wants 64K alignment. @@ -258,19 +262,19 @@ void MapShadow(uptr addr, uptr size) { return; if (meta_begin < mapped_meta_end) meta_begin = mapped_meta_end; - MmapFixedNoReserve(meta_begin, meta_end - meta_begin); + MmapFixedNoReserve(meta_begin, meta_end - meta_begin, "meta shadow"); mapped_meta_end = meta_end; } VPrintf(2, "mapped meta shadow for (%p-%p) at (%p-%p)\n", addr, addr+size, meta_begin, meta_end); } -void MapThreadTrace(uptr addr, uptr size) { +void MapThreadTrace(uptr addr, uptr size, const char *name) { DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size); CHECK_GE(addr, kTraceMemBeg); CHECK_LE(addr + size, kTraceMemEnd); CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment - uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); + uptr addr1 = (uptr)MmapFixedNoReserve(addr, size, name); if (addr1 != addr) { Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", addr, size, addr1); @@ -315,6 +319,7 @@ void Initialize(ThreadState *thr) { ctx = new(ctx_placeholder) Context; const char *options = GetEnv(kTsanOptionsEnv); InitializeFlags(&ctx->flags, options); + CacheBinaryName(); #ifndef SANITIZER_GO InitializeAllocator(); #endif @@ -350,6 +355,9 @@ void Initialize(ThreadState *thr) { int tid = ThreadCreate(thr, 0, 0, true); CHECK_EQ(tid, 0); ThreadStart(thr, tid, internal_getpid()); +#if TSAN_CONTAINS_UBSAN + __ubsan::InitAsPlugin(); +#endif ctx->initialized = true; if (flags()->stop_on_start) { @@ -461,7 +469,7 @@ void GrowShadowStack(ThreadState *thr) { #endif u32 CurrentStackId(ThreadState *thr, uptr pc) { - if (thr->shadow_stack_pos == 0) // May happen during bootstrap. + if (!thr->is_inited) // May happen during bootstrap. return 0; if (pc != 0) { #ifndef SANITIZER_GO diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index 7a60e5c55af4..a13e4b6379f0 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -313,7 +313,7 @@ class Shadow : public FastState { } }; -struct SignalContext; +struct ThreadSignalContext; struct JmpBuf { uptr sp; @@ -371,6 +371,7 @@ struct ThreadState { const int unique_id; bool in_symbolizer; bool in_ignored_lib; + bool is_inited; bool is_dead; bool is_freeing; bool is_vptr_access; @@ -387,7 +388,7 @@ struct ThreadState { DDLogicalThread *dd_lt; atomic_uintptr_t in_signal_handler; - SignalContext *signal_ctx; + ThreadSignalContext *signal_ctx; DenseSlabAllocCache block_cache; DenseSlabAllocCache sync_cache; @@ -430,13 +431,13 @@ class ThreadContext : public ThreadContextBase { u64 epoch1; // Override superclass callbacks. - void OnDead(); - void OnJoined(void *arg); - void OnFinished(); - void OnStarted(void *arg); - void OnCreated(void *arg); - void OnReset(); - void OnDetached(void *arg); + void OnDead() override; + void OnJoined(void *arg) override; + void OnFinished() override; + void OnStarted(void *arg) override; + void OnCreated(void *arg) override; + void OnReset() override; + void OnDetached(void *arg) override; }; struct RacyStacks { @@ -573,7 +574,7 @@ void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) { } void MapShadow(uptr addr, uptr size); -void MapThreadTrace(uptr addr, uptr size); +void MapThreadTrace(uptr addr, uptr size, const char *name); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index ddf2b69bdbbc..09180d88a6fb 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -36,12 +36,8 @@ struct Callback : DDCallback { DDCallback::lt = thr->dd_lt; } - virtual u32 Unwind() { - return CurrentStackId(thr, pc); - } - virtual int UniqueTid() { - return thr->unique_id; - } + u32 Unwind() override { return CurrentStackId(thr, pc); } + int UniqueTid() override { return thr->unique_id; } }; void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) { diff --git a/lib/tsan/rtl/tsan_rtl_thread.cc b/lib/tsan/rtl/tsan_rtl_thread.cc index 8ed1fbf2edae..66c78cfdd7c0 100644 --- a/lib/tsan/rtl/tsan_rtl_thread.cc +++ b/lib/tsan/rtl/tsan_rtl_thread.cc @@ -120,6 +120,7 @@ void ThreadContext::OnStarted(void *arg) { AcquireImpl(thr, 0, &sync); StatInc(thr, StatSyncAcquire); sync.Reset(&thr->clock_cache); + thr->is_inited = true; DPrintf("#%d: ThreadStart epoch=%zu stk_addr=%zx stk_size=%zx " "tls_addr=%zx tls_size=%zx\n", tid, (uptr)epoch0, args->stk_addr, args->stk_size, diff --git a/lib/tsan/rtl/tsan_suppressions.cc b/lib/tsan/rtl/tsan_suppressions.cc index 5413f04af1d2..e382f21f0dff 100644 --- a/lib/tsan/rtl/tsan_suppressions.cc +++ b/lib/tsan/rtl/tsan_suppressions.cc @@ -21,6 +21,7 @@ #include "tsan_mman.h" #include "tsan_platform.h" +#ifndef SANITIZER_GO // Suppressions for true/false positives in standard libraries. static const char *const std_suppressions = // Libstdc++ 4.4 has data races in std::string. @@ -33,7 +34,6 @@ static const char *const std_suppressions = "race:std::_Sp_counted_ptr_inplace<std::thread::_Impl\n"; // Can be overriden in frontend. -#ifndef SANITIZER_GO extern "C" const char *WEAK __tsan_default_suppressions() { return 0; } @@ -44,8 +44,9 @@ namespace __tsan { ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; static SuppressionContext *suppression_ctx = nullptr; static const char *kSuppressionTypes[] = { - kSuppressionRace, kSuppressionMutex, kSuppressionThread, - kSuppressionSignal, kSuppressionLib, kSuppressionDeadlock}; + kSuppressionRace, kSuppressionRaceTop, kSuppressionMutex, + kSuppressionThread, kSuppressionSignal, kSuppressionLib, + kSuppressionDeadlock}; void InitializeSuppressions() { CHECK_EQ(nullptr, suppression_ctx); @@ -94,6 +95,18 @@ static const char *conv(ReportType typ) { Die(); } +static uptr IsSuppressed(const char *stype, const AddressInfo &info, + Suppression **sp) { + if (suppression_ctx->Match(info.function, stype, sp) || + suppression_ctx->Match(info.file, stype, sp) || + suppression_ctx->Match(info.module, stype, sp)) { + DPrintf("ThreadSanitizer: matched suppression '%s'\n", (*sp)->templ); + (*sp)->hit_count++; + return info.address; + } + return 0; +} + uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { CHECK(suppression_ctx); if (!suppression_ctx->SuppressionCount() || stack == 0 || @@ -102,19 +115,14 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) { const char *stype = conv(typ); if (0 == internal_strcmp(stype, kSuppressionNone)) return 0; - Suppression *s; for (const SymbolizedStack *frame = stack->frames; frame; - frame = frame->next) { - const AddressInfo &info = frame->info; - if (suppression_ctx->Match(info.function, stype, &s) || - suppression_ctx->Match(info.file, stype, &s) || - suppression_ctx->Match(info.module, stype, &s)) { - DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ); - s->hit_count++; - *sp = s; - return info.address; - } + frame = frame->next) { + uptr pc = IsSuppressed(stype, frame->info, sp); + if (pc != 0) + return pc; } + if (0 == internal_strcmp(stype, kSuppressionRace) && stack->frames != nullptr) + return IsSuppressed(kSuppressionRaceTop, stack->frames->info, sp); return 0; } diff --git a/lib/tsan/rtl/tsan_suppressions.h b/lib/tsan/rtl/tsan_suppressions.h index e6d279c33a4e..526952d5bd15 100644 --- a/lib/tsan/rtl/tsan_suppressions.h +++ b/lib/tsan/rtl/tsan_suppressions.h @@ -20,6 +20,7 @@ namespace __tsan { const char kSuppressionNone[] = "none"; const char kSuppressionRace[] = "race"; +const char kSuppressionRaceTop[] = "race_top"; const char kSuppressionMutex[] = "mutex"; const char kSuppressionThread[] = "thread"; const char kSuppressionSignal[] = "signal"; diff --git a/lib/tsan/rtl/tsan_symbolize.cc b/lib/tsan/rtl/tsan_symbolize.cc index 3beb44f7ad3b..a6b9bca0501d 100644 --- a/lib/tsan/rtl/tsan_symbolize.cc +++ b/lib/tsan/rtl/tsan_symbolize.cc @@ -38,16 +38,10 @@ void ExitSymbolizer() { // May be overriden by JIT/JAVA/etc, // whatever produces PCs marked with kExternalPCBit. -extern "C" bool __tsan_symbolize_external(uptr pc, - char *func_buf, uptr func_siz, - char *file_buf, uptr file_siz, - int *line, int *col) - SANITIZER_WEAK_ATTRIBUTE; - -bool __tsan_symbolize_external(uptr pc, - char *func_buf, uptr func_siz, - char *file_buf, uptr file_siz, - int *line, int *col) { +extern "C" bool WEAK __tsan_symbolize_external(uptr pc, + char *func_buf, uptr func_siz, + char *file_buf, uptr file_siz, + int *line, int *col) { return false; } diff --git a/lib/tsan/rtl/tsan_symbolize.h b/lib/tsan/rtl/tsan_symbolize.h index b59b6cfb8341..5a9710a3cd30 100644 --- a/lib/tsan/rtl/tsan_symbolize.h +++ b/lib/tsan/rtl/tsan_symbolize.h @@ -18,10 +18,6 @@ namespace __tsan { -// Denotes fake PC values that come from JIT/JAVA/etc. -// For such PC values __tsan_symbolize_external() will be called. -const uptr kExternalPCBit = 1ULL << 60; - void EnterSymbolizer(); void ExitSymbolizer(); SymbolizedStack *SymbolizeCode(uptr addr); diff --git a/lib/tsan/rtl/tsan_sync.cc b/lib/tsan/rtl/tsan_sync.cc index 1041073bed51..4202d30a2e17 100644 --- a/lib/tsan/rtl/tsan_sync.cc +++ b/lib/tsan/rtl/tsan_sync.cc @@ -80,17 +80,21 @@ uptr MetaMap::FreeBlock(ThreadState *thr, uptr pc, uptr p) { return sz; } -void MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) { +bool MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) { + bool has_something = false; u32 *meta = MemToMeta(p); u32 *end = MemToMeta(p + sz); if (end == meta) end++; for (; meta < end; meta++) { u32 idx = *meta; + if (idx == 0) { + // Note: don't write to meta in this case -- the block can be huge. + continue; + } *meta = 0; - for (;;) { - if (idx == 0) - break; + has_something = true; + while (idx != 0) { if (idx & kFlagBlock) { block_alloc_.Free(&thr->block_cache, idx & ~kFlagMask); break; @@ -106,6 +110,64 @@ void MetaMap::FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz) { } } } + return has_something; +} + +// ResetRange removes all meta objects from the range. +// It is called for large mmap-ed regions. The function is best-effort wrt +// freeing of meta objects, because we don't want to page in the whole range +// which can be huge. The function probes pages one-by-one until it finds a page +// without meta objects, at this point it stops freeing meta objects. Because +// thread stacks grow top-down, we do the same starting from end as well. +void MetaMap::ResetRange(ThreadState *thr, uptr pc, uptr p, uptr sz) { + const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize; + const uptr kPageSize = GetPageSizeCached() * kMetaRatio; + if (sz <= 4 * kPageSize) { + // If the range is small, just do the normal free procedure. + FreeRange(thr, pc, p, sz); + return; + } + // First, round both ends of the range to page size. + uptr diff = RoundUp(p, kPageSize) - p; + if (diff != 0) { + FreeRange(thr, pc, p, diff); + p += diff; + sz -= diff; + } + diff = p + sz - RoundDown(p + sz, kPageSize); + if (diff != 0) { + FreeRange(thr, pc, p + sz - diff, diff); + sz -= diff; + } + // Now we must have a non-empty page-aligned range. + CHECK_GT(sz, 0); + CHECK_EQ(p, RoundUp(p, kPageSize)); + CHECK_EQ(sz, RoundUp(sz, kPageSize)); + const uptr p0 = p; + const uptr sz0 = sz; + // Probe start of the range. + while (sz > 0) { + bool has_something = FreeRange(thr, pc, p, kPageSize); + p += kPageSize; + sz -= kPageSize; + if (!has_something) + break; + } + // Probe end of the range. + while (sz > 0) { + bool has_something = FreeRange(thr, pc, p - kPageSize, kPageSize); + sz -= kPageSize; + if (!has_something) + break; + } + // Finally, page out the whole range (including the parts that we've just + // freed). Note: we can't simply madvise, because we need to leave a zeroed + // range (otherwise __tsan_java_move can crash if it encounters a left-over + // meta objects in java heap). + uptr metap = (uptr)MemToMeta(p0); + uptr metasz = sz0 / kMetaRatio; + UnmapOrDie((void*)metap, metasz); + MmapFixedNoReserve(metap, metasz); } MBlock* MetaMap::GetBlock(uptr p) { diff --git a/lib/tsan/rtl/tsan_sync.h b/lib/tsan/rtl/tsan_sync.h index 574810d8a542..2d12cdff8b2f 100644 --- a/lib/tsan/rtl/tsan_sync.h +++ b/lib/tsan/rtl/tsan_sync.h @@ -73,7 +73,8 @@ class MetaMap { void AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz); uptr FreeBlock(ThreadState *thr, uptr pc, uptr p); - void FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz); + bool FreeRange(ThreadState *thr, uptr pc, uptr p, uptr sz); + void ResetRange(ThreadState *thr, uptr pc, uptr p, uptr sz); MBlock* GetBlock(uptr p); SyncVar* GetOrCreateAndLock(ThreadState *thr, uptr pc, diff --git a/lib/tsan/rtl/tsan_update_shadow_word_inl.h b/lib/tsan/rtl/tsan_update_shadow_word_inl.h index c80e0a88d0eb..6e3ac2fa16f6 100644 --- a/lib/tsan/rtl/tsan_update_shadow_word_inl.h +++ b/lib/tsan/rtl/tsan_update_shadow_word_inl.h @@ -38,7 +38,8 @@ do { } StatInc(thr, StatShadowAnotherThread); if (HappensBefore(old, thr)) { - StoreIfNotYetStored(sp, &store_word); + if (old.IsRWWeakerOrEqual(kAccessIsWrite, kIsAtomic)) + StoreIfNotYetStored(sp, &store_word); break; } if (old.IsBothReadsOrAtomic(kAccessIsWrite, kIsAtomic)) diff --git a/lib/tsan/tests/CMakeLists.txt b/lib/tsan/tests/CMakeLists.txt index e0c3f8a1a6d8..1c3f98f3f0bc 100644 --- a/lib/tsan/tests/CMakeLists.txt +++ b/lib/tsan/tests/CMakeLists.txt @@ -36,7 +36,7 @@ macro(add_tsan_unittest testname) # Build unit tests only for 64-bit Linux. if(UNIX AND NOT APPLE) foreach(arch ${TSAN_SUPPORTED_ARCH}) - parse_arguments(TEST "SOURCES;HEADERS" "" ${ARGN}) + cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN}) set(TEST_OBJECTS) foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) tsan_compile(TEST_OBJECTS ${SOURCE} ${arch} ${TEST_HEADERS}) diff --git a/lib/ubsan/CMakeLists.txt b/lib/ubsan/CMakeLists.txt index 09c7a851e075..246236b78ab5 100644 --- a/lib/ubsan/CMakeLists.txt +++ b/lib/ubsan/CMakeLists.txt @@ -8,50 +8,98 @@ set(UBSAN_SOURCES ubsan_value.cc ) +set(UBSAN_STANDALONE_SOURCES + ubsan_init_standalone.cc + ) + set(UBSAN_CXX_SOURCES ubsan_handlers_cxx.cc ubsan_type_hash.cc + ubsan_type_hash_itanium.cc + ubsan_type_hash_win.cc ) include_directories(..) set(UBSAN_CFLAGS ${SANITIZER_COMMON_CFLAGS}) append_no_rtti_flag(UBSAN_CFLAGS) +set(UBSAN_STANDALONE_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_no_rtti_flag(UBSAN_STANDALONE_CFLAGS) set(UBSAN_CXXFLAGS ${SANITIZER_COMMON_CFLAGS}) add_custom_target(ubsan) if(APPLE) - # Build universal binary on APPLE. - add_compiler_rt_osx_static_runtime(clang_rt.ubsan_osx - ARCH ${UBSAN_SUPPORTED_ARCH} - SOURCES ${UBSAN_SOURCES} ${UBSAN_CXX_SOURCES} - $<TARGET_OBJECTS:RTSanitizerCommon.osx> + set(UBSAN_COMMON_SOURCES ${UBSAN_SOURCES}) + if(SANITIZER_CAN_USE_CXXABI) + list(APPEND UBSAN_COMMON_SOURCES ${UBSAN_CXX_SOURCES}) + endif() + + # Common parts of UBSan runtime. + add_compiler_rt_object_libraries(RTUbsan + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${UBSAN_COMMON_SOURCES} CFLAGS ${UBSAN_CXXFLAGS}) - add_dependencies(ubsan clang_rt.ubsan_osx) + + if(COMPILER_RT_HAS_UBSAN) + # Initializer of standalone UBSan runtime. + add_compiler_rt_object_libraries(RTUbsan_standalone + OS ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${UBSAN_SUPPORTED_ARCH} + SOURCES ${UBSAN_STANDALONE_SOURCES} + CFLAGS ${UBSAN_STANDALONE_CFLAGS}) + foreach(os ${SANITIZER_COMMON_SUPPORTED_OS}) + add_compiler_rt_darwin_dynamic_runtime(clang_rt.ubsan_${os}_dynamic ${os} + ARCHS ${UBSAN_SUPPORTED_ARCH} + SOURCES $<TARGET_OBJECTS:RTUbsan.${os}> + $<TARGET_OBJECTS:RTUbsan_standalone.${os}> + $<TARGET_OBJECTS:RTSanitizerCommon.${os}>) + + add_dependencies(ubsan clang_rt.ubsan_${os}_dynamic) + endforeach() + endif() + else() - # Build separate libraries for each target. - foreach(arch ${UBSAN_SUPPORTED_ARCH}) - # Main UBSan runtime. - add_compiler_rt_runtime(clang_rt.ubsan-${arch} ${arch} STATIC - SOURCES ${UBSAN_SOURCES} - CFLAGS ${UBSAN_CFLAGS}) - # C++-specific parts of UBSan runtime. Requires a C++ ABI library. - add_compiler_rt_runtime(clang_rt.ubsan_cxx-${arch} ${arch} STATIC - SOURCES ${UBSAN_CXX_SOURCES} - CFLAGS ${UBSAN_CXXFLAGS}) - add_dependencies(ubsan - clang_rt.san-${arch} - clang_rt.ubsan-${arch} - clang_rt.ubsan_cxx-${arch}) - if (UNIX AND NOT ${arch} STREQUAL "i386" AND NOT ${arch} STREQUAL "i686") - add_sanitizer_rt_symbols(clang_rt.ubsan-${arch} ubsan.syms.extra) - add_sanitizer_rt_symbols(clang_rt.ubsan_cxx-${arch} ubsan.syms.extra) + # Common parts of UBSan runtime. + add_compiler_rt_object_libraries(RTUbsan + ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${UBSAN_SOURCES} CFLAGS ${UBSAN_CFLAGS}) + # C++-specific parts of UBSan runtime. Requires a C++ ABI library. + add_compiler_rt_object_libraries(RTUbsan_cxx + ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH} + SOURCES ${UBSAN_CXX_SOURCES} CFLAGS ${UBSAN_CXXFLAGS}) + + if(COMPILER_RT_HAS_UBSAN) + # Initializer of standalone UBSan runtime. + add_compiler_rt_object_libraries(RTUbsan_standalone + ARCHS ${UBSAN_SUPPORTED_ARCH} + SOURCES ${UBSAN_STANDALONE_SOURCES} CFLAGS ${UBSAN_STANDALONE_CFLAGS}) + + foreach(arch ${UBSAN_SUPPORTED_ARCH}) + # Standalone UBSan runtimes. + add_compiler_rt_runtime(clang_rt.ubsan_standalone-${arch} ${arch} STATIC + SOURCES $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> + $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> + $<TARGET_OBJECTS:RTUbsan.${arch}> + $<TARGET_OBJECTS:RTUbsan_standalone.${arch}> + CFLAGS ${UBSAN_CFLAGS}) + add_compiler_rt_runtime(clang_rt.ubsan_standalone_cxx-${arch} ${arch} STATIC + SOURCES $<TARGET_OBJECTS:RTUbsan_cxx.${arch}> + CFLAGS ${UBSAN_CXXFLAGS}) + add_dependencies(ubsan - clang_rt.ubsan-${arch}-symbols - clang_rt.ubsan_cxx-${arch}-symbols) - endif() - endforeach() + clang_rt.ubsan_standalone-${arch} + clang_rt.ubsan_standalone_cxx-${arch}) + if (UNIX AND NOT ${arch} MATCHES "i386|i686") + add_sanitizer_rt_symbols(clang_rt.ubsan_standalone-${arch} ubsan.syms.extra) + add_sanitizer_rt_symbols(clang_rt.ubsan_standalone_cxx-${arch} ubsan.syms.extra) + add_dependencies(ubsan + clang_rt.ubsan_standalone-${arch}-symbols + clang_rt.ubsan_standalone_cxx-${arch}-symbols) + endif() + endforeach() + endif() endif() add_dependencies(compiler-rt ubsan) diff --git a/lib/ubsan/Makefile.mk b/lib/ubsan/Makefile.mk index d5561f41b152..ec3f5c5da273 100644 --- a/lib/ubsan/Makefile.mk +++ b/lib/ubsan/Makefile.mk @@ -11,8 +11,9 @@ ModuleName := ubsan SubDirs := Sources := $(foreach file,$(wildcard $(Dir)/*.cc),$(notdir $(file))) +StandaloneSources := ubsan_init_standalone.cc CXXSources := ubsan_type_hash.cc ubsan_handlers_cxx.cc -CSources := $(filter-out $(CXXSources),$(Sources)) +CSources := $(filter-out $(StandaloneSources),$(filter-out $(CXXSources),$(Sources))) ObjNames := $(Sources:%.cc=%.o) Implementation := Generic @@ -24,3 +25,4 @@ Dependencies += $(wildcard $(Dir)/../sanitizer_common/*.h) # Define a convenience variable for all the ubsan functions. UbsanFunctions := $(CSources:%.cc=%) UbsanCXXFunctions := $(CXXSources:%.cc=%) +UbsanStandaloneFunctions := $(StandaloneSources:%.cc=%) diff --git a/lib/ubsan/ubsan_diag.cc b/lib/ubsan/ubsan_diag.cc index 4f2a2a9f3562..3d5042b51d2e 100644 --- a/lib/ubsan/ubsan_diag.cc +++ b/lib/ubsan/ubsan_diag.cc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" @@ -25,7 +27,7 @@ using namespace __ubsan; static void MaybePrintStackTrace(uptr pc, uptr bp) { - // We assume that flags are already parsed: InitIfNecessary + // We assume that flags are already parsed, as UBSan runtime // will definitely be called when we print the first diagnostics message. if (!flags()->print_stacktrace) return; @@ -44,17 +46,25 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) { static void MaybeReportErrorSummary(Location Loc) { if (!common_flags()->print_summary) return; - // Don't try to unwind the stack trace in UBSan summaries: just use the - // provided location. + const char *ErrorType = "undefined-behavior"; if (Loc.isSourceLocation()) { SourceLocation SLoc = Loc.getSourceLocation(); if (!SLoc.isInvalid()) { - ReportErrorSummary("undefined-behavior", SLoc.getFilename(), - SLoc.getLine(), ""); + AddressInfo AI; + AI.file = internal_strdup(SLoc.getFilename()); + AI.line = SLoc.getLine(); + AI.column = SLoc.getColumn(); + AI.function = internal_strdup(""); // Avoid printing ?? as function name. + ReportErrorSummary(ErrorType, AI); + AI.Clear(); return; } + } else if (Loc.isSymbolizedStack()) { + const AddressInfo &AI = Loc.getSymbolizedStack()->info; + ReportErrorSummary(ErrorType, AI); + return; } - ReportErrorSummary("undefined-behavior"); + ReportErrorSummary(ErrorType); } namespace { @@ -69,7 +79,7 @@ class Decorator : public SanitizerCommonDecorator { } SymbolizedStack *__ubsan::getSymbolizedLocation(uptr PC) { - InitIfNecessary(); + InitAsStandaloneIfNecessary(); return Symbolizer::GetOrInit()->SymbolizePC(PC); } @@ -111,7 +121,8 @@ static void renderLocation(Location Loc) { LocBuffer.append("<unknown>"); else RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), - SLoc.getColumn(), common_flags()->strip_path_prefix); + SLoc.getColumn(), common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); break; } case Location::LK_Memory: @@ -121,6 +132,7 @@ static void renderLocation(Location Loc) { const AddressInfo &Info = Loc.getSymbolizedStack()->info; if (Info.file) { RenderSourceLocation(&LocBuffer, Info.file, Info.line, Info.column, + common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix); } else if (Info.module) { RenderModuleLocation(&LocBuffer, Info.module, Info.module_offset, @@ -153,8 +165,12 @@ static void renderText(const char *Message, const Diag::Arg *Args) { case Diag::AK_String: Printf("%s", A.String); break; - case Diag::AK_Mangled: { - Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); + case Diag::AK_TypeName: { + if (SANITIZER_WINDOWS) + // The Windows implementation demangles names early. + Printf("'%s'", A.String); + else + Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); break; } case Diag::AK_SInt: @@ -174,7 +190,11 @@ static void renderText(const char *Message, const Diag::Arg *Args) { // FIXME: Support floating-point formatting in sanitizer_common's // printf, and stop using snprintf here. char Buffer[32]; +#if SANITIZER_WINDOWS + sprintf_s(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); +#else snprintf(Buffer, sizeof(Buffer), "%Lg", (long double)A.Float); +#endif Printf("%s", Buffer); break; } @@ -323,7 +343,7 @@ Diag::~Diag() { ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc) : Opts(Opts), SummaryLoc(SummaryLoc) { - InitIfNecessary(); + InitAsStandaloneIfNecessary(); CommonSanitizerReportMutex.Lock(); } @@ -348,11 +368,10 @@ void __ubsan::InitializeSuppressions() { } bool __ubsan::IsVptrCheckSuppressed(const char *TypeName) { - // If .preinit_array is not used, it is possible that the UBSan runtime is not - // initialized. - if (!SANITIZER_CAN_USE_PREINIT_ARRAY) - InitIfNecessary(); + InitAsStandaloneIfNecessary(); CHECK(suppression_ctx); Suppression *s; return suppression_ctx->Match(TypeName, kVptrCheck, &s); } + +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_diag.h b/lib/ubsan/ubsan_diag.h index 44dca90b7a53..615a646d9419 100644 --- a/lib/ubsan/ubsan_diag.h +++ b/lib/ubsan/ubsan_diag.h @@ -113,11 +113,11 @@ public: const char *getText() const { return Text; } }; -/// \brief A mangled C++ name. Really just a strong typedef for 'const char*'. -class MangledName { +/// \brief A C++ type name. Really just a strong typedef for 'const char*'. +class TypeName { const char *Name; public: - MangledName(const char *Name) : Name(Name) {} + TypeName(const char *Name) : Name(Name) {} const char *getName() const { return Name; } }; @@ -141,7 +141,7 @@ public: /// Kinds of arguments, corresponding to members of \c Arg's union. enum ArgKind { AK_String, ///< A string argument, displayed as-is. - AK_Mangled,///< A C++ mangled name, demangled before display. + AK_TypeName,///< A C++ type name, possibly demangled before display. AK_UInt, ///< An unsigned integer argument. AK_SInt, ///< A signed integer argument. AK_Float, ///< A floating-point argument. @@ -152,7 +152,7 @@ public: struct Arg { Arg() {} Arg(const char *String) : Kind(AK_String), String(String) {} - Arg(MangledName MN) : Kind(AK_Mangled), String(MN.getName()) {} + Arg(TypeName TN) : Kind(AK_TypeName), String(TN.getName()) {} Arg(UIntMax UInt) : Kind(AK_UInt), UInt(UInt) {} Arg(SIntMax SInt) : Kind(AK_SInt), SInt(SInt) {} Arg(FloatMax Float) : Kind(AK_Float), Float(Float) {} @@ -202,7 +202,7 @@ public: ~Diag(); Diag &operator<<(const char *Str) { return AddArg(Str); } - Diag &operator<<(MangledName MN) { return AddArg(MN); } + Diag &operator<<(TypeName TN) { return AddArg(TN); } Diag &operator<<(unsigned long long V) { return AddArg(UIntMax(V)); } Diag &operator<<(const void *V) { return AddArg(V); } Diag &operator<<(const TypeDescriptor &V); diff --git a/lib/ubsan/ubsan_flags.cc b/lib/ubsan/ubsan_flags.cc index 0dbffc9b102a..6e5ad4c7967c 100644 --- a/lib/ubsan/ubsan_flags.cc +++ b/lib/ubsan/ubsan_flags.cc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_flags.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" @@ -18,7 +20,7 @@ namespace __ubsan { -static const char *MaybeCallUbsanDefaultOptions() { +const char *MaybeCallUbsanDefaultOptions() { return (&__ubsan_default_options) ? __ubsan_default_options() : ""; } @@ -37,31 +39,22 @@ void RegisterUbsanFlags(FlagParser *parser, Flags *f) { #undef UBSAN_FLAG } -void InitializeFlags(bool standalone) { - Flags *f = flags(); - FlagParser parser; - RegisterUbsanFlags(&parser, f); - - if (standalone) { - RegisterCommonFlags(&parser); - - SetCommonFlagsDefaults(); +void InitializeFlags() { + SetCommonFlagsDefaults(); + { CommonFlags cf; cf.CopyFrom(*common_flags()); cf.print_summary = false; OverrideCommonFlags(cf); - } else { - // Ignore common flags if not standalone. - // This is inconsistent with LSan, which allows common flags in LSAN_FLAGS. - // This is caused by undefined initialization order between ASan and UBsan, - // which makes it impossible to make sure that common flags from ASAN_OPTIONS - // have not been used (in __asan_init) before they are overwritten with flags - // from UBSAN_OPTIONS. - CommonFlags cf_ignored; - RegisterCommonFlags(&parser, &cf_ignored); } + Flags *f = flags(); f->SetDefaults(); + + FlagParser parser; + RegisterCommonFlags(&parser); + RegisterUbsanFlags(&parser, f); + // Override from user-specified string. parser.ParseString(MaybeCallUbsanDefaultOptions()); // Override from environment variable. @@ -71,9 +64,22 @@ void InitializeFlags(bool standalone) { } // namespace __ubsan -#if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { + +#if !SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *__ubsan_default_options() { return ""; } -} // extern "C" #endif + +#if SANITIZER_WINDOWS +const char *__ubsan_default_default_options() { return ""; } +# ifdef _WIN64 +# pragma comment(linker, "/alternatename:__ubsan_default_options=__ubsan_default_default_options") +# else +# pragma comment(linker, "/alternatename:___ubsan_default_options=___ubsan_default_default_options") +# endif +#endif + +} // extern "C" + +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_flags.h b/lib/ubsan/ubsan_flags.h index b47f14e1e2fd..18aed9b052ac 100644 --- a/lib/ubsan/ubsan_flags.h +++ b/lib/ubsan/ubsan_flags.h @@ -15,6 +15,10 @@ #include "sanitizer_common/sanitizer_internal_defs.h" +namespace __sanitizer { +class FlagParser; +} + namespace __ubsan { struct Flags { @@ -28,7 +32,10 @@ struct Flags { extern Flags ubsan_flags; inline Flags *flags() { return &ubsan_flags; } -void InitializeFlags(bool standalone); +void InitializeFlags(); +void RegisterUbsanFlags(FlagParser *parser, Flags *f); + +const char *MaybeCallUbsanDefaultOptions(); } // namespace __ubsan diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index 78e7508f7f93..a65b2f5e3fc7 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_handlers.h" #include "ubsan_diag.h" @@ -419,3 +421,5 @@ void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data) { handleNonNullArg(Data, Opts); Die(); } + +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_handlers_cxx.cc b/lib/ubsan/ubsan_handlers_cxx.cc index 4718e6eacee2..6984a963deb6 100644 --- a/lib/ubsan/ubsan_handlers_cxx.cc +++ b/lib/ubsan/ubsan_handlers_cxx.cc @@ -13,6 +13,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_handlers_cxx.h" #include "ubsan_diag.h" #include "ubsan_type_hash.h" @@ -35,7 +37,7 @@ static void HandleDynamicTypeCacheMiss( return; // Check if error report should be suppressed. - DynamicTypeInfo DTI = getDynamicTypeInfo((void*)Pointer); + DynamicTypeInfo DTI = getDynamicTypeInfoFromObject((void*)Pointer); if (DTI.isValid() && IsVptrCheckSuppressed(DTI.getMostDerivedTypeName())) return; @@ -52,19 +54,19 @@ static void HandleDynamicTypeCacheMiss( // If possible, say what type it actually points to. if (!DTI.isValid()) Diag(Pointer, DL_Note, "object has invalid vptr") - << MangledName(DTI.getMostDerivedTypeName()) + << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "invalid vptr"); else if (!DTI.getOffset()) Diag(Pointer, DL_Note, "object is of type %0") - << MangledName(DTI.getMostDerivedTypeName()) + << TypeName(DTI.getMostDerivedTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %0"); else // FIXME: Find the type at the specified offset, and include that // in the note. Diag(Pointer - DTI.getOffset(), DL_Note, "object is base class subobject at offset %0 within object of type %1") - << DTI.getOffset() << MangledName(DTI.getMostDerivedTypeName()) - << MangledName(DTI.getSubobjectTypeName()) + << DTI.getOffset() << TypeName(DTI.getMostDerivedTypeName()) + << TypeName(DTI.getSubobjectTypeName()) << Range(Pointer, Pointer + sizeof(uptr), "vptr for %2 base class of %1"); } @@ -79,3 +81,42 @@ void __ubsan::__ubsan_handle_dynamic_type_cache_miss_abort( GET_REPORT_OPTIONS(true); HandleDynamicTypeCacheMiss(Data, Pointer, Hash, Opts); } + +static void HandleCFIBadType(CFIBadTypeData *Data, ValueHandle Vtable, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + ScopedReport R(Opts, Loc); + DynamicTypeInfo DTI = getDynamicTypeInfoFromVtable((void*)Vtable); + + static const char *TypeCheckKinds[] = { + "virtual call", + "non-virtual call", + "base-to-derived cast", + "cast to unrelated type", + }; + + Diag(Loc, DL_Error, "control flow integrity check for type %0 failed during " + "%1 (vtable address %2)") + << Data->Type << TypeCheckKinds[Data->TypeCheckKind] << (void *)Vtable; + + // If possible, say what type it actually points to. + if (!DTI.isValid()) + Diag(Vtable, DL_Note, "invalid vtable"); + else + Diag(Vtable, DL_Note, "vtable is of type %0") + << TypeName(DTI.getMostDerivedTypeName()); +} + +void __ubsan::__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data, + ValueHandle Vtable) { + GET_REPORT_OPTIONS(false); + HandleCFIBadType(Data, Vtable, Opts); +} + +void __ubsan::__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data, + ValueHandle Vtable) { + GET_REPORT_OPTIONS(true); + HandleCFIBadType(Data, Vtable, Opts); +} + +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_handlers_cxx.h b/lib/ubsan/ubsan_handlers_cxx.h index cb1bca78b833..92050d99e48e 100644 --- a/lib/ubsan/ubsan_handlers_cxx.h +++ b/lib/ubsan/ubsan_handlers_cxx.h @@ -25,6 +25,12 @@ struct DynamicTypeCacheMissData { unsigned char TypeCheckKind; }; +struct CFIBadTypeData { + SourceLocation Loc; + const TypeDescriptor &Type; + unsigned char TypeCheckKind; +}; + /// \brief Handle a runtime type check failure, caused by an incorrect vptr. /// When this handler is called, all we know is that the type was not in the /// cache; this does not necessarily imply the existence of a bug. @@ -35,6 +41,13 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __ubsan_handle_dynamic_type_cache_miss_abort( DynamicTypeCacheMissData *Data, ValueHandle Pointer, ValueHandle Hash); +/// \brief Handle a control flow integrity check failure by printing a +/// diagnostic. +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__ubsan_handle_cfi_bad_type(CFIBadTypeData *Data, ValueHandle Vtable); +extern "C" SANITIZER_INTERFACE_ATTRIBUTE void +__ubsan_handle_cfi_bad_type_abort(CFIBadTypeData *Data, ValueHandle Vtable); + } #endif // UBSAN_HANDLERS_H diff --git a/lib/ubsan/ubsan_init.cc b/lib/ubsan/ubsan_init.cc index 219273d51921..73398ceafc34 100644 --- a/lib/ubsan/ubsan_init.cc +++ b/lib/ubsan/ubsan_init.cc @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_diag.h" #include "ubsan_init.h" #include "ubsan_flags.h" @@ -21,43 +23,56 @@ using namespace __ubsan; -static bool ubsan_inited; +static enum { + UBSAN_MODE_UNKNOWN = 0, + UBSAN_MODE_STANDALONE, + UBSAN_MODE_PLUGIN +} ubsan_mode; +static StaticSpinMutex ubsan_init_mu; -void __ubsan::InitIfNecessary() { -#if !SANITIZER_CAN_USE_PREINIT_ARRAY - // No need to lock mutex if we're initializing from preinit array. - static StaticSpinMutex init_mu; - SpinMutexLock l(&init_mu); -#endif - if (LIKELY(ubsan_inited)) - return; - bool standalone = false; - if (0 == internal_strcmp(SanitizerToolName, "SanitizerTool")) { - // WARNING: If this condition holds, then either UBSan runs in a standalone - // mode, or initializer for another sanitizer hasn't run yet. In a latter - // case, another sanitizer will overwrite "SanitizerToolName" and reparse - // common flags. It means, that we are not allowed to *use* common flags - // in this function. - SanitizerToolName = "UndefinedBehaviorSanitizer"; - standalone = true; - } - // Initialize UBSan-specific flags. - InitializeFlags(standalone); +static void CommonInit() { InitializeSuppressions(); +} + +static void CommonStandaloneInit() { + SanitizerToolName = "UndefinedBehaviorSanitizer"; + InitializeFlags(); + CacheBinaryName(); + __sanitizer_set_report_path(common_flags()->log_path); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); - ubsan_inited = true; + CommonInit(); + ubsan_mode = UBSAN_MODE_STANDALONE; +} + +void __ubsan::InitAsStandalone() { + if (SANITIZER_CAN_USE_PREINIT_ARRAY) { + CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode); + CommonStandaloneInit(); + return; + } + SpinMutexLock l(&ubsan_init_mu); + CHECK_NE(UBSAN_MODE_PLUGIN, ubsan_mode); + if (ubsan_mode == UBSAN_MODE_UNKNOWN) + CommonStandaloneInit(); } -#if SANITIZER_CAN_USE_PREINIT_ARRAY -__attribute__((section(".preinit_array"), used)) -void (*__local_ubsan_preinit)(void) = __ubsan::InitIfNecessary; -#else -// Use a dynamic initializer. -class UbsanInitializer { - public: - UbsanInitializer() { - InitIfNecessary(); +void __ubsan::InitAsStandaloneIfNecessary() { + if (SANITIZER_CAN_USE_PREINIT_ARRAY) { + CHECK_NE(UBSAN_MODE_UNKNOWN, ubsan_mode); + return; } -}; -static UbsanInitializer ubsan_initializer; -#endif // SANITIZER_CAN_USE_PREINIT_ARRAY + SpinMutexLock l(&ubsan_init_mu); + if (ubsan_mode == UBSAN_MODE_UNKNOWN) + CommonStandaloneInit(); +} + +void __ubsan::InitAsPlugin() { +#if !SANITIZER_CAN_USE_PREINIT_ARRAY + SpinMutexLock l(&ubsan_init_mu); +#endif + CHECK_EQ(UBSAN_MODE_UNKNOWN, ubsan_mode); + CommonInit(); + ubsan_mode = UBSAN_MODE_PLUGIN; +} + +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_init.h b/lib/ubsan/ubsan_init.h index 18356cfca617..103ae24d15b3 100644 --- a/lib/ubsan/ubsan_init.h +++ b/lib/ubsan/ubsan_init.h @@ -15,9 +15,16 @@ namespace __ubsan { -// NOTE: This function might take a lock (if .preinit_array initialization is -// not used). It's generally a bad idea to call it on a fast path. -void InitIfNecessary(); +// Initialize UBSan as a standalone tool. Typically should be called early +// during initialization. +void InitAsStandalone(); + +// Initialize UBSan as a standalone tool, if it hasn't been initialized before. +void InitAsStandaloneIfNecessary(); + +// Initializes UBSan as a plugin tool. This function should be called once +// from "parent tool" (e.g. ASan) initialization. +void InitAsPlugin(); } // namespace __ubsan diff --git a/lib/ubsan/ubsan_init_standalone.cc b/lib/ubsan/ubsan_init_standalone.cc new file mode 100644 index 000000000000..ff1a20efea3d --- /dev/null +++ b/lib/ubsan/ubsan_init_standalone.cc @@ -0,0 +1,35 @@ +//===-- ubsan_init_standalone.cc ------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Initialization of standalone UBSan runtime. +// +//===----------------------------------------------------------------------===// + +#include "ubsan_platform.h" +#if !CAN_SANITIZE_UB +# error "UBSan is not supported on this platform!" +#endif + +#include "sanitizer_common/sanitizer_internal_defs.h" +#include "ubsan_init.h" + +#if SANITIZER_CAN_USE_PREINIT_ARRAY +__attribute__((section(".preinit_array"), used)) +void (*__local_ubsan_preinit)(void) = __ubsan::InitAsStandalone; +#else +// Use a dynamic initializer. +class UbsanStandaloneInitializer { + public: + UbsanStandaloneInitializer() { + __ubsan::InitAsStandalone(); + } +}; +static UbsanStandaloneInitializer ubsan_standalone_initializer; +#endif // SANITIZER_CAN_USE_PREINIT_ARRAY + diff --git a/lib/ubsan/ubsan_platform.h b/lib/ubsan/ubsan_platform.h new file mode 100644 index 000000000000..002ecf32a595 --- /dev/null +++ b/lib/ubsan/ubsan_platform.h @@ -0,0 +1,27 @@ +//===-- ubsan_platform.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the platforms which UBSan is supported at. +// +//===----------------------------------------------------------------------===// +#ifndef UBSAN_PLATFORM_H +#define UBSAN_PLATFORM_H + +// Other platforms should be easy to add, and probably work as-is. +#if (defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)) && \ + (defined(__x86_64__) || defined(__i386__) || defined(__arm__) || \ + defined(__aarch64__) || defined(__mips__) || defined(__powerpc64__)) +# define CAN_SANITIZE_UB 1 +#elif defined(_WIN32) +# define CAN_SANITIZE_UB 1 +#else +# define CAN_SANITIZE_UB 0 +#endif + +#endif diff --git a/lib/ubsan/ubsan_type_hash.cc b/lib/ubsan/ubsan_type_hash.cc index a388bcc6d72e..a217c862c62a 100644 --- a/lib/ubsan/ubsan_type_hash.cc +++ b/lib/ubsan/ubsan_type_hash.cc @@ -11,240 +11,24 @@ // relationships. This file is only linked into C++ compilations, and is // permitted to use language features which require a C++ ABI library. // +// Most of the implementation lives in an ABI-specific source file +// (ubsan_type_hash_{itanium,win}.cc). +// //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_type_hash.h" #include "sanitizer_common/sanitizer_common.h" -// The following are intended to be binary compatible with the definitions -// given in the Itanium ABI. We make no attempt to be ODR-compatible with -// those definitions, since existing ABI implementations aren't. - -namespace std { - class type_info { - public: - virtual ~type_info(); - - const char *__type_name; - }; -} - -namespace __cxxabiv1 { - -/// Type info for classes with no bases, and base class for type info for -/// classes with bases. -class __class_type_info : public std::type_info { - virtual ~__class_type_info(); -}; - -/// Type info for classes with simple single public inheritance. -class __si_class_type_info : public __class_type_info { -public: - virtual ~__si_class_type_info(); - - const __class_type_info *__base_type; -}; - -class __base_class_type_info { -public: - const __class_type_info *__base_type; - long __offset_flags; - - enum __offset_flags_masks { - __virtual_mask = 0x1, - __public_mask = 0x2, - __offset_shift = 8 - }; -}; - -/// Type info for classes with multiple, virtual, or non-public inheritance. -class __vmi_class_type_info : public __class_type_info { -public: - virtual ~__vmi_class_type_info(); - - unsigned int flags; - unsigned int base_count; - __base_class_type_info base_info[1]; -}; - -} - -namespace abi = __cxxabiv1; - -// We implement a simple two-level cache for type-checking results. For each -// (vptr,type) pair, a hash is computed. This hash is assumed to be globally -// unique; if it collides, we will get false negatives, but: -// * such a collision would have to occur on the *first* bad access, -// * the probability of such a collision is low (and for a 64-bit target, is -// negligible), and -// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs -// give better coverage. -// -// The first caching layer is a small hash table with no chaining; buckets are -// reused as needed. The second caching layer is a large hash table with open -// chaining. We can freely evict from either layer since this is just a cache. -// -// FIXME: Make these hash table accesses thread-safe. The races here are benign: -// assuming the unsequenced loads and stores don't misbehave too badly, -// the worst case is false negatives or poor cache behavior, not false -// positives or crashes. - -/// Find a bucket to store the given hash value in. -static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { - static const unsigned HashTableSize = 65537; - static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; - - unsigned First = (V & 65535) ^ 1; - unsigned Probe = First; - for (int Tries = 5; Tries; --Tries) { - if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) - return &__ubsan_vptr_hash_set[Probe]; - Probe += ((V >> 16) & 65535) + 1; - if (Probe >= HashTableSize) - Probe -= HashTableSize; - } - // FIXME: Pick a random entry from the probe sequence to evict rather than - // just taking the first. - return &__ubsan_vptr_hash_set[First]; -} - /// A cache of recently-checked hashes. Mini hash table with "random" evictions. __ubsan::HashValue __ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; -/// \brief Determine whether \p Derived has a \p Base base class subobject at -/// offset \p Offset. -static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, - const abi::__class_type_info *Base, - sptr Offset) { - if (Derived->__type_name == Base->__type_name) - return Offset == 0; - - if (const abi::__si_class_type_info *SI = - dynamic_cast<const abi::__si_class_type_info*>(Derived)) - return isDerivedFromAtOffset(SI->__base_type, Base, Offset); - - const abi::__vmi_class_type_info *VTI = - dynamic_cast<const abi::__vmi_class_type_info*>(Derived); - if (!VTI) - // No base class subobjects. - return false; - - // Look for a base class which is derived from \p Base at the right offset. - for (unsigned int base = 0; base != VTI->base_count; ++base) { - // FIXME: Curtail the recursion if this base can't possibly contain the - // given offset. - sptr OffsetHere = VTI->base_info[base].__offset_flags >> - abi::__base_class_type_info::__offset_shift; - if (VTI->base_info[base].__offset_flags & - abi::__base_class_type_info::__virtual_mask) - // For now, just punt on virtual bases and say 'yes'. - // FIXME: OffsetHere is the offset in the vtable of the virtual base - // offset. Read the vbase offset out of the vtable and use it. - return true; - if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, - Base, Offset - OffsetHere)) - return true; - } - - return false; -} - -/// \brief Find the derived-most dynamic base class of \p Derived at offset -/// \p Offset. -static const abi::__class_type_info *findBaseAtOffset( - const abi::__class_type_info *Derived, sptr Offset) { - if (!Offset) - return Derived; - - if (const abi::__si_class_type_info *SI = - dynamic_cast<const abi::__si_class_type_info*>(Derived)) - return findBaseAtOffset(SI->__base_type, Offset); - - const abi::__vmi_class_type_info *VTI = - dynamic_cast<const abi::__vmi_class_type_info*>(Derived); - if (!VTI) - // No base class subobjects. - return 0; - - for (unsigned int base = 0; base != VTI->base_count; ++base) { - sptr OffsetHere = VTI->base_info[base].__offset_flags >> - abi::__base_class_type_info::__offset_shift; - if (VTI->base_info[base].__offset_flags & - abi::__base_class_type_info::__virtual_mask) - // FIXME: Can't handle virtual bases yet. - continue; - if (const abi::__class_type_info *Base = - findBaseAtOffset(VTI->base_info[base].__base_type, - Offset - OffsetHere)) - return Base; - } - - return 0; -} - -namespace { - -struct VtablePrefix { - /// The offset from the vptr to the start of the most-derived object. - /// This should never be greater than zero, and will usually be exactly - /// zero. - sptr Offset; - /// The type_info object describing the most-derived class type. - std::type_info *TypeInfo; -}; -VtablePrefix *getVtablePrefix(void *Object) { - VtablePrefix **VptrPtr = reinterpret_cast<VtablePrefix**>(Object); - if (!*VptrPtr) - return 0; - VtablePrefix *Prefix = *VptrPtr - 1; - if (Prefix->Offset > 0 || !Prefix->TypeInfo) - // This can't possibly be a valid vtable. - return 0; - return Prefix; -} - +__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromObject(void *Object) { + void *VtablePtr = *reinterpret_cast<void **>(Object); + return getDynamicTypeInfoFromVtable(VtablePtr); } -bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { - // A crash anywhere within this function probably means the vptr is corrupted. - // FIXME: Perform these checks more cautiously. - - // Check whether this is something we've evicted from the cache. - HashValue *Bucket = getTypeCacheHashTableBucket(Hash); - if (*Bucket == Hash) { - __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; - return true; - } - - VtablePrefix *Vtable = getVtablePrefix(Object); - if (!Vtable) - return false; - - // Check that this is actually a type_info object for a class type. - abi::__class_type_info *Derived = - dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo); - if (!Derived) - return false; - - abi::__class_type_info *Base = (abi::__class_type_info*)Type; - if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) - return false; - - // Success. Cache this result. - __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; - *Bucket = Hash; - return true; -} - -__ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) { - VtablePrefix *Vtable = getVtablePrefix(Object); - if (!Vtable) - return DynamicTypeInfo(0, 0, 0); - const abi::__class_type_info *ObjectType = findBaseAtOffset( - static_cast<const abi::__class_type_info*>(Vtable->TypeInfo), - -Vtable->Offset); - return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, - ObjectType ? ObjectType->__type_name : "<unknown>"); -} +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_type_hash.h b/lib/ubsan/ubsan_type_hash.h index 58ecd3de9864..695fed905a73 100644 --- a/lib/ubsan/ubsan_type_hash.h +++ b/lib/ubsan/ubsan_type_hash.h @@ -41,7 +41,10 @@ public: }; /// \brief Get information about the dynamic type of an object. -DynamicTypeInfo getDynamicTypeInfo(void *Object); +DynamicTypeInfo getDynamicTypeInfoFromObject(void *Object); + +/// \brief Get information about the dynamic type of an object from its vtable. +DynamicTypeInfo getDynamicTypeInfoFromVtable(void *Vtable); /// \brief Check whether the dynamic type of \p Object has a \p Type subobject /// at offset 0. diff --git a/lib/ubsan/ubsan_type_hash_itanium.cc b/lib/ubsan/ubsan_type_hash_itanium.cc new file mode 100644 index 000000000000..b84e88d4c71d --- /dev/null +++ b/lib/ubsan/ubsan_type_hash_itanium.cc @@ -0,0 +1,251 @@ +//===-- ubsan_type_hash_itanium.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of type hashing/lookup for Itanium C++ ABI. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB && !SANITIZER_WINDOWS +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +// The following are intended to be binary compatible with the definitions +// given in the Itanium ABI. We make no attempt to be ODR-compatible with +// those definitions, since existing ABI implementations aren't. + +namespace std { + class type_info { + public: + virtual ~type_info(); + + const char *__type_name; + }; +} + +namespace __cxxabiv1 { + +/// Type info for classes with no bases, and base class for type info for +/// classes with bases. +class __class_type_info : public std::type_info { + ~__class_type_info() override; +}; + +/// Type info for classes with simple single public inheritance. +class __si_class_type_info : public __class_type_info { +public: + ~__si_class_type_info() override; + + const __class_type_info *__base_type; +}; + +class __base_class_type_info { +public: + const __class_type_info *__base_type; + long __offset_flags; + + enum __offset_flags_masks { + __virtual_mask = 0x1, + __public_mask = 0x2, + __offset_shift = 8 + }; +}; + +/// Type info for classes with multiple, virtual, or non-public inheritance. +class __vmi_class_type_info : public __class_type_info { +public: + ~__vmi_class_type_info() override; + + unsigned int flags; + unsigned int base_count; + __base_class_type_info base_info[1]; +}; + +} + +namespace abi = __cxxabiv1; + +// We implement a simple two-level cache for type-checking results. For each +// (vptr,type) pair, a hash is computed. This hash is assumed to be globally +// unique; if it collides, we will get false negatives, but: +// * such a collision would have to occur on the *first* bad access, +// * the probability of such a collision is low (and for a 64-bit target, is +// negligible), and +// * the vptr, and thus the hash, can be affected by ASLR, so multiple runs +// give better coverage. +// +// The first caching layer is a small hash table with no chaining; buckets are +// reused as needed. The second caching layer is a large hash table with open +// chaining. We can freely evict from either layer since this is just a cache. +// +// FIXME: Make these hash table accesses thread-safe. The races here are benign: +// assuming the unsequenced loads and stores don't misbehave too badly, +// the worst case is false negatives or poor cache behavior, not false +// positives or crashes. + +/// Find a bucket to store the given hash value in. +static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { + static const unsigned HashTableSize = 65537; + static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; + + unsigned First = (V & 65535) ^ 1; + unsigned Probe = First; + for (int Tries = 5; Tries; --Tries) { + if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) + return &__ubsan_vptr_hash_set[Probe]; + Probe += ((V >> 16) & 65535) + 1; + if (Probe >= HashTableSize) + Probe -= HashTableSize; + } + // FIXME: Pick a random entry from the probe sequence to evict rather than + // just taking the first. + return &__ubsan_vptr_hash_set[First]; +} + +/// \brief Determine whether \p Derived has a \p Base base class subobject at +/// offset \p Offset. +static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, + const abi::__class_type_info *Base, + sptr Offset) { + if (Derived->__type_name == Base->__type_name) + return Offset == 0; + + if (const abi::__si_class_type_info *SI = + dynamic_cast<const abi::__si_class_type_info*>(Derived)) + return isDerivedFromAtOffset(SI->__base_type, Base, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast<const abi::__vmi_class_type_info*>(Derived); + if (!VTI) + // No base class subobjects. + return false; + + // Look for a base class which is derived from \p Base at the right offset. + for (unsigned int base = 0; base != VTI->base_count; ++base) { + // FIXME: Curtail the recursion if this base can't possibly contain the + // given offset. + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // For now, just punt on virtual bases and say 'yes'. + // FIXME: OffsetHere is the offset in the vtable of the virtual base + // offset. Read the vbase offset out of the vtable and use it. + return true; + if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, + Base, Offset - OffsetHere)) + return true; + } + + return false; +} + +/// \brief Find the derived-most dynamic base class of \p Derived at offset +/// \p Offset. +static const abi::__class_type_info *findBaseAtOffset( + const abi::__class_type_info *Derived, sptr Offset) { + if (!Offset) + return Derived; + + if (const abi::__si_class_type_info *SI = + dynamic_cast<const abi::__si_class_type_info*>(Derived)) + return findBaseAtOffset(SI->__base_type, Offset); + + const abi::__vmi_class_type_info *VTI = + dynamic_cast<const abi::__vmi_class_type_info*>(Derived); + if (!VTI) + // No base class subobjects. + return 0; + + for (unsigned int base = 0; base != VTI->base_count; ++base) { + sptr OffsetHere = VTI->base_info[base].__offset_flags >> + abi::__base_class_type_info::__offset_shift; + if (VTI->base_info[base].__offset_flags & + abi::__base_class_type_info::__virtual_mask) + // FIXME: Can't handle virtual bases yet. + continue; + if (const abi::__class_type_info *Base = + findBaseAtOffset(VTI->base_info[base].__base_type, + Offset - OffsetHere)) + return Base; + } + + return 0; +} + +namespace { + +struct VtablePrefix { + /// The offset from the vptr to the start of the most-derived object. + /// This will only be greater than zero in some virtual base class vtables + /// used during object con-/destruction, and will usually be exactly zero. + sptr Offset; + /// The type_info object describing the most-derived class type. + std::type_info *TypeInfo; +}; +VtablePrefix *getVtablePrefix(void *Vtable) { + VtablePrefix *Vptr = reinterpret_cast<VtablePrefix*>(Vtable); + if (!Vptr) + return 0; + VtablePrefix *Prefix = Vptr - 1; + if (!Prefix->TypeInfo) + // This can't possibly be a valid vtable. + return 0; + return Prefix; +} + +} + +bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { + // A crash anywhere within this function probably means the vptr is corrupted. + // FIXME: Perform these checks more cautiously. + + // Check whether this is something we've evicted from the cache. + HashValue *Bucket = getTypeCacheHashTableBucket(Hash); + if (*Bucket == Hash) { + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + return true; + } + + void *VtablePtr = *reinterpret_cast<void **>(Object); + VtablePrefix *Vtable = getVtablePrefix(VtablePtr); + if (!Vtable) + return false; + + // Check that this is actually a type_info object for a class type. + abi::__class_type_info *Derived = + dynamic_cast<abi::__class_type_info*>(Vtable->TypeInfo); + if (!Derived) + return false; + + abi::__class_type_info *Base = (abi::__class_type_info*)Type; + if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) + return false; + + // Success. Cache this result. + __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; + *Bucket = Hash; + return true; +} + +__ubsan::DynamicTypeInfo +__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) { + VtablePrefix *Vtable = getVtablePrefix(VtablePtr); + if (!Vtable) + return DynamicTypeInfo(0, 0, 0); + const abi::__class_type_info *ObjectType = findBaseAtOffset( + static_cast<const abi::__class_type_info*>(Vtable->TypeInfo), + -Vtable->Offset); + return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, + ObjectType ? ObjectType->__type_name : "<unknown>"); +} + +#endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS diff --git a/lib/ubsan/ubsan_type_hash_win.cc b/lib/ubsan/ubsan_type_hash_win.cc new file mode 100644 index 000000000000..271c4aaf6fa8 --- /dev/null +++ b/lib/ubsan/ubsan_type_hash_win.cc @@ -0,0 +1,81 @@ +//===-- ubsan_type_hash_win.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of type hashing/lookup for Microsoft C++ ABI. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB && SANITIZER_WINDOWS +#include "ubsan_type_hash.h" + +#include "sanitizer_common/sanitizer_common.h" + +#include <typeinfo> + +struct CompleteObjectLocator { + int is_image_relative; + int offset_to_top; + int vfptr_offset; + int rtti_addr; + int chd_addr; + int obj_locator_addr; +}; + +struct CompleteObjectLocatorAbs { + int is_image_relative; + int offset_to_top; + int vfptr_offset; + std::type_info *rtti_addr; + void *chd_addr; + CompleteObjectLocator *obj_locator_addr; +}; + +bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { + // FIXME: Implement. + return false; +} + +__ubsan::DynamicTypeInfo +__ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) { + // The virtual table may not have a complete object locator if the object + // was compiled without RTTI (i.e. we might be reading from some other global + // laid out before the virtual table), so we need to carefully validate each + // pointer dereference and perform sanity checks. + CompleteObjectLocator **obj_locator_ptr = + ((CompleteObjectLocator**)VtablePtr)-1; + if (!IsAccessibleMemoryRange((uptr)obj_locator_ptr, sizeof(void*))) + return DynamicTypeInfo(0, 0, 0); + + CompleteObjectLocator *obj_locator = *obj_locator_ptr; + if (!IsAccessibleMemoryRange((uptr)obj_locator, + sizeof(CompleteObjectLocator))) + return DynamicTypeInfo(0, 0, 0); + + std::type_info *tinfo; + if (obj_locator->is_image_relative == 1) { + char *image_base = ((char *)obj_locator) - obj_locator->obj_locator_addr; + tinfo = (std::type_info *)(image_base + obj_locator->rtti_addr); + } else if (obj_locator->is_image_relative == 0) + tinfo = ((CompleteObjectLocatorAbs *)obj_locator)->rtti_addr; + else + // Probably not a complete object locator. + return DynamicTypeInfo(0, 0, 0); + + if (!IsAccessibleMemoryRange((uptr)tinfo, sizeof(std::type_info))) + return DynamicTypeInfo(0, 0, 0); + + // Okay, this is probably a std::type_info. Request its name. + // FIXME: Implement a base class search like we do for Itanium. + return DynamicTypeInfo(tinfo->name(), obj_locator->offset_to_top, + "<unknown>"); +} + +#endif // CAN_SANITIZE_UB && SANITIZER_WINDOWS diff --git a/lib/ubsan/ubsan_value.cc b/lib/ubsan/ubsan_value.cc index ab74720f60cf..79dc4c8e8271 100644 --- a/lib/ubsan/ubsan_value.cc +++ b/lib/ubsan/ubsan_value.cc @@ -12,6 +12,8 @@ // //===----------------------------------------------------------------------===// +#include "ubsan_platform.h" +#if CAN_SANITIZE_UB #include "ubsan_value.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" @@ -81,7 +83,14 @@ FloatMax Value::getFloatValue() const { #endif case 32: { float Value; - internal_memcpy(&Value, &Val, 4); +#if defined(__BIG_ENDIAN__) + // For big endian the float value is in the last 4 bytes. + // On some targets we may only have 4 bytes so we count backwards from + // the end of Val to account for both the 32-bit and 64-bit cases. + internal_memcpy(&Value, ((const char*)(&Val + 1)) - 4, 4); +#else + internal_memcpy(&Value, &Val, 4); +#endif return Value; } case 64: { @@ -100,3 +109,5 @@ FloatMax Value::getFloatValue() const { } UNREACHABLE("unexpected floating point bit width"); } + +#endif // CAN_SANITIZE_UB diff --git a/lib/ubsan/ubsan_value.h b/lib/ubsan/ubsan_value.h index a6810844049f..72eee15551ed 100644 --- a/lib/ubsan/ubsan_value.h +++ b/lib/ubsan/ubsan_value.h @@ -14,12 +14,6 @@ #ifndef UBSAN_VALUE_H #define UBSAN_VALUE_H -// For now, only support Linux, FreeBSD and Darwin. Other platforms should -// be easy to add, and probably work as-is. -#if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__APPLE__) -#error "UBSan not supported for this platform!" -#endif - #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" @@ -32,7 +26,6 @@ __extension__ typedef unsigned __int128 u128; #define HAVE_INT128_T 0 #endif - namespace __ubsan { /// \brief Largest integer types we support. diff --git a/make/platform/clang_darwin.mk b/make/platform/clang_darwin.mk index 4f71c0b46052..79925bcdd1a8 100644 --- a/make/platform/clang_darwin.mk +++ b/make/platform/clang_darwin.mk @@ -119,8 +119,11 @@ UniversalArchs.asan_osx_dynamic := $(call CheckArches,i386 x86_64 x86_64h,asan_o Configs += asan_iossim_dynamic UniversalArchs.asan_iossim_dynamic := $(call CheckArches,i386 x86_64,asan_iossim_dynamic,$(IOSSIM_SDK)) -Configs += ubsan_osx -UniversalArchs.ubsan_osx := $(call CheckArches,i386 x86_64 x86_64h,ubsan_osx,$(OSX_SDK)) +Configs += ubsan_osx_dynamic +UniversalArchs.ubsan_osx_dynamic := $(call CheckArches,i386 x86_64 x86_64h,ubsan_osx_dynamic,$(OSX_SDK)) + +Configs += ubsan_iossim_dynamic +UniversalArchs.ubsan_iossim_dynamic := $(call CheckArches,i386 x86_64,ubsan_iossim_dynamic,$(IOSSIM_SDK)) # Darwin 10.6 has a bug in cctools that makes it unable to use ranlib on our ARM # object files. If we are on that platform, strip out all ARM archs. We still @@ -172,26 +175,31 @@ IOSSIM_DEPLOYMENT_ARGS += -isysroot $(IOSSIM_SDK) CFLAGS.eprintf := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) CFLAGS.10.4 := $(CFLAGS) $(OSX_DEPLOYMENT_ARGS) +SANITIZER_MACOSX_DEPLOYMENT_ARGS := -mmacosx-version-min=10.7 +SANITIZER_IOSSIM_DEPLOYMENT_ARGS := -mios-simulator-version-min=7.0 \ + -isysroot $(IOSSIM_SDK) +SANITIZER_CFLAGS := -fno-builtin -gline-tables-only -stdlib=libc++ + CFLAGS.asan_osx_dynamic := \ - $(CFLAGS) -mmacosx-version-min=10.7 \ - -stdlib=libc++ \ - -isysroot $(OSX_SDK) \ - -fno-builtin \ - -gline-tables-only \ + $(CFLAGS) $(SANITIZER_MACOSX_DEPLOYMENT_ARGS) \ + $(SANITIZER_CFLAGS) \ -DMAC_INTERPOSE_FUNCTIONS=1 \ -DASAN_DYNAMIC=1 CFLAGS.asan_iossim_dynamic := \ - $(CFLAGS) -mios-simulator-version-min=7.0 \ - -isysroot $(IOSSIM_SDK) \ - -fno-builtin \ - -gline-tables-only \ + $(CFLAGS) $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS) \ + $(SANITIZER_CFLAGS) \ -DMAC_INTERPOSE_FUNCTIONS=1 \ -DASAN_DYNAMIC=1 -CFLAGS.ubsan_osx := $(CFLAGS) -mmacosx-version-min=10.6 \ - -isysroot $(OSX_SDK) \ - -fno-builtin +CFLAGS.ubsan_osx_dynamic := \ + $(CFLAGS) $(SANITIZER_MACOSX_DEPLOYMENT_ARGS) \ + $(SANITIZER_CFLAGS) + +CFLAGS.ubsan_iossim_dynamic := \ + $(CFLAGS) $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS) \ + $(SANITIZER_CFLAGS) + CFLAGS.ios.i386 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) CFLAGS.ios.x86_64 := $(CFLAGS) $(IOSSIM_DEPLOYMENT_ARGS) @@ -222,49 +230,81 @@ CFLAGS.profile_ios.armv7k := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) CFLAGS.profile_ios.armv7s := $(CFLAGS) $(IOS_DEPLOYMENT_ARGS) CFLAGS.profile_ios.arm64 := $(CFLAGS) $(IOS6_DEPLOYMENT_ARGS) -# Configure the asan_osx_dynamic library to be built shared. +SANITIZER_LDFLAGS := -stdlib=libc++ -lc++ -lc++abi + SHARED_LIBRARY.asan_osx_dynamic := 1 -LDFLAGS.asan_osx_dynamic := -lc++ -undefined dynamic_lookup -install_name @rpath/libclang_rt.asan_osx_dynamic.dylib \ - -mmacosx-version-min=10.7 \ - -isysroot $(OSX_SDK) +LDFLAGS.asan_osx_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.asan_osx_dynamic.dylib \ + $(SANITIZER_MACOSX_DEPLOYMENT_ARGS) -# Configure the asan_iossim_dynamic library to be built shared. SHARED_LIBRARY.asan_iossim_dynamic := 1 -# configure+make uses Clang, so we're using isysroot instead of --sysroot -# or -Wl,-syslibroot. -LDFLAGS.asan_iossim_dynamic := -undefined dynamic_lookup -install_name @rpath/libclang_rt.asan_iossim_dynamic.dylib \ - -Wl,-ios_simulator_version_min,7.0.0 \ - -mios-simulator-version-min=7.0 -isysroot $(IOSSIM_SDK) +LDFLAGS.asan_iossim_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.asan_iossim_dynamic.dylib \ + -Wl,-ios_simulator_version_min,7.0.0 $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS) + +SHARED_LIBRARY.ubsan_osx_dynamic := 1 +LDFLAGS.ubsan_osx_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.ubsan_osx_dynamic.dylib \ + $(SANITIZER_MACOSX_DEPLOYMENT_ARGS) + +SHARED_LIBRARY.ubsan_iossim_dynamic := 1 +LDFLAGS.ubsan_iossim_dynamic := $(SANITIZER_LDFLAGS) -install_name @rpath/libclang_rt.ubsan_iossim_dynamic.dylib \ + -Wl,-ios_simulator_version_min,7.0.0 $(SANITIZER_IOSSIM_DEPLOYMENT_ARGS) + +ifneq ($(OSX_SDK),) +CFLAGS.asan_osx_dynamic += -isysroot $(OSX_SDK) +LDFLAGS.asan_osx_dynamic += -isysroot $(OSX_SDK) +CFLAGS.ubsan_osx_dynamic += -isysroot $(OSX_SDK) +LDFLAGS.ubsan_osx_dynamic += -isysroot $(OSX_SDK) +endif + +ATOMIC_FUNCTIONS := \ + atomic_flag_clear \ + atomic_flag_clear_explicit \ + atomic_flag_test_and_set \ + atomic_flag_test_and_set_explicit \ + atomic_signal_fence \ + atomic_thread_fence + +FP16_FUNCTIONS := \ + extendhfsf2 \ + truncdfhf2 \ + truncsfhf2 FUNCTIONS.eprintf := eprintf FUNCTIONS.10.4 := eprintf floatundidf floatundisf floatundixf -FUNCTIONS.ios := divmodsi4 udivmodsi4 mulosi4 mulodi4 muloti4 +FUNCTIONS.ios := divmodsi4 udivmodsi4 mulosi4 mulodi4 muloti4 \ + $(ATOMIC_FUNCTIONS) $(FP16_FUNCTIONS) # On x86, the divmod functions reference divsi. FUNCTIONS.ios.i386 := $(FUNCTIONS.ios) \ divsi3 udivsi3 FUNCTIONS.ios.x86_64 := $(FUNCTIONS.ios.i386) -FUNCTIONS.ios.arm64 := mulsc3 muldc3 divsc3 divdc3 +FUNCTIONS.ios.arm64 := mulsc3 muldc3 divsc3 divdc3 $(ATOMIC_FUNCTIONS) -FUNCTIONS.osx := mulosi4 mulodi4 muloti4 +FUNCTIONS.osx := mulosi4 mulodi4 muloti4 $(ATOMIC_FUNCTIONS) $(FP16_FUNCTIONS) FUNCTIONS.profile_osx := GCDAProfiling InstrProfiling InstrProfilingBuffer \ InstrProfilingFile InstrProfilingPlatformDarwin \ - InstrProfilingRuntime + InstrProfilingRuntime InstrProfilingUtil FUNCTIONS.profile_ios := $(FUNCTIONS.profile_osx) FUNCTIONS.asan_osx_dynamic := $(AsanFunctions) $(AsanCXXFunctions) \ $(InterceptionFunctions) \ $(SanitizerCommonFunctions) \ - $(AsanDynamicFunctions) + $(AsanDynamicFunctions) \ + $(UbsanFunctions) $(UbsanCXXFunctions) FUNCTIONS.asan_iossim_dynamic := $(AsanFunctions) $(AsanCXXFunctions) \ $(InterceptionFunctions) \ $(SanitizerCommonFunctions) \ - $(AsanDynamicFunctions) + $(AsanDynamicFunctions) \ + $(UbsanFunctions) $(UbsanCXXFunctions) + +FUNCTIONS.ubsan_osx_dynamic := $(UbsanFunctions) $(UbsanCXXFunctions) \ + $(SanitizerCommonFunctions) \ + $(UbsanStandaloneFunctions) -FUNCTIONS.ubsan_osx := $(UbsanFunctions) $(UbsanCXXFunctions) \ - $(SanitizerCommonFunctions) +FUNCTIONS.ubsan_iossim_dynamic := $(UbsanFunctions) $(UbsanCXXFunctions) \ + $(SanitizerCommonFunctions) \ + $(UbsanStandaloneFunctions) CCKEXT_PROFILE_FUNCTIONS := \ InstrProfiling \ @@ -293,6 +333,7 @@ CCKEXT_COMMON_FUNCTIONS := \ udivmodsi4 \ do_global_dtors \ eprintf \ + extendhfsf2 \ ffsdi2 \ fixdfdi \ fixsfdi \ @@ -323,6 +364,8 @@ CCKEXT_COMMON_FUNCTIONS := \ powisf2 \ subvdi3 \ subvsi3 \ + truncdfhf2 \ + truncsfhf2 \ ucmpdi2 \ udiv_w_sdiv \ udivdi3 \ @@ -369,6 +412,7 @@ CCKEXT_ARM_FUNCTIONS := $(CCKEXT_COMMON_FUNCTIONS) \ modsi3 \ muldf3 \ mulsf3 \ + mulodi4 \ negdf2 \ negsf2 \ subdf3 \ diff --git a/make/platform/clang_linux.mk b/make/platform/clang_linux.mk index cb023f3db9cc..7b109d56c0a6 100644 --- a/make/platform/clang_linux.mk +++ b/make/platform/clang_linux.mk @@ -74,17 +74,11 @@ CFLAGS.builtins-x86_64 := $(CFLAGS) -m64 CFLAGS.profile-i386 := $(CFLAGS) -m32 CFLAGS.profile-x86_64 := $(CFLAGS) -m64 -# Use our stub SDK as the sysroot to support more portable building. For now we -# just do this for the core module, because the stub SDK doesn't have -# enough support to build the profile runtime. -CFLAGS.builtins-i386 += --sysroot=$(ProjSrcRoot)/SDKs/linux -CFLAGS.builtins-x86_64 += --sysroot=$(ProjSrcRoot)/SDKs/linux - FUNCTIONS.builtins-i386 := $(CommonFunctions) $(ArchFunctions.i386) FUNCTIONS.builtins-x86_64 := $(CommonFunctions) $(ArchFunctions.x86_64) FUNCTIONS.profile-i386 := GCDAProfiling InstrProfiling InstrProfilingBuffer \ InstrProfilingFile InstrProfilingPlatformOther \ - InstrProfilingRuntime + InstrProfilingRuntime InstrProfilingUtil FUNCTIONS.profile-x86_64 := $(FUNCTIONS.profile-i386) # Always use optimized variants. diff --git a/make/platform/clang_macho_embedded.mk b/make/platform/clang_macho_embedded.mk index 76e43e66cdb7..d7870d491f15 100644 --- a/make/platform/clang_macho_embedded.mk +++ b/make/platform/clang_macho_embedded.mk @@ -156,6 +156,7 @@ COMMON_FUNCTIONS := \ divsf3 \ divsi3 \ extendsfdf2 \ + extendhfsf2 \ ffssi2 \ fixdfsi \ fixsfsi \ @@ -172,11 +173,19 @@ COMMON_FUNCTIONS := \ negsf2 \ subdf3 \ subsf3 \ + truncdfhf2 \ truncdfsf2 \ + truncsfhf2 \ udivsi3 \ umodsi3 \ unorddf2 \ - unordsf2 + unordsf2 \ + atomic_flag_clear \ + atomic_flag_clear_explicit \ + atomic_flag_test_and_set \ + atomic_flag_test_and_set_explicit \ + atomic_signal_fence \ + atomic_thread_fence ARM_FUNCTIONS := \ aeabi_cdcmpeq \ diff --git a/make/platform/darwin_bni.mk b/make/platform/darwin_bni.mk index 2fd5089baa92..8e066e8e31aa 100644 --- a/make/platform/darwin_bni.mk +++ b/make/platform/darwin_bni.mk @@ -32,7 +32,7 @@ endif CFLAGS := -Wall -Os -fomit-frame-pointer -g $(DEPLOYMENT_FLAGS) CFLAGS.Static := $(CFLAGS) -static DYLIB_FLAGS := $(DEPLOYMENT_FLAGS) \ - -Xarch_arm -Wl,-alias_list,$(SRCROOT)/lib/arm/softfloat-alias.list + -Xarch_arm -Wl,-alias_list,$(SRCROOT)/lib/builtins/arm/softfloat-alias.list VISIBILITY_HIDDEN := 0 VISIBILITY_HIDDEN.Static := 1 @@ -47,7 +47,11 @@ FUNCTIONS := absvdi2 absvsi2 addvdi3 addvsi3 ashldi3 ashrdi3 \ mulodi4 muloti4 mulsc3 mulvdi3 mulvsi3 negdi2 negvdi2 negvsi2 \ paritydi2 paritysi2 popcountdi2 popcountsi2 powidf2 \ powisf2 subvdi3 subvsi3 ucmpdi2 udivdi3 \ - udivmoddi4 umoddi3 apple_versioning eprintf atomic + udivmoddi4 umoddi3 apple_versioning eprintf atomic \ + atomic_flag_clear atomic_flag_clear_explicit \ + atomic_flag_test_and_set atomic_flag_test_and_set_explicit \ + atomic_signal_fence atomic_thread_fence \ + extendhfsf2 truncdfhf2 truncsfhf2 FUNCTIONS.i386 := $(FUNCTIONS) \ divxc3 fixunsxfdi fixunsxfsi fixxfdi floatdixf \ @@ -124,5 +128,8 @@ FUNCTIONS.arm64 := divti3 modti3 \ fixdfti fixsfti \ fixunsdfti fixunssfti fixunssfti \ floattidf floattisf floatuntidf floatuntisf \ - gcc_personality_v0 atomic - + gcc_personality_v0 atomic \ + atomic_flag_clear atomic_flag_clear_explicit \ + atomic_flag_test_and_set \ + atomic_flag_test_and_set_explicit \ + atomic_signal_fence atomic_thread_fence diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 85a1735b1cf9..b4fea075c399 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,6 +24,9 @@ if(NOT ANDROID) if (COMPILER_RT_HAS_PROFILE) list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS profile) endif() + if (WIN32) + list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS KillTheDoctor) + endif() endif() if(UNIX) list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) @@ -58,6 +61,9 @@ if(COMPILER_RT_CAN_EXECUTE_TESTS) add_subdirectory(ubsan) endif() add_subdirectory(cfi) + if(COMPILER_RT_HAS_SAFESTACK) + add_subdirectory(safestack) + endif() endif() if(COMPILER_RT_STANDALONE_BUILD) diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt index e1b81264604d..aff54db1e77a 100644 --- a/test/asan/CMakeLists.txt +++ b/test/asan/CMakeLists.txt @@ -5,9 +5,9 @@ set(ASAN_DYNAMIC_TESTSUITES) macro(get_bits_for_arch arch bits) if (${arch} MATCHES "i386|i686|arm|mips|mipsel") - set(bits 32) + set(${bits} 32) elseif (${arch} MATCHES "x86_64|powerpc64|powerpc64le|aarch64|mips64|mips64el") - set(bits 64) + set(${bits} 64) else() message(FATAL_ERROR "Unknown target architecture: ${arch}") endif() @@ -28,6 +28,7 @@ foreach(arch ${ASAN_SUPPORTED_ARCH}) set(ASAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) else() get_target_flags_for_arch(${arch} ASAN_TEST_TARGET_CFLAGS) + string(REPLACE ";" " " ASAN_TEST_TARGET_CFLAGS "${ASAN_TEST_TARGET_CFLAGS}") endif() if(ANDROID) set(ASAN_TEST_DYNAMIC True) diff --git a/test/asan/TestCases/Android/coverage-android.cc b/test/asan/TestCases/Android/coverage-android.cc index e243059fbbec..5f2631605595 100644 --- a/test/asan/TestCases/Android/coverage-android.cc +++ b/test/asan/TestCases/Android/coverage-android.cc @@ -1,15 +1,15 @@ // Test for direct coverage writing with dlopen. // Test normal exit, coverage level 1. -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%device\" %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func -DSO_DIR=\"%device\" %s -o %t // RUN: adb shell rm -rf %device/coverage-android // RUN: rm -rf %T/coverage-android // RUN: adb shell mkdir -p %device/coverage-android/direct // RUN: mkdir -p %T/coverage-android/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t // RUN: adb pull %device/coverage-android/direct %T/coverage-android/direct // RUN: ls; pwd // RUN: cd %T/coverage-android/direct @@ -18,15 +18,15 @@ // Test sudden death, coverage level 1. -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%device\" %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func -DSO_DIR=\"%device\" %s -o %t // RUN: adb shell rm -rf %device/coverage-android-kill // RUN: rm -rf %T/coverage-android-kill // RUN: adb shell mkdir -p %device/coverage-android-kill/direct // RUN: mkdir -p %T/coverage-android-kill/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t // RUN: adb pull %device/coverage-android-kill/direct %T/coverage-android-kill/direct // RUN: ls; pwd // RUN: cd %T/coverage-android-kill/direct @@ -35,15 +35,15 @@ // Test normal exit, coverage level 2. -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSO_DIR=\"%device\" %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSO_DIR=\"%device\" %s -o %t // RUN: adb shell rm -rf %device/coverage-android // RUN: rm -rf %T/coverage-android // RUN: adb shell mkdir -p %device/coverage-android/direct // RUN: mkdir -p %T/coverage-android/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t // RUN: adb pull %device/coverage-android/direct %T/coverage-android/direct // RUN: ls; pwd // RUN: cd %T/coverage-android/direct @@ -52,15 +52,15 @@ // Test sudden death, coverage level 2. -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSO_DIR=\"%device\" %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSO_DIR=\"%device\" %s -o %t // RUN: adb shell rm -rf %device/coverage-android-kill // RUN: rm -rf %T/coverage-android-kill // RUN: adb shell mkdir -p %device/coverage-android-kill/direct // RUN: mkdir -p %T/coverage-android-kill/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t // RUN: adb pull %device/coverage-android-kill/direct %T/coverage-android-kill/direct // RUN: ls; pwd // RUN: cd %T/coverage-android-kill/direct @@ -69,15 +69,15 @@ // Test normal exit, coverage level 3. -// RUN: %clangxx_asan -fsanitize-coverage=3 -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=3 -DSO_DIR=\"%device\" %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=edge -DSHARED %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=edge -DSO_DIR=\"%device\" %s -o %t // RUN: adb shell rm -rf %device/coverage-android // RUN: rm -rf %T/coverage-android // RUN: adb shell mkdir -p %device/coverage-android/direct // RUN: mkdir -p %T/coverage-android/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android/direct:verbosity=1 %run %t // RUN: adb pull %device/coverage-android/direct %T/coverage-android/direct // RUN: ls; pwd // RUN: cd %T/coverage-android/direct @@ -86,15 +86,15 @@ // Test sudden death, coverage level 3. -// RUN: %clangxx_asan -fsanitize-coverage=3 -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=3 -DSO_DIR=\"%device\" %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=edge -DSHARED -DKILL %s -shared -o %T/libcoverage_android_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=edge -DSO_DIR=\"%device\" %s -o %t // RUN: adb shell rm -rf %device/coverage-android-kill // RUN: rm -rf %T/coverage-android-kill // RUN: adb shell mkdir -p %device/coverage-android-kill/direct // RUN: mkdir -p %T/coverage-android-kill/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%device/coverage-android-kill/direct:verbosity=1 not %run %t // RUN: adb pull %device/coverage-android-kill/direct %T/coverage-android-kill/direct // RUN: ls; pwd // RUN: cd %T/coverage-android-kill/direct diff --git a/test/asan/TestCases/Darwin/atos-symbolizer-dyld-root-path.cc b/test/asan/TestCases/Darwin/atos-symbolizer-dyld-root-path.cc new file mode 100644 index 000000000000..f6070188d8e5 --- /dev/null +++ b/test/asan/TestCases/Darwin/atos-symbolizer-dyld-root-path.cc @@ -0,0 +1,26 @@ +// Check that when having a DYLD_ROOT_PATH set, the symbolizer still works. +// RUN: env DYLD_ROOT_PATH="/" ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 ASAN_SYMBOLIZER_PATH=$(which atos) \ +// RUN: not %run %t 2>&1 | FileCheck %s +// +// Due to a bug in atos, this only works on x86_64. +// REQUIRES: x86_64 + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x); + free(x + argc - 1); // BOOM + // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 + // CHECK: Using atos at user-specified path: + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-4]] + // CHECK: freed by thread T0 here: + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-8]] + // CHECK: allocated by thread T0 here: + // CHECK: atos-symbolizer.cc:[[@LINE-13]] + return res; +} diff --git a/test/asan/TestCases/Darwin/atos-symbolizer.cc b/test/asan/TestCases/Darwin/atos-symbolizer.cc new file mode 100644 index 000000000000..03cadf92d16a --- /dev/null +++ b/test/asan/TestCases/Darwin/atos-symbolizer.cc @@ -0,0 +1,24 @@ +// Check that the `atos` symbolizer works. + +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 ASAN_SYMBOLIZER_PATH=$(which atos) not %run %t 2>&1 | FileCheck %s + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(10 * sizeof(char)); + memset(x, 0, 10); + int res = x[argc]; + free(x); + free(x + argc - 1); // BOOM + // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0 + // CHECK: Using atos at user-specified path: + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-4]] + // CHECK: freed by thread T0 here: + // CHECK: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-8]] + // CHECK: allocated by thread T0 here: + // CHECK: atos-symbolizer.cc:[[@LINE-13]] + return res; +} diff --git a/test/asan/TestCases/Darwin/dladdr-demangling.cc b/test/asan/TestCases/Darwin/dladdr-demangling.cc new file mode 100644 index 000000000000..3d36c4f96355 --- /dev/null +++ b/test/asan/TestCases/Darwin/dladdr-demangling.cc @@ -0,0 +1,33 @@ +// In a non-forking sandbox, we fallback to dladdr(). Test that we provide +// properly demangled C++ names in that case. + +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 not %run sandbox-exec -p '(version 1)(allow default)(deny process-fork)' %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-DLADDR + +#include <stdlib.h> + +class MyClass { + public: + int my_function(int n) { + char *x = (char*)malloc(n * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK-DLADDR: Using dladdr symbolizer + // CHECK-DLADDR: failed to fork external symbolizer + // CHECK: {{ #0 0x.* in MyClass::my_function\(int\)}} + // CHECK: {{freed by thread T0 here:}} + // CHECK: {{ #0 0x.* in wrap_free}} + // CHECK: {{ #1 0x.* in MyClass::my_function\(int\)}} + // CHECK: {{previously allocated by thread T0 here:}} + // CHECK: {{ #0 0x.* in wrap_malloc}} + // CHECK: {{ #1 0x.* in MyClass::my_function\(int\)}} + } +}; + +int main() { + MyClass o; + return o.my_function(10); +} diff --git a/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc b/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc index 0bd4170a353c..486223473d47 100644 --- a/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc +++ b/test/asan/TestCases/Darwin/dyld_insert_libraries_reexec.cc @@ -7,11 +7,25 @@ // RUN: | sed -e 's/.*"\(.*libclang_rt.asan_osx_dynamic.dylib\)".*/\1/'` \ // RUN: %T/dyld_insert_libraries_reexec/libclang_rt.asan_osx_dynamic.dylib // RUN: %clangxx_asan %s -o %T/dyld_insert_libraries_reexec/a.out -// RUN: DYLD_INSERT_LIBRARIES=@executable_path/libclang_rt.asan_osx_dynamic.dylib \ -// RUN: ASAN_OPTIONS=verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ + +// RUN: env DYLD_INSERT_LIBRARIES=@executable_path/libclang_rt.asan_osx_dynamic.dylib \ +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ // RUN: | FileCheck %s -// RUN: ASAN_OPTIONS=verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ -// RUN: | FileCheck --check-prefix=CHECK-NOINSERT %s + +// RUN: IS_OSX_10_11_OR_HIGHER=$([ `sw_vers -productVersion | cut -d'.' -f2` -lt 11 ]; echo $?) + +// On OS X 10.10 and lower, if the dylib is not DYLD-inserted, ASan will re-exec. +// RUN: if [ $IS_OSX_10_11_OR_HIGHER == 0 ]; then \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ +// RUN: | FileCheck --check-prefix=CHECK-NOINSERT %s; \ +// RUN: fi + +// On OS X 10.11 and higher, we don't need to DYLD-insert anymore, and the interceptors +// still installed correctly. Let's just check that things work and we don't try to re-exec. +// RUN: if [ $IS_OSX_10_11_OR_HIGHER == 1 ]; then \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1 %run %T/dyld_insert_libraries_reexec/a.out 2>&1 \ +// RUN: | FileCheck %s; \ +// RUN: fi #include <stdio.h> diff --git a/test/asan/TestCases/Darwin/empty-section.cc b/test/asan/TestCases/Darwin/empty-section.cc new file mode 100644 index 000000000000..5b006b545488 --- /dev/null +++ b/test/asan/TestCases/Darwin/empty-section.cc @@ -0,0 +1,12 @@ +// Regression test with an empty (length = 0) custom section. + +// RUN: %clangxx_asan -g -O0 %s -c -o %t.o +// RUN: %clangxx_asan -g -O0 %t.o -o %t -sectcreate mysegment mysection /dev/null +// RUN: %run %t 2>&1 | FileCheck %s + +#include <stdio.h> + +int main() { + printf("Hello, world!\n"); + // CHECK: Hello, world! +} diff --git a/test/asan/TestCases/Darwin/interception-in-shared-lib-test.cc b/test/asan/TestCases/Darwin/interception-in-shared-lib-test.cc deleted file mode 100644 index 028683d2fc41..000000000000 --- a/test/asan/TestCases/Darwin/interception-in-shared-lib-test.cc +++ /dev/null @@ -1,32 +0,0 @@ -// Check that memset() call from a shared library gets intercepted. -// Please always keep this file in sync with -// ../Linux/interception-in-shared-lib-test.cc. - -// RUN: %clangxx_asan -O0 %s -DSHARED_LIB \ -// RUN: -shared -o %t-so.so \ -// RUN: -fPIC -install_name @rpath/interception-in-shared-lib-test.cc.tmp-so.so -// TODO(glider): figure out how to set rpath in a more portable way and unite -// this test with ../Linux/interception-in-shared-lib-test.cc. -// RUN: %clangxx_asan -O0 %s -o %t -Wl,-rpath,@executable_path %t-so.so && \ -// RUN: not %run %t 2>&1 | FileCheck %s - -#include <stdio.h> -#include <string.h> - -#if defined(SHARED_LIB) -extern "C" -void my_memset(void *p, size_t sz) { - memset(p, 0, sz); -} -#else -extern "C" void my_memset(void *p, size_t sz); - -int main(int argc, char *argv[]) { - char buf[10]; - my_memset(buf, 11); - // CHECK: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}} - // CHECK: {{WRITE of size 11 at 0x.* thread T0}} - // CHECK: {{0x.* in my_memset .*interception-in-shared-lib-test.cc:19}} - return 0; -} -#endif diff --git a/test/asan/TestCases/Darwin/interface_symbols_darwin.c b/test/asan/TestCases/Darwin/interface_symbols_darwin.c index a75044d491fb..bd9bbee84a92 100644 --- a/test/asan/TestCases/Darwin/interface_symbols_darwin.c +++ b/test/asan/TestCases/Darwin/interface_symbols_darwin.c @@ -29,6 +29,18 @@ // RUN: echo __asan_report_store16 >> %t.interface // RUN: echo __asan_report_load_n >> %t.interface // RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_report_exp_load1 >> %t.interface +// RUN: echo __asan_report_exp_load2 >> %t.interface +// RUN: echo __asan_report_exp_load4 >> %t.interface +// RUN: echo __asan_report_exp_load8 >> %t.interface +// RUN: echo __asan_report_exp_load16 >> %t.interface +// RUN: echo __asan_report_exp_store1 >> %t.interface +// RUN: echo __asan_report_exp_store2 >> %t.interface +// RUN: echo __asan_report_exp_store4 >> %t.interface +// RUN: echo __asan_report_exp_store8 >> %t.interface +// RUN: echo __asan_report_exp_store16 >> %t.interface +// RUN: echo __asan_report_exp_load_n >> %t.interface +// RUN: echo __asan_report_exp_store_n >> %t.interface // RUN: echo __asan_get_current_fake_stack >> %t.interface // RUN: echo __asan_addr_is_in_fake_stack >> %t.interface // RUN: echo __asan_mz_calloc >> %t.interface diff --git a/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc b/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc index 59ddd634b400..aa4d92b00a01 100644 --- a/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc +++ b/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc @@ -7,7 +7,7 @@ // RUN: -dynamiclib -o darwin-dummy-shared-lib-so.dylib // FIXME: the following command line may hang in the case of a regression. -// RUN: DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ +// RUN: env DYLD_INSERT_LIBRARIES=darwin-dummy-shared-lib-so.dylib \ // RUN: %run %t 2>&1 | FileCheck %s || exit 1 #if !defined(SHARED_LIB) diff --git a/test/asan/TestCases/Darwin/sandbox-symbolizer.cc b/test/asan/TestCases/Darwin/sandbox-symbolizer.cc new file mode 100644 index 000000000000..4310f9c599e3 --- /dev/null +++ b/test/asan/TestCases/Darwin/sandbox-symbolizer.cc @@ -0,0 +1,29 @@ +// In a non-forking sandbox, we can't spawn an external symbolizer, but dladdr() +// should still work and provide function names. No line numbers though. +// Second, `atos` symbolizer can't inspect a process that has an inaccessible +// task port, in which case we should again fallback to dladdr gracefully. + +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny process-fork)' %t 2>&1 | FileCheck %s +// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s +// RUN: env ASAN_SYMBOLIZER_PATH="" not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny process-fork)' %t 2>&1 | FileCheck %s +// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s +// RUN: env ASAN_SYMBOLIZER_PATH="" not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main}} + // CHECK: {{freed by thread T0 here:}} + // CHECK: {{ #0 0x.* in wrap_free}} + // CHECK: {{ #1 0x.* in main}} + // CHECK: {{previously allocated by thread T0 here:}} + // CHECK: {{ #0 0x.* in wrap_malloc}} + // CHECK: {{ #1 0x.* in main}} +} diff --git a/test/asan/TestCases/Darwin/suppressions-darwin.cc b/test/asan/TestCases/Darwin/suppressions-darwin.cc index fb37296d404f..488bff140225 100644 --- a/test/asan/TestCases/Darwin/suppressions-darwin.cc +++ b/test/asan/TestCases/Darwin/suppressions-darwin.cc @@ -4,17 +4,17 @@ // Check that suppressing the interceptor by name works. // RUN: echo "interceptor_name:memmove" > %t.supp -// RUN: ASAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s // Check that suppressing by interceptor name works even without the symbolizer -// RUN: ASAN_OPTIONS="suppressions='%t.supp':symbolize=false" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp':symbolize=false" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s // Check that suppressing all reports from a library works. // RUN: echo "interceptor_via_lib:CoreFoundation" > %t.supp -// RUN: ASAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s // Check that suppressing library works even without the symbolizer. -// RUN: ASAN_OPTIONS="suppressions='%t.supp':symbolize=false" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp':symbolize=false" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s #include <CoreFoundation/CoreFoundation.h> diff --git a/test/asan/TestCases/Darwin/suppressions-sandbox.cc b/test/asan/TestCases/Darwin/suppressions-sandbox.cc new file mode 100644 index 000000000000..47d80f80db2b --- /dev/null +++ b/test/asan/TestCases/Darwin/suppressions-sandbox.cc @@ -0,0 +1,26 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t -framework Foundation +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// Check that suppressing a function name works within a no-fork sandbox +// RUN: echo "interceptor_via_fun:CFStringCreateWithBytes" > %t.supp +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:suppressions=%t.supp \ +// RUN: sandbox-exec -p '(version 1)(allow default)(deny process-fork)' \ +// RUN: %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s + +#include <CoreFoundation/CoreFoundation.h> + +int main() { + char *a = (char *)malloc(6); + strcpy(a, "hello"); + CFStringRef str = + CFStringCreateWithBytes(kCFAllocatorDefault, (unsigned char *)a, 10, + kCFStringEncodingUTF8, FALSE); // BOOM + fprintf(stderr, "Ignored.\n"); + free(a); +} + +// CHECK-CRASH: AddressSanitizer: heap-buffer-overflow +// CHECK-CRASH-NOT: Ignored. +// CHECK-IGNORE-NOT: AddressSanitizer: heap-buffer-overflow +// CHECK-IGNORE: Ignored. diff --git a/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc b/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc index ed476b223af3..f8a330ad5fe0 100644 --- a/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc +++ b/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc @@ -10,7 +10,7 @@ // execl(). // RUN: %run %t %T/echo-env >/dev/null 2>&1 -// RUN: DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \ +// RUN: env DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \ // RUN: %run %t %T/echo-env 2>&1 | FileCheck %s || exit 1 #if !defined(SHARED_LIB) diff --git a/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc b/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc index d4606f0afb52..54f26f16724c 100644 --- a/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc +++ b/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc @@ -1,2 +1,2 @@ -void *bar(void *input); -void *glob2 = bar((void*)0x2345); +void *bar(void *input, bool sleep_before_init); +void *glob2 = bar((void*)0x2345, true); diff --git a/test/asan/TestCases/Linux/asan_default_suppressions.cc b/test/asan/TestCases/Linux/asan_default_suppressions.cc new file mode 100644 index 000000000000..5ff59c1dfe7f --- /dev/null +++ b/test/asan/TestCases/Linux/asan_default_suppressions.cc @@ -0,0 +1,7 @@ +// Test that we use the suppressions from __asan_default_suppressions. +// RUN: %clangxx_asan %s -o %t && not %run %t 2>&1 | FileCheck %s +extern "C" { + const char *__asan_default_suppressions() { return "FooBar"; } +} +// CHECK: AddressSanitizer: failed to parse suppressions +int main() {} diff --git a/test/asan/TestCases/Linux/asan_prelink_test.cc b/test/asan/TestCases/Linux/asan_prelink_test.cc index 6145c01f7342..9e58f83d40c6 100644 --- a/test/asan/TestCases/Linux/asan_prelink_test.cc +++ b/test/asan/TestCases/Linux/asan_prelink_test.cc @@ -7,7 +7,7 @@ // RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext-segment=0x3600000000 ||\ // RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -Wl,-Ttext=0x3600000000 // RUN: %clangxx_asan %t.o %t.so -Wl,-R. -o %t -// RUN: ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1 %run %t 2>&1 | FileCheck %s // GNU driver doesn't handle .so files properly. // REQUIRES: x86_64-supported-target, asan-64-bits, Clang diff --git a/test/asan/TestCases/Linux/asan_preload_test-1.cc b/test/asan/TestCases/Linux/asan_preload_test-1.cc index e5eab5545b83..4e365b5633f3 100644 --- a/test/asan/TestCases/Linux/asan_preload_test-1.cc +++ b/test/asan/TestCases/Linux/asan_preload_test-1.cc @@ -5,7 +5,7 @@ // RUN: %clangxx %s %t.so -o %t // // RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so -// RUN: LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s +// RUN: env LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime diff --git a/test/asan/TestCases/Linux/asan_preload_test-2.cc b/test/asan/TestCases/Linux/asan_preload_test-2.cc index 0f22264cf1fb..488fd52e682a 100644 --- a/test/asan/TestCases/Linux/asan_preload_test-2.cc +++ b/test/asan/TestCases/Linux/asan_preload_test-2.cc @@ -1,7 +1,7 @@ // Test that preloaded runtime works with unsanitized executables. // // RUN: %clangxx %s -o %t -// RUN: LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s +// RUN: env LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime diff --git a/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc b/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc index 30f1c17700c8..8cf761c905f8 100644 --- a/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc +++ b/test/asan/TestCases/Linux/asan_rt_confict_test-1.cc @@ -2,7 +2,7 @@ // executable is prohibited. // // RUN: %clangxx_asan_static %s -o %t -// RUN: LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s +// RUN: env LD_PRELOAD=%shared_libasan not %run %t 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime // XFAIL: android diff --git a/test/asan/TestCases/Linux/clang_gcc_abi.cc b/test/asan/TestCases/Linux/clang_gcc_abi.cc index e833881661d2..669d1524077c 100644 --- a/test/asan/TestCases/Linux/clang_gcc_abi.cc +++ b/test/asan/TestCases/Linux/clang_gcc_abi.cc @@ -8,9 +8,10 @@ #include <stdlib.h> +__attribute__((noinline)) int boom() { volatile int three = 3; - char *s = (char *)malloc(three); + char * volatile s = (char *)malloc(three); // CHECK: #1 0x{{.*}} in boom {{.*}}clang_gcc_abi.cc:[[@LINE-1]] return s[three]; //BOOM } diff --git a/test/asan/TestCases/Linux/coverage-levels.cc b/test/asan/TestCases/Linux/coverage-levels.cc deleted file mode 100644 index cc196c5a9e18..000000000000 --- a/test/asan/TestCases/Linux/coverage-levels.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Test various levels of coverage -// -// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 %s -o %t -// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 -// RUN: %clangxx_asan -O1 -fsanitize-coverage=2 %s -o %t -// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 -// RUN: %clangxx_asan -O1 -fsanitize-coverage=3 %s -o %t -// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 - -// RUN: ASAN_OPTIONS=coverage=1:coverage_bitset=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET -// RUN: ASAN_OPTIONS=coverage=1:coverage_pcs=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOPCS -// -// REQUIRES: asan-64-bits - -volatile int sink; -int main(int argc, char **argv) { - if (argc == 0) - sink = 0; -} - -// CHECK1: CovDump: bitset of 1 bits written, 1 bits are set -// CHECK1: 1 PCs written -// CHECK2: CovDump: bitset of 3 bits written, 2 bits are set -// CHECK2: 2 PCs written -// CHECK3: CovDump: bitset of 4 bits written, 3 bits are set -// CHECK3: 3 PCs written -// CHECK3_NOBITSET-NOT: bitset of -// CHECK3_NOPCS-NOT: PCs written diff --git a/test/asan/TestCases/Linux/coverage-missing.cc b/test/asan/TestCases/Linux/coverage-missing.cc new file mode 100644 index 000000000000..36f33b505e27 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage-missing.cc @@ -0,0 +1,84 @@ +// Test for "sancov.py missing ...". + +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_dir=%T/coverage-missing + +// First case: coverage from executable. main() is called on every code path. +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t -DFOOBAR -DMAIN +// RUN: rm -rf %T/coverage-missing +// RUN: mkdir -p %T/coverage-missing +// RUN: cd %T/coverage-missing +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS %t +// RUN: %sancov print *.sancov > main.txt +// RUN: rm *.sancov +// RUN: [ $(cat main.txt | wc -l) == 1 ] +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS %t x +// RUN: %sancov print *.sancov > foo.txt +// RUN: rm *.sancov +// RUN: [ $(cat foo.txt | wc -l) == 3 ] +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS %t x x +// RUN: %sancov print *.sancov > bar.txt +// RUN: rm *.sancov +// RUN: [ $(cat bar.txt | wc -l) == 4 ] +// RUN: %sancov missing %t < foo.txt > foo-missing.txt +// RUN: sort main.txt foo-missing.txt -o foo-missing-with-main.txt +// The "missing from foo" set may contain a few bogus PCs from the sanitizer +// runtime, but it must include the entire "bar" code path as a subset. Sorted +// lists can be tested for set inclusion with diff + grep. +// RUN: ( diff bar.txt foo-missing-with-main.txt || true ) | not grep "^<" + +// Second case: coverage from DSO. +// cd %T +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %dynamiclib -DFOOBAR -shared -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func %s %dynamiclib -o %t -DMAIN +// RUN: export LIBNAME=`basename %dynamiclib` +// RUN: rm -rf %T/coverage-missing +// RUN: mkdir -p %T/coverage-missing +// RUN: cd %T/coverage-missing +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS %t x +// RUN: %sancov print $LIBNAME.*.sancov > foo.txt +// RUN: rm *.sancov +// RUN: [ $(cat foo.txt | wc -l) == 2 ] +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS %t x x +// RUN: %sancov print $LIBNAME.*.sancov > bar.txt +// RUN: rm *.sancov +// RUN: [ $(cat bar.txt | wc -l) == 3 ] +// RUN: %sancov missing %dynamiclib < foo.txt > foo-missing.txt +// RUN: ( diff bar.txt foo-missing.txt || true ) | not grep "^<" + +// REQUIRES: x86_64-supported-target, i386-supported-target +// XFAIL: android + +#include <stdio.h> + +void foo1(); +void foo2(); +void bar1(); +void bar2(); +void bar3(); + +#if defined(FOOBAR) +void foo1() { fprintf(stderr, "foo1\n"); } +void foo2() { fprintf(stderr, "foo2\n"); } + +void bar1() { fprintf(stderr, "bar1\n"); } +void bar2() { fprintf(stderr, "bar2\n"); } +void bar3() { fprintf(stderr, "bar3\n"); } +#endif + +#if defined(MAIN) +int main(int argc, char **argv) { + switch (argc) { + case 1: + break; + case 2: + foo1(); + foo2(); + break; + case 3: + bar1(); + bar2(); + bar3(); + break; + } +} +#endif diff --git a/test/asan/TestCases/Linux/coverage-tracing.cc b/test/asan/TestCases/Linux/coverage-tracing.cc deleted file mode 100644 index 49dbb5e9528b..000000000000 --- a/test/asan/TestCases/Linux/coverage-tracing.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Test -mllvm -sanitizer-coverage-experimental-tracing -// -// RUN: %clangxx_asan -O1 -fsanitize-coverage=1 -mllvm -sanitizer-coverage-experimental-tracing %s -o %t -// RUN: rm -rf %T/coverage-tracing -// RUN: mkdir %T/coverage-tracing -// RUN: cd %T/coverage-tracing -// RUN: A=x; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK1; mv trace-points.*.sancov $A.points -// RUN: A=f; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points -// RUN: A=b; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points -// RUN: A=bf; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points -// RUN: A=fb; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points -// RUN: A=ffb; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points -// RUN: A=fff; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points -// RUN: A=bbf; ASAN_OPTIONS=coverage=1:verbosity=1 %run %t $A 100 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK301; mv trace-points.*.sancov $A.points -// RUN: diff f.points fff.points -// RUN: diff bf.points fb.points -// RUN: diff bf.points ffb.points -// RUN: diff bf.points bbf.points -// RUN: not diff x.points f.points -// RUN: not diff x.points b.points -// RUN: not diff x.points bf.points -// RUN: not diff f.points b.points -// RUN: not diff f.points bf.points -// RUN: not diff b.points bf.points -// RUN: rm -rf %T/coverage-tracing -// -// REQUIRES: asan-64-bits - -#include <stdlib.h> -volatile int sink; -__attribute__((noinline)) void foo() { sink++; } -__attribute__((noinline)) void bar() { sink++; } - -int main(int argc, char **argv) { - if (argc != 3) return 0; - int n = strtol(argv[2], 0, 10); - while (n-- > 0) { - for (int i = 0; argv[1][i]; i++) { - if (argv[1][i] == 'f') foo(); - else if (argv[1][i] == 'b') bar(); - } - } -} - -// CHECK: CovDump: Trace: 3 PCs written -// CHECK1: CovDump: Trace: 1 Events written -// CHECK2: CovDump: Trace: 2 Events written -// CHECK3: CovDump: Trace: 3 Events written -// CHECK4: CovDump: Trace: 4 Events written -// CHECK301: CovDump: Trace: 301 Events written diff --git a/test/asan/TestCases/Linux/init-order-dlopen.cc b/test/asan/TestCases/Linux/init-order-dlopen.cc new file mode 100644 index 000000000000..fcfb5d143df6 --- /dev/null +++ b/test/asan/TestCases/Linux/init-order-dlopen.cc @@ -0,0 +1,47 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=178 + +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s %libdl -Wl,--export-dynamic -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_init_order=true %run %t 2>&1 + +// dlopen() can not be intercepted on Android, making strict_init_order nearly +// useless there. +// UNSUPPORTED: android + +#if defined(SHARED_LIB) +#include <stdio.h> + +struct Bar { + Bar(int val) : val(val) { printf("Bar::Bar(%d)\n", val); } + int val; +}; + +int get_foo_val(); +Bar global_bar(get_foo_val()); +#else // SHARED LIB +#include <dlfcn.h> +#include <stdio.h> +#include <string> +struct Foo { + Foo() : val(42) { printf("Foo::Foo()\n"); } + int val; +}; + +Foo global_foo; + +int get_foo_val() { + return global_foo.val; +} + +int main(int argc, char *argv[]) { + std::string path = std::string(argv[0]) + "-so.so"; + void *handle = dlopen(path.c_str(), RTLD_NOW); + if (!handle) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + printf("%d\n", get_foo_val()); + return 0; +} +#endif // SHARED_LIB diff --git a/test/asan/TestCases/Linux/initialization-bug-any-order.cc b/test/asan/TestCases/Linux/initialization-bug-any-order.cc index a462f4a163f1..0f2fccae79bb 100644 --- a/test/asan/TestCases/Linux/initialization-bug-any-order.cc +++ b/test/asan/TestCases/Linux/initialization-bug-any-order.cc @@ -4,9 +4,9 @@ // strict init-order checking). // RUN: %clangxx_asan -O0 %s %p/../Helpers/initialization-bug-extra.cc -o %t -// RUN: ASAN_OPTIONS=strict_init_order=true not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_init_order=true not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O0 %p/../Helpers/initialization-bug-extra.cc %s -o %t -// RUN: ASAN_OPTIONS=strict_init_order=true not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_init_order=true not %run %t 2>&1 | FileCheck %s // Do not test with optimization -- the error may be optimized away. diff --git a/test/asan/TestCases/Linux/interface_symbols_linux.c b/test/asan/TestCases/Linux/interface_symbols_linux.c index a616732ff9f8..9e876799d384 100644 --- a/test/asan/TestCases/Linux/interface_symbols_linux.c +++ b/test/asan/TestCases/Linux/interface_symbols_linux.c @@ -24,8 +24,22 @@ // RUN: echo __asan_report_store16 >> %t.interface // RUN: echo __asan_report_load_n >> %t.interface // RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_report_exp_load1 >> %t.interface +// RUN: echo __asan_report_exp_load2 >> %t.interface +// RUN: echo __asan_report_exp_load4 >> %t.interface +// RUN: echo __asan_report_exp_load8 >> %t.interface +// RUN: echo __asan_report_exp_load16 >> %t.interface +// RUN: echo __asan_report_exp_store1 >> %t.interface +// RUN: echo __asan_report_exp_store2 >> %t.interface +// RUN: echo __asan_report_exp_store4 >> %t.interface +// RUN: echo __asan_report_exp_store8 >> %t.interface +// RUN: echo __asan_report_exp_store16 >> %t.interface +// RUN: echo __asan_report_exp_load_n >> %t.interface +// RUN: echo __asan_report_exp_store_n >> %t.interface // RUN: echo __asan_get_current_fake_stack >> %t.interface // RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: echo __asan_alloca_poison >> %t.interface +// RUN: echo __asan_allocas_unpoison >> %t.interface // RUN: cat %t.interface | sort -u | diff %t.symbols - // FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing diff --git a/test/asan/TestCases/Linux/kernel-area.cc b/test/asan/TestCases/Linux/kernel-area.cc index 8dd509f84975..8d3f7d6f8e88 100644 --- a/test/asan/TestCases/Linux/kernel-area.cc +++ b/test/asan/TestCases/Linux/kernel-area.cc @@ -4,19 +4,19 @@ // Test that kernel area is not sanitized on 32-bit machines. // // RUN: %clangxx_asan %s -o %t -// RUN: ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits -// RUN: ASAN_OPTIONS=verbosity=1:full_address_space=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits -// RUN: ASAN_OPTIONS=verbosity=1:full_address_space=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-kernel-64-bits +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1:full_address_space=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%kernel_bits +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1:full_address_space=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-kernel-64-bits // -// CHECK-kernel-32-bits: || `[0x38000000, 0xbfffffff]` || HighMem || -// CHECK-kernel-32-bits: || `[0x27000000, 0x37ffffff]` || HighShadow || -// CHECK-kernel-32-bits: || `[0x24000000, 0x26ffffff]` || ShadowGap || +// CHECK-kernel-32-bits: || `[0x38{{0+}}, 0xb{{f+}}]` || HighMem || +// CHECK-kernel-32-bits: || `[0x27{{0+}}, 0x37{{f+}}]` || HighShadow || +// CHECK-kernel-32-bits: || `[0x24{{0+}}, 0x26{{f+}}]` || ShadowGap || // -// CHECK-kernel-64-bits: || `[0x40000000, 0xffffffff]` || HighMem || -// CHECK-kernel-64-bits: || `[0x28000000, 0x3fffffff]` || HighShadow || -// CHECK-kernel-64-bits: || `[0x24000000, 0x27ffffff]` || ShadowGap || +// CHECK-kernel-64-bits: || `[0x4{{0+}}, 0x{{f+}}]` || HighMem || +// CHECK-kernel-64-bits: || `[0x28{{0+}}, 0x3{{f+}}]` || HighShadow || +// CHECK-kernel-64-bits: || `[0x24{{0+}}, 0x27{{f+}}]` || ShadowGap || // -// REQUIRES: asan-32-bits +// REQUIRES: asan-32-bits,i386-supported-target int main() { return 0; diff --git a/test/asan/TestCases/Linux/leak.cc b/test/asan/TestCases/Linux/leak.cc index 36dc6ddb8adf..15c03b45e4c9 100644 --- a/test/asan/TestCases/Linux/leak.cc +++ b/test/asan/TestCases/Linux/leak.cc @@ -2,9 +2,9 @@ // REQUIRES: leak-detection // // RUN: %clangxx_asan %s -o %t -// RUN: ASAN_OPTIONS=detect_leaks=1 not %run %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS="" not %run %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=detect_leaks=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_leaks=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_leaks=0 %run %t #include <stdio.h> int *t; diff --git a/test/asan/TestCases/Linux/leak_check_segv.cc b/test/asan/TestCases/Linux/leak_check_segv.cc new file mode 100644 index 000000000000..8160d5fe56bb --- /dev/null +++ b/test/asan/TestCases/Linux/leak_check_segv.cc @@ -0,0 +1,23 @@ +// Test that SIGSEGV during leak checking does not crash the process. +// RUN: %clangxx_asan -O1 %s -o %t && LSAN_OPTIONS="verbosity=1" not %run %t 2>&1 +// REQUIRES: leak-detection +#include <stdlib.h> +#include <stdio.h> +#include <sys/mman.h> +#include <sanitizer/lsan_interface.h> + +char data[10 * 1024 * 1024]; + +int main() { + void *p = malloc(10 * 1024 * 1024); + // surprise-surprise! + mprotect((void*)(((unsigned long)p + 4095) & ~4095), 16 * 1024, PROT_NONE); + mprotect((void*)(((unsigned long)data + 4095) & ~4095), 16 * 1024, PROT_NONE); + __lsan_do_leak_check(); + fprintf(stderr, "DONE\n"); +} + +// CHECK: Tracer caught signal 11 +// CHECK: LeakSanitizer has encountered a fatal error +// CHECK-NOT: DONE + diff --git a/test/asan/TestCases/Linux/malloc-in-qsort.cc b/test/asan/TestCases/Linux/malloc-in-qsort.cc index 80af409d66a9..f7c7c5fe3df7 100644 --- a/test/asan/TestCases/Linux/malloc-in-qsort.cc +++ b/test/asan/TestCases/Linux/malloc-in-qsort.cc @@ -1,6 +1,6 @@ // RUN: %clangxx_asan -O2 %s -o %t -// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST -// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW // Test how well we unwind in presence of qsort in the stack // (i.e. if we can unwind through a function compiled w/o frame pointers). diff --git a/test/asan/TestCases/Linux/malloc_delete_mismatch.cc b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc index 18d65ce0008f..33d0ede15f3c 100644 --- a/test/asan/TestCases/Linux/malloc_delete_mismatch.cc +++ b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc @@ -4,14 +4,14 @@ // RUN: %clangxx_asan -g %s -o %t 2>&1 // Find error and provide malloc context. -// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=ALLOC-STACK +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:alloc_dealloc_mismatch=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=ALLOC-STACK // No error here. -// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:alloc_dealloc_mismatch=0 %run %t // Also works if no malloc context is available. -// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi // XFAIL: armv7l-unknown-linux-gnueabihf #include <stdlib.h> diff --git a/test/asan/TestCases/Linux/nohugepage_test.cc b/test/asan/TestCases/Linux/nohugepage_test.cc index b549f3bc2119..aeb70c94ec99 100644 --- a/test/asan/TestCases/Linux/nohugepage_test.cc +++ b/test/asan/TestCases/Linux/nohugepage_test.cc @@ -3,8 +3,8 @@ // where asan consumed too much RAM due to transparent hugetables. // // RUN: %clangxx_asan -g %s -o %t -// RUN: ASAN_OPTIONS=no_huge_pages_for_shadow=1 %run %t 2>&1 | FileCheck %s -// RUN: %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:no_huge_pages_for_shadow=1 %run %t 2>&1 | FileCheck %s +// RUN: %run %t 2>&1 | FileCheck %s // // Would be great to run the test with no_huge_pages_for_shadow=0, but // the result will depend on the OS version and settings... @@ -22,15 +22,31 @@ #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> +#include <errno.h> #include <sanitizer/asan_interface.h> -char FileContents[1 << 14]; +char FileContents[1 << 16]; void FileToString(const char *path) { FileContents[0] = 0; int fd = open(path, 0); if (fd < 0) return; - ssize_t res = read(fd, FileContents, sizeof(FileContents) - 1); + char *p = FileContents; + ssize_t size = sizeof(FileContents) - 1; + ssize_t res = 0; + do { + ssize_t got = read (fd, p, size); + if (got == 0) + break; + else if (got > 0) + { + p += got; + res += got; + size -= got; + } + else if (errno != EINTR) + break; + } while (size > 0 && res < sizeof(FileContents)); if (res >= 0) FileContents[res] = 0; } diff --git a/test/asan/TestCases/Linux/odr-violation.cc b/test/asan/TestCases/Linux/odr-violation.cc index ddc68a2db0f1..e9311d16bd5f 100644 --- a/test/asan/TestCases/Linux/odr-violation.cc +++ b/test/asan/TestCases/Linux/odr-violation.cc @@ -1,19 +1,27 @@ // FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 // XFAIL: android // +// We use fast_unwind_on_malloc=0 to have full unwinding even w/o frame +// pointers. This setting is not on by default because it's too expensive. +// // Different size: detect a bug if detect_odr_violation>=1 // RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t-ODR-SO.so // RUN: %clangxx_asan %s %t-ODR-SO.so -Wl,-R. -o %t-ODR-EXE -// RUN: ASAN_OPTIONS=detect_odr_violation=1 not %run %t-ODR-EXE 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=detect_odr_violation=0 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED -// RUN: not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=1 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=0 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0 not %run %t-ODR-EXE 2>&1 | FileCheck %s // // Same size: report a bug only if detect_odr_violation>=2. // RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t-ODR-SO.so -DSZ=100 -// RUN: ASAN_OPTIONS=detect_odr_violation=1 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED -// RUN: ASAN_OPTIONS=detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s -// RUN: not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=1 %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=2 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0 not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: echo "odr_violation:foo::ZZZ" > %t.supp +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=2:suppressions=%t.supp not %run %t-ODR-EXE 2>&1 | FileCheck %s +// RUN: echo "odr_violation:foo::G" > %t.supp +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_malloc=0:detect_odr_violation=2:suppressions=%t.supp %run %t-ODR-EXE 2>&1 | FileCheck %s --check-prefix=DISABLED +// RUN: rm -f %t.supp // GNU driver doesn't handle .so files properly. // REQUIRES: Clang diff --git a/test/asan/TestCases/Linux/overflow-in-qsort.cc b/test/asan/TestCases/Linux/overflow-in-qsort.cc index 21bdff60cd30..26fe67d60729 100644 --- a/test/asan/TestCases/Linux/overflow-in-qsort.cc +++ b/test/asan/TestCases/Linux/overflow-in-qsort.cc @@ -1,6 +1,6 @@ // RUN: %clangxx_asan -O2 %s -o %t -// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST -// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_fatal=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:fast_unwind_on_fatal=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SLOW // Test how well we unwind in presence of qsort in the stack // (i.e. if we can unwind through a function compiled w/o frame pointers). diff --git a/test/asan/TestCases/Linux/ptrace.cc b/test/asan/TestCases/Linux/ptrace.cc index 7e5acb64c7a1..6840ebe28bfd 100644 --- a/test/asan/TestCases/Linux/ptrace.cc +++ b/test/asan/TestCases/Linux/ptrace.cc @@ -31,8 +31,8 @@ int main(void) { // CHECK: AddressSanitizer: stack-buffer-overflow // CHECK: {{.*ptrace.cc:}}[[@LINE-2]] assert(!res); -#if __WORDSIZE == 64 - printf("%zx\n", regs.rip); +#ifdef __x86_64__ + printf("%lx\n", (unsigned long)regs.rip); #else printf("%lx\n", regs.eip); #endif @@ -42,7 +42,7 @@ int main(void) { assert(!res); printf("%lx\n", (unsigned long)fpregs.cwd); -#if __WORDSIZE == 32 +#ifndef __x86_64__ user_fpxregs_struct fpxregs; res = ptrace(PTRACE_GETFPXREGS, pid, NULL, &fpxregs); assert(!res); diff --git a/test/asan/TestCases/Linux/quarantine_size_mb.cc b/test/asan/TestCases/Linux/quarantine_size_mb.cc index 4499992444f9..1820cb90c4ef 100644 --- a/test/asan/TestCases/Linux/quarantine_size_mb.cc +++ b/test/asan/TestCases/Linux/quarantine_size_mb.cc @@ -1,10 +1,10 @@ // Test quarantine_size_mb (and the deprecated quarantine_size) // RUN: %clangxx_asan %s -o %t -// RUN: ASAN_OPTIONS=quarantine_size=10485760:verbosity=1:hard_rss_limit_mb=50 %run %t 2>&1 | FileCheck %s --check-prefix=Q10 -// RUN: ASAN_OPTIONS=quarantine_size_mb=10:verbosity=1:hard_rss_limit_mb=50 %run %t 2>&1 | FileCheck %s --check-prefix=Q10 -// RUN: ASAN_OPTIONS=quarantine_size_mb=10:quarantine_size=20:verbosity=1 not %run %t 2>&1 | FileCheck %s --check-prefix=BOTH -// RUN: ASAN_OPTIONS=quarantine_size_mb=1000:hard_rss_limit_mb=50 not %run %t 2>&1 | FileCheck %s --check-prefix=RSS_LIMIT -// RUN: ASAN_OPTIONS=hard_rss_limit_mb=50 not %run %t 2>&1 | FileCheck %s --check-prefix=RSS_LIMIT +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:quarantine_size=10485760:verbosity=1:hard_rss_limit_mb=50 %run %t 2>&1 | FileCheck %s --check-prefix=Q10 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:quarantine_size_mb=10:verbosity=1:hard_rss_limit_mb=50 %run %t 2>&1 | FileCheck %s --check-prefix=Q10 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:quarantine_size_mb=10:quarantine_size=20:verbosity=1 not %run %t 2>&1 | FileCheck %s --check-prefix=BOTH +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:quarantine_size_mb=1000:hard_rss_limit_mb=50 not %run %t 2>&1 | FileCheck %s --check-prefix=RSS_LIMIT +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:hard_rss_limit_mb=50 not %run %t 2>&1 | FileCheck %s --check-prefix=RSS_LIMIT #include <string.h> char *g; diff --git a/test/asan/TestCases/Linux/read_binary_name_regtest.c b/test/asan/TestCases/Linux/read_binary_name_regtest.c new file mode 100644 index 000000000000..0e408d0e366d --- /dev/null +++ b/test/asan/TestCases/Linux/read_binary_name_regtest.c @@ -0,0 +1,50 @@ +// Regression test for https://crbug.com/502974, where ASan was unable to read +// the binary name because of sandbox restrictions. +// This test uses seccomp-BPF to restrict the readlink() system call and makes +// sure ASan is still able to +// RUN: not ls /usr/include/linux/seccomp.h || ( %clang_asan %s -o %t && not %run %t 2>&1 | FileCheck %s ) +// UNSUPPORTED: android + +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/prctl.h> +#include <sys/syscall.h> +#include <linux/filter.h> +#include <linux/seccomp.h> + +#define syscall_nr (offsetof(struct seccomp_data, nr)) + +void corrupt() { + void *p = malloc(10); + free(p); + free(p); +} + +int main() { + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + + struct sock_filter filter[] = { + /* Grab the system call number */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr), + // If this is __NR_readlink, + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_readlink, 0, 1), + // return with EPERM, + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | EPERM), + // otherwise allow the syscall. + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) + }; + struct sock_fprog prog; + prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])); + prog.filter = filter; + + int res = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0); + if (res != 0) { + fprintf(stderr, "PR_SET_SECCOMP unsupported!\n"); + } + corrupt(); + // CHECK: AddressSanitizer + // CHECK-NOT: reading executable name failed + return 0; +} diff --git a/test/asan/TestCases/Linux/signal_during_stop_the_world.cc b/test/asan/TestCases/Linux/signal_during_stop_the_world.cc new file mode 100644 index 000000000000..b1a41fe20c05 --- /dev/null +++ b/test/asan/TestCases/Linux/signal_during_stop_the_world.cc @@ -0,0 +1,60 @@ +// Test StopTheWorld behavior during signal storm. +// Historically StopTheWorld crashed because did not handle EINTR properly. +// The test is somewhat convoluted, but that's what caused crashes previously. + +// RUN: %clangxx_asan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/prctl.h> +#include <sys/wait.h> +#include <time.h> +#include <pthread.h> +#include <sanitizer/lsan_interface.h> + +static void handler(int signo); +static void *thr(void *arg); + +int main() { + struct sigaction act = {}; + act.sa_handler = handler; + sigaction(SIGPROF, &act, 0); + + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "failed to fork\n"); + exit(1); + } + if (pid == 0) { + // Child constantly sends signals to parent to cause spurious return from + // waitpid in StopTheWorld. + prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0); + pid_t parent = getppid(); + for (;;) { + // There is no strong reason for these two particular signals, + // but at least one of them ought to unblock waitpid. + kill(parent, SIGCHLD); + kill(parent, SIGPROF); + } + } + usleep(10000); // Let the child start. + __lsan_do_leak_check(); + // Kill and join the child. + kill(pid, SIGTERM); + waitpid(pid, 0, 0); + sleep(1); // If the tracer thread still runs, give it time to crash. + fprintf(stderr, "DONE\n"); +// CHECK: DONE +} + +static void handler(int signo) { +} + +static void *thr(void *arg) { + for (;;) + sleep(1); + return 0; +} diff --git a/test/asan/TestCases/Linux/sized_delete_test.cc b/test/asan/TestCases/Linux/sized_delete_test.cc index 343cb0a86fed..1146b8239d8c 100644 --- a/test/asan/TestCases/Linux/sized_delete_test.cc +++ b/test/asan/TestCases/Linux/sized_delete_test.cc @@ -1,14 +1,10 @@ -// RUN: %clangxx_asan -Xclang -fsized-deallocation -O0 %s -o %t -// RUN: not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR -// RUN: ASAN_OPTIONS=new_delete_type_mismatch=1 not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR -// RUN: not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY -// RUN: ASAN_OPTIONS=new_delete_type_mismatch=1 not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY -// RUN: ASAN_OPTIONS=new_delete_type_mismatch=0 %run %t scalar -// RUN: ASAN_OPTIONS=new_delete_type_mismatch=0 %run %t array - -// Sized-delete is implemented with a weak delete() definition. -// Weak symbols are kind of broken on Android. -// XFAIL: android, asan-dynamic-runtime +// RUN: %clangxx_asan -fsized-deallocation -O0 %s -o %t +// RUN: not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:new_delete_type_mismatch=1 not %run %t scalar 2>&1 | FileCheck %s -check-prefix=SCALAR +// RUN: not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:new_delete_type_mismatch=1 not %run %t array 2>&1 | FileCheck %s -check-prefix=ARRAY +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:new_delete_type_mismatch=0 %run %t scalar +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:new_delete_type_mismatch=0 %run %t array #include <new> #include <stdio.h> diff --git a/test/asan/TestCases/Linux/stack-overflow-sigbus.cc b/test/asan/TestCases/Linux/stack-overflow-sigbus.cc index 5f597e9c51f0..23c4d23ce406 100644 --- a/test/asan/TestCases/Linux/stack-overflow-sigbus.cc +++ b/test/asan/TestCases/Linux/stack-overflow-sigbus.cc @@ -1,6 +1,6 @@ // Test ASan detection of stack-overflow condition when Linux sends SIGBUS. -// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s #include <assert.h> #include <stdio.h> diff --git a/test/asan/TestCases/Linux/stack-trace-dlclose.cc b/test/asan/TestCases/Linux/stack-trace-dlclose.cc index b3bd9f7780f5..f207a94ae65e 100644 --- a/test/asan/TestCases/Linux/stack-trace-dlclose.cc +++ b/test/asan/TestCases/Linux/stack-trace-dlclose.cc @@ -3,7 +3,7 @@ // // RUN: %clangxx_asan -DSHARED %s -shared -o %T/stack_trace_dlclose.so -fPIC // RUN: %clangxx_asan -DSO_DIR=\"%T\" %s %libdl -o %t -// RUN: ASAN_OPTIONS=exitcode=0 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:exitcode=0 %run %t 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi // XFAIL: armv7l-unknown-linux-gnueabihf @@ -40,6 +40,6 @@ int main(int argc, char **argv) { } #endif -// CHECK: {{ #0 0x.* in malloc}} +// CHECK: {{ #0 0x.* in (__interceptor_)?malloc}} // CHECK: {{ #1 0x.* \(<unknown module>\)}} // CHECK: {{ #2 0x.* in main}} diff --git a/test/asan/TestCases/Linux/static_tls.cc b/test/asan/TestCases/Linux/static_tls.cc new file mode 100644 index 000000000000..785228b29238 --- /dev/null +++ b/test/asan/TestCases/Linux/static_tls.cc @@ -0,0 +1,29 @@ +// REQUIRES: asan-64-bits +// Regression test: __tls_get_addr interceptor must recognize static TLS. +// +// RUN: %clangxx_asan -DSHARED %s -shared -o %t-so.so -fPIC +// RUN: %clangxx_asan %s -ldl -pthread -o %t %t-so.so +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 %run %t 2>&1 | FileCheck %s + +// CHECK: before +// CHECK: __tls_get_addr: static tls +// CHECK: after + +// XFAIL: powerpc64 + +#ifndef SHARED +#include <stdio.h> + +unsigned *f(); +int main(int argc, char *argv[]) { + fprintf(stderr, "before\n"); + f(); + fprintf(stderr, "after\n"); + return 0; +} +#else // SHARED +static __thread unsigned ThreadLocal; +unsigned *f() { + return &ThreadLocal; +} +#endif diff --git a/test/asan/TestCases/Linux/stress_dtls.c b/test/asan/TestCases/Linux/stress_dtls.c index cb901ee59953..7af33b9457ea 100644 --- a/test/asan/TestCases/Linux/stress_dtls.c +++ b/test/asan/TestCases/Linux/stress_dtls.c @@ -12,9 +12,9 @@ // RUN: %clangxx_asan %s -ldl -pthread -o %t // RUN: %run %t 0 3 // RUN: %run %t 2 3 -// RUN: ASAN_OPTIONS=verbosity=2 %run %t 10 2 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=verbosity=2:intercept_tls_get_addr=1 %run %t 10 2 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=verbosity=2:intercept_tls_get_addr=0 %run %t 10 2 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 %run %t 10 2 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2:intercept_tls_get_addr=1 %run %t 10 2 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2:intercept_tls_get_addr=0 %run %t 10 2 2>&1 | FileCheck %s --check-prefix=CHECK0 // CHECK: __tls_get_addr // CHECK: Creating thread 0 // CHECK: __tls_get_addr diff --git a/test/asan/TestCases/Posix/allow_user_segv.cc b/test/asan/TestCases/Posix/allow_user_segv.cc index b6443fab85df..b299ae8cb21e 100644 --- a/test/asan/TestCases/Posix/allow_user_segv.cc +++ b/test/asan/TestCases/Posix/allow_user_segv.cc @@ -1,8 +1,8 @@ // Regression test for // https://code.google.com/p/address-sanitizer/issues/detail?id=180 -// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=$ASAN_OPTIONS:allow_user_segv_handler=true not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && ASAN_OPTIONS=$ASAN_OPTIONS:allow_user_segv_handler=true not %run %t 2>&1 | FileCheck %s #include <signal.h> #include <stdio.h> diff --git a/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc b/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc index 3ce6446eca22..043130f9e549 100644 --- a/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc +++ b/test/asan/TestCases/Posix/asan-symbolize-sanity-test.cc @@ -6,7 +6,7 @@ // RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so // RUN: %clangxx_asan -O0 %s %libdl -o %t -// RUN: env ASAN_OPTIONS=symbolize=0 not %run %t 2>&1 | %asan_symbolize | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:symbolize=0 not %run %t 2>&1 | %asan_symbolize | FileCheck %s // XFAIL: arm-linux-gnueabi // XFAIL: armv7l-unknown-linux-gnueabihf diff --git a/test/asan/TestCases/Linux/coverage-direct-activation.cc b/test/asan/TestCases/Posix/coverage-direct-activation.cc index 9b2a0d8897c8..af73f5d2952d 100644 --- a/test/asan/TestCases/Linux/coverage-direct-activation.cc +++ b/test/asan/TestCases/Posix/coverage-direct-activation.cc @@ -1,18 +1,18 @@ // Test for direct coverage writing enabled at activation time. -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_activation_test_1.so -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib -fPIC // RUN: %clangxx -c -DSO_DIR=\"%T\" %s -o %t.o -// RUN: %clangxx_asan -fsanitize-coverage=1 %t.o %libdl -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func %t.o %libdl -o %t // RUN: rm -rf %T/coverage-direct-activation // RUN: mkdir -p %T/coverage-direct-activation/normal -// RUN: ASAN_OPTIONS=coverage=1,coverage_direct=0,coverage_dir=%T/coverage-direct-activation/normal:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1,coverage_direct=0,coverage_dir=%T/coverage-direct-activation/normal:verbosity=1 %run %t %dynamiclib // RUN: %sancov print %T/coverage-direct-activation/normal/*.sancov >%T/coverage-direct-activation/normal/out.txt // RUN: mkdir -p %T/coverage-direct-activation/direct -// RUN: ASAN_OPTIONS=start_deactivated=1,coverage_direct=1,verbosity=1 \ -// RUN: ASAN_ACTIVATION_OPTIONS=coverage=1,coverage_dir=%T/coverage-direct-activation/direct %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1,coverage_direct=1,verbosity=1 \ +// RUN: ASAN_ACTIVATION_OPTIONS=coverage=1,coverage_dir=%T/coverage-direct-activation/direct %run %t %dynamiclib // RUN: cd %T/coverage-direct-activation/direct // RUN: %sancov rawunpack *.sancov.raw // RUN: %sancov print *.sancov >out.txt @@ -23,8 +23,8 @@ // RUN: diff -u coverage-direct-activation/normal/out.txt coverage-direct-activation/direct/out.txt // RUN: mkdir -p %T/coverage-direct-activation/direct2 -// RUN: ASAN_OPTIONS=start_deactivated=1,coverage=1,coverage_direct=1,verbosity=1 \ -// RUN: ASAN_ACTIVATION_OPTIONS=coverage_dir=%T/coverage-direct-activation/direct2 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1,coverage=1,coverage_direct=1,verbosity=1 \ +// RUN: ASAN_ACTIVATION_OPTIONS=coverage_dir=%T/coverage-direct-activation/direct2 %run %t %dynamiclib // RUN: cd %T/coverage-direct-activation/direct2 // RUN: %sancov rawunpack *.sancov.raw // RUN: %sancov print *.sancov >out.txt @@ -47,8 +47,8 @@ void bar() { printf("bar\n"); } int main(int argc, char **argv) { fprintf(stderr, "PID: %d\n", getpid()); - void *handle1 = - dlopen(SO_DIR "/libcoverage_direct_activation_test_1.so", RTLD_LAZY); + assert(argc > 1); + void *handle1 = dlopen(argv[1], RTLD_LAZY); // %dynamiclib assert(handle1); void (*bar1)() = (void (*)())dlsym(handle1, "bar"); assert(bar1); diff --git a/test/asan/TestCases/Linux/coverage-direct-large.cc b/test/asan/TestCases/Posix/coverage-direct-large.cc index 25c950e0bb30..2769172c39ab 100644 --- a/test/asan/TestCases/Linux/coverage-direct-large.cc +++ b/test/asan/TestCases/Posix/coverage-direct-large.cc @@ -2,18 +2,18 @@ // Current implementation maps output file in chunks of 64K. This test overflows // 1 chunk. -// RUN: %clangxx_asan -fsanitize-coverage=1 -O0 -DSHARED %s -shared -o %T/libcoverage_direct_large_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 -O0 -DSO_DIR=\"%T\" %s %libdl -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func -O0 -DSHARED %s -shared -o %dynamiclib -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func -O0 %s %libdl -o %t // RUN: rm -rf %T/coverage-direct-large // RUN: mkdir -p %T/coverage-direct-large/normal && cd %T/coverage-direct-large/normal -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=0:verbosity=1 %run %t %dynamiclib // RUN: %sancov print *.sancov >out.txt // RUN: cd ../.. // RUN: mkdir -p %T/coverage-direct-large/direct && cd %T/coverage-direct-large/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:verbosity=1 %run %t %dynamiclib // RUN: %sancov rawunpack *.sancov.raw // RUN: %sancov print *.sancov >out.txt // RUN: cd ../.. @@ -49,11 +49,11 @@ extern "C" void so_entry() { #include <assert.h> #include <dlfcn.h> -int main(void) { +#include <stdio.h> +int main(int argc, char **argv) { F4(CALL, f) - - void *handle1 = - dlopen(SO_DIR "/libcoverage_direct_large_test_1.so", RTLD_LAZY); + assert(argc > 1); + void *handle1 = dlopen(argv[1], RTLD_LAZY); // %dynamiclib assert(handle1); void (*so_entry)() = (void (*)())dlsym(handle1, "so_entry"); assert(so_entry); diff --git a/test/asan/TestCases/Linux/coverage-direct.cc b/test/asan/TestCases/Posix/coverage-direct.cc index 45222fa1a03e..5371a859c24f 100644 --- a/test/asan/TestCases/Linux/coverage-direct.cc +++ b/test/asan/TestCases/Posix/coverage-direct.cc @@ -1,16 +1,16 @@ // Test for direct coverage writing with dlopen at coverage level 1 to 3. -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%T\" %s %libdl -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func %s %libdl -o %t // RUN: rm -rf %T/coverage-direct // RUN: mkdir -p %T/coverage-direct/normal -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t %dynamiclib // RUN: %sancov print %T/coverage-direct/normal/*.sancov >%T/coverage-direct/normal/out.txt // RUN: mkdir -p %T/coverage-direct/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t %dynamiclib // RUN: cd %T/coverage-direct/direct // RUN: %sancov rawunpack *.sancov.raw // RUN: %sancov print *.sancov >out.txt @@ -19,17 +19,17 @@ // RUN: diff -u coverage-direct/normal/out.txt coverage-direct/direct/out.txt -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSO_DIR=\"%T\" %s %libdl -o %t +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSHARED %s -shared -o %dynamiclib -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSO_DIR=\"%T\" %s %libdl -o %t // RUN: rm -rf %T/coverage-direct // RUN: mkdir -p %T/coverage-direct/normal -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t %dynamiclib // RUN: %sancov print %T/coverage-direct/normal/*.sancov >%T/coverage-direct/normal/out.txt // RUN: mkdir -p %T/coverage-direct/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t %dynamiclib // RUN: cd %T/coverage-direct/direct // RUN: %sancov rawunpack *.sancov.raw // RUN: %sancov print *.sancov >out.txt @@ -38,17 +38,17 @@ // RUN: diff -u coverage-direct/normal/out.txt coverage-direct/direct/out.txt -// RUN: %clangxx_asan -fsanitize-coverage=3 -DSHARED %s -shared -o %T/libcoverage_direct_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=3 -DSO_DIR=\"%T\" %s %libdl -o %t +// RUN: %clangxx_asan -fsanitize-coverage=edge -DSHARED %s -shared -o %dynamiclib -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=edge -DSO_DIR=\"%T\" %s %libdl -o %t // RUN: rm -rf %T/coverage-direct // RUN: mkdir -p %T/coverage-direct/normal -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=0:coverage_dir=%T/coverage-direct/normal:verbosity=1 %run %t %dynamiclib // RUN: %sancov print %T/coverage-direct/normal/*.sancov >%T/coverage-direct/normal/out.txt // RUN: mkdir -p %T/coverage-direct/direct -// RUN: ASAN_OPTIONS=coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:coverage_dir=%T/coverage-direct/direct:verbosity=1 %run %t %dynamiclib // RUN: cd %T/coverage-direct/direct // RUN: %sancov rawunpack *.sancov.raw // RUN: %sancov print *.sancov >out.txt @@ -71,8 +71,8 @@ void bar() { printf("bar\n"); } int main(int argc, char **argv) { fprintf(stderr, "PID: %d\n", getpid()); - void *handle1 = - dlopen(SO_DIR "/libcoverage_direct_test_1.so", RTLD_LAZY); + assert(argc > 1); + void *handle1 = dlopen(argv[1], RTLD_LAZY); assert(handle1); void (*bar1)() = (void (*)())dlsym(handle1, "bar"); assert(bar1); diff --git a/test/asan/TestCases/Linux/coverage-fork-direct.cc b/test/asan/TestCases/Posix/coverage-fork-direct.cc index 51cbbd821b8e..39363911fec3 100644 --- a/test/asan/TestCases/Linux/coverage-fork-direct.cc +++ b/test/asan/TestCases/Posix/coverage-fork-direct.cc @@ -1,7 +1,7 @@ -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t // RUN: rm -rf %T/coverage-fork-direct // RUN: mkdir -p %T/coverage-fork-direct && cd %T/coverage-fork-direct -// RUN: (ASAN_OPTIONS=coverage=1:coverage_direct=1:verbosity=1 %run %t; \ +// RUN: (ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=1:verbosity=1 %run %t; \ // RUN: %sancov rawunpack *.sancov.raw; %sancov print *.sancov) 2>&1 // // XFAIL: android diff --git a/test/asan/TestCases/Linux/coverage-fork.cc b/test/asan/TestCases/Posix/coverage-fork.cc index 38c200942609..c1f0fc3a84b8 100644 --- a/test/asan/TestCases/Linux/coverage-fork.cc +++ b/test/asan/TestCases/Posix/coverage-fork.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -// RUN: export ASAN_OPTIONS=coverage=1:coverage_direct=0:verbosity=1 +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_direct=0:verbosity=1 // RUN: rm -rf %T/coverage-fork // RUN: mkdir -p %T/coverage-fork && cd %T/coverage-fork // RUN: %run %t 2>&1 | FileCheck %s diff --git a/test/asan/TestCases/Linux/coverage-module-unloaded.cc b/test/asan/TestCases/Posix/coverage-module-unloaded.cc index f8d9c57f81be..fe08bdd792b1 100644 --- a/test/asan/TestCases/Linux/coverage-module-unloaded.cc +++ b/test/asan/TestCases/Posix/coverage-module-unloaded.cc @@ -1,13 +1,13 @@ // Check that unloading a module doesn't break coverage dumping for remaining // modules. -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_1.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_module_unloaded_test_2.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSO_DIR=\"%T\" %s %libdl -o %t -// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib1 -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib2 -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func %s %libdl -o %t +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 // RUN: mkdir -p %T/coverage-module-unloaded && cd %T/coverage-module-unloaded -// RUN: %run %t 2>&1 | FileCheck %s -// RUN: %run %t foo 2>&1 | FileCheck %s -// RUN: cd .. && rm coverage-module-unloaded -r +// RUN: %run %t %dynamiclib1 %dynamiclib2 2>&1 | FileCheck %s +// RUN: %run %t %dynamiclib1 %dynamiclib2 foo 2>&1 | FileCheck %s +// RUN: rm -r %T/coverage-module-unloaded // // https://code.google.com/p/address-sanitizer/issues/detail?id=263 // XFAIL: android @@ -25,14 +25,13 @@ void bar() { printf("bar\n"); } int main(int argc, char **argv) { fprintf(stderr, "PID: %d\n", getpid()); - void *handle1 = - dlopen(SO_DIR "/libcoverage_module_unloaded_test_1.so", RTLD_LAZY); + assert(argc > 2); + void *handle1 = dlopen(argv[1], RTLD_LAZY); // %dynamiclib1 assert(handle1); void (*bar1)() = (void (*)())dlsym(handle1, "bar"); assert(bar1); bar1(); - void *handle2 = - dlopen(SO_DIR "/libcoverage_module_unloaded_test_2.so", RTLD_LAZY); + void *handle2 = dlopen(argv[2], RTLD_LAZY); // %dynamiclib2 assert(handle2); void (*bar2)() = (void (*)())dlsym(handle2, "bar"); assert(bar2); @@ -50,7 +49,7 @@ int main(int argc, char **argv) { // CHECK: PID: [[PID:[0-9]+]] // CHECK: [[PID]].sancov: 1 PCs written -// CHECK: .so.[[PID]] -// If we get coverage for both DSOs, it means the module wasn't unloaded and -// this test is useless. -// CHECK-NOT: .so.[[PID]] +// CHECK: coverage-module-unloaded{{.*}}1.[[PID]] +// CHECK: coverage-module-unloaded{{.*}}2.[[PID]] +// Even though we've unloaded one of the libs we still dump the coverage file +// for that lib (although the data will be inaccurate, if at all useful) diff --git a/test/asan/TestCases/Linux/coverage-sandboxing.cc b/test/asan/TestCases/Posix/coverage-sandboxing.cc index 1a72c6bb9a6e..dd2c1ec43be5 100644 --- a/test/asan/TestCases/Linux/coverage-sandboxing.cc +++ b/test/asan/TestCases/Posix/coverage-sandboxing.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_asan -fsanitize-coverage=2 -DSHARED %s -shared -o %T/libcoverage_sandboxing_test.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_sandboxing_test -// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: %clangxx_asan -fsanitize-coverage=bb -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t %ld_flags_rpath_exe +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 // RUN: rm -rf %T/coverage_sandboxing_test // RUN: mkdir %T/coverage_sandboxing_test && cd %T/coverage_sandboxing_test // RUN: mkdir vanilla && cd vanilla @@ -12,12 +12,12 @@ // RUN: %run %t a b 2>&1 | FileCheck %s --check-prefix=CHECK-sandbox // RUN: %sancov unpack coverage_sandboxing_test.sancov.packed // RUN: cd .. -// RUN: %sancov print vanilla/libcoverage_sandboxing_test.so.*.sancov > vanilla.txt -// RUN: %sancov print sandbox1/libcoverage_sandboxing_test.so.*.sancov > sandbox1.txt -// RUN: %sancov print sandbox2/libcoverage_sandboxing_test.so.*.sancov > sandbox2.txt +// RUN: %sancov print vanilla/`basename %dynamiclib`*.sancov > vanilla.txt +// RUN: %sancov print sandbox1/`basename %dynamiclib`*.sancov > sandbox1.txt +// RUN: %sancov print sandbox2/`basename %dynamiclib`*.sancov > sandbox2.txt // RUN: diff vanilla.txt sandbox1.txt // RUN: diff vanilla.txt sandbox2.txt -// RUN: cd ../ && rm coverage_sandboxing_test -r +// RUN: rm -r %T/coverage_sandboxing_test // https://code.google.com/p/address-sanitizer/issues/detail?id=263 // XFAIL: android @@ -78,8 +78,8 @@ int main(int argc, char **argv) { #endif // CHECK-vanilla: PID: [[PID:[0-9]+]] -// CHECK-vanilla: [[PID]].sancov: 1 PCs written // CHECK-vanilla: .so.[[PID]].sancov: 258 PCs written +// CHECK-vanilla: [[PID]].sancov: 1 PCs written // CHECK-sandbox: PID: [[PID:[0-9]+]] // CHECK-sandbox: 258 PCs written to packed file diff --git a/test/asan/TestCases/Linux/coverage.cc b/test/asan/TestCases/Posix/coverage.cc index 06fe1a295eaf..99e348fdaf9d 100644 --- a/test/asan/TestCases/Linux/coverage.cc +++ b/test/asan/TestCases/Posix/coverage.cc @@ -1,14 +1,21 @@ -// RUN: %clangxx_asan -fsanitize-coverage=1 -DSHARED %s -shared -o %T/libcoverage_test.so -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -Wl,-R,\$ORIGIN -L%T -lcoverage_test -// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 -// RUN: mkdir -p %T/coverage && cd %T/coverage +// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so +// RUN: %clangxx_asan -fsanitize-coverage=func %s %ld_flags_rpath_exe -o %t +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 +// RUN: rm -rf %T/coverage && mkdir -p %T/coverage && cd %T/coverage // RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-main +// RUN: %sancov print `ls coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 // RUN: %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo +// RUN: %sancov print `ls coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %run %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar +// RUN: %sancov print `ls *coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %run %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar +// RUN: %sancov print `ls *coverage.*sancov | grep -v '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 +// RUN: %sancov print `ls *coverage.*sancov | grep '.so'` 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 +// RUN: %sancov merge `ls *coverage.*sancov | grep -v '.so'` > merged-cov +// RUN: %sancov print merged-cov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: not %run %t foo bar 4 2>&1 | FileCheck %s --check-prefix=CHECK-report // RUN: not %run %t foo bar 4 5 2>&1 | FileCheck %s --check-prefix=CHECK-segv -// RUN: cd .. && rm coverage -r +// RUN: rm -r %T/coverage // // https://code.google.com/p/address-sanitizer/issues/detail?id=263 // XFAIL: android @@ -57,15 +64,18 @@ int main(int argc, char **argv) { // CHECK-foo-NOT: .so.[[PID]] // // CHECK-bar: PID: [[PID:[0-9]+]] -// CHECK-bar: [[PID]].sancov: 1 PCs written // CHECK-bar: .so.[[PID]].sancov: 1 PCs written +// CHECK-bar: [[PID]].sancov: 1 PCs written // // CHECK-foo-bar: PID: [[PID:[0-9]+]] -// CHECK-foo-bar: [[PID]].sancov: 2 PCs written // CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written +// CHECK-foo-bar: [[PID]].sancov: 2 PCs written // // CHECK-report: AddressSanitizer: global-buffer-overflow // CHECK-report: PCs written // // CHECK-segv: AddressSanitizer: SEGV // CHECK-segv: PCs written +// +// CHECK-SANCOV1: 1 PCs total +// CHECK-SANCOV2: 2 PCs total diff --git a/test/asan/TestCases/Posix/init-order-dlopen.cc b/test/asan/TestCases/Posix/init-order-dlopen.cc deleted file mode 100644 index 3c6f093b03d1..000000000000 --- a/test/asan/TestCases/Posix/init-order-dlopen.cc +++ /dev/null @@ -1,72 +0,0 @@ -// Regression test for -// https://code.google.com/p/address-sanitizer/issues/detail?id=178 - -// Assume we're on Darwin and try to pass -U to the linker. If this flag is -// unsupported, don't use it. -// RUN: %clangxx_asan -O0 -DSHARED_LIB %s \ -// RUN: -fPIC -shared -o %t-so.so -Wl,-U,_inc_global || \ -// RUN: %clangxx_asan -O0 -DSHARED_LIB %s \ -// RUN: -fPIC -shared -o %t-so.so -// If the linker doesn't support --export-dynamic (which is ELF-specific), -// try to link without that option. -// FIXME: find a better solution. -// RUN: %clangxx_asan -O0 %s -pthread %libdl -o %t -Wl,--export-dynamic || \ -// RUN: %clangxx_asan -O0 %s -pthread %libdl -o %t -// RUN: ASAN_OPTIONS=strict_init_order=true %run %t 2>&1 | FileCheck %s -#if !defined(SHARED_LIB) -#include <dlfcn.h> -#include <pthread.h> -#include <stdio.h> -#include <unistd.h> - -#include <string> - -using std::string; - -int foo() { - return 42; -} -int global = foo(); - -__attribute__((visibility("default"))) -extern "C" -void inc_global() { - global++; -} - -void *global_poller(void *arg) { - while (true) { - if (global != 42) - break; - usleep(100); - } - return 0; -} - -int main(int argc, char *argv[]) { - pthread_t p; - pthread_create(&p, 0, global_poller, 0); - string path = string(argv[0]) + "-so.so"; - if (0 == dlopen(path.c_str(), RTLD_NOW)) { - fprintf(stderr, "dlerror: %s\n", dlerror()); - return 1; - } - pthread_join(p, 0); - printf("PASSED\n"); - // CHECK: PASSED - return 0; -} -#else // SHARED_LIB -#include <stdio.h> -#include <unistd.h> - -extern "C" void inc_global(); - -int slow_init() { - sleep(1); - inc_global(); - return 42; -} - -int slowly_init_glob = slow_init(); -#endif // SHARED_LIB diff --git a/test/asan/TestCases/Linux/interception-in-shared-lib-test.cc b/test/asan/TestCases/Posix/interception-in-shared-lib-test.cc index b828d5524ee0..a7d2bfb9b43c 100644 --- a/test/asan/TestCases/Linux/interception-in-shared-lib-test.cc +++ b/test/asan/TestCases/Posix/interception-in-shared-lib-test.cc @@ -1,13 +1,8 @@ // Check that memset() call from a shared library gets intercepted. -// Please always keep this file in sync with -// ../Darwin/interception-in-shared-lib-test.cc. // RUN: %clangxx_asan -O0 %s -DSHARED_LIB \ -// RUN: -shared -o %T/libinterception-in-shared-lib-test.so \ -// RUN: -fPIC -// TODO(glider): figure out how to set rpath in a more portable way and unite -// this test with ../Darwin/interception-in-shared-lib-test.cc. -// RUN: %clangxx_asan -O0 %s -o %t -Wl,-R,\$ORIGIN -L%T -linterception-in-shared-lib-test && \ +// RUN: -shared -o %dynamiclib -fPIC %ld_flags_rpath_so +// RUN: %clangxx_asan -O0 %s -o %t %ld_flags_rpath_exe && \ // RUN: not %run %t 2>&1 | FileCheck %s #include <stdio.h> @@ -26,7 +21,7 @@ int main(int argc, char *argv[]) { my_memset(buf, 11); // CHECK: {{.*ERROR: AddressSanitizer: stack-buffer-overflow}} // CHECK: {{WRITE of size 11 at 0x.* thread T0}} - // CHECK: {{0x.* in my_memset .*interception-in-shared-lib-test.cc:19}} + // CHECK: {{0x.* in my_memset .*interception-in-shared-lib-test.cc:}}[[@LINE-10]] return 0; } #endif diff --git a/test/asan/TestCases/Posix/ioctl.cc b/test/asan/TestCases/Posix/ioctl.cc index 78f152fe93fe..d25f6ecbee1f 100644 --- a/test/asan/TestCases/Posix/ioctl.cc +++ b/test/asan/TestCases/Posix/ioctl.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_asan -O0 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 -g %s -o %t && ASAN_OPTIONS=$ASAN_OPTIONS:handle_ioctl=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -g %s -o %t && ASAN_OPTIONS=$ASAN_OPTIONS:handle_ioctl=1 not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O0 -g %s -o %t && %run %t // RUN: %clangxx_asan -O3 -g %s -o %t && %run %t diff --git a/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc b/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc index f852a62ea9a3..b1c99430c75d 100644 --- a/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc +++ b/test/asan/TestCases/Posix/large_allocator_unpoisons_on_free.cc @@ -2,7 +2,7 @@ // RUN: %clangxx_asan %s -o %t // The memory is released only when the deallocated chunk leaves the quarantine, // otherwise the mmap(p, ...) call overwrites the malloc header. -// RUN: ASAN_OPTIONS=quarantine_size_mb=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:quarantine_size_mb=0 %run %t #include <assert.h> #include <string.h> diff --git a/test/asan/TestCases/log_path_fork_test.cc.disabled b/test/asan/TestCases/Posix/log_path_fork_test.cc.disabled index cfe90dfb54d3..9f09b78f41c7 100644 --- a/test/asan/TestCases/log_path_fork_test.cc.disabled +++ b/test/asan/TestCases/Posix/log_path_fork_test.cc.disabled @@ -1,7 +1,7 @@ // RUN: %clangxx_asan %s -o %t // RUN: rm -f %t.log.* // Set verbosity to 1 so that the log files are opened prior to fork(). -// RUN: env ASAN_OPTIONS="log_path=%t.log verbosity=1" not %run %t 2> %t.out +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:log_path=%t.log verbosity=1" not %run %t 2> %t.out // RUN: for f in %t.log.* ; do FileCheck %s < $f; done // RUN: [ `ls %t.log.* | wc -l` == 2 ] diff --git a/test/asan/TestCases/Posix/new_array_cookie_test.cc b/test/asan/TestCases/Posix/new_array_cookie_test.cc index 85d51f361835..bc681857ae67 100644 --- a/test/asan/TestCases/Posix/new_array_cookie_test.cc +++ b/test/asan/TestCases/Posix/new_array_cookie_test.cc @@ -1,8 +1,8 @@ // REQUIRES: asan-64-bits // RUN: %clangxx_asan -O3 %s -o %t // RUN: not %run %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s -// RUN: ASAN_OPTIONS=poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE #include <stdio.h> #include <stdlib.h> struct C { diff --git a/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc b/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc index c35ccebb8c79..5998d06756d7 100644 --- a/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc +++ b/test/asan/TestCases/Posix/new_array_cookie_uaf_test.cc @@ -1,7 +1,7 @@ // REQUIRES: asan-64-bits // RUN: %clangxx_asan -O3 %s -o %t -// RUN: ASAN_OPTIONS=poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s --check-prefix=COOKIE -// RUN: ASAN_OPTIONS=poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:poison_array_cookie=1 not %run %t 2>&1 | FileCheck %s --check-prefix=COOKIE +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:poison_array_cookie=0 not %run %t 2>&1 | FileCheck %s --check-prefix=NO_COOKIE #include <stdio.h> #include <stdlib.h> #include <assert.h> diff --git a/test/asan/TestCases/Posix/start-deactivated.cc b/test/asan/TestCases/Posix/start-deactivated.cc index d7e0dfc98e6d..2ca8015fc742 100644 --- a/test/asan/TestCases/Posix/start-deactivated.cc +++ b/test/asan/TestCases/Posix/start-deactivated.cc @@ -5,21 +5,20 @@ // RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so // RUN: %clangxx -O0 %s -c -o %t.o // RUN: %clangxx_asan -O0 %t.o %libdl -o %t -// RUN: ASAN_OPTIONS=start_deactivated=1,allocator_may_return_null=0 \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1,allocator_may_return_null=0 \ // RUN: ASAN_ACTIVATION_OPTIONS=allocator_may_return_null=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK -// RUN: ASAN_OPTIONS=start_deactivated=1 \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1 \ // RUN: ASAN_ACTIVATION_OPTIONS=help=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-HELP -// RUN: ASAN_OPTIONS=start_deactivated=1,verbosity=1 \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1,verbosity=1 \ // RUN: ASAN_ACTIVATION_OPTIONS=help=1,handle_segv=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-UNSUPPORTED -// RUN: ASAN_OPTIONS=start_deactivated=1 \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1 \ // RUN: ASAN_ACTIVATION_OPTIONS=help=1,handle_segv=0 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-UNSUPPORTED-V0 // Check that verbosity=1 in activation flags affects reporting of unrecognized activation flags. -// RUN: ASAN_OPTIONS=start_deactivated=1 \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:start_deactivated=1 \ // RUN: ASAN_ACTIVATION_OPTIONS=help=1,handle_segv=0,verbosity=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-UNSUPPORTED // XFAIL: arm-linux-gnueabi -// XFAIL: armv7l-unknown-linux-gnueabihf #if !defined(SHARED_LIB) #include <assert.h> diff --git a/test/asan/TestCases/Posix/tsd_dtor_leak.cc b/test/asan/TestCases/Posix/tsd_dtor_leak.cc index 6952245227b4..69d28194fb9e 100644 --- a/test/asan/TestCases/Posix/tsd_dtor_leak.cc +++ b/test/asan/TestCases/Posix/tsd_dtor_leak.cc @@ -1,7 +1,7 @@ // Regression test for a leak in tsd: // https://code.google.com/p/address-sanitizer/issues/detail?id=233 // RUN: %clangxx_asan -O1 %s -pthread -o %t -// RUN: ASAN_OPTIONS=quarantine_size_mb=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:quarantine_size_mb=0 %run %t #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/asan/TestCases/Windows/bind_io_completion_callback.cc b/test/asan/TestCases/Windows/bind_io_completion_callback.cc new file mode 100644 index 000000000000..c062a799fdcc --- /dev/null +++ b/test/asan/TestCases/Windows/bind_io_completion_callback.cc @@ -0,0 +1,70 @@ +// Make sure we can throw exceptions from work items executed via +// BindIoCompletionCallback. +// +// Clang doesn't support exceptions on Windows yet, so for the time being we +// build this program in two parts: the code with exceptions is built with CL, +// the rest is built with Clang. This represents the typical scenario when we +// build a large project using "clang-cl -fallback -fsanitize=address". +// +// RUN: cl -c %s -Fo%t.obj +// RUN: %clangxx_asan -o %t.exe %s %t.obj +// RUN: %run %t.exe 2>&1 | FileCheck %s + +#include <windows.h> +#include <stdio.h> + +void ThrowAndCatch(); + +#if !defined(__clang__) +__declspec(noinline) +void Throw() { + fprintf(stderr, "Throw\n"); +// CHECK: Throw + throw 1; +} + +void ThrowAndCatch() { + int local; + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch\n"); +// CHECK: Catch + } +} +#else + +char buffer[65536]; +HANDLE done; +OVERLAPPED ov; + +void CALLBACK completion_callback(DWORD error, DWORD bytesRead, + LPOVERLAPPED pov) { + ThrowAndCatch(); + SetEvent(done); +} + +int main(int argc, char **argv) { + done = CreateEvent(0, false, false, "job is done"); + if (!done) + return 1; + HANDLE file = CreateFile( + argv[0], GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, + NULL); + if (!file) + return 2; + if (!BindIoCompletionCallback(file, completion_callback, 0)) + return 3; + + if (!ReadFile(file, buffer, sizeof(buffer), NULL, &ov) && + GetLastError() != ERROR_IO_PENDING) + return 4; + + if (WAIT_OBJECT_0 != WaitForSingleObject(done, INFINITE)) + return 5; + fprintf(stderr, "Done!\n"); +// CHECK: Done! +} +#endif diff --git a/test/asan/TestCases/Windows/coverage-basic.cc b/test/asan/TestCases/Windows/coverage-basic.cc new file mode 100644 index 000000000000..44b499fcb4cd --- /dev/null +++ b/test/asan/TestCases/Windows/coverage-basic.cc @@ -0,0 +1,25 @@ +// RUN: rm -rf %T/coverage-basic +// RUN: mkdir %T/coverage-basic && cd %T/coverage-basic +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o test.exe +// RUN: env ASAN_OPTIONS=coverage=1 %run ./test.exe +// +// RUN: %sancov print *.sancov | FileCheck %s +#include <stdio.h> + +void foo() { fprintf(stderr, "FOO\n"); } +void bar() { fprintf(stderr, "BAR\n"); } + +int main(int argc, char **argv) { + if (argc == 2) { + foo(); + bar(); + } else { + bar(); + foo(); + } +} + +// CHECK: 0x{{[0-9a-f]*}} +// CHECK: 0x{{[0-9a-f]*}} +// CHECK: 0x{{[0-9a-f]*}} +// CHECK-NOT: 0x{{[0-9a-f]*}} diff --git a/test/asan/TestCases/Windows/default_options.cc b/test/asan/TestCases/Windows/default_options.cc new file mode 100644 index 000000000000..6e0a28f33692 --- /dev/null +++ b/test/asan/TestCases/Windows/default_options.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// FIXME: merge this with the common default_options test when we can run common +// tests on Windows. + +const char *kAsanDefaultOptions="verbosity=1 help=1"; + +extern "C" +__attribute__((no_sanitize_address)) +const char *__asan_default_options() { + // CHECK: Available flags for AddressSanitizer: + return kAsanDefaultOptions; +} + +int main() { + return 0; +} diff --git a/test/asan/TestCases/Windows/dll_noreturn.cc b/test/asan/TestCases/Windows/dll_noreturn.cc index 6ec90725145f..79f923eccf84 100644 --- a/test/asan/TestCases/Windows/dll_noreturn.cc +++ b/test/asan/TestCases/Windows/dll_noreturn.cc @@ -9,7 +9,7 @@ void noreturn_f() { char buffer[42]; buffer[subscript] = 42; _exit(1); -// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T0 // CHECK-NEXT: noreturn_f {{.*}}dll_noreturn.cc:[[@LINE-4]] // CHECK-NEXT: test_function {{.*}}dll_noreturn.cc diff --git a/test/asan/TestCases/Windows/dll_report_globals_symbolization_at_startup.cc b/test/asan/TestCases/Windows/dll_report_globals_symbolization_at_startup.cc new file mode 100644 index 000000000000..fbdb1c145bbe --- /dev/null +++ b/test/asan/TestCases/Windows/dll_report_globals_symbolization_at_startup.cc @@ -0,0 +1,40 @@ +// RUN: %clang_cl_asan -LD -O0 -DDLL %s -Fe%t.dll +// RUN: %clang_cl_asan -O0 -DEXE %s %t.lib -Fe%te.exe +// RUN: env ASAN_OPTIONS=report_globals=2 %run %te.exe 2>&1 | FileCheck %s + +// FIXME: Currently, the MT runtime build crashes on startup due to dbghelp.dll +// initialization failure. +// REQUIRES: asan-dynamic-runtime + +#include <windows.h> +#include <stdio.h> + +extern "C" { +#if defined(EXE) +__declspec(dllimport) int foo_from_dll(); + +// CHECK: in DLL(reason=1) +int main(int argc, char **argv) { + foo_from_dll(); +// CHECK: hello! + printf("hello!\n"); + fflush(0); +// CHECK: in DLL(reason=0) +} +#elif defined(DLL) +// This global is registered at startup. +int x[42]; + +__declspec(dllexport) int foo_from_dll() { + return x[2]; +} + +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + printf("in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} +#else +# error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc index 8f53623419ce..04d3e2ec554b 100644 --- a/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc +++ b/test/asan/TestCases/Windows/dll_thread_stack_array_left_oob.cc @@ -9,7 +9,7 @@ DWORD WINAPI thread_proc(void *context) { int subscript = -1; char stack_buffer[42]; stack_buffer[subscript] = 42; -// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T1 // CHECK-NEXT: thread_proc {{.*}}dll_thread_stack_array_left_oob.cc:[[@LINE-3]] // diff --git a/test/asan/TestCases/Windows/free_hook_realloc.cc b/test/asan/TestCases/Windows/free_hook_realloc.cc new file mode 100644 index 000000000000..297218bf8e99 --- /dev/null +++ b/test/asan/TestCases/Windows/free_hook_realloc.cc @@ -0,0 +1,37 @@ +// Check that free hook doesn't conflict with Realloc. +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// FIXME: merge this with the common free_hook_realloc test when we can run +// common tests on Windows. + +#include <stdlib.h> +#include <io.h> +#include <sanitizer/allocator_interface.h> + +static void *glob_ptr; + +extern "C" { +void __sanitizer_free_hook(const volatile void *ptr) { + if (ptr == glob_ptr) { + *(int*)ptr = 0; + write(1, "FreeHook\n", sizeof("FreeHook\n")); + } +} +} + +int main() { + int *x = (int*)malloc(100); + x[0] = 42; + glob_ptr = x; + int *y = (int*)realloc(x, 200); + // Verify that free hook was called and didn't spoil the memory. + if (y[0] != 42) { + _exit(1); + } + write(1, "Passed\n", sizeof("Passed\n")); + free(y); + // CHECK: FreeHook + // CHECK: Passed + return 0; +} diff --git a/test/asan/TestCases/Windows/on_error_callback.cc b/test/asan/TestCases/Windows/on_error_callback.cc new file mode 100644 index 000000000000..9e690a342b56 --- /dev/null +++ b/test/asan/TestCases/Windows/on_error_callback.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// FIXME: merge this with the common on_error_callback test when we can run +// common tests on Windows. + +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void __asan_on_error() { + fprintf(stderr, "__asan_on_error called"); + fflush(0); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: __asan_on_error called +} diff --git a/test/asan/TestCases/Windows/queue_user_work_item.cc b/test/asan/TestCases/Windows/queue_user_work_item.cc new file mode 100644 index 000000000000..d99ea6fc2e45 --- /dev/null +++ b/test/asan/TestCases/Windows/queue_user_work_item.cc @@ -0,0 +1,55 @@ +// Make sure we can throw exceptions from work items executed via +// QueueUserWorkItem. +// +// Clang doesn't support exceptions on Windows yet, so for the time being we +// build this program in two parts: the code with exceptions is built with CL, +// the rest is built with Clang. This represents the typical scenario when we +// build a large project using "clang-cl -fallback -fsanitize=address". +// +// RUN: cl -c %s -Fo%t.obj +// RUN: %clangxx_asan -o %t.exe %s %t.obj +// RUN: %run %t.exe 2>&1 | FileCheck %s + +#include <windows.h> +#include <stdio.h> + +void ThrowAndCatch(); + +#if !defined(__clang__) +__declspec(noinline) +void Throw() { + fprintf(stderr, "Throw\n"); +// CHECK: Throw + throw 1; +} + +void ThrowAndCatch() { + int local; + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch\n"); +// CHECK: Catch + } +} +#else + +HANDLE done; + +DWORD CALLBACK work_item(LPVOID) { + ThrowAndCatch(); + SetEvent(done); + return 0; +} + +int main(int argc, char **argv) { + done = CreateEvent(0, false, false, "job is done"); + if (!done) + return 1; + QueueUserWorkItem(&work_item, nullptr, 0); + if (WAIT_OBJECT_0 != WaitForSingleObject(done, INFINITE)) + return 2; + fprintf(stderr, "Done!\n"); +// CHECK: Done! +} +#endif diff --git a/test/asan/TestCases/Windows/queue_user_work_item_report.cc b/test/asan/TestCases/Windows/queue_user_work_item_report.cc new file mode 100644 index 000000000000..a57e1e767dc7 --- /dev/null +++ b/test/asan/TestCases/Windows/queue_user_work_item_report.cc @@ -0,0 +1,29 @@ +// RUN: %clang_cl_asan -O0 %s -Fe%t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <windows.h> + +HANDLE done; + +DWORD CALLBACK work_item(LPVOID) { + int subscript = -1; + volatile char stack_buffer[42]; + stack_buffer[subscript] = 42; +// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T1 +// CHECK: {{#0 .* work_item .*queue_user_work_item_report.cc}}:[[@LINE-3]] +// CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame +// CHECK: work_item + SetEvent(done); + return 0; +} + +int main(int argc, char **argv) { + done = CreateEvent(0, false, false, "job is done"); + if (!done) + return 1; +// CHECK-NOT: Thread T1 created + QueueUserWorkItem(&work_item, nullptr, 0); + if (WAIT_OBJECT_0 != WaitForSingleObject(done, INFINITE)) + return 2; +} diff --git a/test/asan/TestCases/Windows/report_globals_reload_dll.cc b/test/asan/TestCases/Windows/report_globals_reload_dll.cc new file mode 100644 index 000000000000..8b050975aac1 --- /dev/null +++ b/test/asan/TestCases/Windows/report_globals_reload_dll.cc @@ -0,0 +1,51 @@ +// Make sure we can handle reloading the same DLL multiple times. +// RUN: %clang_cl_asan -LD -O0 -DDLL %s -Fe%t.dll +// RUN: %clang_cl_asan -O0 -DEXE %s -Fe%te.exe +// RUN: env ASAN_OPTIONS=report_globals=1 %run %te.exe %t.dll 2>&1 | FileCheck %s + +#include <windows.h> +#include <stdio.h> +#include <string.h> + +extern "C" { +#if defined(EXE) +int main(int argc, char **argv) { + if (argc != 2) { + printf("Usage: %s [client].dll\n", argv[0]); + return 101; + } + const char *dll_name = argv[1]; + +// CHECK: time to load DLL + printf("time to load DLL\n"); + fflush(0); + +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) +// CHECK: in DLL(reason=1) +// CHECK: in DLL(reason=0) + for (int i = 0; i < 30; ++i) { + HMODULE dll = LoadLibrary(dll_name); + if (dll == NULL) + return 3; + + if (!FreeLibrary(dll)) + return 4; + } + +// CHECK: All OK! + printf("All OK!\n"); + fflush(0); +} +#elif defined(DLL) +BOOL WINAPI DllMain(HMODULE, DWORD reason, LPVOID) { + printf("in DLL(reason=%d)\n", (int)reason); + fflush(0); + return TRUE; +} +#else +# error oops! +#endif +} diff --git a/test/asan/TestCases/Windows/globals_multiple_dlls.cc b/test/asan/TestCases/Windows/report_globals_vs_freelibrary.cc index 634e5782796c..72bf36ad0be7 100644 --- a/test/asan/TestCases/Windows/globals_multiple_dlls.cc +++ b/test/asan/TestCases/Windows/report_globals_vs_freelibrary.cc @@ -1,7 +1,3 @@ -// Make sure everything works even if the main module doesn't have any stack -// variables, thus doesn't explicitly reference any symbol exported by the -// runtime thunk. -// // RUN: %clang_cl_asan -LD -O0 -DDLL %s -Fe%t.dll // RUN: %clang_cl_asan -O0 -DEXE %s -Fe%te.exe // RUN: env ASAN_OPTIONS=report_globals=2 %run %te.exe %t.dll 2>&1 | FileCheck %s diff --git a/test/asan/TestCases/Windows/stack_array_left_oob.cc b/test/asan/TestCases/Windows/stack_array_left_oob.cc index 040d855b48e2..845a1f33261f 100644 --- a/test/asan/TestCases/Windows/stack_array_left_oob.cc +++ b/test/asan/TestCases/Windows/stack_array_left_oob.cc @@ -7,7 +7,7 @@ int main() { int subscript = -1; char buffer[42]; buffer[subscript] = 42; -// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T0 // CHECK-NEXT: {{#0 .* main .*stack_array_left_oob.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T0 at offset [[OFFSET:.*]] in frame diff --git a/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc b/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc index 17b9b1bf8ecb..63cb8ae1f43c 100644 --- a/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc +++ b/test/asan/TestCases/Windows/thread_stack_array_left_oob.cc @@ -7,7 +7,7 @@ DWORD WINAPI thread_proc(void *) { int subscript = -1; volatile char stack_buffer[42]; stack_buffer[subscript] = 42; -// CHECK: AddressSanitizer: stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: AddressSanitizer: stack-buffer-underflow on address [[ADDR:0x[0-9a-f]+]] // CHECK: WRITE of size 1 at [[ADDR]] thread T1 // CHECK: {{#0 .* thread_proc .*thread_stack_array_left_oob.cc}}:[[@LINE-3]] // CHECK: Address [[ADDR]] is located in stack of thread T1 at offset {{.*}} in frame diff --git a/test/asan/TestCases/alloca_instruments_all_paddings.cc b/test/asan/TestCases/alloca_instruments_all_paddings.cc index d60a3b22dcb9..e2c7fafb193e 100644 --- a/test/asan/TestCases/alloca_instruments_all_paddings.cc +++ b/test/asan/TestCases/alloca_instruments_all_paddings.cc @@ -1,4 +1,5 @@ // RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %clangxx_asan -O3 -mllvm -asan-instrument-allocas %s -o %t // RUN: %run %t 2>&1 // diff --git a/test/asan/TestCases/alloca_loop_unpoisoning.cc b/test/asan/TestCases/alloca_loop_unpoisoning.cc new file mode 100644 index 000000000000..3621a09aa720 --- /dev/null +++ b/test/asan/TestCases/alloca_loop_unpoisoning.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %run %t 2>&1 +// +// REQUIRES: stable-runtime + +// This testcase checks that allocas and VLAs inside loop are correctly unpoisoned. + +#include <assert.h> +#include <alloca.h> +#include <stdint.h> +#include "sanitizer/asan_interface.h" + +void *top, *bot; + +__attribute__((noinline)) void foo(int len) { + char x; + top = &x; + char array[len]; // NOLINT + assert(!(reinterpret_cast<uintptr_t>(array) & 31L)); + alloca(len); + for (int i = 0; i < 32; ++i) { + char array[i]; // NOLINT + bot = alloca(i); + assert(!(reinterpret_cast<uintptr_t>(bot) & 31L)); + } +} + +int main(int argc, char **argv) { + foo(32); + void *q = __asan_region_is_poisoned(bot, (char *)top - (char *)bot); + assert(!q); + return 0; +} diff --git a/test/asan/TestCases/alloca_vla_interact.cc b/test/asan/TestCases/alloca_vla_interact.cc new file mode 100644 index 000000000000..531cc243055d --- /dev/null +++ b/test/asan/TestCases/alloca_vla_interact.cc @@ -0,0 +1,41 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: %run %t 2>&1 +// +// REQUIRES: stable-runtime +// XFAIL: powerpc64 + +// This testcase checks correct interaction between VLAs and allocas. + +#include <assert.h> +#include <alloca.h> +#include <stdint.h> +#include "sanitizer/asan_interface.h" + +#define RZ 32 + +__attribute__((noinline)) void foo(int len) { + char *top, *bot; + // This alloca call should live until the end of foo. + char *alloca1 = (char *)alloca(len); + assert(!(reinterpret_cast<uintptr_t>(alloca1) & 31L)); + // This should be first poisoned address after loop. + top = alloca1 - RZ; + for (int i = 0; i < 32; ++i) { + // Check that previous alloca was unpoisoned at the end of iteration. + if (i) assert(!__asan_region_is_poisoned(bot, 96)); + // VLA is unpoisoned at the end of iteration. + volatile char array[i]; + assert(!(reinterpret_cast<uintptr_t>(array) & 31L)); + // Alloca is unpoisoned at the end of iteration, + // because dominated by VLA. + bot = (char *)alloca(i) - RZ; + } + // Check that all allocas from loop were unpoisoned correctly. + void *q = __asan_region_is_poisoned(bot, (char *)top - (char *)bot + 1); + assert(q == top); +} + +int main(int argc, char **argv) { + foo(32); + return 0; +} diff --git a/test/asan/TestCases/allocator_returns_null.cc b/test/asan/TestCases/allocator_returns_null.cc index da6fbd43099e..bc6cd2035163 100644 --- a/test/asan/TestCases/allocator_returns_null.cc +++ b/test/asan/TestCases/allocator_returns_null.cc @@ -4,16 +4,16 @@ // // RUN: %clangxx_asan -O0 %s -o %t // RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL -// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH -// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL -// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH -// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL -// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH -// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL -// RUN: env ASAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH -// RUN: env ASAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL #include <limits.h> #include <stdlib.h> diff --git a/test/asan/TestCases/asan_and_llvm_coverage_test.cc b/test/asan/TestCases/asan_and_llvm_coverage_test.cc index 35bdfcb353c2..05de12b66bba 100644 --- a/test/asan/TestCases/asan_and_llvm_coverage_test.cc +++ b/test/asan/TestCases/asan_and_llvm_coverage_test.cc @@ -1,5 +1,5 @@ // RUN: %clangxx_asan -coverage -O0 %s -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=1 %run %t 2>&1 | FileCheck %s // XFAIL: android #include <stdio.h> int foo() { return 1; } diff --git a/test/asan/TestCases/asan_options-help.cc b/test/asan/TestCases/asan_options-help.cc index f10830f16085..a5e19e0c2003 100644 --- a/test/asan/TestCases/asan_options-help.cc +++ b/test/asan/TestCases/asan_options-help.cc @@ -1,5 +1,5 @@ // RUN: %clangxx_asan -O0 %s -o %t -// RUN: ASAN_OPTIONS=help=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:help=1 %run %t 2>&1 | FileCheck %s int main() { } diff --git a/test/asan/TestCases/atexit_stats.cc b/test/asan/TestCases/atexit_stats.cc index be6534475245..596bfdaa0d53 100644 --- a/test/asan/TestCases/atexit_stats.cc +++ b/test/asan/TestCases/atexit_stats.cc @@ -1,6 +1,6 @@ // Make sure we report atexit stats. // RUN: %clangxx_asan -O3 %s -o %t -// RUN: env ASAN_OPTIONS=atexit=1:print_stats=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:atexit=1:print_stats=1 %run %t 2>&1 | FileCheck %s // // No atexit output on Android due to // https://code.google.com/p/address-sanitizer/issues/detail?id=263 diff --git a/test/asan/TestCases/atoi_strict.c b/test/asan/TestCases/atoi_strict.c new file mode 100644 index 000000000000..f3739506fb3a --- /dev/null +++ b/test/asan/TestCases/atoi_strict.c @@ -0,0 +1,55 @@ +// Test strict_string_checks option in atoi function +// RUN: %clang_asan %s -o %t +// RUN: %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +void test1(char *array) { + // Last symbol is non-digit + memset(array, '1', 10); + array[9] = 'a'; + int r = atoi(array); + assert(r == 111111111); +} + +void test2(char *array) { + // Single non-digit symbol + array[9] = 'a'; + int r = atoi(array + 9); + assert(r == 0); +} + +void test3(char *array) { + // Incorrect number format + memset(array, ' ', 10); + array[9] = '-'; + array[8] = '-'; + int r = atoi(array); + assert(r == 0); +} + +int main(int argc, char **argv) { + char *array = (char*)malloc(10); + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(array); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1: READ of size 11 + if (!strcmp(argv[1], "test2")) test2(array); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2: READ of size 2 + if (!strcmp(argv[1], "test3")) test3(array); + // CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK3: READ of size 11 + free(array); + return 0; +} diff --git a/test/asan/TestCases/atol_strict.c b/test/asan/TestCases/atol_strict.c new file mode 100644 index 000000000000..f106150f3eaa --- /dev/null +++ b/test/asan/TestCases/atol_strict.c @@ -0,0 +1,55 @@ +// Test strict_string_checks option in atol function +// RUN: %clang_asan %s -o %t +// RUN: %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +void test1(char *array) { + // Last symbol is non-digit + memset(array, '1', 10); + array[9] = 'a'; + long r = atol(array); + assert(r == 111111111); +} + +void test2(char *array) { + // Single non-digit symbol + array[9] = 'a'; + long r = atol(array + 9); + assert(r == 0); +} + +void test3(char *array) { + // Incorrect number format + memset(array, ' ', 10); + array[9] = '-'; + array[8] = '-'; + long r = atol(array); + assert(r == 0); +} + +int main(int argc, char **argv) { + char *array = (char*)malloc(10); + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(array); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1: READ of size 11 + if (!strcmp(argv[1], "test2")) test2(array); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2: READ of size 2 + if (!strcmp(argv[1], "test3")) test3(array); + // CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK3: READ of size 11 + free(array); + return 0; +} diff --git a/test/asan/TestCases/atoll_strict.c b/test/asan/TestCases/atoll_strict.c new file mode 100644 index 000000000000..23405d2d220d --- /dev/null +++ b/test/asan/TestCases/atoll_strict.c @@ -0,0 +1,55 @@ +// Test strict_string_checks option in atoll function +// RUN: %clang_asan %s -o %t +// RUN: %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +void test1(char *array) { + // Last symbol is non-digit + memset(array, '1', 10); + array[9] = 'a'; + long long r = atoll(array); + assert(r == 111111111); +} + +void test2(char *array) { + // Single non-digit symbol + array[9] = 'a'; + long long r = atoll(array + 9); + assert(r == 0); +} + +void test3(char *array) { + // Incorrect number format + memset(array, ' ', 10); + array[9] = '-'; + array[8] = '-'; + long long r = atoll(array); + assert(r == 0); +} + +int main(int argc, char **argv) { + char *array = (char*)malloc(10); + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(array); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1: READ of size 11 + if (!strcmp(argv[1], "test2")) test2(array); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2: READ of size 2 + if (!strcmp(argv[1], "test3")) test3(array); + // CHECK3: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK3: READ of size 11 + free(array); + return 0; +} diff --git a/test/asan/TestCases/closed-fds.cc b/test/asan/TestCases/closed-fds.cc new file mode 100644 index 000000000000..af0ac26743ca --- /dev/null +++ b/test/asan/TestCases/closed-fds.cc @@ -0,0 +1,33 @@ +// Check that when the program closed its std(in|out|err), running the external +// symbolizer still works. + +// RUN: rm -f %t.log.* +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 && ASAN_OPTIONS=$ASAN_OPTIONS:log_path=%t.log:verbosity=2 not %run %t 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-FILE < %t.log.* + +// FIXME: copy %t.log back from the device and re-enable on Android. +// UNSUPPORTED: android + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int result = fprintf(stderr, "Closing streams.\n"); + assert(result > 0); + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + result = fprintf(stderr, "Can you hear me now?\n"); + assert(result < 0); + char *x = (char *)malloc(10 * sizeof(char)); + free(x); + x[argc] = 'X'; // BOOM + // CHECK-FILE: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK-FILE: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK-FILE: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK-FILE: {{ #0 0x.* in main .*closed-fds.cc:}}[[@LINE-4]] + return 0; +} diff --git a/test/asan/TestCases/contiguous_container_crash.cc b/test/asan/TestCases/contiguous_container_crash.cc index 143ae9d8edee..1ae1ff164302 100644 --- a/test/asan/TestCases/contiguous_container_crash.cc +++ b/test/asan/TestCases/contiguous_container_crash.cc @@ -1,7 +1,8 @@ // RUN: %clangxx_asan -O %s -o %t // RUN: not %run %t crash 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s -// RUN: not %run %t bad-bounds 2>&1 | FileCheck --check-prefix=CHECK-BAD %s -// RUN: env ASAN_OPTIONS=detect_container_overflow=0 %run %t crash +// RUN: not %run %t bad-bounds 2>&1 | FileCheck --check-prefix=CHECK-BAD-BOUNDS %s +// RUN: not %run %t bad-alignment 2>&1 | FileCheck --check-prefix=CHECK-BAD-ALIGNMENT %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_container_overflow=0 %run %t crash // // Test crash due to __sanitizer_annotate_contiguous_container. @@ -21,21 +22,31 @@ int TestCrash() { t[60] = 0; __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 100, &t[0] + 50); +// CHECK-CRASH: AddressSanitizer: container-overflow return (int)t[60 * one]; // Touches the poisoned memory. } void BadBounds() { long t[100]; +// CHECK-BAD-BOUNDS: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 101, &t[0] + 50); } +void BadAlignment() { + int t[100]; +// CHECK-BAD-ALIGNMENT: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container +// CHECK-BAD-ALIGNMENT: ERROR: beg is not aligned by 8 + __sanitizer_annotate_contiguous_container(&t[1], &t[0] + 100, &t[1] + 10, + &t[0] + 50); +} + int main(int argc, char **argv) { assert(argc == 2); if (!strcmp(argv[1], "crash")) return TestCrash(); else if (!strcmp(argv[1], "bad-bounds")) BadBounds(); + else if (!strcmp(argv[1], "bad-alignment")) + BadAlignment(); } -// CHECK-CRASH: AddressSanitizer: container-overflow -// CHECK-BAD: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container diff --git a/test/asan/TestCases/Linux/coverage-and-lsan.cc b/test/asan/TestCases/coverage-and-lsan.cc index 4cb8e2af3084..f65889c0a1bf 100644 --- a/test/asan/TestCases/Linux/coverage-and-lsan.cc +++ b/test/asan/TestCases/coverage-and-lsan.cc @@ -1,11 +1,11 @@ // Make sure coverage is dumped even if there are reported leaks. // -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t // // RUN: rm -rf %T/coverage-and-lsan // // RUN: mkdir -p %T/coverage-and-lsan/normal -// RUN: ASAN_OPTIONS=coverage=1:coverage_dir=%T/coverage-and-lsan:verbosity=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_dir=%T/coverage-and-lsan:verbosity=1 not %run %t 2>&1 | FileCheck %s // RUN: %sancov print %T/coverage-and-lsan/*.sancov 2>&1 // // REQUIRES: leak-detection diff --git a/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc b/test/asan/TestCases/coverage-caller-callee-total-count.cc index 7598f6bc7bc5..ac6d2486e462 100644 --- a/test/asan/TestCases/Linux/coverage-caller-callee-total-count.cc +++ b/test/asan/TestCases/coverage-caller-callee-total-count.cc @@ -1,7 +1,7 @@ // Test __sanitizer_get_total_unique_coverage for caller-callee coverage -// RUN: %clangxx_asan -fsanitize-coverage=4 %s -o %t -// RUN: ASAN_OPTIONS=coverage=1 %run %t +// RUN: %clangxx_asan -fsanitize-coverage=edge,indirect-calls %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1 %run %t // RUN: rm -f caller-callee*.sancov // // REQUIRES: asan-64-bits diff --git a/test/asan/TestCases/Linux/coverage-caller-callee.cc b/test/asan/TestCases/coverage-caller-callee.cc index cd318962b8e0..9c42817776f9 100644 --- a/test/asan/TestCases/Linux/coverage-caller-callee.cc +++ b/test/asan/TestCases/coverage-caller-callee.cc @@ -1,13 +1,13 @@ // Test caller-callee coverage with large number of threads // and various numbers of callers and callees. -// RUN: %clangxx_asan -fsanitize-coverage=4 %s -o %t -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1 -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 9 2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2 -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 7 3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3 -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1 -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2 -// RUN: ASAN_OPTIONS=coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3 +// RUN: %clangxx_asan -fsanitize-coverage=edge,indirect-calls %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 10 1 2>&1 | FileCheck %s --check-prefix=CHECK-10-1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 9 2 2>&1 | FileCheck %s --check-prefix=CHECK-9-2 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 7 3 2>&1 | FileCheck %s --check-prefix=CHECK-7-3 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 17 1 2>&1 | FileCheck %s --check-prefix=CHECK-17-1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 15 2 2>&1 | FileCheck %s --check-prefix=CHECK-15-2 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 18 3 2>&1 | FileCheck %s --check-prefix=CHECK-18-3 // RUN: rm -f caller-callee*.sancov // // REQUIRES: asan-64-bits diff --git a/test/asan/TestCases/Linux/coverage-disabled.cc b/test/asan/TestCases/coverage-disabled.cc index cb33542a5701..dd28485a6bcf 100644 --- a/test/asan/TestCases/Linux/coverage-disabled.cc +++ b/test/asan/TestCases/coverage-disabled.cc @@ -1,19 +1,19 @@ // Test that no data is collected without a runtime flag. // -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t // // RUN: rm -rf %T/coverage-disabled // // RUN: mkdir -p %T/coverage-disabled/normal -// RUN: ASAN_OPTIONS=coverage_direct=0:coverage_dir=%T/coverage-disabled/normal:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage_direct=0:coverage_dir=%T/coverage-disabled/normal:verbosity=1 %run %t // RUN: not %sancov print %T/coverage-disabled/normal/*.sancov 2>&1 // // RUN: mkdir -p %T/coverage-disabled/direct -// RUN: ASAN_OPTIONS=coverage_direct=1:coverage_dir=%T/coverage-disabled/direct:verbosity=1 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage_direct=1:coverage_dir=%T/coverage-disabled/direct:verbosity=1 %run %t // RUN: cd %T/coverage-disabled/direct // RUN: not %sancov rawunpack *.sancov // -// XFAIL: android +// UNSUPPORTED: android int main(int argc, char **argv) { return 0; diff --git a/test/asan/TestCases/coverage-levels.cc b/test/asan/TestCases/coverage-levels.cc new file mode 100644 index 000000000000..aa3641927cf2 --- /dev/null +++ b/test/asan/TestCases/coverage-levels.cc @@ -0,0 +1,34 @@ +// Test various levels of coverage +// +// RUN: %clangxx_asan -O1 -fsanitize-coverage=func %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %clangxx_asan -O1 -fsanitize-coverage=bb %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %clangxx_asan -O1 -fsanitize-coverage=edge %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 +// RUN: %clangxx_asan -O1 -fsanitize-coverage=edge -mllvm -sanitizer-coverage-block-threshold=0 %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_bitset=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 +// RUN: %clangxx_asan -O1 -fsanitize-coverage=edge,8bit-counters %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_counters=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK_COUNTERS + +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_bitset=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOBITSET +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:coverage_pcs=0:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3_NOPCS +// +// REQUIRES: asan-64-bits + +volatile int sink; +int main(int argc, char **argv) { + if (argc == 0) + sink = 0; +} + +// CHECK1: CovDump: bitset of 1 bits written for '{{.*}}', 1 bits are set +// CHECK1: 1 PCs written +// CHECK2: CovDump: bitset of 3 bits written for '{{.*}}', 2 bits are set +// CHECK2: 2 PCs written +// CHECK3: CovDump: bitset of 4 bits written for '{{.*}}', 3 bits are set +// CHECK3: 3 PCs written +// CHECK3_NOBITSET-NOT: bitset of +// CHECK3_NOPCS-NOT: PCs written +// CHECK_COUNTERS: CovDump: 4 counters written for diff --git a/test/asan/TestCases/Linux/coverage-maybe-open-file.cc b/test/asan/TestCases/coverage-maybe-open-file.cc index 4580de411799..b261fb0fc775 100644 --- a/test/asan/TestCases/Linux/coverage-maybe-open-file.cc +++ b/test/asan/TestCases/coverage-maybe-open-file.cc @@ -1,11 +1,11 @@ // FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 // XFAIL: android // -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t // RUN: rm -rf %T/coverage-maybe-open-file // RUN: mkdir -p %T/coverage-maybe-open-file && cd %T/coverage-maybe-open-file -// RUN: ASAN_OPTIONS=coverage=1 %run %t | FileCheck %s --check-prefix=CHECK-success -// RUN: ASAN_OPTIONS=coverage=0 %run %t | FileCheck %s --check-prefix=CHECK-fail +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1 %run %t | FileCheck %s --check-prefix=CHECK-success +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=0 %run %t | FileCheck %s --check-prefix=CHECK-fail // RUN: [ "$(cat test.sancov.packed)" == "test" ] // RUN: cd .. && rm -rf %T/coverage-maybe-open-file @@ -15,6 +15,7 @@ #include <sanitizer/coverage_interface.h> +// FIXME: the code below might not work on Windows. int main(int argc, char **argv) { int fd = __sanitizer_maybe_open_cov_file("test"); if (fd > 0) { diff --git a/test/asan/TestCases/coverage-order-pcs.cc b/test/asan/TestCases/coverage-order-pcs.cc new file mode 100644 index 000000000000..3f56354e44a7 --- /dev/null +++ b/test/asan/TestCases/coverage-order-pcs.cc @@ -0,0 +1,56 @@ +// Test coverage_order_pcs=1 flag which orders the PCs by their appearance. +// RUN: DIR=%T/coverage-order-pcs +// RUN: rm -rf $DIR +// RUN: mkdir $DIR +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %run %t +// RUN: mv $DIR/*sancov $DIR/A + +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %run %t 1 +// RUN: mv $DIR/*sancov $DIR/B + +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %run %t +// RUN: mv $DIR/*sancov $DIR/C + +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %run %t 1 +// RUN: mv $DIR/*sancov $DIR/D +// +// RUN: (%sancov print $DIR/A; %sancov print $DIR/B; %sancov print $DIR/C; %sancov print $DIR/D) | FileCheck %s +// +// RUN: rm -rf $DIR +// Ordering works only in 64-bit mode for now. +// REQUIRES: asan-64-bits +#include <stdio.h> + +void foo() { fprintf(stderr, "FOO\n"); } +void bar() { fprintf(stderr, "BAR\n"); } + +int main(int argc, char **argv) { + if (argc == 2) { + foo(); + bar(); + } else { + bar(); + foo(); + } +} + +// Run A: no ordering +// CHECK: [[FOO:0x[0-9a-f]*]] +// CHECK-NEXT: [[BAR:0x[0-9a-f]*]] +// CHECK-NEXT: [[MAIN:0x[0-9a-f]*]] +// +// Run B: still no ordering +// CHECK-NEXT: [[FOO]] +// CHECK-NEXT: [[BAR]] +// CHECK-NEXT: [[MAIN]] +// +// Run C: MAIN, BAR, FOO +// CHECK-NEXT: [[MAIN]] +// CHECK-NEXT: [[BAR]] +// CHECK-NEXT: [[FOO]] +// +// Run D: MAIN, FOO, BAR +// CHECK-NEXT: [[MAIN]] +// CHECK-NEXT: [[FOO]] +// CHECK-NEXT: [[BAR]] diff --git a/test/asan/TestCases/Linux/coverage-reset.cc b/test/asan/TestCases/coverage-reset.cc index d3d35e21b5a7..8e025600fda7 100644 --- a/test/asan/TestCases/Linux/coverage-reset.cc +++ b/test/asan/TestCases/coverage-reset.cc @@ -1,7 +1,7 @@ // Test __sanitizer_reset_coverage(). -// RUN: %clangxx_asan -fsanitize-coverage=1 %s -o %t -// RUN: ASAN_OPTIONS=coverage=1 %run %t +// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1 %run %t #include <sanitizer/coverage_interface.h> #include <stdio.h> diff --git a/test/asan/TestCases/coverage-tracing.cc b/test/asan/TestCases/coverage-tracing.cc new file mode 100644 index 000000000000..21a98515f648 --- /dev/null +++ b/test/asan/TestCases/coverage-tracing.cc @@ -0,0 +1,50 @@ +// Test -fsanitize-coverage=trace-bb +// +// RUN: %clangxx_asan -O1 -fsanitize-coverage=func,trace-bb %s -o %t +// RUN: rm -rf %T/coverage-tracing +// RUN: mkdir %T/coverage-tracing +// RUN: cd %T/coverage-tracing +// RUN: A=x; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK1; mv trace-points.*.sancov $A.points +// RUN: A=f; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points +// RUN: A=b; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK2; mv trace-points.*.sancov $A.points +// RUN: A=bf; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points +// RUN: A=fb; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK3; mv trace-points.*.sancov $A.points +// RUN: A=ffb; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points +// RUN: A=fff; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 1 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK4; mv trace-points.*.sancov $A.points +// RUN: A=bbf; ASAN_OPTIONS=$ASAN_OPTIONS:coverage=1:verbosity=1 %run %t $A 100 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK301; mv trace-points.*.sancov $A.points +// RUN: diff f.points fff.points +// RUN: diff bf.points fb.points +// RUN: diff bf.points ffb.points +// RUN: diff bf.points bbf.points +// RUN: not diff x.points f.points +// RUN: not diff x.points b.points +// RUN: not diff x.points bf.points +// RUN: not diff f.points b.points +// RUN: not diff f.points bf.points +// RUN: not diff b.points bf.points +// RUN: rm -rf %T/coverage-tracing +// +// REQUIRES: asan-64-bits + +#include <stdlib.h> +volatile int sink; +__attribute__((noinline)) void foo() { sink++; } +__attribute__((noinline)) void bar() { sink++; } + +int main(int argc, char **argv) { + if (argc != 3) return 0; + int n = strtol(argv[2], 0, 10); + while (n-- > 0) { + for (int i = 0; argv[1][i]; i++) { + if (argv[1][i] == 'f') foo(); + else if (argv[1][i] == 'b') bar(); + } + } +} + +// CHECK: CovDump: Trace: 3 PCs written +// CHECK1: CovDump: Trace: 1 Events written +// CHECK2: CovDump: Trace: 2 Events written +// CHECK3: CovDump: Trace: 3 Events written +// CHECK4: CovDump: Trace: 4 Events written +// CHECK301: CovDump: Trace: 301 Events written diff --git a/test/asan/TestCases/debug_mapping.cc b/test/asan/TestCases/debug_mapping.cc index f96abf6d11cf..04de97548012 100644 --- a/test/asan/TestCases/debug_mapping.cc +++ b/test/asan/TestCases/debug_mapping.cc @@ -1,6 +1,6 @@ // Checks that the debugging API returns correct shadow scale and offset. // RUN: %clangxx_asan -O %s -o %t -// RUN: env ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1 %run %t 2>&1 | FileCheck %s #include <sanitizer/asan_interface.h> #include <stdio.h> @@ -8,14 +8,14 @@ // printed because of verbosity=1 // CHECK: SHADOW_SCALE: [[SCALE:[0-9]+]] -// CHECK: SHADOW_OFFSET: [[OFFSET:[0-9]+]] +// CHECK: SHADOW_OFFSET: [[OFFSET:0x[0-9a-f]+]] int main() { size_t scale, offset; __asan_get_shadow_mapping(&scale, &offset); - fprintf(stderr, "scale: %lx\n", scale); - fprintf(stderr, "offset: %lx\n", offset); + fprintf(stderr, "scale: %d\n", (int)scale); + fprintf(stderr, "offset: 0x%lx\n", offset); // CHECK: scale: [[SCALE]] // CHECK: offset: [[OFFSET]] diff --git a/test/asan/TestCases/debug_ppc64_mapping.cc b/test/asan/TestCases/debug_ppc64_mapping.cc index 3ddd3e1404ce..ad7e25ce3bd0 100644 --- a/test/asan/TestCases/debug_ppc64_mapping.cc +++ b/test/asan/TestCases/debug_ppc64_mapping.cc @@ -1,6 +1,6 @@ // RUN: %clang_asan -O0 %s -o %t -// RUN: env ASAN_OPTIONS=verbosity=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64-V0 -// RUN: env ASAN_OPTIONS=verbosity=2 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64-V0 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=2 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PPC64 // REQUIRES: powerpc64-supported-target #include <stdio.h> diff --git a/test/asan/TestCases/debug_stacks.cc b/test/asan/TestCases/debug_stacks.cc index 57bb5465035a..15af76dc438a 100644 --- a/test/asan/TestCases/debug_stacks.cc +++ b/test/asan/TestCases/debug_stacks.cc @@ -2,6 +2,9 @@ // malloc and free stacks. // RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// FIXME: Figure out why allocation/free stack traces may be too short on ARM. +// REQUIRES: stable-runtime + #include <sanitizer/asan_interface.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/asan/TestCases/deep_call_stack.cc b/test/asan/TestCases/deep_call_stack.cc index a5b846e3eb75..0a26a80758c6 100644 --- a/test/asan/TestCases/deep_call_stack.cc +++ b/test/asan/TestCases/deep_call_stack.cc @@ -1,10 +1,10 @@ // Check that UAR mode can handle very deep recusrion. -// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 // RUN: %clangxx_asan -O2 %s -o %t && \ // RUN: (ulimit -s 4096; %run %t) 2>&1 | FileCheck %s // Also check that use_sigaltstack+verbosity doesn't crash. -// RUN: env ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 %run %t | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:verbosity=1:use_sigaltstack=1 %run %t | FileCheck %s #include <stdio.h> __attribute__((noinline)) diff --git a/test/asan/TestCases/deep_stack_uaf.cc b/test/asan/TestCases/deep_stack_uaf.cc index 3e88d697fcef..7b0f56ef3c66 100644 --- a/test/asan/TestCases/deep_stack_uaf.cc +++ b/test/asan/TestCases/deep_stack_uaf.cc @@ -1,7 +1,7 @@ // Check that we can store lots of stack frames if asked to. // RUN: %clangxx_asan -O0 %s -o %t 2>&1 -// RUN: env ASAN_OPTIONS=malloc_context_size=120:redzone=512 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=120:redzone=512 not %run %t 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi // XFAIL: armv7l-unknown-linux-gnueabihf #include <stdlib.h> diff --git a/test/asan/TestCases/default_options.cc b/test/asan/TestCases/default_options.cc index 1614fbe71b4b..a3aa6637e8ac 100644 --- a/test/asan/TestCases/default_options.cc +++ b/test/asan/TestCases/default_options.cc @@ -1,9 +1,6 @@ // RUN: %clangxx_asan -O2 %s -o %t // RUN: %run %t 2>&1 | FileCheck %s -// __asan_default_options() are not supported on Windows. -// XFAIL: win32 - const char *kAsanDefaultOptions="verbosity=1 help=1"; extern "C" diff --git a/test/asan/TestCases/double-free.cc b/test/asan/TestCases/double-free.cc index f0dd29174849..2966aadff706 100644 --- a/test/asan/TestCases/double-free.cc +++ b/test/asan/TestCases/double-free.cc @@ -2,8 +2,8 @@ // RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX // Also works if no malloc context is available. -// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi // XFAIL: armv7l-unknown-linux-gnueabihf diff --git a/test/asan/TestCases/dump_instruction_bytes.cc b/test/asan/TestCases/dump_instruction_bytes.cc index 981e3c31327f..33f382cb12be 100644 --- a/test/asan/TestCases/dump_instruction_bytes.cc +++ b/test/asan/TestCases/dump_instruction_bytes.cc @@ -1,7 +1,7 @@ // Check that ASan prints the faulting instruction bytes on // dump_instruction_bytes=1 // RUN: %clangxx_asan %s -o %t -// RUN: env ASAN_OPTIONS=dump_instruction_bytes=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-DUMP +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:dump_instruction_bytes=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-DUMP // RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NODUMP // // REQUIRES: x86_64-supported-target,i386-supported-target diff --git a/test/asan/TestCases/free_hook_realloc.cc b/test/asan/TestCases/free_hook_realloc.cc index 4b2753252a8d..cbc5d6fed56e 100644 --- a/test/asan/TestCases/free_hook_realloc.cc +++ b/test/asan/TestCases/free_hook_realloc.cc @@ -2,9 +2,6 @@ // RUN: %clangxx_asan -O2 %s -o %t // RUN: %run %t 2>&1 | FileCheck %s -// Malloc/free hooks are not supported on Windows. -// XFAIL: win32 - #include <stdlib.h> #include <unistd.h> #include <sanitizer/allocator_interface.h> diff --git a/test/asan/TestCases/gc-test.cc b/test/asan/TestCases/gc-test.cc index 4ffa51dd22d3..3fedd6a68d38 100644 --- a/test/asan/TestCases/gc-test.cc +++ b/test/asan/TestCases/gc-test.cc @@ -1,9 +1,9 @@ // RUN: %clangxx_asan %s -pthread -o %t -// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 -// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0 // RUN: %clangxx_asan -O3 %s -pthread -o %t -// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 -// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=0 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK0 // REQUIRES: stable-runtime #include <assert.h> diff --git a/test/asan/TestCases/heap-overflow-large.cc b/test/asan/TestCases/heap-overflow-large.cc index eb2fcc3220e7..566b1158a5da 100644 --- a/test/asan/TestCases/heap-overflow-large.cc +++ b/test/asan/TestCases/heap-overflow-large.cc @@ -15,9 +15,9 @@ int main(int argc, char *argv[]) { int *x = new int[5]; memset(x, 0, sizeof(x[0]) * 5); int index = atoi(argv[1]); - int res = x[index]; + unsigned res = x[index]; // CHECK: main // CHECK-NOT: CHECK failed delete[] x; - return res ? res : 1; + return (res % 10) + 1; } diff --git a/test/asan/TestCases/heap-overflow.cc b/test/asan/TestCases/heap-overflow.cc index 70a1203562be..caecea704966 100644 --- a/test/asan/TestCases/heap-overflow.cc +++ b/test/asan/TestCases/heap-overflow.cc @@ -2,7 +2,7 @@ // RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=print_stats=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:print_stats=1 not %run %t 2>&1 | FileCheck %s // FIXME: Fix this test under GCC. // REQUIRES: Clang diff --git a/test/asan/TestCases/heavy_uar_test.cc b/test/asan/TestCases/heavy_uar_test.cc index 1f8caea21690..a70dcef14345 100644 --- a/test/asan/TestCases/heavy_uar_test.cc +++ b/test/asan/TestCases/heavy_uar_test.cc @@ -1,4 +1,4 @@ -// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 // RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi diff --git a/test/asan/TestCases/init-order-atexit.cc b/test/asan/TestCases/init-order-atexit.cc index e0dac325ce58..1bbc655b17f1 100644 --- a/test/asan/TestCases/init-order-atexit.cc +++ b/test/asan/TestCases/init-order-atexit.cc @@ -1,6 +1,3 @@ -// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 -// XFAIL: android -// // Test for the following situation: // (1) global A is constructed. // (2) exit() is called during construction of global B. @@ -8,7 +5,7 @@ // We do *not* want to report init-order bug in this case. // RUN: %clangxx_asan -O0 %s %p/Helpers/init-order-atexit-extra.cc -o %t -// RUN: env ASAN_OPTIONS=strict_init_order=true not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_init_order=true not %run %t 2>&1 | FileCheck %s #include <stdio.h> #include <stdlib.h> diff --git a/test/asan/TestCases/init-order-pthread-create.cc b/test/asan/TestCases/init-order-pthread-create.cc index eeff308a4cd5..12362fc440dc 100644 --- a/test/asan/TestCases/init-order-pthread-create.cc +++ b/test/asan/TestCases/init-order-pthread-create.cc @@ -2,29 +2,40 @@ // called. // RUN: %clangxx_asan %s %p/Helpers/init-order-pthread-create-extra.cc -pthread -o %t -// RUN: env ASAN_OPTIONS=strict_init_order=true %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_init_order=true %run %t #include <stdio.h> #include <pthread.h> +#include <unistd.h> -void *run(void *arg) { - return arg; +void *bar(void *input, bool sleep_before_init) { + if (sleep_before_init) + usleep(500000); + return input; } -void *foo(void *input) { - pthread_t t; - pthread_create(&t, 0, run, input); - void *res; - pthread_join(t, &res); - return res; -} +void *glob = bar((void*)0x1234, false); +extern void *glob2; -void *bar(void *input) { - return input; +void *poll(void *arg) { + void **glob = (void**)arg; + while (true) { + usleep(100000); + printf("glob is now: %p\n", *glob); + } } -void *glob = foo((void*)0x1234); -extern void *glob2; +struct GlobalPollerStarter { + GlobalPollerStarter() { + pthread_t p; + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&p, 0, poll, &glob); + pthread_attr_destroy(&attr); + printf("glob poller is started"); + } +} global_poller; int main() { printf("%p %p\n", glob, glob2); diff --git a/test/asan/TestCases/initialization-blacklist.cc b/test/asan/TestCases/initialization-blacklist.cc index 8ea6b46c1833..bcdb111b8bfb 100644 --- a/test/asan/TestCases/initialization-blacklist.cc +++ b/test/asan/TestCases/initialization-blacklist.cc @@ -3,15 +3,15 @@ // RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-blacklist-extra.cc\ // RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-blacklist-extra.cc\ // RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-blacklist-extra.cc\ // RUN: %p/Helpers/initialization-blacklist-extra2.cc \ // RUN: -fsanitize-blacklist=%p/Helpers/initialization-blacklist.txt -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // Function is defined in another TU. int readBadGlobal(); diff --git a/test/asan/TestCases/initialization-bug.cc b/test/asan/TestCases/initialization-bug.cc index badc6d1d1165..6257d67c308d 100644 --- a/test/asan/TestCases/initialization-bug.cc +++ b/test/asan/TestCases/initialization-bug.cc @@ -1,7 +1,7 @@ // Test to make sure basic initialization order errors are caught. -// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t-INIT-ORDER-EXE +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true not %run %t-INIT-ORDER-EXE 2>&1 | FileCheck %s // Do not test with optimization -- the error may be optimized away. @@ -32,6 +32,8 @@ int __attribute__((noinline)) initX() { // CHECK: {{AddressSanitizer: initialization-order-fiasco}} // CHECK: {{READ of size .* at 0x.* thread T0}} // CHECK: {{0x.* is located 0 bytes inside of global variable .*(y|z).*}} + // CHECK: registered at: + // CHECK: 0x{{.*}} in __asan_register_globals } // This initializer begins our initialization order problems. diff --git a/test/asan/TestCases/initialization-constexpr.cc b/test/asan/TestCases/initialization-constexpr.cc index 644246186e02..1188766b6020 100644 --- a/test/asan/TestCases/initialization-constexpr.cc +++ b/test/asan/TestCases/initialization-constexpr.cc @@ -5,13 +5,13 @@ // not dynamic initialization). // RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-constexpr-extra.cc --std=c++11 -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 class Integer { private: diff --git a/test/asan/TestCases/initialization-nobug.cc b/test/asan/TestCases/initialization-nobug.cc index 1249deb425aa..3890edf07202 100644 --- a/test/asan/TestCases/initialization-nobug.cc +++ b/test/asan/TestCases/initialization-nobug.cc @@ -2,13 +2,13 @@ // order checking. If successful, this will just return 0. // RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-nobug-extra.cc -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-nobug-extra.cc -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-nobug-extra.cc -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-nobug-extra.cc -o %t -// RUN: env ASAN_OPTIONS=check_initialization_order=true %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_initialization_order=true %run %t 2>&1 // Simple access: // Make sure that accessing a global in the same TU is safe diff --git a/test/asan/TestCases/interface_test.cc b/test/asan/TestCases/interface_test.cc index dc9d0652c8c3..9419f07e91ea 100644 --- a/test/asan/TestCases/interface_test.cc +++ b/test/asan/TestCases/interface_test.cc @@ -1,8 +1,8 @@ // Check that user may include ASan interface header. // RUN: %clang_asan %s -o %t && %run %t // RUN: %clang_asan -x c %s -o %t && %run %t -// RUN: %clang %s -o %t && %run %t -// RUN: %clang -x c %s -o %t && %run %t +// RUN: %clang %s -pie -o %t && %run %t +// RUN: %clang -x c %s -pie -o %t && %run %t #include <sanitizer/asan_interface.h> int main() { diff --git a/test/asan/TestCases/invalid-free.cc b/test/asan/TestCases/invalid-free.cc index cb545ccc215e..c6f7b842a91d 100644 --- a/test/asan/TestCases/invalid-free.cc +++ b/test/asan/TestCases/invalid-free.cc @@ -2,8 +2,8 @@ // RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX // Also works if no malloc context is available. -// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi // XFAIL: armv7l-unknown-linux-gnueabihf diff --git a/test/asan/TestCases/log-path_test.cc b/test/asan/TestCases/log-path_test.cc index 7dd1fadda86d..d253a6f50cf3 100644 --- a/test/asan/TestCases/log-path_test.cc +++ b/test/asan/TestCases/log-path_test.cc @@ -9,21 +9,21 @@ // Good log_path. // RUN: rm -f %t.log.* -// RUN: env ASAN_OPTIONS=log_path=%t.log not %run %t 2> %t.out +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:log_path=%t.log not %run %t 2> %t.out // RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.* // Invalid log_path. -// RUN: env ASAN_OPTIONS=log_path=/dev/null/INVALID not %run %t 2> %t.out +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:log_path=/dev/null/INVALID not %run %t 2> %t.out // RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out // Too long log_path. -// RUN: env ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \ +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \ // RUN: not %run %t 2> %t.out // RUN: FileCheck %s --check-prefix=CHECK-LONG < %t.out // Run w/o errors should not produce any log. // RUN: rm -f %t.log.* -// RUN: env ASAN_OPTIONS=log_path=%t.log %run %t ARG ARG ARG +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:log_path=%t.log %run %t ARG ARG ARG // RUN: not cat %t.log.* // FIXME: log_path is not supported on Windows yet. diff --git a/test/asan/TestCases/malloc_context_size.cc b/test/asan/TestCases/malloc_context_size.cc index 0d9f31598545..91e1bdc5613e 100644 --- a/test/asan/TestCases/malloc_context_size.cc +++ b/test/asan/TestCases/malloc_context_size.cc @@ -1,9 +1,9 @@ // RUN: %clangxx_asan -O0 %s -o %t -// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=malloc_context_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=TWO +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=0:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=0:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=1:fast_unwind_on_malloc=0 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=1:fast_unwind_on_malloc=1 not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:malloc_context_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=TWO int main() { char *x = new char[20]; diff --git a/test/asan/TestCases/malloc_fill.cc b/test/asan/TestCases/malloc_fill.cc index 5c926803708d..13a73a719ddd 100644 --- a/test/asan/TestCases/malloc_fill.cc +++ b/test/asan/TestCases/malloc_fill.cc @@ -1,8 +1,8 @@ // Check that we fill malloc-ed memory correctly. // RUN: %clangxx_asan %s -o %t // RUN: %run %t | FileCheck %s -// RUN: env ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %run %t | FileCheck %s --check-prefix=CHECK-10-8 -// RUN: env ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %run %t | FileCheck %s --check-prefix=CHECK-20-ab +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:max_malloc_fill_size=10:malloc_fill_byte=8 %run %t | FileCheck %s --check-prefix=CHECK-10-8 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:max_malloc_fill_size=20:malloc_fill_byte=171 %run %t | FileCheck %s --check-prefix=CHECK-20-ab #include <stdio.h> int main(int argc, char **argv) { diff --git a/test/asan/TestCases/max_redzone.cc b/test/asan/TestCases/max_redzone.cc index 01c25a9f3efc..c5539bcfb16f 100644 --- a/test/asan/TestCases/max_redzone.cc +++ b/test/asan/TestCases/max_redzone.cc @@ -1,8 +1,8 @@ // Test max_redzone runtime option. -// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=max_redzone=16 %run %t 0 2>&1 +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:max_redzone=16 %run %t 0 2>&1 // RUN: %clangxx_asan -O0 %s -o %t && %run %t 1 2>&1 -// RUN: %clangxx_asan -O3 %s -o %t && env ASAN_OPTIONS=max_redzone=16 %run %t 0 2>&1 +// RUN: %clangxx_asan -O3 %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:max_redzone=16 %run %t 0 2>&1 // RUN: %clangxx_asan -O3 %s -o %t && %run %t 1 2>&1 #include <stdio.h> diff --git a/test/asan/TestCases/memcmp_strict_test.cc b/test/asan/TestCases/memcmp_strict_test.cc index 16b7673dd547..a15d0a35e5ec 100644 --- a/test/asan/TestCases/memcmp_strict_test.cc +++ b/test/asan/TestCases/memcmp_strict_test.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=strict_memcmp=0 %run %t -// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=strict_memcmp=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_memcmp=0 %run %t +// RUN: %clangxx_asan -O0 %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_memcmp=1 not %run %t 2>&1 | FileCheck %s // Default to strict_memcmp=1. // RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s diff --git a/test/asan/TestCases/mmap_limit_mb.cc b/test/asan/TestCases/mmap_limit_mb.cc index d4ffb2eac246..02410525b2d6 100644 --- a/test/asan/TestCases/mmap_limit_mb.cc +++ b/test/asan/TestCases/mmap_limit_mb.cc @@ -3,10 +3,10 @@ // RUN: %clangxx_asan -O2 %s -o %t // RUN: %run %t 20 16 // RUN: %run %t 30 1000000 -// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 %run %t 20 16 -// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 %run %t 20 1000000 -// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 not %run %t 500 16 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=mmap_limit_mb=300 not %run %t 500 1000000 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:mmap_limit_mb=300 %run %t 20 16 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:mmap_limit_mb=300 %run %t 20 1000000 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:mmap_limit_mb=300 not %run %t 500 16 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:mmap_limit_mb=300 not %run %t 500 1000000 2>&1 | FileCheck %s // XFAIL: arm-linux-gnueabi #include <assert.h> diff --git a/test/asan/TestCases/no_asan_gen_globals.c b/test/asan/TestCases/no_asan_gen_globals.c index 0a383da1384d..2b13deace4b5 100644 --- a/test/asan/TestCases/no_asan_gen_globals.c +++ b/test/asan/TestCases/no_asan_gen_globals.c @@ -1,5 +1,7 @@ // FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 // XFAIL: android +// FIXME: http://llvm.org/bugs/show_bug.cgi?id=22682 +// REQUIRES: asan-64-bits // // Make sure __asan_gen_* strings do not end up in the symbol table. diff --git a/test/asan/TestCases/on_error_callback.cc b/test/asan/TestCases/on_error_callback.cc index c378c8b2de1b..0ad83d549af2 100644 --- a/test/asan/TestCases/on_error_callback.cc +++ b/test/asan/TestCases/on_error_callback.cc @@ -1,8 +1,5 @@ // RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s -// FIXME: __asan_on_error() is not supported on Windows yet. -// XFAIL: win32 - #include <stdio.h> #include <stdlib.h> diff --git a/test/asan/TestCases/poison_partial.cc b/test/asan/TestCases/poison_partial.cc index ce9c98b7859a..8a8921566dbc 100644 --- a/test/asan/TestCases/poison_partial.cc +++ b/test/asan/TestCases/poison_partial.cc @@ -1,8 +1,8 @@ // RUN: %clangxx_asan -O0 %s -o %t // RUN: not %run %t 2>&1 | FileCheck %s // RUN: not %run %t heap 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=poison_partial=0 %run %t -// RUN: env ASAN_OPTIONS=poison_partial=0 %run %t heap +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:poison_partial=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:poison_partial=0 %run %t heap #include <string.h> char g[21]; char *x; diff --git a/test/asan/TestCases/print_summary.cc b/test/asan/TestCases/print_summary.cc index 79411c529469..675934071252 100644 --- a/test/asan/TestCases/print_summary.cc +++ b/test/asan/TestCases/print_summary.cc @@ -1,14 +1,16 @@ // RUN: %clangxx_asan -O0 %s -o %t -// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=YES -// RUN: env ASAN_OPTIONS=print_summary=false not %run %t 2>&1 | FileCheck %s --check-prefix=NO +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=SOURCE +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:symbolize=false not %run %t 2>&1 | FileCheck %s --check-prefix=MODULE +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:print_summary=false not %run %t 2>&1 | FileCheck %s --check-prefix=MISSING int main() { char *x = new char[20]; delete[] x; return x[0]; - // YES: ERROR: AddressSanitizer: heap-use-after-free - // YES: SUMMARY: AddressSanitizer: heap-use-after-free - // NO: ERROR: AddressSanitizer: heap-use-after-free - // NO-NOT: SUMMARY: AddressSanitizer: heap-use-after-free + // SOURCE: ERROR: AddressSanitizer: heap-use-after-free + // SOURCE: SUMMARY: AddressSanitizer: heap-use-after-free {{.*}}print_summary.cc:[[@LINE-2]]{{.*}} main + // MODULE: ERROR: AddressSanitizer: heap-use-after-free + // MODULE: SUMMARY: AddressSanitizer: heap-use-after-free ({{.*}}+0x{{.*}}) + // MISSING: ERROR: AddressSanitizer: heap-use-after-free + // MISSING-NOT: SUMMARY } - diff --git a/test/asan/TestCases/printf-1.c b/test/asan/TestCases/printf-1.c index 5657083c5865..2df74b67a31a 100644 --- a/test/asan/TestCases/printf-1.c +++ b/test/asan/TestCases/printf-1.c @@ -1,6 +1,6 @@ // RUN: %clang_asan -O2 %s -o %t -// RUN: env ASAN_OPTIONS=check_printf=1 %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_printf=1 %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_printf=0 %run %t 2>&1 | FileCheck %s // RUN: %run %t 2>&1 | FileCheck %s #include <stdio.h> diff --git a/test/asan/TestCases/printf-2.c b/test/asan/TestCases/printf-2.c index e9cb47e24c15..b3ab96111142 100644 --- a/test/asan/TestCases/printf-2.c +++ b/test/asan/TestCases/printf-2.c @@ -1,9 +1,9 @@ // RUN: %clang_asan -O2 %s -o %t // We need replace_str=0 and replace_intrin=0 to avoid reporting errors in // strlen() and memcpy() called by printf(). -// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s -// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s -// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_str=0:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_str=0:replace_intrin=0:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_str=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s // FIXME: printf is not intercepted on Windows yet. // XFAIL: win32 diff --git a/test/asan/TestCases/printf-3.c b/test/asan/TestCases/printf-3.c index d16833d83c6e..bc9fece5dd96 100644 --- a/test/asan/TestCases/printf-3.c +++ b/test/asan/TestCases/printf-3.c @@ -1,6 +1,6 @@ // RUN: %clang_asan -O2 %s -o %t -// RUN: env ASAN_OPTIONS=check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s -// RUN: env ASAN_OPTIONS=check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s // RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s // FIXME: printf is not intercepted on Windows yet. diff --git a/test/asan/TestCases/printf-4.c b/test/asan/TestCases/printf-4.c index e269211d4871..b2a14ff4f25a 100644 --- a/test/asan/TestCases/printf-4.c +++ b/test/asan/TestCases/printf-4.c @@ -1,8 +1,8 @@ // RUN: %clang_asan -O2 %s -o %t // We need replace_str=0 and replace_intrin=0 to avoid reporting errors in // strlen() and memcpy() called by puts(). -// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s -// RUN: env ASAN_OPTIONS=replace_str=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_str=0:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_str=0:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s // FIXME: printf is not intercepted on Windows yet. // XFAIL: win32 diff --git a/test/asan/TestCases/printf-5.c b/test/asan/TestCases/printf-5.c index ac2c1c4b2997..d4e2a0ab9cce 100644 --- a/test/asan/TestCases/printf-5.c +++ b/test/asan/TestCases/printf-5.c @@ -1,8 +1,8 @@ // RUN: %clang_asan -O2 %s -o %t // We need replace_intrin=0 to avoid reporting errors in memcpy. -// RUN: env ASAN_OPTIONS=replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s -// RUN: env ASAN_OPTIONS=replace_intrin=0:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s -// RUN: env ASAN_OPTIONS=replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_intrin=0:check_printf=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_intrin=0:check_printf=0 %run %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:replace_intrin=0 not %run %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s // FIXME: printf is not intercepted on Windows yet. // XFAIL: win32 diff --git a/test/asan/TestCases/sleep_before_dying.c b/test/asan/TestCases/sleep_before_dying.c index 28ae0bf66d47..2029f572a9eb 100644 --- a/test/asan/TestCases/sleep_before_dying.c +++ b/test/asan/TestCases/sleep_before_dying.c @@ -1,5 +1,5 @@ // RUN: %clang_asan -O2 %s -o %t -// RUN: env ASAN_OPTIONS="sleep_before_dying=1" not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:sleep_before_dying=1" not %run %t 2>&1 | FileCheck %s #include <stdlib.h> int main() { diff --git a/test/asan/TestCases/stack-overflow.cc b/test/asan/TestCases/stack-overflow.cc index d4bb74730f12..d792c466f977 100644 --- a/test/asan/TestCases/stack-overflow.cc +++ b/test/asan/TestCases/stack-overflow.cc @@ -1,18 +1,18 @@ // Test ASan detection of stack-overflow condition. -// RUN: %clangxx_asan -O0 %s -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 %s -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O0 %s -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 %s -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O0 %s -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 %s -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O0 %s -DTHREAD -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 %s -DTHREAD -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O0 %s -DTHREAD -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 %s -DTHREAD -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O0 %s -DTHREAD -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_asan -O3 %s -DTHREAD -pthread -o %t && env ASAN_OPTIONS=use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DTHREAD -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTHREAD -DSMALL_FRAME -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DTHREAD -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTHREAD -DSAVE_ALL_THE_REGISTERS -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O0 %s -DTHREAD -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTHREAD -pthread -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:use_sigaltstack=1 not %run %t 2>&1 | FileCheck %s // RUN: not %run %t 2>&1 | FileCheck %s // REQUIRES: stable-runtime diff --git a/test/asan/TestCases/stack-use-after-return.cc b/test/asan/TestCases/stack-use-after-return.cc index 437c457748c4..669e8703b82f 100644 --- a/test/asan/TestCases/stack-use-after-return.cc +++ b/test/asan/TestCases/stack-use-after-return.cc @@ -1,9 +1,9 @@ -// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: export ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 // RUN: %clangxx_asan -O0 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O1 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O2 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s // RUN: %clangxx_asan -O3 %s -pthread -o %t && not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS=detect_stack_use_after_return=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=0 %run %t // Regression test for a CHECK failure with small stack size and large frame. // RUN: %clangxx_asan -O3 %s -pthread -o %t -DkSize=10000 -DUseThread -DkStackSize=65536 && not %run %t 2>&1 | FileCheck --check-prefix=THREAD %s // diff --git a/test/asan/TestCases/strcasestr-1.c b/test/asan/TestCases/strcasestr-1.c new file mode 100644 index 000000000000..c6f9d193e503 --- /dev/null +++ b/test/asan/TestCases/strcasestr-1.c @@ -0,0 +1,24 @@ +// Test haystack overflow in strcasestr function +// RUN: %clang_asan %s -o %t && ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strstr asan option +// Disable other interceptors because strlen may be called inside strcasestr +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strstr=false:replace_str=false %run %t 2>&1 + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = "c"; + char s1[] = {'a', 'C'}; + char s3 = 0; + r = strcasestr(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1 + 1); + return 0; +} diff --git a/test/asan/TestCases/strcasestr-2.c b/test/asan/TestCases/strcasestr-2.c new file mode 100644 index 000000000000..a4bc6362636e --- /dev/null +++ b/test/asan/TestCases/strcasestr-2.c @@ -0,0 +1,24 @@ +// Test needle overflow in strcasestr function +// RUN: %clang_asan %s -o %t && ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strstr asan option +// Disable other interceptors because strlen may be called inside strcasestr +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strstr=false:replace_str=false %run %t 2>&1 + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ab"; + char s2[] = {'c'}; + char s3 = 0; + r = strcasestr(s1, s2); + assert(r == 0); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + return 0; +} diff --git a/test/asan/TestCases/strcasestr_strict.c b/test/asan/TestCases/strcasestr_strict.c new file mode 100644 index 000000000000..03c066bb1b9d --- /dev/null +++ b/test/asan/TestCases/strcasestr_strict.c @@ -0,0 +1,28 @@ +// Test strict_string_checks option in strcasestr function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char *s1 = (char*)malloc(size); + char *s2 = (char*)malloc(size); + memset(s1, 'o', size); + memset(s2, 'O', size); + s2[size - 1]='\0'; + char* r = strcasestr(s1, s2); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == s1); + free(s1); + free(s2); + return 0; +} diff --git a/test/asan/TestCases/strcat_strict.c b/test/asan/TestCases/strcat_strict.c new file mode 100644 index 000000000000..8321f5b620f9 --- /dev/null +++ b/test/asan/TestCases/strcat_strict.c @@ -0,0 +1,44 @@ +// Test strict_string_checks option in strcat function +// RUN: %clang_asan %s -o %t +// RUN: not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-STRICT --check-prefix=CHECK1 +// RUN: not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-STRICT --check-prefix=CHECK2 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +void test1(char *to, int to_size, char *from) { + // One of arguments points to not allocated memory. + char* r = strcat(to + to_size, from); +} + +void test2(char *to, int to_size, char *from) { + // "to" is not zero-terminated. + memset(to, 'z', to_size); + char* r = strcat(to, from); +} + +int main(int argc, char **argv) { + size_t to_size = 100; + char *to = (char*)malloc(to_size); + size_t from_size = 20; + char *from = (char*)malloc(from_size); + memset(from, 'z', from_size); + from[from_size - 1] = '\0'; + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(to, to_size, from); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1-STRICT: READ of size 1 + // CHECK1-NONSTRICT: WRITE of size 20 + if (!strcmp(argv[1], "test2")) test2(to, to_size, from); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2-STRICT: READ of size 101 + // CHECK2-NONSTRICT: WRITE of size 20 + free(to); + free(from); + return 0; +} diff --git a/test/asan/TestCases/strchr_strict.c b/test/asan/TestCases/strchr_strict.c new file mode 100644 index 000000000000..48c1f139583e --- /dev/null +++ b/test/asan/TestCases/strchr_strict.c @@ -0,0 +1,22 @@ +// Test strict_string_checks option in strchr function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char fill = 'o'; + char *s = (char*)malloc(size); + memset(s, fill, size); + char c = 'o'; + char* r = strchr(s, c); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == s); + free(s); + return 0; +} diff --git a/test/asan/TestCases/strcmp_strict.c b/test/asan/TestCases/strcmp_strict.c new file mode 100644 index 000000000000..316765e18371 --- /dev/null +++ b/test/asan/TestCases/strcmp_strict.c @@ -0,0 +1,26 @@ +// Test strict_string_checks option in strcmp function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char fill = 'o'; + char *s1 = (char*)malloc(size); + memset(s1, fill, size); + char *s2 = (char*)malloc(size); + memset(s2, fill, size); + s1[size - 1] = 'z'; + s2[size - 1] = 'x'; + int r = strcmp(s1, s2); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == 1); + free(s1); + free(s2); + return 0; +} diff --git a/test/asan/TestCases/strcspn-1.c b/test/asan/TestCases/strcspn-1.c new file mode 100644 index 000000000000..ef02a049530a --- /dev/null +++ b/test/asan/TestCases/strcspn-1.c @@ -0,0 +1,19 @@ +// Test string s1 overflow in strcspn function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strspn asan option +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strspn=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t r; + char s2[] = "ab"; + char s1[] = {'c', 'a'}; + char s3 = 0; + r = strcspn(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} diff --git a/test/asan/TestCases/strcspn-2.c b/test/asan/TestCases/strcspn-2.c new file mode 100644 index 000000000000..aa82aa60abfe --- /dev/null +++ b/test/asan/TestCases/strcspn-2.c @@ -0,0 +1,19 @@ +// Test stopset overflow in strcspn function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strcspn asan option +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strspn=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t r; + char s1[] = "ab"; + char s2[] = {'a'}; + char s3 = 0; + r = strcspn(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 0); + return 0; +} diff --git a/test/asan/TestCases/strcspn_strict.c b/test/asan/TestCases/strcspn_strict.c new file mode 100644 index 000000000000..7198f9a08723 --- /dev/null +++ b/test/asan/TestCases/strcspn_strict.c @@ -0,0 +1,26 @@ +// Test strict_string_checks option in strcspn function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char fill = 'o'; + char *s1 = (char*)malloc(size); + char *s2 = (char*)malloc(size); + memset(s1, fill, size); + s1[0] = 'z'; + memset(s2, fill, size); + s2[size-1] = '\0'; + size_t r = strcspn(s1, s2); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == 1); + free(s1); + free(s2); + return 0; +} diff --git a/test/asan/TestCases/strip_path_prefix.c b/test/asan/TestCases/strip_path_prefix.c index 4556e9031e2d..fc9ebd1691cc 100644 --- a/test/asan/TestCases/strip_path_prefix.c +++ b/test/asan/TestCases/strip_path_prefix.c @@ -1,5 +1,5 @@ // RUN: %clang_asan -O2 %s -o %t -// RUN: env ASAN_OPTIONS="strip_path_prefix='/'" not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:strip_path_prefix='%S/'" not %run %t 2>&1 | FileCheck %s #include <stdlib.h> int main() { @@ -8,5 +8,5 @@ int main() { return x[5]; // Check that paths in error report don't start with slash. // CHECK: heap-use-after-free - // CHECK-NOT: #0 0x{{.*}} ({{[/].*}}) + // CHECK: #0 0x{{.*}} in main strip_path_prefix.c:[[@LINE-3]] } diff --git a/test/asan/TestCases/strncat_strict.c b/test/asan/TestCases/strncat_strict.c new file mode 100644 index 000000000000..16de17689d0c --- /dev/null +++ b/test/asan/TestCases/strncat_strict.c @@ -0,0 +1,44 @@ +// Test strict_string_checks option in strncat function +// RUN: %clang_asan %s -o %t +// RUN: not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-NONSTRICT --check-prefix=CHECK1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1-STRICT --check-prefix=CHECK1 +// RUN: not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-NONSTRICT --check-prefix=CHECK2 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2-STRICT --check-prefix=CHECK2 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +void test1(char *to, int to_size, char *from) { + // One of arguments points to not allocated memory. + char* r = strncat(to + to_size, from, 2); +} + +void test2(char *to, int to_size, char *from) { + // "to" is not zero-terminated. + memset(to, 'z', to_size); + char* r = strncat(to, from, 1); +} + +int main(int argc, char **argv) { + size_t to_size = 100; + char *to = (char*)malloc(to_size); + size_t from_size = 20; + char *from = (char*)malloc(from_size); + memset(from, 'z', from_size); + from[from_size - 1] = '\0'; + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(to, to_size, from); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1-STRICT: READ of size 1 + // CHECK1-NONSTRICT: WRITE of size 3 + if (!strcmp(argv[1], "test2")) test2(to, to_size, from); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2-STRICT: READ of size 101 + // CHECK2-NONSTRICT: WRITE of size 2 + free(to); + free(from); + return 0; +} diff --git a/test/asan/TestCases/strpbrk-1.c b/test/asan/TestCases/strpbrk-1.c new file mode 100644 index 000000000000..7cd45bd0979a --- /dev/null +++ b/test/asan/TestCases/strpbrk-1.c @@ -0,0 +1,19 @@ +// Test string s1 overflow in strpbrk function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strpbrk asan option +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strpbrk=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + char *r; + char s2[] = "ab"; + char s1[] = {'c', 'a'}; + char s3 = 0; + r = strpbrk(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1 + 1); + return 0; +} diff --git a/test/asan/TestCases/strpbrk-2.c b/test/asan/TestCases/strpbrk-2.c new file mode 100644 index 000000000000..0d50c002a21a --- /dev/null +++ b/test/asan/TestCases/strpbrk-2.c @@ -0,0 +1,19 @@ +// Test stopset overflow in strpbrk function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strpbrk asan option +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strpbrk=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + char *r; + char s1[] = "c"; + char s2[] = {'b', 'c'}; + char s3 = 0; + r = strpbrk(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1); + return 0; +} diff --git a/test/asan/TestCases/strpbrk_strict.c b/test/asan/TestCases/strpbrk_strict.c new file mode 100644 index 000000000000..2521e96ba5f0 --- /dev/null +++ b/test/asan/TestCases/strpbrk_strict.c @@ -0,0 +1,25 @@ +// Test strict_string_checks option in strpbrk function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char fill = 'o'; + char *s1 = (char*)malloc(size); + char *s2 = (char*)malloc(2); + memset(s1, fill, size); + s2[0] = fill; + s2[1]='\0'; + char* r = strpbrk(s1, s2); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == s1); + free(s1); + free(s2); + return 0; +} diff --git a/test/asan/TestCases/strspn-1.c b/test/asan/TestCases/strspn-1.c new file mode 100644 index 000000000000..24d0d2daac1e --- /dev/null +++ b/test/asan/TestCases/strspn-1.c @@ -0,0 +1,19 @@ +// Test string s1 overflow in strspn function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strspn asan option +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strspn=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t r; + char s2[] = "ab"; + char s1[] = {'a', 'c'}; + char s3 = 0; + r = strspn(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 1); + return 0; +} diff --git a/test/asan/TestCases/strspn-2.c b/test/asan/TestCases/strspn-2.c new file mode 100644 index 000000000000..e4621e5bfede --- /dev/null +++ b/test/asan/TestCases/strspn-2.c @@ -0,0 +1,19 @@ +// Test stopset overflow in strspn function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strspn asan option +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strspn=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t r; + char s1[] = "bbc"; + char s2[] = {'a', 'b'}; + char s3 = 0; + r = strspn(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r >= 2); + return 0; +} diff --git a/test/asan/TestCases/strspn_strict.c b/test/asan/TestCases/strspn_strict.c new file mode 100644 index 000000000000..7df6c0da9ab0 --- /dev/null +++ b/test/asan/TestCases/strspn_strict.c @@ -0,0 +1,25 @@ +// Test strict_str`ing_checks option in strspn function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char fill = 'o'; + char *s1 = (char*)malloc(size); + char *s2 = (char*)malloc(2); + memset(s1, fill, size); + s1[0] = s2[0] = 'z'; + s2[1] = '\0'; + size_t r = strspn(s1, s2); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == 1); + free(s1); + free(s2); + return 0; +} diff --git a/test/asan/TestCases/strstr-1.c b/test/asan/TestCases/strstr-1.c new file mode 100644 index 000000000000..193334e9bb34 --- /dev/null +++ b/test/asan/TestCases/strstr-1.c @@ -0,0 +1,20 @@ +// Test haystack overflow in strstr function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strstr asan option +// Disable other interceptors because strlen may be called inside strstr +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strstr=false:replace_str=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + char *r = 0; + char s2[] = "c"; + char s1[] = {'a', 'c'}; + char s3 = 0; + r = strstr(s1, s2); + // CHECK:'s{{[1|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == s1 + 1); + return 0; +} diff --git a/test/asan/TestCases/strstr-2.c b/test/asan/TestCases/strstr-2.c new file mode 100644 index 000000000000..cd116212fa95 --- /dev/null +++ b/test/asan/TestCases/strstr-2.c @@ -0,0 +1,20 @@ +// Test needle overflow in strstr function +// RUN: %clang_asan %s -o %t && env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +// Test intercept_strstr asan option +// Disable other interceptors because strlen may be called inside strstr +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:intercept_strstr=false:replace_str=false %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ab"; + char s2[] = {'c'}; + char s3 = 0; + r = strstr(s1, s2); + // CHECK:'s{{[2|3]}}' <== Memory access at offset {{[0-9]+ .*}}flows this variable + assert(r == 0); + return 0; +} diff --git a/test/asan/TestCases/strstr_strict.c b/test/asan/TestCases/strstr_strict.c new file mode 100644 index 000000000000..f7eca6aeb900 --- /dev/null +++ b/test/asan/TestCases/strstr_strict.c @@ -0,0 +1,25 @@ +// Test strict_string_checks option in strstr function +// RUN: %clang_asan %s -o %t && %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t size = 100; + char fill = 'o'; + char *s1 = (char*)malloc(size); + char *s2 = (char*)malloc(size); + memset(s1, fill, size); + memset(s2, fill, size); + s2[size - 1]='\0'; + char* r = strstr(s1, s2); + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: READ of size 101 + assert(r == s1); + free(s1); + free(s2); + return 0; +} diff --git a/test/asan/TestCases/strtol_strict.c b/test/asan/TestCases/strtol_strict.c new file mode 100644 index 000000000000..fac3b3a5439d --- /dev/null +++ b/test/asan/TestCases/strtol_strict.c @@ -0,0 +1,116 @@ +// Test strict_string_checks option in strtol function +// RUN: %clang_asan -DTEST1 %s -o %t +// RUN: %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3 +// RUN: %run %t test4 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test4 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test4 2>&1 | FileCheck %s --check-prefix=CHECK4 +// RUN: %run %t test5 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test5 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test5 2>&1 | FileCheck %s --check-prefix=CHECK5 +// RUN: %run %t test6 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test6 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test6 2>&1 | FileCheck %s --check-prefix=CHECK6 +// RUN: %run %t test7 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test7 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test7 2>&1 | FileCheck %s --check-prefix=CHECK7 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sanitizer/asan_interface.h> + +void test1(char *array, char *endptr) { + // Buffer overflow if there is no terminating null (depends on base) + long r = strtol(array, &endptr, 3); + assert(array + 2 == endptr); + assert(r == 5); +} + +void test2(char *array, char *endptr) { + // Buffer overflow if there is no terminating null (depends on base) + array[2] = 'z'; + long r = strtol(array, &endptr, 35); + assert(array + 2 == endptr); + assert(r == 37); +} + +void test3(char *array, char *endptr) { + // Buffer overflow if base is invalid. + memset(array, 0, 8); + ASAN_POISON_MEMORY_REGION(array, 8); + long r = strtol(array + 1, NULL, -1); + assert(r == 0); + ASAN_UNPOISON_MEMORY_REGION(array, 8); +} + +void test4(char *array, char *endptr) { + // Buffer overflow if base is invalid. + long r = strtol(array + 3, NULL, 1); + assert(r == 0); +} + +void test5(char *array, char *endptr) { + // Overflow if no digits are found. + array[0] = ' '; + array[1] = '+'; + array[2] = '-'; + long r = strtol(array, NULL, 0); + assert(r == 0); +} + +void test6(char *array, char *endptr) { + // Overflow if no digits are found. + array[0] = ' '; + array[1] = array[2] = 'z'; + long r = strtol(array, &endptr, 0); + assert(array == endptr); + assert(r == 0); +} + +void test7(char *array, char *endptr) { + // Overflow if no digits are found. + array[2] = 'z'; + long r = strtol(array + 2, NULL, 0); + assert(r == 0); +} + +int main(int argc, char **argv) { + char *array0 = (char*)malloc(11); + char* array = array0 + 8; + char *endptr = NULL; + array[0] = '1'; + array[1] = '2'; + array[2] = '3'; + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(array, endptr); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1: READ of size 4 + if (!strcmp(argv[1], "test2")) test2(array, endptr); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2: READ of size 4 + if (!strcmp(argv[1], "test3")) test3(array0, endptr); + // CHECK3: {{.*ERROR: AddressSanitizer: use-after-poison on address}} + // CHECK3: READ of size 1 + if (!strcmp(argv[1], "test4")) test4(array, endptr); + // CHECK4: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK4: READ of size 1 + if (!strcmp(argv[1], "test5")) test5(array, endptr); + // CHECK5: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK5: READ of size 4 + if (!strcmp(argv[1], "test6")) test6(array, endptr); + // CHECK6: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK6: READ of size 4 + if (!strcmp(argv[1], "test7")) test7(array, endptr); + // CHECK7: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK7: READ of size 2 + free(array0); + return 0; +} diff --git a/test/asan/TestCases/strtoll_strict.c b/test/asan/TestCases/strtoll_strict.c new file mode 100644 index 000000000000..983da9f7ed30 --- /dev/null +++ b/test/asan/TestCases/strtoll_strict.c @@ -0,0 +1,116 @@ +// Test strict_string_checks option in strtoll function +// RUN: %clang_asan -DTEST1 %s -o %t +// RUN: %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test1 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test2 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test3 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test3 2>&1 | FileCheck %s --check-prefix=CHECK3 +// RUN: %run %t test4 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test4 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test4 2>&1 | FileCheck %s --check-prefix=CHECK4 +// RUN: %run %t test5 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test5 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test5 2>&1 | FileCheck %s --check-prefix=CHECK5 +// RUN: %run %t test6 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test6 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test6 2>&1 | FileCheck %s --check-prefix=CHECK6 +// RUN: %run %t test7 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=false %run %t test7 2>&1 +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:strict_string_checks=true not %run %t test7 2>&1 | FileCheck %s --check-prefix=CHECK7 + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sanitizer/asan_interface.h> + +void test1(char *array, char *endptr) { + // Buffer overflow if there is no terminating null (depends on base) + long long r = strtoll(array, &endptr, 3); + assert(array + 2 == endptr); + assert(r == 5); +} + +void test2(char *array, char *endptr) { + // Buffer overflow if there is no terminating null (depends on base) + array[2] = 'z'; + long long r = strtoll(array, &endptr, 35); + assert(array + 2 == endptr); + assert(r == 37); +} + +void test3(char *array, char *endptr) { + // Buffer overflow if base is invalid. + memset(array, 0, 8); + ASAN_POISON_MEMORY_REGION(array, 8); + long long r = strtoll(array + 1, NULL, -1); + assert(r == 0); + ASAN_UNPOISON_MEMORY_REGION(array, 8); +} + +void test4(char *array, char *endptr) { + // Buffer overflow if base is invalid. + long long r = strtoll(array + 3, NULL, 1); + assert(r == 0); +} + +void test5(char *array, char *endptr) { + // Overflow if no digits are found. + array[0] = ' '; + array[1] = '+'; + array[2] = '-'; + long long r = strtoll(array, NULL, 0); + assert(r == 0); +} + +void test6(char *array, char *endptr) { + // Overflow if no digits are found. + array[0] = ' '; + array[1] = array[2] = 'z'; + long long r = strtoll(array, &endptr, 0); + assert(array == endptr); + assert(r == 0); +} + +void test7(char *array, char *endptr) { + // Overflow if no digits are found. + array[2] = 'z'; + long long r = strtoll(array + 2, NULL, 0); + assert(r == 0); +} + +int main(int argc, char **argv) { + char *array0 = (char*)malloc(11); + char* array = array0 + 8; + char *endptr = NULL; + array[0] = '1'; + array[1] = '2'; + array[2] = '3'; + if (argc != 2) return 1; + if (!strcmp(argv[1], "test1")) test1(array, endptr); + // CHECK1: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK1: READ of size 4 + if (!strcmp(argv[1], "test2")) test2(array, endptr); + // CHECK2: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK2: READ of size 4 + if (!strcmp(argv[1], "test3")) test3(array0, endptr); + // CHECK3: {{.*ERROR: AddressSanitizer: use-after-poison on address}} + // CHECK3: READ of size 1 + if (!strcmp(argv[1], "test4")) test4(array, endptr); + // CHECK4: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK4: READ of size 1 + if (!strcmp(argv[1], "test5")) test5(array, endptr); + // CHECK5: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK5: READ of size 4 + if (!strcmp(argv[1], "test6")) test6(array, endptr); + // CHECK6: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK6: READ of size 4 + if (!strcmp(argv[1], "test7")) test7(array, endptr); + // CHECK7: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK7: READ of size 2 + free(array0); + return 0; +} diff --git a/test/asan/TestCases/suppressions-exec-relative-location.cc b/test/asan/TestCases/suppressions-exec-relative-location.cc new file mode 100644 index 000000000000..84f0262dc0bc --- /dev/null +++ b/test/asan/TestCases/suppressions-exec-relative-location.cc @@ -0,0 +1,47 @@ +// Check that without suppressions, we catch the issue. +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s + +// If the executable is started from a different location, we should still +// find the suppression file located relative to the location of the executable. +// RUN: rm -rf %T/suppressions-exec-relative-location +// RUN: mkdir -p %T/suppressions-exec-relative-location +// RUN: %clangxx_asan -O0 %s -o %T/suppressions-exec-relative-location/exec +// RUN: echo "interceptor_via_fun:crash_function" > \ +// RUN: %T/suppressions-exec-relative-location/supp.txt +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions=supp.txt" \ +// RUN: %run %T/suppressions-exec-relative-location/exec 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: rm -rf %T/suppressions-exec-relative-location + +// If the wrong absolute path is given, we don't try to construct +// a relative path with it. +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='/absolute/path'" not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-WRONG-FILE-NAME %s + +// Test that we reject directory as filename. +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='folder/only/'" not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-WRONG-FILE-NAME %s + +// XFAIL: android +// XFAIL: win32 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void crash_function() { + char *a = (char *)malloc(6); + free(a); + size_t len = strlen(a); // BOOM + fprintf(stderr, "strlen ignored, len = %zu\n", len); +} + +int main() { + crash_function(); +} + +// CHECK-CRASH: AddressSanitizer: heap-use-after-free +// CHECK-IGNORE-NOT: AddressSanitizer: heap-buffer-overflow +// CHECK-IGNORE: ignored +// CHECK-WRONG-FILE-NAME: failed to read suppressions file diff --git a/test/asan/TestCases/suppressions-function.cc b/test/asan/TestCases/suppressions-function.cc index 0a6c9995827e..fe5419f17938 100644 --- a/test/asan/TestCases/suppressions-function.cc +++ b/test/asan/TestCases/suppressions-function.cc @@ -3,8 +3,8 @@ // RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s // RUN: echo "interceptor_via_fun:crash_function" > %t.supp -// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s -// RUN: %clangxx_asan -O3 %s -o %t && ASAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: %clangxx_asan -O3 %s -o %t && ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s // XFAIL: android diff --git a/test/asan/TestCases/suppressions-interceptor.cc b/test/asan/TestCases/suppressions-interceptor.cc index 45a14d1a422a..8bb1f1a92d3b 100644 --- a/test/asan/TestCases/suppressions-interceptor.cc +++ b/test/asan/TestCases/suppressions-interceptor.cc @@ -3,7 +3,7 @@ // RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s // RUN: echo "interceptor_name:strlen" > %t.supp -// RUN: ASAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s // XFAIL: android diff --git a/test/asan/TestCases/suppressions-library.cc b/test/asan/TestCases/suppressions-library.cc index 28f19f54efc1..52fd60910539 100644 --- a/test/asan/TestCases/suppressions-library.cc +++ b/test/asan/TestCases/suppressions-library.cc @@ -1,11 +1,11 @@ -// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so -install_name @rpath/suppressions-library.cc.tmp-so.so -// RUN: %clangxx_asan -O0 %s %t-so.so -o %t -rpath @executable_path +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so +// RUN: %clangxx_asan -O0 %s -o %t %ld_flags_rpath_exe // Check that without suppressions, we catch the issue. // RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s -// RUN: echo "interceptor_via_lib:suppressions-library.cc.tmp-so.so" > %t.supp -// RUN: ASAN_OPTIONS="suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s +// RUN: echo "interceptor_via_lib:"`basename %dynamiclib` > %t.supp +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:suppressions='%t.supp'" %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s // XFAIL: android diff --git a/test/asan/TestCases/uar_and_exceptions.cc b/test/asan/TestCases/uar_and_exceptions.cc index 0bfe29729555..bdeca434e6c2 100644 --- a/test/asan/TestCases/uar_and_exceptions.cc +++ b/test/asan/TestCases/uar_and_exceptions.cc @@ -1,5 +1,5 @@ // Test that use-after-return works with exceptions. -// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// export ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 // RUN: %clangxx_asan -O0 %s -o %t && %run %t // Clang doesn't support exceptions on Windows yet. diff --git a/test/asan/TestCases/use-after-poison.cc b/test/asan/TestCases/use-after-poison.cc index 3b247ff531b9..ecca2c85028f 100644 --- a/test/asan/TestCases/use-after-poison.cc +++ b/test/asan/TestCases/use-after-poison.cc @@ -2,7 +2,7 @@ // RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s // // Check that we can disable it -// RUN: env ASAN_OPTIONS=allow_user_poisoning=0 %run %t +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:allow_user_poisoning=0 %run %t #include <stdlib.h> diff --git a/test/asan/TestCases/use-after-scope.cc b/test/asan/TestCases/use-after-scope.cc index f98a8e6b62e1..e244ee34b101 100644 --- a/test/asan/TestCases/use-after-scope.cc +++ b/test/asan/TestCases/use-after-scope.cc @@ -1,6 +1,6 @@ // RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// RUN: env ASAN_OPTIONS="detect_stack_use_after_return=1" not %run %t 2>&1 | FileCheck %s +// RUN: env ASAN_OPTIONS="$ASAN_OPTIONS:detect_stack_use_after_return=1" not %run %t 2>&1 | FileCheck %s // XFAIL: * int main() { diff --git a/test/asan/TestCases/verbose-log-path_test.cc b/test/asan/TestCases/verbose-log-path_test.cc new file mode 100644 index 000000000000..12372ec68078 --- /dev/null +++ b/test/asan/TestCases/verbose-log-path_test.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan %s -o %T/verbose-log-path_test-binary + +// Good log_path. +// RUN: rm -f %T/asan.log.* +// RUN: env ASAN_OPTIONS=$ASAN_OPTIONS:log_path=%T/asan.log:log_exe_name=1 not %run %T/verbose-log-path_test-binary 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %T/asan.log.verbose-log-path_test-binary.* + +// FIXME: only FreeBSD and Linux have verbose log paths now. +// XFAIL: win32,android + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + if (argc > 2) return 0; + char *x = (char*)malloc(10); + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + free(x); + return res; +} +// CHECK-ERROR: ERROR: AddressSanitizer diff --git a/test/asan/TestCases/vla_chrome_testcase.cc b/test/asan/TestCases/vla_chrome_testcase.cc new file mode 100644 index 000000000000..8ee040120c48 --- /dev/null +++ b/test/asan/TestCases/vla_chrome_testcase.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// + +// This is reduced testcase based on Chromium code. +// See http://reviews.llvm.org/D6055?vs=on&id=15616&whitespace=ignore-all#toc. + +#include <stdint.h> +#include <assert.h> + +int a = 7; +int b; +int c; +int *p; + +__attribute__((noinline)) void fn3(int *first, int second) { +} + +int main() { + int d = b && c; + int e[a]; // NOLINT + assert(!(reinterpret_cast<uintptr_t>(e) & 31L)); + int f; + if (d) + fn3(&f, sizeof 0 * (&c - e)); + e[a] = 0; +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 4 at [[ADDR]] thread T0 + return 0; +} diff --git a/test/asan/TestCases/vla_condition_overflow.cc b/test/asan/TestCases/vla_condition_overflow.cc new file mode 100644 index 000000000000..17f28d823252 --- /dev/null +++ b/test/asan/TestCases/vla_condition_overflow.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// +// REQUIRES: stable-runtime + +#include <assert.h> +#include <stdint.h> + +__attribute__((noinline)) void foo(int index, int len) { + if (index > len) { + char str[len]; //NOLINT + assert(!(reinterpret_cast<uintptr_t>(str) & 31L)); + str[index] = '1'; // BOOM +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 + } +} + +int main(int argc, char **argv) { + foo(33, 10); + return 0; +} diff --git a/test/asan/TestCases/vla_loop_overfow.cc b/test/asan/TestCases/vla_loop_overfow.cc new file mode 100644 index 000000000000..4f20c8d19d14 --- /dev/null +++ b/test/asan/TestCases/vla_loop_overfow.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 -mllvm -asan-instrument-allocas %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// +// REQUIRES: stable-runtime + +#include <assert.h> +#include <stdint.h> + +void foo(int index, int len) { + for (int i = 1; i < len; ++i) { + char array[len]; // NOLINT + assert(!(reinterpret_cast<uintptr_t>(array) & 31L)); + array[index + i] = 0; +// CHECK: ERROR: AddressSanitizer: dynamic-stack-buffer-overflow on address [[ADDR:0x[0-9a-f]+]] +// CHECK: WRITE of size 1 at [[ADDR]] thread T0 + } +} + +int main(int argc, char **argv) { + foo(9, 21); + return 0; +} diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg index a6f443cfb6a5..c5164713dbe4 100644 --- a/test/asan/lit.cfg +++ b/test/asan/lit.cfg @@ -29,6 +29,9 @@ def push_dynamic_library_lookup_path(config, new_path): # Setup config name. config.name = 'AddressSanitizer' + config.name_suffix +# Setup default ASAN_OPTIONS +config.environment['ASAN_OPTIONS'] = 'symbolize_vs_style=false' + # testFormat: The test format to use to interpret tests. external_bash = (not sys.platform in ['win32']) config.test_format = lit.formats.ShTest(external_bash) @@ -135,6 +138,20 @@ config.substitutions.append( ("%libdl", libdl_flag) ) config.available_features.add("asan-" + config.bits + "-bits") +if config.host_os == 'Darwin': + config.substitutions.append( ("%ld_flags_rpath_exe", '-Wl,-rpath,@executable_path/ %dynamiclib') ) + config.substitutions.append( ("%ld_flags_rpath_so", '-install_name @rpath/`basename %dynamiclib`') ) +elif config.host_os == 'FreeBSD': + config.substitutions.append( ("%ld_flags_rpath_exe", "-Wl,-z,origin -Wl,-rpath,\$ORIGIN -L%T -l%xdynamiclib_namespec") ) + config.substitutions.append( ("%ld_flags_rpath_so", '') ) +elif config.host_os == 'Linux': + config.substitutions.append( ("%ld_flags_rpath_exe", "-Wl,-rpath,\$ORIGIN -L%T -l%xdynamiclib_namespec") ) + config.substitutions.append( ("%ld_flags_rpath_so", '') ) + +# Must be defined after the substitutions that use %dynamiclib. +config.substitutions.append( ("%dynamiclib", '%T/lib%xdynamiclib_namespec.so') ) +config.substitutions.append( ("%xdynamiclib_namespec", '$(basename %t).dynamic') ) + # Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL # because the test hangs. if config.target_arch != 'arm': diff --git a/test/builtins/Unit/absvdi2_test.c b/test/builtins/Unit/absvdi2_test.c index f0bf560681e7..f69ae4151072 100644 --- a/test/builtins/Unit/absvdi2_test.c +++ b/test/builtins/Unit/absvdi2_test.c @@ -19,7 +19,7 @@ // Effects: aborts if abs(x) < 0 -di_int __absvdi2(di_int a); +COMPILER_RT_ABI di_int __absvdi2(di_int a); int test__absvdi2(di_int a) { diff --git a/test/builtins/Unit/absvsi2_test.c b/test/builtins/Unit/absvsi2_test.c index 3b88078dfb4a..c395cca7adc8 100644 --- a/test/builtins/Unit/absvsi2_test.c +++ b/test/builtins/Unit/absvsi2_test.c @@ -19,7 +19,7 @@ // Effects: aborts if abs(x) < 0 -si_int __absvsi2(si_int a); +COMPILER_RT_ABI si_int __absvsi2(si_int a); int test__absvsi2(si_int a) { diff --git a/test/builtins/Unit/absvti2_test.c b/test/builtins/Unit/absvti2_test.c index 9b71f200ac7b..6c626e97d930 100644 --- a/test/builtins/Unit/absvti2_test.c +++ b/test/builtins/Unit/absvti2_test.c @@ -21,7 +21,7 @@ // Effects: aborts if abs(x) < 0 -ti_int __absvti2(ti_int a); +COMPILER_RT_ABI ti_int __absvti2(ti_int a); int test__absvti2(ti_int a) { diff --git a/test/builtins/Unit/adddf3vfp_test.c b/test/builtins/Unit/adddf3vfp_test.c index 5ad42f7b3a6e..739515291fe8 100644 --- a/test/builtins/Unit/adddf3vfp_test.c +++ b/test/builtins/Unit/adddf3vfp_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #if __arm__ -extern double __adddf3vfp(double a, double b); +extern COMPILER_RT_ABI double __adddf3vfp(double a, double b); int test__adddf3vfp(double a, double b) { diff --git a/test/builtins/Unit/addsf3vfp_test.c b/test/builtins/Unit/addsf3vfp_test.c index 95e057c36305..4b3dcccac4d3 100644 --- a/test/builtins/Unit/addsf3vfp_test.c +++ b/test/builtins/Unit/addsf3vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __addsf3vfp(float a, float b); +extern COMPILER_RT_ABI float __addsf3vfp(float a, float b); #if __arm__ int test__addsf3vfp(float a, float b) diff --git a/test/builtins/Unit/addtf3_test.c b/test/builtins/Unit/addtf3_test.c index 4a3aacd96e6d..7b92ccee1af0 100644 --- a/test/builtins/Unit/addtf3_test.c +++ b/test/builtins/Unit/addtf3_test.c @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 @@ -18,7 +19,7 @@ #include "fp_test.h" // Returns: a + b -long double __addtf3(long double a, long double b); +COMPILER_RT_ABI long double __addtf3(long double a, long double b); int test__addtf3(long double a, long double b, uint64_t expectedHi, uint64_t expectedLo) diff --git a/test/builtins/Unit/addvdi3_test.c b/test/builtins/Unit/addvdi3_test.c index 0d7271d5a4ae..5f8729a61266 100644 --- a/test/builtins/Unit/addvdi3_test.c +++ b/test/builtins/Unit/addvdi3_test.c @@ -18,7 +18,7 @@ // Effects: aborts if a + b overflows -di_int __addvdi3(di_int a, di_int b); +COMPILER_RT_ABI di_int __addvdi3(di_int a, di_int b); int test__addvdi3(di_int a, di_int b) { diff --git a/test/builtins/Unit/addvsi3_test.c b/test/builtins/Unit/addvsi3_test.c index 59fd9d2ae15f..b5358d0f5d47 100644 --- a/test/builtins/Unit/addvsi3_test.c +++ b/test/builtins/Unit/addvsi3_test.c @@ -18,7 +18,7 @@ // Effects: aborts if a + b overflows -si_int __addvsi3(si_int a, si_int b); +COMPILER_RT_ABI si_int __addvsi3(si_int a, si_int b); int test__addvsi3(si_int a, si_int b) { diff --git a/test/builtins/Unit/addvti3_test.c b/test/builtins/Unit/addvti3_test.c index fe093e3b26a2..e2f75cf86148 100644 --- a/test/builtins/Unit/addvti3_test.c +++ b/test/builtins/Unit/addvti3_test.c @@ -20,7 +20,7 @@ // Effects: aborts if a + b overflows -ti_int __addvti3(ti_int a, ti_int b); +COMPILER_RT_ABI ti_int __addvti3(ti_int a, ti_int b); int test__addvti3(ti_int a, ti_int b) { diff --git a/test/builtins/Unit/ashldi3_test.c b/test/builtins/Unit/ashldi3_test.c index fb80c6f77e40..398fb69beb78 100644 --- a/test/builtins/Unit/ashldi3_test.c +++ b/test/builtins/Unit/ashldi3_test.c @@ -18,7 +18,7 @@ // Precondition: 0 <= b < bits_in_dword -di_int __ashldi3(di_int a, si_int b); +COMPILER_RT_ABI di_int __ashldi3(di_int a, si_int b); int test__ashldi3(di_int a, si_int b, di_int expected) { diff --git a/test/builtins/Unit/ashlti3_test.c b/test/builtins/Unit/ashlti3_test.c index 2361d16b3699..595e353065a9 100644 --- a/test/builtins/Unit/ashlti3_test.c +++ b/test/builtins/Unit/ashlti3_test.c @@ -20,7 +20,7 @@ // Precondition: 0 <= b < bits_in_tword -ti_int __ashlti3(ti_int a, si_int b); +COMPILER_RT_ABI ti_int __ashlti3(ti_int a, si_int b); int test__ashlti3(ti_int a, si_int b, ti_int expected) { diff --git a/test/builtins/Unit/ashrdi3_test.c b/test/builtins/Unit/ashrdi3_test.c index ac517e191880..ee6409c870be 100644 --- a/test/builtins/Unit/ashrdi3_test.c +++ b/test/builtins/Unit/ashrdi3_test.c @@ -18,7 +18,7 @@ // Precondition: 0 <= b < bits_in_dword -di_int __ashrdi3(di_int a, si_int b); +COMPILER_RT_ABI di_int __ashrdi3(di_int a, si_int b); int test__ashrdi3(di_int a, si_int b, di_int expected) { diff --git a/test/builtins/Unit/ashrti3_test.c b/test/builtins/Unit/ashrti3_test.c index 62ba1017ecd9..201582d4eac4 100644 --- a/test/builtins/Unit/ashrti3_test.c +++ b/test/builtins/Unit/ashrti3_test.c @@ -20,7 +20,7 @@ // Precondition: 0 <= b < bits_in_tword -ti_int __ashrti3(ti_int a, si_int b); +COMPILER_RT_ABI ti_int __ashrti3(ti_int a, si_int b); int test__ashrti3(ti_int a, si_int b, ti_int expected) { diff --git a/test/builtins/Unit/clzdi2_test.c b/test/builtins/Unit/clzdi2_test.c index 58403f091f48..41e120932dfd 100644 --- a/test/builtins/Unit/clzdi2_test.c +++ b/test/builtins/Unit/clzdi2_test.c @@ -18,7 +18,7 @@ // Precondition: a != 0 -si_int __clzdi2(di_int a); +COMPILER_RT_ABI si_int __clzdi2(di_int a); int test__clzdi2(di_int a, si_int expected) { diff --git a/test/builtins/Unit/clzsi2_test.c b/test/builtins/Unit/clzsi2_test.c index cc1da64b03ef..80b300feea86 100644 --- a/test/builtins/Unit/clzsi2_test.c +++ b/test/builtins/Unit/clzsi2_test.c @@ -18,7 +18,7 @@ // Precondition: a != 0 -si_int __clzsi2(si_int a); +COMPILER_RT_ABI si_int __clzsi2(si_int a); int test__clzsi2(si_int a, si_int expected) { diff --git a/test/builtins/Unit/clzti2_test.c b/test/builtins/Unit/clzti2_test.c index 5a0e3e8b1e98..3a2c6fabb82e 100644 --- a/test/builtins/Unit/clzti2_test.c +++ b/test/builtins/Unit/clzti2_test.c @@ -20,7 +20,7 @@ // Precondition: a != 0 -si_int __clzti2(ti_int a); +COMPILER_RT_ABI si_int __clzti2(ti_int a); int test__clzti2(ti_int a, si_int expected) { diff --git a/test/builtins/Unit/cmpdi2_test.c b/test/builtins/Unit/cmpdi2_test.c index 609ab1a63a61..33a12a042400 100644 --- a/test/builtins/Unit/cmpdi2_test.c +++ b/test/builtins/Unit/cmpdi2_test.c @@ -18,7 +18,7 @@ // if (a == b) returns 1 // if (a > b) returns 2 -si_int __cmpdi2(di_int a, di_int b); +COMPILER_RT_ABI si_int __cmpdi2(di_int a, di_int b); int test__cmpdi2(di_int a, di_int b, si_int expected) { diff --git a/test/builtins/Unit/cmpti2_test.c b/test/builtins/Unit/cmpti2_test.c index 15ee4fc68d53..d951923b2da3 100644 --- a/test/builtins/Unit/cmpti2_test.c +++ b/test/builtins/Unit/cmpti2_test.c @@ -20,7 +20,7 @@ // if (a == b) returns 1 // if (a > b) returns 2 -si_int __cmpti2(ti_int a, ti_int b); +COMPILER_RT_ABI si_int __cmpti2(ti_int a, ti_int b); int test__cmpti2(ti_int a, ti_int b, si_int expected) { diff --git a/test/builtins/Unit/ctzdi2_test.c b/test/builtins/Unit/ctzdi2_test.c index 1f2d101a1943..bde66b1e5450 100644 --- a/test/builtins/Unit/ctzdi2_test.c +++ b/test/builtins/Unit/ctzdi2_test.c @@ -18,7 +18,7 @@ // Precondition: a != 0 -si_int __ctzdi2(di_int a); +COMPILER_RT_ABI si_int __ctzdi2(di_int a); int test__ctzdi2(di_int a, si_int expected) { diff --git a/test/builtins/Unit/ctzsi2_test.c b/test/builtins/Unit/ctzsi2_test.c index 36f221595b68..cbc101fca04d 100644 --- a/test/builtins/Unit/ctzsi2_test.c +++ b/test/builtins/Unit/ctzsi2_test.c @@ -18,7 +18,7 @@ // Precondition: a != 0 -si_int __ctzsi2(si_int a); +COMPILER_RT_ABI si_int __ctzsi2(si_int a); int test__ctzsi2(si_int a, si_int expected) { diff --git a/test/builtins/Unit/ctzti2_test.c b/test/builtins/Unit/ctzti2_test.c index 9a972f9e2f94..0ca1920bd74b 100644 --- a/test/builtins/Unit/ctzti2_test.c +++ b/test/builtins/Unit/ctzti2_test.c @@ -20,7 +20,7 @@ // Precondition: a != 0 -si_int __ctzti2(ti_int a); +COMPILER_RT_ABI si_int __ctzti2(ti_int a); int test__ctzti2(ti_int a, si_int expected) { diff --git a/test/builtins/Unit/divdc3_test.c b/test/builtins/Unit/divdc3_test.c index 9224cddceeb8..80b9e86e1e2d 100644 --- a/test/builtins/Unit/divdc3_test.c +++ b/test/builtins/Unit/divdc3_test.c @@ -18,7 +18,8 @@ // Returns: the quotient of (a + ib) / (c + id) -double _Complex __divdc3(double __a, double __b, double __c, double __d); +COMPILER_RT_ABI double _Complex +__divdc3(double __a, double __b, double __c, double __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/divdf3vfp_test.c b/test/builtins/Unit/divdf3vfp_test.c index e13822ffcaa0..6f0808abc931 100644 --- a/test/builtins/Unit/divdf3vfp_test.c +++ b/test/builtins/Unit/divdf3vfp_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #if __arm__ -extern double __divdf3vfp(double a, double b); +extern COMPILER_RT_ABI double __divdf3vfp(double a, double b); int test__divdf3vfp(double a, double b) { diff --git a/test/builtins/Unit/divdi3_test.c b/test/builtins/Unit/divdi3_test.c index c25f917a419e..1d459803e1ee 100644 --- a/test/builtins/Unit/divdi3_test.c +++ b/test/builtins/Unit/divdi3_test.c @@ -16,7 +16,7 @@ // Returns: a / b -di_int __divdi3(di_int a, di_int b); +COMPILER_RT_ABI di_int __divdi3(di_int a, di_int b); int test__divdi3(di_int a, di_int b, di_int expected) { diff --git a/test/builtins/Unit/divmodsi4_test.c b/test/builtins/Unit/divmodsi4_test.c index bea31ea9a445..6fb1c985ab5c 100644 --- a/test/builtins/Unit/divmodsi4_test.c +++ b/test/builtins/Unit/divmodsi4_test.c @@ -16,7 +16,7 @@ // Returns: a / b -extern si_int __divmodsi4(si_int a, si_int b, si_int* rem); +extern COMPILER_RT_ABI si_int __divmodsi4(si_int a, si_int b, si_int* rem); int test__divmodsi4(si_int a, si_int b, diff --git a/test/builtins/Unit/divsc3_test.c b/test/builtins/Unit/divsc3_test.c index 9d060a2d1489..2d7c6593796f 100644 --- a/test/builtins/Unit/divsc3_test.c +++ b/test/builtins/Unit/divsc3_test.c @@ -18,7 +18,8 @@ // Returns: the quotient of (a + ib) / (c + id) -float _Complex __divsc3(float __a, float __b, float __c, float __d); +COMPILER_RT_ABI float _Complex +__divsc3(float __a, float __b, float __c, float __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/divsf3vfp_test.c b/test/builtins/Unit/divsf3vfp_test.c index 8382558412cf..f742441991f8 100644 --- a/test/builtins/Unit/divsf3vfp_test.c +++ b/test/builtins/Unit/divsf3vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __divsf3vfp(float a, float b); +extern COMPILER_RT_ABI float __divsf3vfp(float a, float b); #if __arm__ int test__divsf3vfp(float a, float b) diff --git a/test/builtins/Unit/divsi3_test.c b/test/builtins/Unit/divsi3_test.c index 6fda54ff37ec..c523367455a9 100644 --- a/test/builtins/Unit/divsi3_test.c +++ b/test/builtins/Unit/divsi3_test.c @@ -16,7 +16,7 @@ // Returns: a / b -si_int __divsi3(si_int a, si_int b); +COMPILER_RT_ABI si_int __divsi3(si_int a, si_int b); int test__divsi3(si_int a, si_int b, si_int expected) { diff --git a/test/builtins/Unit/divtc3_test.c b/test/builtins/Unit/divtc3_test.c index 7bb74d755146..ad2c96dc0409 100644 --- a/test/builtins/Unit/divtc3_test.c +++ b/test/builtins/Unit/divtc3_test.c @@ -11,16 +11,17 @@ // //===----------------------------------------------------------------------===// +#include <stdio.h> + #if _ARCH_PPC #include "int_lib.h" #include <math.h> #include <complex.h> -#include <stdio.h> // Returns: the quotient of (a + ib) / (c + id) -long double _Complex +COMPILER_RT_ABI long double _Complex __divtc3(long double __a, long double __b, long double __c, long double __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/divtf3_test.c b/test/builtins/Unit/divtf3_test.c index dad631cc4f17..e0def45ffd58 100644 --- a/test/builtins/Unit/divtf3_test.c +++ b/test/builtins/Unit/divtf3_test.c @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 @@ -18,7 +19,7 @@ #include "fp_test.h" // Returns: a / b -long double __divtf3(long double a, long double b); +COMPILER_RT_ABI long double __divtf3(long double a, long double b); int test__divtf3(long double a, long double b, uint64_t expectedHi, uint64_t expectedLo) diff --git a/test/builtins/Unit/divti3_test.c b/test/builtins/Unit/divti3_test.c index bc81c2a5418d..3a94dab8c7a6 100644 --- a/test/builtins/Unit/divti3_test.c +++ b/test/builtins/Unit/divti3_test.c @@ -18,7 +18,7 @@ // Returns: a / b -ti_int __divti3(ti_int a, ti_int b); +COMPILER_RT_ABI ti_int __divti3(ti_int a, ti_int b); int test__divti3(ti_int a, ti_int b, ti_int expected) { diff --git a/test/builtins/Unit/divxc3_test.c b/test/builtins/Unit/divxc3_test.c index aa8a7625d139..509b4b18e978 100644 --- a/test/builtins/Unit/divxc3_test.c +++ b/test/builtins/Unit/divxc3_test.c @@ -20,7 +20,7 @@ // Returns: the quotient of (a + ib) / (c + id) -long double _Complex +COMPILER_RT_ABI long double _Complex __divxc3(long double __a, long double __b, long double __c, long double __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/extebdsfdf2vfp_test.c b/test/builtins/Unit/extebdsfdf2vfp_test.c index 3a009cf2d7f8..53c72828f741 100644 --- a/test/builtins/Unit/extebdsfdf2vfp_test.c +++ b/test/builtins/Unit/extebdsfdf2vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern double __extendsfdf2vfp(float a); +extern COMPILER_RT_ABI double __extendsfdf2vfp(float a); #if __arm__ int test__extendsfdf2vfp(float a) diff --git a/test/builtins/Unit/extenddftf2_test.c b/test/builtins/Unit/extenddftf2_test.c index 05acc08c0951..2cfb32b2604a 100644 --- a/test/builtins/Unit/extenddftf2_test.c +++ b/test/builtins/Unit/extenddftf2_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 #include "fp_test.h" -long double __extenddftf2(double a); +COMPILER_RT_ABI long double __extenddftf2(double a); int test__extenddftf2(double a, uint64_t expectedHi, uint64_t expectedLo) { diff --git a/test/builtins/Unit/extendhfsf2_test.c b/test/builtins/Unit/extendhfsf2_test.c new file mode 100644 index 000000000000..5dd994cae1c6 --- /dev/null +++ b/test/builtins/Unit/extendhfsf2_test.c @@ -0,0 +1,113 @@ +//===--------------- extendhfsf2_test.c - Test __extendhfsf2 --------------===// +// +// 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 tests __extendhfsf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#include "fp_test.h" + +float __extendhfsf2(uint16_t a); + +int test__extendhfsf2(uint16_t a, float expected) +{ + float x = __extendhfsf2(a); + int ret = compareResultH(x, expected); + + if (ret){ + printf("error in test__extendhfsf2(%#.4x) = %f, " + "expected %f\n", a, x, expected); + } + return ret; +} + +char assumption_1[sizeof(__fp16) * CHAR_BIT == 16] = {0}; + +int main() +{ + // qNaN + if (test__extendhfsf2(UINT16_C(0x7e00), + makeQNaN32())) + return 1; + // NaN + if (test__extendhfsf2(UINT16_C(0x7e00), + makeNaN32(UINT32_C(0x8000)))) + return 1; + // inf + if (test__extendhfsf2(UINT16_C(0x7c00), + makeInf32())) + return 1; + if (test__extendhfsf2(UINT16_C(0xfc00), + -makeInf32())) + return 1; + // zero + if (test__extendhfsf2(UINT16_C(0x0), + 0.0f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x8000), + -0.0f)) + return 1; + + if (test__extendhfsf2(UINT16_C(0x4248), + 3.1415926535f)) + return 1; + if (test__extendhfsf2(UINT16_C(0xc248), + -3.1415926535f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x7c00), + 0x1.987124876876324p+100f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x6e62), + 0x1.988p+12f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x3c00), + 0x1.0p+0f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x0400), + 0x1.0p-14f)) + return 1; + // denormal + if (test__extendhfsf2(UINT16_C(0x0010), + 0x1.0p-20f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x0001), + 0x1.0p-24f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x8001), + -0x1.0p-24f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x0001), + 0x1.5p-25f)) + return 1; + // and back to zero + if (test__extendhfsf2(UINT16_C(0x0000), + 0x1.0p-25f)) + return 1; + if (test__extendhfsf2(UINT16_C(0x8000), + -0x1.0p-25f)) + return 1; + // max (precise) + if (test__extendhfsf2(UINT16_C(0x7bff), + 65504.0f)) + return 1; + // max (rounded) + if (test__extendhfsf2(UINT16_C(0x7bff), + 65504.0f)) + return 1; + // max (to +inf) + if (test__extendhfsf2(UINT16_C(0x7c00), + makeInf32())) + return 1; + if (test__extendhfsf2(UINT16_C(0xfc00), + -makeInf32())) + return 1; + return 0; +} diff --git a/test/builtins/Unit/extendsftf2_test.c b/test/builtins/Unit/extendsftf2_test.c index 5f41928b862f..7dff5b6be3d9 100644 --- a/test/builtins/Unit/extendsftf2_test.c +++ b/test/builtins/Unit/extendsftf2_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 #include "fp_test.h" -long double __extendsftf2(float a); +COMPILER_RT_ABI long double __extendsftf2(float a); int test__extendsftf2(float a, uint64_t expectedHi, uint64_t expectedLo) { diff --git a/test/builtins/Unit/ffsdi2_test.c b/test/builtins/Unit/ffsdi2_test.c index 9041127d1a62..a27d154fd351 100644 --- a/test/builtins/Unit/ffsdi2_test.c +++ b/test/builtins/Unit/ffsdi2_test.c @@ -17,7 +17,7 @@ // Returns: the index of the least significant 1-bit in a, or // the value zero if a is zero. The least significant bit is index one. -si_int __ffsdi2(di_int a); +COMPILER_RT_ABI si_int __ffsdi2(di_int a); int test__ffsdi2(di_int a, si_int expected) { diff --git a/test/builtins/Unit/ffsti2_test.c b/test/builtins/Unit/ffsti2_test.c index f944ed0a1e45..396269d51369 100644 --- a/test/builtins/Unit/ffsti2_test.c +++ b/test/builtins/Unit/ffsti2_test.c @@ -19,7 +19,7 @@ // Returns: the index of the least significant 1-bit in a, or // the value zero if a is zero. The least significant bit is index one. -si_int __ffsti2(ti_int a); +COMPILER_RT_ABI si_int __ffsti2(ti_int a); int test__ffsti2(ti_int a, si_int expected) { diff --git a/test/builtins/Unit/fixdfdi_test.c b/test/builtins/Unit/fixdfdi_test.c index d08afe3a5336..4a7cfa31c7f0 100644 --- a/test/builtins/Unit/fixdfdi_test.c +++ b/test/builtins/Unit/fixdfdi_test.c @@ -22,7 +22,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -di_int __fixdfdi(double a); +COMPILER_RT_ABI di_int __fixdfdi(double a); int test__fixdfdi(double a, di_int expected) { diff --git a/test/builtins/Unit/fixdfti_test.c b/test/builtins/Unit/fixdfti_test.c index bfa88fd70e14..b5da456fc6b3 100644 --- a/test/builtins/Unit/fixdfti_test.c +++ b/test/builtins/Unit/fixdfti_test.c @@ -24,7 +24,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -ti_int __fixdfti(double a); +COMPILER_RT_ABI ti_int __fixdfti(double a); int test__fixdfti(double a, ti_int expected) { diff --git a/test/builtins/Unit/fixsfdi_test.c b/test/builtins/Unit/fixsfdi_test.c index d3e934a5c02a..f37ecef047aa 100644 --- a/test/builtins/Unit/fixsfdi_test.c +++ b/test/builtins/Unit/fixsfdi_test.c @@ -22,7 +22,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -di_int __fixsfdi(float a); +COMPILER_RT_ABI di_int __fixsfdi(float a); int test__fixsfdi(float a, di_int expected) { diff --git a/test/builtins/Unit/fixsfti_test.c b/test/builtins/Unit/fixsfti_test.c index 2b0b99774819..38748aabc91b 100644 --- a/test/builtins/Unit/fixsfti_test.c +++ b/test/builtins/Unit/fixsfti_test.c @@ -24,7 +24,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -ti_int __fixsfti(float a); +COMPILER_RT_ABI ti_int __fixsfti(float a); int test__fixsfti(float a, ti_int expected) { diff --git a/test/builtins/Unit/fixtfsi_test.c b/test/builtins/Unit/fixtfsi_test.c new file mode 100644 index 000000000000..45ad0d243785 --- /dev/null +++ b/test/builtins/Unit/fixtfsi_test.c @@ -0,0 +1,65 @@ +//===--------------- fixtfsi_test.c - Test __fixtfsi ----------------------===// +// +// 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 tests __fixtfsi for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +int __fixtfsi(long double a); + +int test__fixtfsi(long double a, int expected) +{ + int x = __fixtfsi(a); + int ret = (x != expected); + + if (ret){ + printf("error in test__fixtfsi(%.20Lf) = %d, " + "expected %d\n", a, x, expected); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + if (test__fixtfsi(makeInf128(), 0x7fffffff)) + return 1; + if (test__fixtfsi(0, 0x0)) + return 1; + if (test__fixtfsi(0x1.23456789abcdefp+5, 0x24)) + return 1; + if (test__fixtfsi(0x1.23456789abcdefp-3, 0x0)) + return 1; + if (test__fixtfsi(0x1.23456789abcdefp+20, 0x123456)) + return 1; + if (test__fixtfsi(0x1.23456789abcdefp+40, 0x7fffffff)) + return 1; + if (test__fixtfsi(0x1.23456789abcdefp+256, 0x7fffffff)) + return 1; + if (test__fixtfsi(-0x1.23456789abcdefp+20, 0xffedcbaa)) + return 1; + if (test__fixtfsi(-0x1.23456789abcdefp+40, 0x80000001)) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/builtins/Unit/fixunsdfdi_test.c b/test/builtins/Unit/fixunsdfdi_test.c index 0803fd28f5bb..3998482876f3 100644 --- a/test/builtins/Unit/fixunsdfdi_test.c +++ b/test/builtins/Unit/fixunsdfdi_test.c @@ -24,7 +24,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -du_int __fixunsdfdi(double a); +COMPILER_RT_ABI du_int __fixunsdfdi(double a); int test__fixunsdfdi(double a, du_int expected) { diff --git a/test/builtins/Unit/fixunsdfsi_test.c b/test/builtins/Unit/fixunsdfsi_test.c index 54fe35b5c35a..551fc88a5241 100644 --- a/test/builtins/Unit/fixunsdfsi_test.c +++ b/test/builtins/Unit/fixunsdfsi_test.c @@ -24,7 +24,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -su_int __fixunsdfsi(double a); +COMPILER_RT_ABI su_int __fixunsdfsi(double a); int test__fixunsdfsi(double a, su_int expected) { diff --git a/test/builtins/Unit/fixunsdfsivfp_test.c b/test/builtins/Unit/fixunsdfsivfp_test.c index 3727cf7b02fb..ebd0be274f62 100644 --- a/test/builtins/Unit/fixunsdfsivfp_test.c +++ b/test/builtins/Unit/fixunsdfsivfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern unsigned int __fixunsdfsivfp(double a); +extern COMPILER_RT_ABI unsigned int __fixunsdfsivfp(double a); #if __arm__ int test__fixunsdfsivfp(double a) diff --git a/test/builtins/Unit/fixunsdfti_test.c b/test/builtins/Unit/fixunsdfti_test.c index 9f89de493680..e1aa56d7631e 100644 --- a/test/builtins/Unit/fixunsdfti_test.c +++ b/test/builtins/Unit/fixunsdfti_test.c @@ -26,7 +26,7 @@ #ifdef CRT_HAS_128BIT -tu_int __fixunsdfti(double a); +COMPILER_RT_ABI tu_int __fixunsdfti(double a); int test__fixunsdfti(double a, tu_int expected) { diff --git a/test/builtins/Unit/fixunssfdi_test.c b/test/builtins/Unit/fixunssfdi_test.c index ac89be7bbb27..812457a002de 100644 --- a/test/builtins/Unit/fixunssfdi_test.c +++ b/test/builtins/Unit/fixunssfdi_test.c @@ -24,7 +24,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -du_int __fixunssfdi(float a); +COMPILER_RT_ABI du_int __fixunssfdi(float a); int test__fixunssfdi(float a, du_int expected) { diff --git a/test/builtins/Unit/fixunssfsi_test.c b/test/builtins/Unit/fixunssfsi_test.c index ce6a9287515f..94a8b0867eca 100644 --- a/test/builtins/Unit/fixunssfsi_test.c +++ b/test/builtins/Unit/fixunssfsi_test.c @@ -24,7 +24,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -su_int __fixunssfsi(float a); +COMPILER_RT_ABI su_int __fixunssfsi(float a); int test__fixunssfsi(float a, su_int expected) { diff --git a/test/builtins/Unit/fixunssfti_test.c b/test/builtins/Unit/fixunssfti_test.c index 7965b9500d05..979d661910f0 100644 --- a/test/builtins/Unit/fixunssfti_test.c +++ b/test/builtins/Unit/fixunssfti_test.c @@ -26,7 +26,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -tu_int __fixunssfti(float a); +COMPILER_RT_ABI tu_int __fixunssfti(float a); int test__fixunssfti(float a, tu_int expected) { diff --git a/test/builtins/Unit/fixunstfdi_test.c b/test/builtins/Unit/fixunstfdi_test.c index d0a5db7a9c97..60ea503d2778 100644 --- a/test/builtins/Unit/fixunstfdi_test.c +++ b/test/builtins/Unit/fixunstfdi_test.c @@ -11,10 +11,11 @@ // //===----------------------------------------------------------------------===// +#include <stdio.h> + #if _ARCH_PPC #include "int_lib.h" -#include <stdio.h> // Returns: convert a to a unsigned long long, rounding toward zero. // Negative values all become zero. @@ -24,7 +25,7 @@ // value in long double is representable in du_int or is negative // (no range checking performed) -du_int __fixunstfdi(long double a); +COMPILER_RT_ABI du_int __fixunstfdi(long double a); int test__fixunstfdi(long double a, du_int expected) { diff --git a/test/builtins/Unit/fixunstfsi_test.c b/test/builtins/Unit/fixunstfsi_test.c new file mode 100644 index 000000000000..4bf8fdec607c --- /dev/null +++ b/test/builtins/Unit/fixunstfsi_test.c @@ -0,0 +1,64 @@ +//===--------------- fixunstfsi_test.c - Test __fixunstfsi ----------------===// +// +// 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 tests __fixunstfsi for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#if __LDBL_MANT_DIG__ == 113 + +#include "fp_test.h" + +unsigned int __fixunstfsi(long double a); + +int test__fixunstfsi(long double a, unsigned int expected) +{ + unsigned int x = __fixunstfsi(a); + int ret = (x != expected); + + if (ret) + { + printf("error in test__fixunstfsi(%.20Lf) = %u, " + "expected %u\n", a, x, expected); + } + return ret; +} + +char assumption_1[sizeof(long double) * CHAR_BIT == 128] = {0}; + +#endif + +int main() +{ +#if __LDBL_MANT_DIG__ == 113 + if (test__fixunstfsi(makeInf128(), UINT32_C(0xffffffff))) + return 1; + if (test__fixunstfsi(0, UINT32_C(0x0))) + return 1; + if (test__fixunstfsi(0x1.23456789abcdefp+5, UINT32_C(0x24))) + return 1; + if (test__fixunstfsi(0x1.23456789abcdefp-3, UINT32_C(0x0))) + return 1; + if (test__fixunstfsi(0x1.23456789abcdefp+20, UINT32_C(0x123456))) + return 1; + if (test__fixunstfsi(0x1.23456789abcdefp+40, UINT32_C(0xffffffff))) + return 1; + if (test__fixunstfsi(0x1.23456789abcdefp+256, UINT32_C(0xffffffff))) + return 1; + if (test__fixunstfsi(-0x1.23456789abcdefp+3, UINT32_C(0x0))) + return 1; + +#else + printf("skipped\n"); + +#endif + return 0; +} diff --git a/test/builtins/Unit/fixunsxfdi_test.c b/test/builtins/Unit/fixunsxfdi_test.c index 4308f6f4ef1f..6f42079695f2 100644 --- a/test/builtins/Unit/fixunsxfdi_test.c +++ b/test/builtins/Unit/fixunsxfdi_test.c @@ -28,7 +28,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -du_int __fixunsxfdi(long double a); +COMPILER_RT_ABI du_int __fixunsxfdi(long double a); int test__fixunsxfdi(long double a, du_int expected) { diff --git a/test/builtins/Unit/fixunsxfsi_test.c b/test/builtins/Unit/fixunsxfsi_test.c index cb2a7f487210..0d78dcb53c0f 100644 --- a/test/builtins/Unit/fixunsxfsi_test.c +++ b/test/builtins/Unit/fixunsxfsi_test.c @@ -26,7 +26,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -su_int __fixunsxfsi(long double a); +COMPILER_RT_ABI su_int __fixunsxfsi(long double a); int test__fixunsxfsi(long double a, su_int expected) { diff --git a/test/builtins/Unit/fixunsxfti_test.c b/test/builtins/Unit/fixunsxfti_test.c index 7d18b1267b3a..94b5aebe4d16 100644 --- a/test/builtins/Unit/fixunsxfti_test.c +++ b/test/builtins/Unit/fixunsxfti_test.c @@ -27,7 +27,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -tu_int __fixunsxfti(long double a); +COMPILER_RT_ABI tu_int __fixunsxfti(long double a); int test__fixunsxfti(long double a, tu_int expected) { diff --git a/test/builtins/Unit/fixxfdi_test.c b/test/builtins/Unit/fixxfdi_test.c index 43ac0f8aaa3c..0a90a56e6101 100644 --- a/test/builtins/Unit/fixxfdi_test.c +++ b/test/builtins/Unit/fixxfdi_test.c @@ -25,7 +25,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -di_int __fixxfdi(long double a); +COMPILER_RT_ABI di_int __sfixxfdi(long double a); int test__fixxfdi(long double a, di_int expected) { diff --git a/test/builtins/Unit/fixxfti_test.c b/test/builtins/Unit/fixxfti_test.c index 87914c5341bd..b8573cc14173 100644 --- a/test/builtins/Unit/fixxfti_test.c +++ b/test/builtins/Unit/fixxfti_test.c @@ -25,7 +25,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -ti_int __fixxfti(long double a); +COMPILER_RT_ABI ti_int __fixxfti(long double a); int test__fixxfti(long double a, ti_int expected) { diff --git a/test/builtins/Unit/floatdidf_test.c b/test/builtins/Unit/floatdidf_test.c index af3dacd4f38b..9bf2be97c7a4 100644 --- a/test/builtins/Unit/floatdidf_test.c +++ b/test/builtins/Unit/floatdidf_test.c @@ -22,7 +22,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -double __floatdidf(di_int a); +COMPILER_RT_ABI double __floatdidf(di_int a); int test__floatdidf(di_int a, double expected) { diff --git a/test/builtins/Unit/floatdisf_test.c b/test/builtins/Unit/floatdisf_test.c index 3e71df7b2228..a55c6a9617e9 100644 --- a/test/builtins/Unit/floatdisf_test.c +++ b/test/builtins/Unit/floatdisf_test.c @@ -22,7 +22,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -float __floatdisf(di_int a); +COMPILER_RT_ABI float __floatdisf(di_int a); int test__floatdisf(di_int a, float expected) { diff --git a/test/builtins/Unit/floatdixf_test.c b/test/builtins/Unit/floatdixf_test.c index 337666426318..f6ab5a4665c1 100644 --- a/test/builtins/Unit/floatdixf_test.c +++ b/test/builtins/Unit/floatdixf_test.c @@ -23,7 +23,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -long double __floatdixf(di_int a); +long COMPILER_RT_ABI double __floatdixf(di_int a); int test__floatdixf(di_int a, long double expected) { diff --git a/test/builtins/Unit/floatsidfvfp_test.c b/test/builtins/Unit/floatsidfvfp_test.c index e21ecda59945..e5ea3a4d4dc9 100644 --- a/test/builtins/Unit/floatsidfvfp_test.c +++ b/test/builtins/Unit/floatsidfvfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern double __floatsidfvfp(int a); +extern COMPILER_RT_ABI double __floatsidfvfp(int a); #if __arm__ int test__floatsidfvfp(int a) diff --git a/test/builtins/Unit/floatsisfvfp_test.c b/test/builtins/Unit/floatsisfvfp_test.c index d20905bd91e3..ab21e2e7ff4d 100644 --- a/test/builtins/Unit/floatsisfvfp_test.c +++ b/test/builtins/Unit/floatsisfvfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __floatsisfvfp(int a); +extern COMPILER_RT_ABI float __floatsisfvfp(int a); #if __arm__ int test__floatsisfvfp(int a) diff --git a/test/builtins/Unit/floatsitf_test.c b/test/builtins/Unit/floatsitf_test.c index db4d020af2a9..8373c7d96e26 100644 --- a/test/builtins/Unit/floatsitf_test.c +++ b/test/builtins/Unit/floatsitf_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 #include "fp_test.h" -long double __floatsitf(int a); +long COMPILER_RT_ABI double __floatsitf(int a); int test__floatsitf(int a, uint64_t expectedHi, uint64_t expectedLo) { diff --git a/test/builtins/Unit/floattidf_test.c b/test/builtins/Unit/floattidf_test.c index 476304f96f10..3af382ac001a 100644 --- a/test/builtins/Unit/floattidf_test.c +++ b/test/builtins/Unit/floattidf_test.c @@ -24,7 +24,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -double __floattidf(ti_int a); +COMPILER_RT_ABI double __floattidf(ti_int a); int test__floattidf(ti_int a, double expected) { diff --git a/test/builtins/Unit/floattisf_test.c b/test/builtins/Unit/floattisf_test.c index 75b8e917f76d..0f5dc544da68 100644 --- a/test/builtins/Unit/floattisf_test.c +++ b/test/builtins/Unit/floattisf_test.c @@ -24,7 +24,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -float __floattisf(ti_int a); +COMPILER_RT_ABI float __floattisf(ti_int a); int test__floattisf(ti_int a, float expected) { diff --git a/test/builtins/Unit/floattixf_test.c b/test/builtins/Unit/floattixf_test.c index ce3566867a47..d281debdcf55 100644 --- a/test/builtins/Unit/floattixf_test.c +++ b/test/builtins/Unit/floattixf_test.c @@ -25,7 +25,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -long double __floattixf(ti_int a); +COMPILER_RT_ABI long double __floattixf(ti_int a); int test__floattixf(ti_int a, long double expected) { diff --git a/test/builtins/Unit/floatundidf_test.c b/test/builtins/Unit/floatundidf_test.c index ae91ac374eb0..97fb1e5ec098 100644 --- a/test/builtins/Unit/floatundidf_test.c +++ b/test/builtins/Unit/floatundidf_test.c @@ -22,7 +22,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -double __floatundidf(du_int a); +COMPILER_RT_ABI double __floatundidf(du_int a); int test__floatundidf(di_int a, double expected) { diff --git a/test/builtins/Unit/floatundisf_test.c b/test/builtins/Unit/floatundisf_test.c index 394c945a3919..40b6bcc459af 100644 --- a/test/builtins/Unit/floatundisf_test.c +++ b/test/builtins/Unit/floatundisf_test.c @@ -22,7 +22,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -float __floatundisf(du_int a); +COMPILER_RT_ABI float __floatundisf(du_int a); int test__floatundisf(du_int a, float expected) { diff --git a/test/builtins/Unit/floatundixf_test.c b/test/builtins/Unit/floatundixf_test.c index 1974fa01012a..690dce19957a 100644 --- a/test/builtins/Unit/floatundixf_test.c +++ b/test/builtins/Unit/floatundixf_test.c @@ -24,7 +24,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -long double __floatundixf(du_int a); +COMPILER_RT_ABI long double __floatundixf(du_int a); int test__floatundixf(du_int a, long double expected) { diff --git a/test/builtins/Unit/floatunsitf_test.c b/test/builtins/Unit/floatunsitf_test.c index 1af72d246ab2..c3d3fe949c4e 100644 --- a/test/builtins/Unit/floatunsitf_test.c +++ b/test/builtins/Unit/floatunsitf_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 #include "fp_test.h" -long double __floatunsitf(unsigned int a); +COMPILER_RT_ABI long double __floatunsitf(unsigned int a); int test__floatunsitf(unsigned int a, uint64_t expectedHi, uint64_t expectedLo) { diff --git a/test/builtins/Unit/floatunssidfvfp_test.c b/test/builtins/Unit/floatunssidfvfp_test.c index 4883af1cf5fa..75e4bbd6c21f 100644 --- a/test/builtins/Unit/floatunssidfvfp_test.c +++ b/test/builtins/Unit/floatunssidfvfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern double __floatunssidfvfp(unsigned int a); +extern COMPILER_RT_ABI double __floatunssidfvfp(unsigned int a); #if __arm__ int test__floatunssidfvfp(unsigned int a) diff --git a/test/builtins/Unit/floatunssisfvfp_test.c b/test/builtins/Unit/floatunssisfvfp_test.c index 917061a91b18..47f837cc89a8 100644 --- a/test/builtins/Unit/floatunssisfvfp_test.c +++ b/test/builtins/Unit/floatunssisfvfp_test.c @@ -14,9 +14,9 @@ #include <stdio.h> #include <stdlib.h> #include <math.h> +#include "int_lib.h" - -extern float __floatunssisfvfp(unsigned int a); +extern COMPILER_RT_ABI float __floatunssisfvfp(unsigned int a); #if __arm__ int test__floatunssisfvfp(unsigned int a) diff --git a/test/builtins/Unit/floatuntidf_test.c b/test/builtins/Unit/floatuntidf_test.c index 3cab027051c7..9855ff79703e 100644 --- a/test/builtins/Unit/floatuntidf_test.c +++ b/test/builtins/Unit/floatuntidf_test.c @@ -24,7 +24,7 @@ // seee eeee eeee mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -double __floatuntidf(tu_int a); +COMPILER_RT_ABI double __floatuntidf(tu_int a); int test__floatuntidf(tu_int a, double expected) { diff --git a/test/builtins/Unit/floatuntisf_test.c b/test/builtins/Unit/floatuntisf_test.c index aeac3ee41e54..9b5ff790a64f 100644 --- a/test/builtins/Unit/floatuntisf_test.c +++ b/test/builtins/Unit/floatuntisf_test.c @@ -24,7 +24,7 @@ // seee eeee emmm mmmm mmmm mmmm mmmm mmmm -float __floatuntisf(tu_int a); +COMPILER_RT_ABI float __floatuntisf(tu_int a); int test__floatuntisf(tu_int a, float expected) { diff --git a/test/builtins/Unit/floatuntixf_test.c b/test/builtins/Unit/floatuntixf_test.c index 9c3434fdde0c..c58b55d3e4c1 100644 --- a/test/builtins/Unit/floatuntixf_test.c +++ b/test/builtins/Unit/floatuntixf_test.c @@ -25,7 +25,7 @@ // gggg gggg gggg gggg gggg gggg gggg gggg | gggg gggg gggg gggg seee eeee eeee eeee | // 1mmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm | mmmm mmmm mmmm mmmm mmmm mmmm mmmm mmmm -long double __floatuntixf(tu_int a); +COMPILER_RT_ABI long double __floatuntixf(tu_int a); int test__floatuntixf(tu_int a, long double expected) { diff --git a/test/builtins/Unit/fp_test.h b/test/builtins/Unit/fp_test.h index da58ca989cda..1f0e7be6d6a4 100644 --- a/test/builtins/Unit/fp_test.h +++ b/test/builtins/Unit/fp_test.h @@ -14,11 +14,17 @@ #include <stdlib.h> #include <limits.h> #include <string.h> +#include <stdint.h> enum EXPECTED_RESULT { LESS_0, LESS_EQUAL_0, EQUAL_0, GREATER_0, GREATER_EQUAL_0, NEQUAL_0 }; +static inline uint16_t fromRep16(uint16_t x) +{ + return x; +} + static inline float fromRep32(uint32_t x) { float ret; @@ -33,6 +39,7 @@ static inline double fromRep64(uint64_t x) return ret; } +#if __LDBL_MANT_DIG__ == 113 static inline long double fromRep128(uint64_t hi, uint64_t lo) { __uint128_t x = ((__uint128_t)hi << 64) + lo; @@ -40,6 +47,12 @@ static inline long double fromRep128(uint64_t hi, uint64_t lo) memcpy(&ret, &x, 16); return ret; } +#endif + +static inline uint16_t toRep16(uint16_t x) +{ + return x; +} static inline uint32_t toRep32(float x) { @@ -55,12 +68,32 @@ static inline uint64_t toRep64(double x) return ret; } +#if __LDBL_MANT_DIG__ == 113 static inline __uint128_t toRep128(long double x) { __uint128_t ret; memcpy(&ret, &x, 16); return ret; } +#endif + +static inline int compareResultH(uint16_t result, + uint16_t expected) +{ + uint16_t rep = toRep16(result); + + if (rep == expected){ + return 0; + } + // test other posible NaN representation(signal NaN) + else if (expected == 0x7e00U){ + if ((rep & 0x7c00U) == 0x7c00U && + (rep & 0x3ffU) > 0){ + return 0; + } + } + return 1; +} static inline int compareResultF(float result, uint32_t expected) @@ -98,6 +131,7 @@ static inline int compareResultD(double result, return 1; } +#if __LDBL_MANT_DIG__ == 113 // return 0 if equal // use two 64-bit integers intead of one 128-bit integer // because 128-bit integer constant can't be assigned directly @@ -121,6 +155,7 @@ static inline int compareResultLD(long double result, } return 1; } +#endif static inline int compareResultCMP(int result, enum EXPECTED_RESULT expected) @@ -177,6 +212,11 @@ static inline char *expectedStr(enum EXPECTED_RESULT expected) return ""; } +static inline uint16_t makeQNaN16() +{ + return fromRep16(0x7e00U); +} + static inline float makeQNaN32() { return fromRep32(0x7fc00000U); @@ -187,10 +227,17 @@ static inline double makeQNaN64() return fromRep64(0x7ff8000000000000UL); } +#if __LDBL_MANT_DIG__ == 113 static inline long double makeQNaN128() { return fromRep128(0x7fff800000000000UL, 0x0UL); } +#endif + +static inline uint16_t makeNaN16(uint16_t rand) +{ + return fromRep16(0x7c00U | (rand & 0x7fffU)); +} static inline float makeNaN32(uint32_t rand) { @@ -202,10 +249,17 @@ static inline double makeNaN64(uint64_t rand) return fromRep64(0x7ff0000000000000UL | (rand & 0xfffffffffffffUL)); } +#if __LDBL_MANT_DIG__ == 113 static inline long double makeNaN128(uint64_t rand) { return fromRep128(0x7fff000000000000UL | (rand & 0xffffffffffffUL), 0x0UL); } +#endif + +static inline uint16_t makeInf16() +{ + return fromRep16(0x7c00U); +} static inline float makeInf32() { @@ -217,7 +271,9 @@ static inline double makeInf64() return fromRep64(0x7ff0000000000000UL); } +#if __LDBL_MANT_DIG__ == 113 static inline long double makeInf128() { return fromRep128(0x7fff000000000000UL, 0x0UL); } +#endif diff --git a/test/builtins/Unit/lshrdi3_test.c b/test/builtins/Unit/lshrdi3_test.c index ffc6a69d0e9f..d48ae4dd79fa 100644 --- a/test/builtins/Unit/lshrdi3_test.c +++ b/test/builtins/Unit/lshrdi3_test.c @@ -18,7 +18,7 @@ // Precondition: 0 <= b < bits_in_dword -di_int __lshrdi3(di_int a, si_int b); +COMPILER_RT_ABI di_int __lshrdi3(di_int a, si_int b); int test__lshrdi3(di_int a, si_int b, di_int expected) { diff --git a/test/builtins/Unit/lshrti3_test.c b/test/builtins/Unit/lshrti3_test.c index 3f33c089cd62..f5a0dd616cb5 100644 --- a/test/builtins/Unit/lshrti3_test.c +++ b/test/builtins/Unit/lshrti3_test.c @@ -20,7 +20,7 @@ // Precondition: 0 <= b < bits_in_dword -ti_int __lshrti3(ti_int a, si_int b); +COMPILER_RT_ABI ti_int __lshrti3(ti_int a, si_int b); int test__lshrti3(ti_int a, si_int b, ti_int expected) { diff --git a/test/builtins/Unit/moddi3_test.c b/test/builtins/Unit/moddi3_test.c index 9f6801d6f41b..62e8f227b34b 100644 --- a/test/builtins/Unit/moddi3_test.c +++ b/test/builtins/Unit/moddi3_test.c @@ -16,7 +16,7 @@ // Returns: a % b -di_int __moddi3(di_int a, di_int b); +COMPILER_RT_ABI di_int __moddi3(di_int a, di_int b); int test__moddi3(di_int a, di_int b, di_int expected) { diff --git a/test/builtins/Unit/modsi3_test.c b/test/builtins/Unit/modsi3_test.c index 52ec9a0ae36b..8c9f58832b9c 100644 --- a/test/builtins/Unit/modsi3_test.c +++ b/test/builtins/Unit/modsi3_test.c @@ -17,7 +17,7 @@ /* Returns: a % b */ -si_int __modsi3(si_int a, si_int b); +COMPILER_RT_ABI si_int __modsi3(si_int a, si_int b); int test__modsi3(si_int a, si_int b, si_int expected) { si_int x = __modsi3(a, b); diff --git a/test/builtins/Unit/modti3_test.c b/test/builtins/Unit/modti3_test.c index ba9f9804dfca..99413aaa2f6a 100644 --- a/test/builtins/Unit/modti3_test.c +++ b/test/builtins/Unit/modti3_test.c @@ -18,7 +18,7 @@ // Returns: a % b -ti_int __modti3(ti_int a, ti_int b); +COMPILER_RT_ABI ti_int __modti3(ti_int a, ti_int b); int test__modti3(ti_int a, ti_int b, ti_int expected) { diff --git a/test/builtins/Unit/muldc3_test.c b/test/builtins/Unit/muldc3_test.c index 112b6120358a..6902ef3222a9 100644 --- a/test/builtins/Unit/muldc3_test.c +++ b/test/builtins/Unit/muldc3_test.c @@ -18,7 +18,8 @@ // Returns: the product of a + ib and c + id -double _Complex __muldc3(double __a, double __b, double __c, double __d); +COMPILER_RT_ABI double _Complex +__muldc3(double __a, double __b, double __c, double __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/muldf3vfp_test.c b/test/builtins/Unit/muldf3vfp_test.c index 73454bf290aa..766972d7d1d7 100644 --- a/test/builtins/Unit/muldf3vfp_test.c +++ b/test/builtins/Unit/muldf3vfp_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> #if __arm__ -extern double __muldf3vfp(double a, double b); +extern COMPILER_RT_ABI double __muldf3vfp(double a, double b); int test__muldf3vfp(double a, double b) { diff --git a/test/builtins/Unit/muldi3_test.c b/test/builtins/Unit/muldi3_test.c index 83b525592645..651dd0177347 100644 --- a/test/builtins/Unit/muldi3_test.c +++ b/test/builtins/Unit/muldi3_test.c @@ -14,7 +14,7 @@ #include "int_lib.h" #include <stdio.h> -di_int __muldi3(di_int a, di_int b); +COMPILER_RT_ABI di_int __muldi3(di_int a, di_int b); int test__muldi3(di_int a, di_int b, di_int expected) { diff --git a/test/builtins/Unit/mulodi4_test.c b/test/builtins/Unit/mulodi4_test.c index 10a0eaac61f9..4546609fb8ec 100644 --- a/test/builtins/Unit/mulodi4_test.c +++ b/test/builtins/Unit/mulodi4_test.c @@ -14,7 +14,7 @@ #include "int_lib.h" #include <stdio.h> -extern di_int __mulodi4(di_int a, di_int b, int* overflow); +extern COMPILER_RT_ABI di_int __mulodi4(di_int a, di_int b, int* overflow); int test__mulodi4(di_int a, di_int b, di_int expected, int expected_overflow) { diff --git a/test/builtins/Unit/mulosi4_test.c b/test/builtins/Unit/mulosi4_test.c index fc509db2c178..6a27d69bd6b7 100644 --- a/test/builtins/Unit/mulosi4_test.c +++ b/test/builtins/Unit/mulosi4_test.c @@ -18,7 +18,7 @@ // Effects: aborts if a * b overflows -si_int __mulosi4(si_int a, si_int b, int *overflow); +COMPILER_RT_ABI si_int __mulosi4(si_int a, si_int b, int *overflow); int test__mulosi4(si_int a, si_int b, si_int expected, int expected_overflow) { diff --git a/test/builtins/Unit/muloti4_test.c b/test/builtins/Unit/muloti4_test.c index 95439a4197a3..d00e7bb6b68b 100644 --- a/test/builtins/Unit/muloti4_test.c +++ b/test/builtins/Unit/muloti4_test.c @@ -20,7 +20,7 @@ // Effects: sets overflow if a * b overflows -ti_int __muloti4(ti_int a, ti_int b, int *overflow); +COMPILER_RT_ABI ti_int __muloti4(ti_int a, ti_int b, int *overflow); int test__muloti4(ti_int a, ti_int b, ti_int expected, int expected_overflow) { diff --git a/test/builtins/Unit/mulsc3_test.c b/test/builtins/Unit/mulsc3_test.c index 7a1b3ae0092d..eeb537ac135d 100644 --- a/test/builtins/Unit/mulsc3_test.c +++ b/test/builtins/Unit/mulsc3_test.c @@ -18,7 +18,8 @@ // Returns: the product of a + ib and c + id -float _Complex __mulsc3(float __a, float __b, float __c, float __d); +COMPILER_RT_ABI float _Complex +__mulsc3(float __a, float __b, float __c, float __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/mulsf3vfp_test.c b/test/builtins/Unit/mulsf3vfp_test.c index 92cf1f128497..5b0f6c14384d 100644 --- a/test/builtins/Unit/mulsf3vfp_test.c +++ b/test/builtins/Unit/mulsf3vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __mulsf3vfp(float a, float b); +extern COMPILER_RT_ABI float __mulsf3vfp(float a, float b); #if __arm__ int test__mulsf3vfp(float a, float b) diff --git a/test/builtins/Unit/multc3_test.c b/test/builtins/Unit/multc3_test.c index f8c66c0e426f..b1482638a913 100644 --- a/test/builtins/Unit/multc3_test.c +++ b/test/builtins/Unit/multc3_test.c @@ -11,16 +11,17 @@ // //===----------------------------------------------------------------------===// +#include <stdio.h> + #if _ARCH_PPC #include "int_lib.h" #include <math.h> #include <complex.h> -#include <stdio.h> // Returns: the product of a + ib and c + id -long double _Complex +COMPILER_RT_ABI long double _Complex __multc3(long double __a, long double __b, long double __c, long double __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/multf3_test.c b/test/builtins/Unit/multf3_test.c index 89610056b27e..98a8f7a3231e 100644 --- a/test/builtins/Unit/multf3_test.c +++ b/test/builtins/Unit/multf3_test.c @@ -18,7 +18,7 @@ #include "fp_test.h" // Returns: a * b -long double __multf3(long double a, long double b); +COMPILER_RT_ABI long double __multf3(long double a, long double b); int test__multf3(long double a, long double b, uint64_t expectedHi, uint64_t expectedLo) diff --git a/test/builtins/Unit/multi3_test.c b/test/builtins/Unit/multi3_test.c index 2ccbd06c3e7d..04b1b8aa851e 100644 --- a/test/builtins/Unit/multi3_test.c +++ b/test/builtins/Unit/multi3_test.c @@ -16,7 +16,7 @@ #ifdef CRT_HAS_128BIT -ti_int __multi3(ti_int a, ti_int b); +COMPILER_RT_ABI ti_int __multi3(ti_int a, ti_int b); int test__multi3(ti_int a, ti_int b, ti_int expected) { diff --git a/test/builtins/Unit/mulvdi3_test.c b/test/builtins/Unit/mulvdi3_test.c index a023bf611279..7f16c4c4f612 100644 --- a/test/builtins/Unit/mulvdi3_test.c +++ b/test/builtins/Unit/mulvdi3_test.c @@ -18,7 +18,7 @@ // Effects: aborts if a * b overflows -di_int __mulvdi3(di_int a, di_int b); +COMPILER_RT_ABI di_int __mulvdi3(di_int a, di_int b); int test__mulvdi3(di_int a, di_int b, di_int expected) { diff --git a/test/builtins/Unit/mulvsi3_test.c b/test/builtins/Unit/mulvsi3_test.c index 1eb53a5d5daa..64df4fe24daf 100644 --- a/test/builtins/Unit/mulvsi3_test.c +++ b/test/builtins/Unit/mulvsi3_test.c @@ -18,7 +18,7 @@ // Effects: aborts if a * b overflows -si_int __mulvsi3(si_int a, si_int b); +COMPILER_RT_ABI si_int __mulvsi3(si_int a, si_int b); int test__mulvsi3(si_int a, si_int b, si_int expected) { diff --git a/test/builtins/Unit/mulvti3_test.c b/test/builtins/Unit/mulvti3_test.c index 6336f4550e0d..bf2f7316ba19 100644 --- a/test/builtins/Unit/mulvti3_test.c +++ b/test/builtins/Unit/mulvti3_test.c @@ -20,7 +20,7 @@ // Effects: aborts if a * b overflows -ti_int __mulvti3(ti_int a, ti_int b); +COMPILER_RT_ABI ti_int __mulvti3(ti_int a, ti_int b); int test__mulvti3(ti_int a, ti_int b, ti_int expected) { diff --git a/test/builtins/Unit/mulxc3_test.c b/test/builtins/Unit/mulxc3_test.c index 4297c162bd8e..e77e94fa9cf9 100644 --- a/test/builtins/Unit/mulxc3_test.c +++ b/test/builtins/Unit/mulxc3_test.c @@ -20,7 +20,7 @@ // Returns: the product of a + ib and c + id -long double _Complex +COMPILER_RT_ABI long double _Complex __mulxc3(long double __a, long double __b, long double __c, long double __d); enum {zero, non_zero, inf, NaN, non_zero_nan}; diff --git a/test/builtins/Unit/negdf2vfp_test.c b/test/builtins/Unit/negdf2vfp_test.c index dc55428d6780..f0e66777e3a1 100644 --- a/test/builtins/Unit/negdf2vfp_test.c +++ b/test/builtins/Unit/negdf2vfp_test.c @@ -16,7 +16,7 @@ #include <math.h> -extern double __negdf2vfp(double a); +extern COMPILER_RT_ABI double __negdf2vfp(double a); #if __arm__ int test__negdf2vfp(double a) diff --git a/test/builtins/Unit/negdi2_test.c b/test/builtins/Unit/negdi2_test.c index 510b3b046b56..beccd71ee796 100644 --- a/test/builtins/Unit/negdi2_test.c +++ b/test/builtins/Unit/negdi2_test.c @@ -16,7 +16,7 @@ // Returns: -a -di_int __negdi2(di_int a); +COMPILER_RT_ABI di_int __negdi2(di_int a); int test__negdi2(di_int a, di_int expected) { diff --git a/test/builtins/Unit/negsf2vfp_test.c b/test/builtins/Unit/negsf2vfp_test.c index ef54cee1dbee..9e47d89094ce 100644 --- a/test/builtins/Unit/negsf2vfp_test.c +++ b/test/builtins/Unit/negsf2vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __negsf2vfp(float a); +extern COMPILER_RT_ABI float __negsf2vfp(float a); #if __arm__ int test__negsf2vfp(float a) diff --git a/test/builtins/Unit/negti2_test.c b/test/builtins/Unit/negti2_test.c index a40c0c324bb2..b07597868ced 100644 --- a/test/builtins/Unit/negti2_test.c +++ b/test/builtins/Unit/negti2_test.c @@ -18,7 +18,7 @@ // Returns: -a -ti_int __negti2(ti_int a); +COMPILER_RT_ABI ti_int __negti2(ti_int a); int test__negti2(ti_int a, ti_int expected) { diff --git a/test/builtins/Unit/negvdi2_test.c b/test/builtins/Unit/negvdi2_test.c index 9617b95ff105..5c202e55cfb7 100644 --- a/test/builtins/Unit/negvdi2_test.c +++ b/test/builtins/Unit/negvdi2_test.c @@ -18,7 +18,7 @@ // Effects: aborts if -a overflows -di_int __negvdi2(di_int a); +COMPILER_RT_ABI di_int __negvdi2(di_int a); int test__negvdi2(di_int a) { diff --git a/test/builtins/Unit/negvsi2_test.c b/test/builtins/Unit/negvsi2_test.c index 5ea0e2e86c2a..6330803d5fcf 100644 --- a/test/builtins/Unit/negvsi2_test.c +++ b/test/builtins/Unit/negvsi2_test.c @@ -18,7 +18,7 @@ // Effects: aborts if -a overflows -si_int __negvsi2(si_int a); +COMPILER_RT_ABI si_int __negvsi2(si_int a); int test__negvsi2(si_int a) { diff --git a/test/builtins/Unit/negvti2_test.c b/test/builtins/Unit/negvti2_test.c index 8126cdb5b668..005f8a8acadf 100644 --- a/test/builtins/Unit/negvti2_test.c +++ b/test/builtins/Unit/negvti2_test.c @@ -20,8 +20,8 @@ // Effects: aborts if -a overflows -ti_int __negvti2(ti_int a); -ti_int __negti2(ti_int a); +COMPILER_RT_ABI ti_int __negvti2(ti_int a); +COMPILER_RT_ABI ti_int __negti2(ti_int a); int test__negvti2(ti_int a) { diff --git a/test/builtins/Unit/paritydi2_test.c b/test/builtins/Unit/paritydi2_test.c index 5360e374d439..98220bd8cb61 100644 --- a/test/builtins/Unit/paritydi2_test.c +++ b/test/builtins/Unit/paritydi2_test.c @@ -17,7 +17,7 @@ // Returns: 1 if number of bits is odd else returns 0 -si_int __paritydi2(di_int a); +COMPILER_RT_ABI si_int __paritydi2(di_int a); int naive_parity(di_int a) { diff --git a/test/builtins/Unit/paritysi2_test.c b/test/builtins/Unit/paritysi2_test.c index 3ea8473aad5c..175aeb2c9c00 100644 --- a/test/builtins/Unit/paritysi2_test.c +++ b/test/builtins/Unit/paritysi2_test.c @@ -17,7 +17,7 @@ // Returns: 1 if number of bits is odd else returns 0 -si_int __paritysi2(si_int a); +COMPILER_RT_ABI si_int __paritysi2(si_int a); int naive_parity(si_int a) { diff --git a/test/builtins/Unit/parityti2_test.c b/test/builtins/Unit/parityti2_test.c index 8f065b953d22..cc1e999f29eb 100644 --- a/test/builtins/Unit/parityti2_test.c +++ b/test/builtins/Unit/parityti2_test.c @@ -19,7 +19,7 @@ // Returns: 1 if number of bits is odd else returns 0 -si_int __parityti2(ti_int a); +COMPILER_RT_ABI si_int __parityti2(ti_int a); int naive_parity(ti_int a) { diff --git a/test/builtins/Unit/popcountdi2_test.c b/test/builtins/Unit/popcountdi2_test.c index 4c5611796434..bfd4977b416c 100644 --- a/test/builtins/Unit/popcountdi2_test.c +++ b/test/builtins/Unit/popcountdi2_test.c @@ -17,7 +17,7 @@ // Returns: count of 1 bits -si_int __popcountdi2(di_int a); +COMPILER_RT_ABI si_int __popcountdi2(di_int a); int naive_popcount(di_int a) { diff --git a/test/builtins/Unit/popcountsi2_test.c b/test/builtins/Unit/popcountsi2_test.c index d0a05c45e544..10b757d814e4 100644 --- a/test/builtins/Unit/popcountsi2_test.c +++ b/test/builtins/Unit/popcountsi2_test.c @@ -17,7 +17,7 @@ // Returns: count of 1 bits -si_int __popcountsi2(si_int a); +COMPILER_RT_ABI si_int __popcountsi2(si_int a); int naive_popcount(si_int a) { diff --git a/test/builtins/Unit/popcountti2_test.c b/test/builtins/Unit/popcountti2_test.c index d17e03afd665..3a3c3cb4fa2c 100644 --- a/test/builtins/Unit/popcountti2_test.c +++ b/test/builtins/Unit/popcountti2_test.c @@ -19,7 +19,7 @@ // Returns: count of 1 bits -si_int __popcountti2(ti_int a); +COMPILER_RT_ABI si_int __popcountti2(ti_int a); int naive_popcount(ti_int a) { diff --git a/test/builtins/Unit/powidf2_test.c b/test/builtins/Unit/powidf2_test.c index 2abc84de7676..c499d9abaeb2 100644 --- a/test/builtins/Unit/powidf2_test.c +++ b/test/builtins/Unit/powidf2_test.c @@ -17,7 +17,7 @@ // Returns: a ^ b -double __powidf2(double a, si_int b); +COMPILER_RT_ABI double __powidf2(double a, si_int b); int test__powidf2(double a, si_int b, double expected) { diff --git a/test/builtins/Unit/powisf2_test.c b/test/builtins/Unit/powisf2_test.c index 98409f432e66..1186ef4af5b0 100644 --- a/test/builtins/Unit/powisf2_test.c +++ b/test/builtins/Unit/powisf2_test.c @@ -17,7 +17,7 @@ // Returns: a ^ b -float __powisf2(float a, si_int b); +COMPILER_RT_ABI float __powisf2(float a, si_int b); int test__powisf2(float a, si_int b, float expected) { diff --git a/test/builtins/Unit/powitf2_test.c b/test/builtins/Unit/powitf2_test.c index 817cb1130dee..13c890a0dcaf 100644 --- a/test/builtins/Unit/powitf2_test.c +++ b/test/builtins/Unit/powitf2_test.c @@ -11,15 +11,16 @@ // //===----------------------------------------------------------------------===// +#include <stdio.h> + #if _ARCH_PPC #include "int_lib.h" -#include <stdio.h> #include <math.h> // Returns: a ^ b -long double __powitf2(long double a, si_int b); +COMPILER_RT_ABI long double __powitf2(long double a, si_int b); int test__powitf2(long double a, si_int b, long double expected) { diff --git a/test/builtins/Unit/powixf2_test.c b/test/builtins/Unit/powixf2_test.c index 201870b37f15..a28f1f9696f7 100644 --- a/test/builtins/Unit/powixf2_test.c +++ b/test/builtins/Unit/powixf2_test.c @@ -19,7 +19,7 @@ // Returns: a ^ b -long double __powixf2(long double a, si_int b); +COMPILER_RT_ABI long double __powixf2(long double a, si_int b); int test__powixf2(long double a, si_int b, long double expected) { diff --git a/test/builtins/Unit/ppc/floatditf_test.c b/test/builtins/Unit/ppc/floatditf_test.c index 578260aeddc4..71ecf7c4b5dd 100644 --- a/test/builtins/Unit/ppc/floatditf_test.c +++ b/test/builtins/Unit/ppc/floatditf_test.c @@ -1,7 +1,7 @@ #include <stdint.h> #include <stdio.h> -long double __floatditf(int64_t); +COMPILER_RT_ABI long double __floatditf(int64_t); #include "floatunditf_test.h" #include "DD.h" diff --git a/test/builtins/Unit/ppc/floatunditf_test.c b/test/builtins/Unit/ppc/floatunditf_test.c index 68390d197229..4d1ce088476b 100644 --- a/test/builtins/Unit/ppc/floatunditf_test.c +++ b/test/builtins/Unit/ppc/floatunditf_test.c @@ -1,7 +1,7 @@ #include <stdint.h> #include <stdio.h> -long double __floatunditf(uint64_t); +COMPILER_RT_ABI long double __floatunditf(uint64_t); #include "floatunditf_test.h" #include "DD.h" diff --git a/test/builtins/Unit/subdf3vfp_test.c b/test/builtins/Unit/subdf3vfp_test.c index 86d6f2fef22b..5d5d7117088f 100644 --- a/test/builtins/Unit/subdf3vfp_test.c +++ b/test/builtins/Unit/subdf3vfp_test.c @@ -17,7 +17,7 @@ #if __arm__ -extern double __subdf3vfp(double a, double b); +extern COMPILER_RT_ABI double __subdf3vfp(double a, double b); int test__subdf3vfp(double a, double b) { diff --git a/test/builtins/Unit/subsf3vfp_test.c b/test/builtins/Unit/subsf3vfp_test.c index 223e7f8f3936..fe60058de6f7 100644 --- a/test/builtins/Unit/subsf3vfp_test.c +++ b/test/builtins/Unit/subsf3vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __subsf3vfp(float a, float b); +extern COMPILER_RT_ABI float __subsf3vfp(float a, float b); #if __arm__ int test__subsf3vfp(float a, float b) diff --git a/test/builtins/Unit/subtf3_test.c b/test/builtins/Unit/subtf3_test.c index 2ab249a994c3..ad115a40ca39 100644 --- a/test/builtins/Unit/subtf3_test.c +++ b/test/builtins/Unit/subtf3_test.c @@ -18,7 +18,7 @@ #include "fp_test.h" // Returns: a - b -long double __subtf3(long double a, long double b); +COMPILER_RT_ABI long double __subtf3(long double a, long double b); int test__subtf3(long double a, long double b, uint64_t expectedHi, uint64_t expectedLo) diff --git a/test/builtins/Unit/subvdi3_test.c b/test/builtins/Unit/subvdi3_test.c index 0fb8f5156896..96e825cafb1f 100644 --- a/test/builtins/Unit/subvdi3_test.c +++ b/test/builtins/Unit/subvdi3_test.c @@ -19,7 +19,7 @@ // Effects: aborts if a - b overflows -di_int __subvdi3(di_int a, di_int b); +COMPILER_RT_ABI di_int __subvdi3(di_int a, di_int b); int test__subvdi3(di_int a, di_int b) { diff --git a/test/builtins/Unit/subvsi3_test.c b/test/builtins/Unit/subvsi3_test.c index 14e6ce193904..03ef5045d257 100644 --- a/test/builtins/Unit/subvsi3_test.c +++ b/test/builtins/Unit/subvsi3_test.c @@ -19,7 +19,7 @@ // Effects: aborts if a - b overflows -si_int __subvsi3(si_int a, si_int b); +COMPILER_RT_ABI si_int __subvsi3(si_int a, si_int b); int test__subvsi3(si_int a, si_int b) { diff --git a/test/builtins/Unit/subvti3_test.c b/test/builtins/Unit/subvti3_test.c index b2c225c3a7f4..40eb5186917a 100644 --- a/test/builtins/Unit/subvti3_test.c +++ b/test/builtins/Unit/subvti3_test.c @@ -21,7 +21,7 @@ // Effects: aborts if a - b overflows -ti_int __subvti3(ti_int a, ti_int b); +COMPILER_RT_ABI ti_int __subvti3(ti_int a, ti_int b); int test__subvti3(ti_int a, ti_int b) { diff --git a/test/builtins/Unit/truncdfhf2_test.c b/test/builtins/Unit/truncdfhf2_test.c new file mode 100644 index 000000000000..6627a001eab8 --- /dev/null +++ b/test/builtins/Unit/truncdfhf2_test.c @@ -0,0 +1,114 @@ +//===--------------- truncdfhf2_test.c - Test __truncdfhf2 ----------------===// +// +// 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 tests __truncdfhf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#include "fp_test.h" + +uint16_t __truncdfhf2(double a); + +int test__truncdfhf2(double a, uint16_t expected) +{ + uint16_t x = __truncdfhf2(a); + int ret = compareResultH(x, expected); + + if (ret){ + printf("error in test__truncdfhf2(%f) = %#.4x, " + "expected %#.4x\n", a, x, fromRep16(expected)); + } + return ret; +} + +char assumption_1[sizeof(__fp16) * CHAR_BIT == 16] = {0}; + +int main() +{ + // qNaN + if (test__truncdfhf2(makeQNaN64(), + UINT16_C(0x7e00))) + return 1; + // NaN + if (test__truncdfhf2(makeNaN64(UINT64_C(0x8000)), + UINT16_C(0x7e00))) + return 1; + // inf + if (test__truncdfhf2(makeInf64(), + UINT16_C(0x7c00))) + return 1; + if (test__truncdfhf2(-makeInf64(), + UINT16_C(0xfc00))) + return 1; + // zero + if (test__truncdfhf2(0.0, UINT16_C(0x0))) + return 1; + if (test__truncdfhf2(-0.0, UINT16_C(0x8000))) + return 1; + + if (test__truncdfhf2(3.1415926535, + UINT16_C(0x4248))) + return 1; + if (test__truncdfhf2(-3.1415926535, + UINT16_C(0xc248))) + return 1; + if (test__truncdfhf2(0x1.987124876876324p+1000, + UINT16_C(0x7c00))) + return 1; + if (test__truncdfhf2(0x1.987124876876324p+12, + UINT16_C(0x6e62))) + return 1; + if (test__truncdfhf2(0x1.0p+0, + UINT16_C(0x3c00))) + return 1; + if (test__truncdfhf2(0x1.0p-14, + UINT16_C(0x0400))) + return 1; + // denormal + if (test__truncdfhf2(0x1.0p-20, + UINT16_C(0x0010))) + return 1; + if (test__truncdfhf2(0x1.0p-24, + UINT16_C(0x0001))) + return 1; + if (test__truncdfhf2(-0x1.0p-24, + UINT16_C(0x8001))) + return 1; + if (test__truncdfhf2(0x1.5p-25, + UINT16_C(0x0001))) + return 1; + // and back to zero + if (test__truncdfhf2(0x1.0p-25, + UINT16_C(0x0000))) + return 1; + if (test__truncdfhf2(-0x1.0p-25, + UINT16_C(0x8000))) + return 1; + // max (precise) + if (test__truncdfhf2(65504.0, + UINT16_C(0x7bff))) + return 1; + // max (rounded) + if (test__truncdfhf2(65519.0, + UINT16_C(0x7bff))) + return 1; + // max (to +inf) + if (test__truncdfhf2(65520.0, + UINT16_C(0x7c00))) + return 1; + if (test__truncdfhf2(-65520.0, + UINT16_C(0xfc00))) + return 1; + if (test__truncdfhf2(65536.0, + UINT16_C(0x7c00))) + return 1; + return 0; +} diff --git a/test/builtins/Unit/truncdfsf2_test.c b/test/builtins/Unit/truncdfsf2_test.c new file mode 100644 index 000000000000..a208a3a56bed --- /dev/null +++ b/test/builtins/Unit/truncdfsf2_test.c @@ -0,0 +1,38 @@ +//===--------------- truncdfsf2_test.c - Test __truncdfsf2 ----------------===// +// +// 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 tests __truncdfsf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#include "fp_test.h" + +float __truncdfsf2(double a); + +int test__truncdfsf2(double a) +{ + float actual = __truncdfsf2(a); + float expected = a; + + if (actual != expected) { + printf("error in test__truncdfsf2(%lf) = %f, " + "expected %f\n", a, actual, expected); + return 1; + } + return 0; +} + +int main() +{ + if (test__truncdfsf2(340282366920938463463374607431768211456.0)) + return 1; + return 0; +} diff --git a/test/builtins/Unit/truncdfsf2vfp_test.c b/test/builtins/Unit/truncdfsf2vfp_test.c index afc7868b0de0..eaeda658259e 100644 --- a/test/builtins/Unit/truncdfsf2vfp_test.c +++ b/test/builtins/Unit/truncdfsf2vfp_test.c @@ -11,12 +11,13 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #include <stdlib.h> #include <math.h> -extern float __truncdfsf2vfp(double a); +extern COMPILER_RT_ABI float __truncdfsf2vfp(double a); #if __arm__ int test__truncdfsf2vfp(double a) diff --git a/test/builtins/Unit/truncsfhf2_test.c b/test/builtins/Unit/truncsfhf2_test.c new file mode 100644 index 000000000000..5bc3c8e22d59 --- /dev/null +++ b/test/builtins/Unit/truncsfhf2_test.c @@ -0,0 +1,114 @@ +//===--------------- truncsfhf2_test.c - Test __truncsfhf2 ----------------===// +// +// 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 tests __truncsfhf2 for the compiler_rt library. +// +//===----------------------------------------------------------------------===// + +#include <stdio.h> + +#include "fp_test.h" + +uint16_t __truncsfhf2(float a); + +int test__truncsfhf2(float a, uint16_t expected) +{ + uint16_t x = __truncsfhf2(a); + int ret = compareResultH(x, expected); + + if (ret){ + printf("error in test__truncsfhf2(%f) = %#.4x, " + "expected %#.4x\n", a, x, fromRep16(expected)); + } + return ret; +} + +char assumption_1[sizeof(__fp16) * CHAR_BIT == 16] = {0}; + +int main() +{ + // qNaN + if (test__truncsfhf2(makeQNaN32(), + UINT16_C(0x7e00))) + return 1; + // NaN + if (test__truncsfhf2(makeNaN32(UINT32_C(0x8000)), + UINT16_C(0x7e00))) + return 1; + // inf + if (test__truncsfhf2(makeInf32(), + UINT16_C(0x7c00))) + return 1; + if (test__truncsfhf2(-makeInf32(), + UINT16_C(0xfc00))) + return 1; + // zero + if (test__truncsfhf2(0.0f, UINT16_C(0x0))) + return 1; + if (test__truncsfhf2(-0.0f, UINT16_C(0x8000))) + return 1; + + if (test__truncsfhf2(3.1415926535f, + UINT16_C(0x4248))) + return 1; + if (test__truncsfhf2(-3.1415926535f, + UINT16_C(0xc248))) + return 1; + if (test__truncsfhf2(0x1.987124876876324p+100f, + UINT16_C(0x7c00))) + return 1; + if (test__truncsfhf2(0x1.987124876876324p+12f, + UINT16_C(0x6e62))) + return 1; + if (test__truncsfhf2(0x1.0p+0f, + UINT16_C(0x3c00))) + return 1; + if (test__truncsfhf2(0x1.0p-14f, + UINT16_C(0x0400))) + return 1; + // denormal + if (test__truncsfhf2(0x1.0p-20f, + UINT16_C(0x0010))) + return 1; + if (test__truncsfhf2(0x1.0p-24f, + UINT16_C(0x0001))) + return 1; + if (test__truncsfhf2(-0x1.0p-24f, + UINT16_C(0x8001))) + return 1; + if (test__truncsfhf2(0x1.5p-25f, + UINT16_C(0x0001))) + return 1; + // and back to zero + if (test__truncsfhf2(0x1.0p-25f, + UINT16_C(0x0000))) + return 1; + if (test__truncsfhf2(-0x1.0p-25f, + UINT16_C(0x8000))) + return 1; + // max (precise) + if (test__truncsfhf2(65504.0f, + UINT16_C(0x7bff))) + return 1; + // max (rounded) + if (test__truncsfhf2(65519.0f, + UINT16_C(0x7bff))) + return 1; + // max (to +inf) + if (test__truncsfhf2(65520.0f, + UINT16_C(0x7c00))) + return 1; + if (test__truncsfhf2(65536.0f, + UINT16_C(0x7c00))) + return 1; + if (test__truncsfhf2(-65520.0f, + UINT16_C(0xfc00))) + return 1; + return 0; +} diff --git a/test/builtins/Unit/trunctfdf2_test.c b/test/builtins/Unit/trunctfdf2_test.c index 46855e31bb5e..0366a8ce99f2 100644 --- a/test/builtins/Unit/trunctfdf2_test.c +++ b/test/builtins/Unit/trunctfdf2_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 #include "fp_test.h" -double __trunctfdf2(long double a); +COMPILER_RT_ABI double __trunctfdf2(long double a); int test__trunctfdf2(long double a, uint64_t expected) { diff --git a/test/builtins/Unit/trunctfsf2_test.c b/test/builtins/Unit/trunctfsf2_test.c index 44e7fd1827fe..a6b922ce122d 100644 --- a/test/builtins/Unit/trunctfsf2_test.c +++ b/test/builtins/Unit/trunctfsf2_test.c @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "int_lib.h" #include <stdio.h> #if __LDBL_MANT_DIG__ == 113 #include "fp_test.h" -float __trunctfsf2(long double a); +COMPILER_RT_ABI float __trunctfsf2(long double a); int test__trunctfsf2(long double a, uint32_t expected) { diff --git a/test/builtins/Unit/ucmpdi2_test.c b/test/builtins/Unit/ucmpdi2_test.c index 22059077c795..65ae4fce0e79 100644 --- a/test/builtins/Unit/ucmpdi2_test.c +++ b/test/builtins/Unit/ucmpdi2_test.c @@ -18,7 +18,7 @@ // if (a == b) returns 1 // if (a > b) returns 2 -si_int __ucmpdi2(du_int a, du_int b); +COMPILER_RT_ABI si_int __ucmpdi2(du_int a, du_int b); int test__ucmpdi2(du_int a, du_int b, si_int expected) { diff --git a/test/builtins/Unit/ucmpti2_test.c b/test/builtins/Unit/ucmpti2_test.c index 0713da8971af..826cd6439dd3 100644 --- a/test/builtins/Unit/ucmpti2_test.c +++ b/test/builtins/Unit/ucmpti2_test.c @@ -20,7 +20,7 @@ // if (a == b) returns 1 // if (a > b) returns 2 -si_int __ucmpti2(tu_int a, tu_int b); +COMPILER_RT_ABI si_int __ucmpti2(tu_int a, tu_int b); int test__ucmpti2(tu_int a, tu_int b, si_int expected) { diff --git a/test/builtins/Unit/udivdi3_test.c b/test/builtins/Unit/udivdi3_test.c index 24843f8bde6a..48c99e30973a 100644 --- a/test/builtins/Unit/udivdi3_test.c +++ b/test/builtins/Unit/udivdi3_test.c @@ -16,7 +16,7 @@ // Returns: a / b -du_int __udivdi3(du_int a, du_int b); +COMPILER_RT_ABI du_int __udivdi3(du_int a, du_int b); int test__udivdi3(du_int a, du_int b, du_int expected_q) { diff --git a/test/builtins/Unit/udivmoddi4_test.c b/test/builtins/Unit/udivmoddi4_test.c index 43bf1a11f348..79af1eeff613 100644 --- a/test/builtins/Unit/udivmoddi4_test.c +++ b/test/builtins/Unit/udivmoddi4_test.c @@ -17,7 +17,7 @@ // Effects: if rem != 0, *rem = a % b // Returns: a / b -du_int __udivmoddi4(du_int a, du_int b, du_int* rem); +COMPILER_RT_ABI du_int __udivmoddi4(du_int a, du_int b, du_int* rem); int test__udivmoddi4(du_int a, du_int b, du_int expected_q, du_int expected_r) { diff --git a/test/builtins/Unit/udivmodsi4_test.c b/test/builtins/Unit/udivmodsi4_test.c index d734cd1fdf71..4c14e297d567 100644 --- a/test/builtins/Unit/udivmodsi4_test.c +++ b/test/builtins/Unit/udivmodsi4_test.c @@ -16,7 +16,7 @@ // Returns: a / b -extern su_int __udivmodsi4(su_int a, su_int b, su_int* rem); +extern COMPILER_RT_ABI su_int __udivmodsi4(su_int a, su_int b, su_int* rem); int test__udivmodsi4(su_int a, su_int b, su_int expected_result, su_int expected_rem) diff --git a/test/builtins/Unit/udivmodti4_test.c b/test/builtins/Unit/udivmodti4_test.c index 751aa868325f..c4246613d2e6 100644 --- a/test/builtins/Unit/udivmodti4_test.c +++ b/test/builtins/Unit/udivmodti4_test.c @@ -19,7 +19,7 @@ // Effects: if rem != 0, *rem = a % b // Returns: a / b -tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); +COMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int* rem); int test__udivmodti4(tu_int a, tu_int b, tu_int expected_q, tu_int expected_r) { diff --git a/test/builtins/Unit/udivsi3_test.c b/test/builtins/Unit/udivsi3_test.c index 325e4e6e8859..49053865bf49 100644 --- a/test/builtins/Unit/udivsi3_test.c +++ b/test/builtins/Unit/udivsi3_test.c @@ -16,7 +16,7 @@ // Returns: a / b -su_int __udivsi3(su_int a, su_int b); +COMPILER_RT_ABI su_int __udivsi3(su_int a, su_int b); int test__udivsi3(su_int a, su_int b, su_int expected_q) { diff --git a/test/builtins/Unit/udivti3_test.c b/test/builtins/Unit/udivti3_test.c index af5aad9a391d..f24ff03b2424 100644 --- a/test/builtins/Unit/udivti3_test.c +++ b/test/builtins/Unit/udivti3_test.c @@ -18,7 +18,7 @@ // Returns: a / b -tu_int __udivti3(tu_int a, tu_int b); +COMPILER_RT_ABI tu_int __udivti3(tu_int a, tu_int b); int test__udivti3(tu_int a, tu_int b, tu_int expected_q) { diff --git a/test/builtins/Unit/umoddi3_test.c b/test/builtins/Unit/umoddi3_test.c index a8f39b41550d..b46fb4022817 100644 --- a/test/builtins/Unit/umoddi3_test.c +++ b/test/builtins/Unit/umoddi3_test.c @@ -16,7 +16,7 @@ // Returns: a % b -du_int __umoddi3(du_int a, du_int b); +COMPILER_RT_ABI du_int __umoddi3(du_int a, du_int b); int test__umoddi3(du_int a, du_int b, du_int expected_r) { diff --git a/test/builtins/Unit/umodsi3_test.c b/test/builtins/Unit/umodsi3_test.c index 66da695ffd96..3655da1395f7 100644 --- a/test/builtins/Unit/umodsi3_test.c +++ b/test/builtins/Unit/umodsi3_test.c @@ -16,7 +16,7 @@ // Returns: a % b -su_int __umodsi3(su_int a, su_int b); +COMPILER_RT_ABI su_int __umodsi3(su_int a, su_int b); int test__umodsi3(su_int a, su_int b, su_int expected_r) { diff --git a/test/builtins/Unit/umodti3_test.c b/test/builtins/Unit/umodti3_test.c index 93b556ccaa21..21e82a4a21e0 100644 --- a/test/builtins/Unit/umodti3_test.c +++ b/test/builtins/Unit/umodti3_test.c @@ -18,7 +18,7 @@ // Returns: a % b -tu_int __umodti3(tu_int a, tu_int b); +COMPILER_RT_ABI tu_int __umodti3(tu_int a, tu_int b); int test__umodti3(tu_int a, tu_int b, tu_int expected_r) { diff --git a/test/cfi/CMakeLists.txt b/test/cfi/CMakeLists.txt index f519fb089505..21463ac408de 100644 --- a/test/cfi/CMakeLists.txt +++ b/test/cfi/CMakeLists.txt @@ -9,15 +9,32 @@ if(NOT COMPILER_RT_STANDALONE_BUILD) FileCheck clang not + ubsan ) if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR) list(APPEND CFI_TEST_DEPS LLVMgold ) endif() + if(APPLE) + list(APPEND CFI_TEST_DEPS + LTO + ) + endif() + if(WIN32 AND EXISTS ${CMAKE_SOURCE_DIR}/tools/lld) + list(APPEND CFI_TEST_DEPS + lld + ) + endif() endif() add_lit_testsuite(check-cfi "Running the cfi regression tests" ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CFI_TEST_DEPS}) + +add_lit_target(check-cfi-and-supported "Running the cfi regression tests" + ${CMAKE_CURRENT_BINARY_DIR} + PARAMS check_supported=1 + DEPENDS ${CFI_TEST_DEPS}) + set_target_properties(check-cfi PROPERTIES FOLDER "Tests") diff --git a/test/cfi/README.txt b/test/cfi/README.txt new file mode 100644 index 000000000000..6b82f5edc235 --- /dev/null +++ b/test/cfi/README.txt @@ -0,0 +1,8 @@ +The tests in this directory use a common convention for exercising the +functionality associated with bit sets of different sizes. When certain +macros are defined the tests instantiate classes that force the bit sets +to be of certain sizes. + +- B32 forces 32-bit bit sets. +- B64 forces 64-bit bit sets. +- BM forces memory bit sets. diff --git a/test/cfi/anon-namespace.cpp b/test/cfi/anon-namespace.cpp index 0c2c689966f1..f25f3405516e 100644 --- a/test/cfi/anon-namespace.cpp +++ b/test/cfi/anon-namespace.cpp @@ -1,27 +1,32 @@ // RUN: %clangxx_cfi -c -DTU1 -o %t1.o %s // RUN: %clangxx_cfi -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp -// RUN: %clangxx_cfi -o %t %t1.o %t2.o -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t1 %t1.o %t2.o +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx_cfi -c -DTU1 -DB32 -o %t1.o %s // RUN: %clangxx_cfi -c -DTU2 -DB32 -o %t2.o %S/../cfi/anon-namespace.cpp -// RUN: %clangxx_cfi -o %t %t1.o %t2.o -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t2 %t1.o %t2.o +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx_cfi -c -DTU1 -DB64 -o %t1.o %s // RUN: %clangxx_cfi -c -DTU2 -DB64 -o %t2.o %S/../cfi/anon-namespace.cpp -// RUN: %clangxx_cfi -o %t %t1.o %t2.o -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t3 %t1.o %t2.o +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx_cfi -c -DTU1 -DBM -o %t1.o %s // RUN: %clangxx_cfi -c -DTU2 -DBM -o %t2.o %S/../cfi/anon-namespace.cpp -// RUN: %clangxx_cfi -o %t %t1.o %t2.o -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t4 %t1.o %t2.o +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx -c -DTU1 -o %t1.o %s // RUN: %clangxx -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp -// RUN: %clangxx -o %t %t1.o %t2.o -// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s +// RUN: %clangxx -o %t5 %t1.o %t2.o +// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s + +// RUN: %clangxx_cfi_diag -c -DTU1 -o %t1.o %s +// RUN: %clangxx_cfi_diag -c -DTU2 -o %t2.o %S/../cfi/anon-namespace.cpp +// RUN: %clangxx_cfi_diag -o %t6 %t1.o %t2.o +// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s // Tests that the CFI mechanism treats classes in the anonymous namespace in // different translation units as having distinct identities. This is done by @@ -33,6 +38,8 @@ // are different. It currently does so because bitset names have global scope // so we have to mangle the file path into the bitset name. +// REQUIRES: cxxabi + #include <stdio.h> #include "utils.h" @@ -83,10 +90,14 @@ int main() { // NCFI: 1 fprintf(stderr, "1\n"); + // CFI-DIAG: runtime error: control flow integrity check for type '(anonymous namespace)::B' failed during base-to-derived cast + // CFI-DIAG-NEXT: note: vtable is of type '{{.*}}anonymous namespace{{.*}}::B' + // CFI-DIAG: runtime error: control flow integrity check for type '(anonymous namespace)::B' failed during virtual call + // CFI-DIAG-NEXT: note: vtable is of type '{{.*}}anonymous namespace{{.*}}::B' ((B *)a)->f(); // UB here - // CFI-NOT: 2 - // NCFI: 2 + // CFI-NOT: {{^2$}} + // NCFI: {{^2$}} fprintf(stderr, "2\n"); } diff --git a/test/cfi/bad-cast.cpp b/test/cfi/bad-cast.cpp new file mode 100644 index 000000000000..9ba6f6dbacec --- /dev/null +++ b/test/cfi/bad-cast.cpp @@ -0,0 +1,150 @@ +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 a 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t1 b 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t1 c 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t1 d 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t1 e 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t1 f 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %expect_crash %t1 g 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t1 h 2>&1 | FileCheck --check-prefix=PASS %s + +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 a 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t2 b 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t2 c 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t2 d 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t2 e 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t2 f 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %expect_crash %t2 g 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t2 h 2>&1 | FileCheck --check-prefix=PASS %s + +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 a 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t3 b 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t3 c 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t3 d 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t3 e 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t3 f 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %expect_crash %t3 g 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t3 h 2>&1 | FileCheck --check-prefix=PASS %s + +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 a 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t4 b 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t4 c 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t4 d 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t4 e 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t4 f 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %expect_crash %t4 g 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %t4 h 2>&1 | FileCheck --check-prefix=PASS %s + +// RUN: %clangxx_cfi -fsanitize=cfi-cast-strict -o %t5 %s +// RUN: %expect_crash %t5 a 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 b 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 c 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 d 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 e 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 f 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 g 2>&1 | FileCheck --check-prefix=FAIL %s +// RUN: %expect_crash %t5 h 2>&1 | FileCheck --check-prefix=FAIL %s + +// RUN: %clangxx -o %t6 %s +// RUN: %t6 a 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 b 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 c 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 d 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 e 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 f 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 g 2>&1 | FileCheck --check-prefix=PASS %s +// RUN: %t6 h 2>&1 | FileCheck --check-prefix=PASS %s + +// RUN: %clangxx_cfi_diag -o %t7 %s +// RUN: %t7 a 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s +// RUN: %t7 b 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s +// RUN: %t7 c 2>&1 | FileCheck --check-prefix=CFI-DIAG-D %s +// RUN: %t7 g 2>&1 | FileCheck --check-prefix=CFI-DIAG-U %s + +// Tests that the CFI enforcement detects bad casts. + +// REQUIRES: cxxabi + +#include <stdio.h> +#include "utils.h" + +struct A { + virtual void f(); +}; + +void A::f() {} + +struct B : A { + virtual void f(); +}; + +void B::f() {} + +struct C : A { +}; + +int main(int argc, char **argv) { +#ifdef B32 + break_optimization(new Deriver<B, 0>); +#endif + +#ifdef B64 + break_optimization(new Deriver<B, 0>); + break_optimization(new Deriver<B, 1>); +#endif + +#ifdef BM + break_optimization(new Deriver<B, 0>); + break_optimization(new Deriver<B, 1>); + break_optimization(new Deriver<B, 2>); +#endif + + B *b = new B; + break_optimization(b); + + // FAIL: 1 + // PASS: 1 + fprintf(stderr, "1\n"); + + A a; + + // CFI-DIAG-D: runtime error: control flow integrity check for type 'B' failed during base-to-derived cast + // CFI-DIAG-D-NEXT: note: vtable is of type '{{(struct )?}}A' + + // CFI-DIAG-U: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type + // CFI-DIAG-U-NEXT: note: vtable is of type '{{(struct )?}}A' + + switch (argv[1][0]) { + case 'a': + static_cast<B *>(&a); // UB + break; + case 'b': + static_cast<B &>(a); // UB + break; + case 'c': + static_cast<B &&>(a); // UB + break; + case 'd': + static_cast<C *>(&a); // UB, strict only + break; + case 'e': + static_cast<C &>(a); // UB, strict only + break; + case 'f': + static_cast<C &&>(a); // UB, strict only + break; + case 'g': + static_cast<B *>(static_cast<void *>(&a)); // Non-UB bad cast + break; + case 'h': + static_cast<C *>(static_cast<void *>(&a)); // Non-UB bad cast, strict only + break; + } + + // FAIL-NOT: {{^2$}} + // PASS: {{^2$}} + fprintf(stderr, "2\n"); +} diff --git a/test/cfi/lit.cfg b/test/cfi/lit.cfg index d78820daa055..1fa5a1e25137 100644 --- a/test/cfi/lit.cfg +++ b/test/cfi/lit.cfg @@ -1,35 +1,19 @@ import lit.formats import os -import subprocess -import sys config.name = 'cfi' config.suffixes = ['.cpp'] config.test_source_root = os.path.dirname(__file__) -def is_darwin_lto_supported(): - return os.path.exists(os.path.join(config.llvm_shlib_dir, 'libLTO.dylib')) - -def is_linux_lto_supported(): - if not os.path.exists(os.path.join(config.llvm_shlib_dir, 'LLVMgold.so')): - return False - - ld_cmd = subprocess.Popen([config.gold_executable, '--help'], stdout = subprocess.PIPE) - ld_out = ld_cmd.stdout.read().decode() - ld_cmd.wait() - - if not '-plugin' in ld_out: - return False - - return True - clangxx = ' '.join([config.clang] + config.cxx_mode_flags) config.substitutions.append((r"%clangxx ", clangxx + ' ')) - -if sys.platform == 'darwin' and is_darwin_lto_supported(): - config.substitutions.append((r"%clangxx_cfi ", 'env DYLD_LIBRARY_PATH=' + config.llvm_shlib_dir + ' ' + clangxx + ' -fsanitize=cfi ')) -elif sys.platform.startswith('linux') and is_linux_lto_supported(): - config.substitutions.append((r"%clangxx_cfi ", clangxx + ' -fuse-ld=gold -fsanitize=cfi ')) +if config.lto_supported: + clangxx_cfi = ' '.join(config.lto_launch + [clangxx] + config.lto_flags + ['-flto -fsanitize=cfi ']) + config.substitutions.append((r"%clangxx_cfi ", clangxx_cfi)) + config.substitutions.append((r"%clangxx_cfi_diag ", clangxx_cfi + '-fno-sanitize-trap=cfi -fsanitize-recover=cfi ')) else: config.unsupported = True + +if lit_config.params.get('check_supported', None) and config.unsupported: + raise BaseException("Tests unsupported") diff --git a/test/cfi/multiple-inheritance.cpp b/test/cfi/multiple-inheritance.cpp index 523af6f72f2f..e2a9abebb955 100644 --- a/test/cfi/multiple-inheritance.cpp +++ b/test/cfi/multiple-inheritance.cpp @@ -1,26 +1,32 @@ -// RUN: %clangxx_cfi -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %expect_crash %t1 x 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %expect_crash %t2 x 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %expect_crash %t3 x 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: not --crash %t x 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %expect_crash %t4 x 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx -o %t %s -// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s -// RUN: %t x 2>&1 | FileCheck --check-prefix=NCFI %s +// RUN: %clangxx -o %t5 %s +// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s +// RUN: %t5 x 2>&1 | FileCheck --check-prefix=NCFI %s + +// RUN: %clangxx_cfi_diag -o %t6 %s +// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG2 %s +// RUN: %t6 x 2>&1 | FileCheck --check-prefix=CFI-DIAG1 %s // Tests that the CFI mechanism is sensitive to multiple inheritance and only // permits calls via virtual tables for the correct base class. +// REQUIRES: cxxabi + #include <stdio.h> #include "utils.h" @@ -70,13 +76,17 @@ int main(int argc, char **argv) { if (argc > 1) { A *a = c; + // CFI-DIAG1: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type + // CFI-DIAG1-NEXT: note: vtable is of type '{{(struct )?}}C' ((B *)a)->g(); // UB here } else { + // CFI-DIAG2: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type + // CFI-DIAG2-NEXT: note: vtable is of type '{{(struct )?}}C' B *b = c; ((A *)b)->f(); // UB here } - // CFI-NOT: 2 - // NCFI: 2 + // CFI-NOT: {{^2$}} + // NCFI: {{^2$}} fprintf(stderr, "2\n"); } diff --git a/test/cfi/nvcall.cpp b/test/cfi/nvcall.cpp new file mode 100644 index 000000000000..04419bd9d855 --- /dev/null +++ b/test/cfi/nvcall.cpp @@ -0,0 +1,72 @@ +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx -o %t5 %s +// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s + +// RUN: %clangxx_cfi_diag -o %t6 %s +// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s + +// Tests that the CFI mechanism crashes the program when making a non-virtual +// call to an object of the wrong class, by casting a pointer to such an object +// and attempting to make a call through it. + +// REQUIRES: cxxabi + +#include <stdio.h> +#include "utils.h" + +struct A { + virtual void v(); +}; + +void A::v() {} + +struct B { + void f(); + virtual void g(); +}; + +void B::f() {} +void B::g() {} + +int main() { +#ifdef B32 + break_optimization(new Deriver<B, 0>); +#endif + +#ifdef B64 + break_optimization(new Deriver<B, 0>); + break_optimization(new Deriver<B, 1>); +#endif + +#ifdef BM + break_optimization(new Deriver<B, 0>); + break_optimization(new Deriver<B, 1>); + break_optimization(new Deriver<B, 2>); +#endif + + A *a = new A; + break_optimization(a); + + // CFI: 1 + // NCFI: 1 + fprintf(stderr, "1\n"); + + // CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during non-virtual call + // CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A' + ((B *)a)->f(); // UB here + + // CFI-NOT: {{^2$}} + // NCFI: {{^2$}} + fprintf(stderr, "2\n"); +} diff --git a/test/cfi/overwrite.cpp b/test/cfi/overwrite.cpp index d7e58d9277e9..a24e628326f3 100644 --- a/test/cfi/overwrite.cpp +++ b/test/cfi/overwrite.cpp @@ -1,23 +1,28 @@ -// RUN: %clangxx_cfi -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx -o %t %s -// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s +// RUN: %clangxx -o %t5 %s +// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s + +// RUN: %clangxx_cfi_diag -o %t6 %s +// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s // Tests that the CFI mechanism crashes the program when a virtual table is // replaced with a compatible table of function pointers that does not belong to // any class, by manually overwriting the virtual table of an object and // attempting to make a call through it. +// REQUIRES: cxxabi + #include <stdio.h> #include "utils.h" @@ -31,7 +36,7 @@ void foo() { fprintf(stderr, "foo\n"); } -void *fake_vtable[] = { (void *)&foo }; +void *fake_vtable[] = { 0, 0, (void *)&foo }; int main() { #ifdef B32 @@ -50,7 +55,7 @@ int main() { #endif A *a = new A; - *((void **)a) = fake_vtable; // UB here + *((void **)a) = fake_vtable + 2; // UB here break_optimization(a); // CFI: 1 @@ -59,9 +64,11 @@ int main() { // CFI-NOT: foo // NCFI: foo + // CFI-DIAG: runtime error: control flow integrity check for type 'A' failed during virtual call + // CFI-DIAG-NEXT: note: invalid vtable a->f(); - // CFI-NOT: 2 - // NCFI: 2 + // CFI-NOT: {{^2$}} + // NCFI: {{^2$}} fprintf(stderr, "2\n"); } diff --git a/test/cfi/sibling.cpp b/test/cfi/sibling.cpp new file mode 100644 index 000000000000..a865cbc0cf6b --- /dev/null +++ b/test/cfi/sibling.cpp @@ -0,0 +1,67 @@ +// XFAIL: * + +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s + +// RUN: %clangxx -o %t5 %s +// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s + +// Tests that the CFI enforcement distinguishes betwen non-overriding siblings. +// XFAILed as not implemented yet. + +#include <stdio.h> +#include "utils.h" + +struct A { + virtual void f(); +}; + +void A::f() {} + +struct B : A { + virtual void f(); +}; + +void B::f() {} + +struct C : A { +}; + +int main() { +#ifdef B32 + break_optimization(new Deriver<B, 0>); +#endif + +#ifdef B64 + break_optimization(new Deriver<B, 0>); + break_optimization(new Deriver<B, 1>); +#endif + +#ifdef BM + break_optimization(new Deriver<B, 0>); + break_optimization(new Deriver<B, 1>); + break_optimization(new Deriver<B, 2>); +#endif + + B *b = new B; + break_optimization(b); + + // CFI: 1 + // NCFI: 1 + fprintf(stderr, "1\n"); + + ((C *)b)->f(); // UB here + + // CFI-NOT: 2 + // NCFI: 2 + fprintf(stderr, "2\n"); +} diff --git a/test/cfi/simple-fail.cpp b/test/cfi/simple-fail.cpp index cf24f86e0064..c7bebb0deccc 100644 --- a/test/cfi/simple-fail.cpp +++ b/test/cfi/simple-fail.cpp @@ -1,58 +1,63 @@ -// RUN: %clangxx_cfi -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O1 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O1 -o %t5 %s +// RUN: %expect_crash %t5 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O1 -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O1 -DB32 -o %t6 %s +// RUN: %expect_crash %t6 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O1 -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O1 -DB64 -o %t7 %s +// RUN: %expect_crash %t7 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O1 -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O1 -DBM -o %t8 %s +// RUN: %expect_crash %t8 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O2 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O2 -o %t9 %s +// RUN: %expect_crash %t9 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O2 -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O2 -DB32 -o %t10 %s +// RUN: %expect_crash %t10 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O2 -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O2 -DB64 -o %t11 %s +// RUN: %expect_crash %t11 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O2 -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O2 -DBM -o %t12 %s +// RUN: %expect_crash %t12 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O3 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O3 -o %t13 %s +// RUN: %expect_crash %t13 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O3 -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O3 -DB32 -o %t14 %s +// RUN: %expect_crash %t14 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O3 -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O3 -DB64 -o %t15 %s +// RUN: %expect_crash %t15 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -O3 -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -O3 -DBM -o %t16 %s +// RUN: %expect_crash %t16 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx -o %t %s -// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s +// RUN: %clangxx_cfi_diag -o %t17 %s +// RUN: %t17 2>&1 | FileCheck --check-prefix=CFI-DIAG %s + +// RUN: %clangxx -o %t18 %s +// RUN: %t18 2>&1 | FileCheck --check-prefix=NCFI %s // Tests that the CFI mechanism crashes the program when making a virtual call // to an object of the wrong class but with a compatible vtable, by casting a // pointer to such an object and attempting to make a call through it. +// REQUIRES: cxxabi + #include <stdio.h> #include "utils.h" @@ -91,9 +96,13 @@ int main() { // NCFI: 1 fprintf(stderr, "1\n"); + // CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during cast to unrelated type + // CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A' + // CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during virtual call + // CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A' ((B *)a)->f(); // UB here - // CFI-NOT: 2 - // NCFI: 2 + // CFI-NOT: {{^2$}} + // NCFI: {{^2$}} fprintf(stderr, "2\n"); } diff --git a/test/cfi/simple-pass.cpp b/test/cfi/simple-pass.cpp index 50e7d9256084..4d856eb48ce7 100644 --- a/test/cfi/simple-pass.cpp +++ b/test/cfi/simple-pass.cpp @@ -3,95 +3,119 @@ // Tests that the CFI mechanism does not crash the program when making various // kinds of valid calls involving classes with various different linkages and -// types of inheritance. +// types of inheritance, and both virtual and non-virtual member functions. #include "utils.h" struct A { virtual void f(); + void g(); }; void A::f() {} +void A::g() {} struct A2 : A { virtual void f(); + void g(); }; void A2::f() {} +void A2::g() {} struct B { virtual void f() {} + void g() {} }; struct B2 : B { virtual void f() {} + void g() {} }; namespace { struct C { virtual void f(); + void g(); }; void C::f() {} +void C::g() {} struct C2 : C { virtual void f(); + void g(); }; void C2::f() {} +void C2::g() {} struct D { virtual void f() {} + void g() {} }; struct D2 : D { virtual void f() {} + void g() {} }; } struct E { virtual void f() {} + void g() {} }; struct E2 : virtual E { virtual void f() {} + void g() {} }; int main() { A *a = new A; break_optimization(a); a->f(); + a->g(); a = new A2; break_optimization(a); a->f(); + a->g(); B *b = new B; break_optimization(b); b->f(); + b->g(); b = new B2; break_optimization(b); b->f(); + b->g(); C *c = new C; break_optimization(c); c->f(); + c->g(); c = new C2; break_optimization(c); c->f(); + c->g(); D *d = new D; break_optimization(d); d->f(); + d->g(); d = new D2; break_optimization(d); d->f(); + d->g(); E *e = new E; break_optimization(e); e->f(); + e->g(); e = new E2; break_optimization(e); e->f(); + e->g(); } diff --git a/test/cfi/vdtor.cpp b/test/cfi/vdtor.cpp index e21883c380dd..fb484ea73308 100644 --- a/test/cfi/vdtor.cpp +++ b/test/cfi/vdtor.cpp @@ -1,21 +1,26 @@ -// RUN: %clangxx_cfi -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -o %t1 %s +// RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB32 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB32 -o %t2 %s +// RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DB64 -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DB64 -o %t3 %s +// RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx_cfi -DBM -o %t %s -// RUN: not --crash %t 2>&1 | FileCheck --check-prefix=CFI %s +// RUN: %clangxx_cfi -DBM -o %t4 %s +// RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s -// RUN: %clangxx -o %t %s -// RUN: %t 2>&1 | FileCheck --check-prefix=NCFI %s +// RUN: %clangxx -o %t5 %s +// RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s + +// RUN: %clangxx_cfi_diag -o %t6 %s +// RUN: %t6 2>&1 | FileCheck --check-prefix=CFI-DIAG %s // Tests that the CFI enforcement also applies to virtual destructor calls made // via 'delete'. +// REQUIRES: cxxabi + #include <stdio.h> #include "utils.h" @@ -54,9 +59,11 @@ int main() { // NCFI: 1 fprintf(stderr, "1\n"); + // CFI-DIAG: runtime error: control flow integrity check for type 'B' failed during virtual call + // CFI-DIAG-NEXT: note: vtable is of type '{{(struct )?}}A' delete (B *)a; // UB here - // CFI-NOT: 2 - // NCFI: 2 + // CFI-NOT: {{^2$}} + // NCFI: {{^2$}} fprintf(stderr, "2\n"); } diff --git a/test/dfsan/basic.c b/test/dfsan/basic.c index 6582727e5e61..990fd1161069 100644 --- a/test/dfsan/basic.c +++ b/test/dfsan/basic.c @@ -1,5 +1,5 @@ -// RUN: %clang_dfsan -m64 %s -o %t && %run %t -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t +// RUN: %clang_dfsan %s -o %t && %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %s -o %t && %run %t // Tests that labels are propagated through loads and stores. diff --git a/test/dfsan/custom.cc b/test/dfsan/custom.cc index d7bb3e3073fb..057b0608e038 100644 --- a/test/dfsan/custom.cc +++ b/test/dfsan/custom.cc @@ -1,7 +1,7 @@ -// RUN: %clang_dfsan -m64 %s -o %t && DFSAN_OPTIONS="strict_data_dependencies=0" %run %t -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && DFSAN_OPTIONS="strict_data_dependencies=0" %run %t -// RUN: %clang_dfsan -DSTRICT_DATA_DEPENDENCIES -m64 %s -o %t && %run %t -// RUN: %clang_dfsan -DSTRICT_DATA_DEPENDENCIES -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t +// RUN: %clang_dfsan %s -o %t && DFSAN_OPTIONS="strict_data_dependencies=0" %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %s -o %t && DFSAN_OPTIONS="strict_data_dependencies=0" %run %t +// RUN: %clang_dfsan -DSTRICT_DATA_DEPENDENCIES %s -o %t && %run %t +// RUN: %clang_dfsan -DSTRICT_DATA_DEPENDENCIES -mllvm -dfsan-args-abi %s -o %t && %run %t // Tests custom implementations of various glibc functions. @@ -870,6 +870,11 @@ void test_sprintf() { test_sprintf_chunk("z", "%c", 'z'); // %n, %s, %d, %f, and %% already tested + + // Test formatting with width passed as an argument. + r = sprintf(buf, "hi %*d my %*s friend %.*f", 3, 1, 6, "dear", 4, 3.14159265359); + assert(r == 30); + assert(strcmp(buf, "hi 1 my dear friend 3.1416") == 0); } void test_snprintf() { diff --git a/test/dfsan/dump_labels.c b/test/dfsan/dump_labels.c index 67801af1838f..3bbc1e2c0b90 100644 --- a/test/dfsan/dump_labels.c +++ b/test/dfsan/dump_labels.c @@ -1,4 +1,4 @@ -// RUN: %clang_dfsan -m64 %s -o %t +// RUN: %clang_dfsan %s -o %t // RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout %run %t 2>&1 | FileCheck %s // RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t c 2>&1 | FileCheck %s --check-prefix=CHECK-OOL // RUN: DFSAN_OPTIONS=dump_labels_at_exit=/dev/stdout not %run %t u 2>&1 | FileCheck %s --check-prefix=CHECK-OOL diff --git a/test/dfsan/flags.c b/test/dfsan/flags.c index 79069b96f83a..914f54f42b6c 100644 --- a/test/dfsan/flags.c +++ b/test/dfsan/flags.c @@ -1,6 +1,6 @@ -// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && %run %t 2>&1 | FileCheck %s -// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_unimplemented=0 %run %t 2>&1 | count 0 -// RUN: %clang_dfsan -m64 %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_nonzero_labels=1 %run %t 2>&1 | FileCheck --check-prefix=CHECK-NONZERO %s +// RUN: %clang_dfsan %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clang_dfsan %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_unimplemented=0 %run %t 2>&1 | count 0 +// RUN: %clang_dfsan %s -fsanitize-blacklist=%S/Inputs/flags_abilist.txt -mllvm -dfsan-debug-nonzero-labels -o %t && DFSAN_OPTIONS=warn_nonzero_labels=1 %run %t 2>&1 | FileCheck --check-prefix=CHECK-NONZERO %s // Tests that flags work correctly. diff --git a/test/dfsan/fncall.c b/test/dfsan/fncall.c index e780f3145e87..458fba66dbbe 100644 --- a/test/dfsan/fncall.c +++ b/test/dfsan/fncall.c @@ -1,5 +1,5 @@ -// RUN: %clang_dfsan -m64 %s -o %t && %run %t -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t +// RUN: %clang_dfsan %s -o %t && %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %s -o %t && %run %t // Tests that labels are propagated through function calls. diff --git a/test/dfsan/label_count.c b/test/dfsan/label_count.c index a4b608701811..b42ce5824256 100644 --- a/test/dfsan/label_count.c +++ b/test/dfsan/label_count.c @@ -1,11 +1,11 @@ -// RUN: %clang_dfsan -DLIB -m64 -c %s -o %t.lib.o && \ -// RUN: %clang_dfsan -m64 -c %s -o %t.o && \ -// RUN: %clang_dfsan -m64 %t.lib.o %t.o -o %t.bin && \ +// RUN: %clang_dfsan -DLIB -c %s -o %t.lib.o && \ +// RUN: %clang_dfsan -c %s -o %t.o && \ +// RUN: %clang_dfsan %t.lib.o %t.o -o %t.bin && \ // RUN: %run %t.bin -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 -DLIB -c %s -o %t.lib.o && \ -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 -c %s -o %t.o && \ -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %t.o %t.lib.o -o %t.bin && \ +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -DLIB -c %s -o %t.lib.o && \ +// RUN: %clang_dfsan -mllvm -dfsan-args-abi -c %s -o %t.o && \ +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %t.o %t.lib.o -o %t.bin && \ // RUN: %run %t.bin #include <sanitizer/dfsan_interface.h> diff --git a/test/dfsan/lit.cfg b/test/dfsan/lit.cfg index d4adb9a5110a..e4d4e8f57af9 100644 --- a/test/dfsan/lit.cfg +++ b/test/dfsan/lit.cfg @@ -9,7 +9,7 @@ config.name = 'DataFlowSanitizer' config.test_source_root = os.path.dirname(__file__) # Setup default compiler flags used with -fsanitize=dataflow option. -clang_dfsan_cflags = ["-fsanitize=dataflow"] +clang_dfsan_cflags = ["-fsanitize=dataflow", "-m64"] clang_dfsan_cxxflags = config.cxx_mode_flags + clang_dfsan_cflags def build_invocation(compile_flags): diff --git a/test/dfsan/propagate.c b/test/dfsan/propagate.c index 733538cb109a..c30a087d6636 100644 --- a/test/dfsan/propagate.c +++ b/test/dfsan/propagate.c @@ -1,5 +1,5 @@ -// RUN: %clang_dfsan -m64 %s -o %t && %run %t -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t +// RUN: %clang_dfsan %s -o %t && %run %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %s -o %t && %run %t // Tests that labels are propagated through computation and that union labels // are properly created. diff --git a/test/dfsan/vararg.c b/test/dfsan/vararg.c index 2227ba715639..f51e39c71899 100644 --- a/test/dfsan/vararg.c +++ b/test/dfsan/vararg.c @@ -1,7 +1,7 @@ -// RUN: %clang_dfsan -m64 %s -o %t +// RUN: %clang_dfsan %s -o %t // RUN: not %run %t 2>&1 | FileCheck %s // RUN: %run %t foo -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %s -o %t // RUN: not %run %t 2>&1 | FileCheck %s // RUN: %run %t foo diff --git a/test/dfsan/write_callback.c b/test/dfsan/write_callback.c index bb35f3250740..3ba027a0a46a 100644 --- a/test/dfsan/write_callback.c +++ b/test/dfsan/write_callback.c @@ -1,5 +1,5 @@ -// RUN: %clang_dfsan -m64 %s -o %t && %run %t | FileCheck %s -// RUN: %clang_dfsan -mllvm -dfsan-args-abi -m64 %s -o %t && %run %t | FileCheck %s +// RUN: %clang_dfsan %s -o %t && %run %t | FileCheck %s +// RUN: %clang_dfsan -mllvm -dfsan-args-abi %s -o %t && %run %t | FileCheck %s // Tests that the custom implementation of write() does writes with or without // a callback set using dfsan_set_write_callback(). diff --git a/test/lit.common.cfg b/test/lit.common.cfg index 0a551862c32b..02a3cd613029 100644 --- a/test/lit.common.cfg +++ b/test/lit.common.cfg @@ -5,6 +5,7 @@ # It is mostly copied from lit.cfg used by Clang. import os import platform +import subprocess import lit.formats import lit.util @@ -37,7 +38,9 @@ else: config.available_features.add(compiler_id) # Clear some environment variables that might affect Clang. -possibly_dangerous_env_vars = ['COMPILER_PATH', 'RC_DEBUG_OPTIONS', +possibly_dangerous_env_vars = ['ASAN_OPTIONS', 'DFSAN_OPTIONS', 'LSAN_OPTIONS', + 'MSAN_OPTIONS', 'UBSAN_OPTIONS', + 'COMPILER_PATH', 'RC_DEBUG_OPTIONS', 'CINDEXTEST_PREAMBLE_FILE', 'LIBRARY_PATH', 'CPATH', 'C_INCLUDE_PATH', 'CPLUS_INCLUDE_PATH', 'OBJC_INCLUDE_PATH', 'OBJCPLUS_INCLUDE_PATH', @@ -77,6 +80,15 @@ config.substitutions.append( ('%run', config.emulator) ) # Define CHECK-%os to check for OS-dependent output. config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) +if config.host_os == 'Windows': + # FIXME: This isn't quite right. Specifically, it will succeed if the program + # does not crash but exits with a non-zero exit code. We ought to merge + # KillTheDoctor and not --crash to make the latter more useful and remove the + # need for this substitution. + config.substitutions.append( ("%expect_crash ", "not KillTheDoctor ") ) +else: + config.substitutions.append( ("%expect_crash ", "not --crash ") ) + # Add supported compiler_rt architectures to a list of available features. compiler_rt_arch = getattr(config, 'compiler_rt_arch', None) if compiler_rt_arch: @@ -87,4 +99,42 @@ compiler_rt_debug = getattr(config, 'compiler_rt_debug', False) if not compiler_rt_debug: config.available_features.add('compiler-rt-optimized') +sanitizer_can_use_cxxabi = getattr(config, 'sanitizer_can_use_cxxabi', True) +if sanitizer_can_use_cxxabi: + config.available_features.add('cxxabi') + lit.util.usePlatformSdkOnDarwin(config, lit_config) + +def is_darwin_lto_supported(): + return os.path.exists(os.path.join(config.llvm_shlib_dir, 'libLTO.dylib')) + +def is_linux_lto_supported(): + if not os.path.exists(os.path.join(config.llvm_shlib_dir, 'LLVMgold.so')): + return False + + ld_cmd = subprocess.Popen([config.gold_executable, '--help'], stdout = subprocess.PIPE) + ld_out = ld_cmd.stdout.read().decode() + ld_cmd.wait() + + if not '-plugin' in ld_out: + return False + + return True + +def is_windows_lto_supported(): + return os.path.exists(os.path.join(config.llvm_tools_dir, 'lld-link2.exe')) + +if config.host_os == 'Darwin' and is_darwin_lto_supported(): + config.lto_supported = True + config.lto_launch = ["env", "DYLD_LIBRARY_PATH=" + config.llvm_shlib_dir] + config.lto_flags = [] +elif config.host_os == 'Linux' and is_linux_lto_supported(): + config.lto_supported = True + config.lto_launch = [] + config.lto_flags = ["-fuse-ld=gold"] +elif config.host_os == 'Windows' and is_windows_lto_supported(): + config.lto_supported = True + config.lto_launch = [] + config.lto_flags = ["-fuse-ld=lld-link2"] +else: + config.lto_supported = False diff --git a/test/lit.common.configured.in b/test/lit.common.configured.in index 4a5966e44f32..f250e8275a29 100644 --- a/test/lit.common.configured.in +++ b/test/lit.common.configured.in @@ -27,6 +27,7 @@ set_default("python_executable", "@PYTHON_EXECUTABLE@") set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@) set_default("compiler_rt_libdir", "@COMPILER_RT_LIBRARY_OUTPUT_DIR@") set_default("emulator", "@COMPILER_RT_EMULATOR@") +set_default("sanitizer_can_use_cxxabi", @SANITIZER_CAN_USE_CXXABI_PYBOOL@) # LLVM tools dir can be passed in lit parameters, so try to # apply substitution. diff --git a/test/lsan/CMakeLists.txt b/test/lsan/CMakeLists.txt index 7f49b0d3983d..6cca00a90b6b 100644 --- a/test/lsan/CMakeLists.txt +++ b/test/lsan/CMakeLists.txt @@ -10,14 +10,12 @@ configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) -if(NOT APPLE AND NOT ANDROID) - set(LSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) - if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND LSAN_TEST_DEPS lsan asan) - endif() - add_lit_testsuite(check-lsan "Running the LeakSanitizer tests" - ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig - ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig - DEPENDS ${LSAN_TEST_DEPS}) - set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests") +set(LSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND LSAN_TEST_DEPS lsan asan) endif() +add_lit_testsuite(check-lsan "Running the LeakSanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR}/LsanConfig + ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig + DEPENDS ${LSAN_TEST_DEPS}) +set_target_properties(check-lsan PROPERTIES FOLDER "LSan tests") diff --git a/test/lsan/TestCases/recoverable_leak_check.cc b/test/lsan/TestCases/recoverable_leak_check.cc new file mode 100644 index 000000000000..0fe377f65af1 --- /dev/null +++ b/test/lsan/TestCases/recoverable_leak_check.cc @@ -0,0 +1,32 @@ +// Test for on-demand leak checking. +// RUN: LSAN_BASE="use_stacks=0:use_registers=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t foo 2>&1 | FileCheck %s +// RUN: LSAN_OPTIONS=$LSAN_BASE %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sanitizer/lsan_interface.h> + +void *p; + +int main(int argc, char *argv[]) { + p = malloc(23); + + assert(__lsan_do_recoverable_leak_check() == 0); + + fprintf(stderr, "Test alloc: %p.\n", malloc(1337)); +// CHECK: Test alloc: + + assert(__lsan_do_recoverable_leak_check() == 1); +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1337 byte + + // Test that we correctly reset chunk tags. + p = 0; + assert(__lsan_do_recoverable_leak_check() == 1); +// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer: 1360 byte + + _exit(0); +} diff --git a/test/msan/Linux/fopencookie.cc b/test/msan/Linux/fopencookie.cc new file mode 100644 index 000000000000..e5b8f936e0d2 --- /dev/null +++ b/test/msan/Linux/fopencookie.cc @@ -0,0 +1,65 @@ +// Test fopencookie interceptor. +// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins -O0 %s -o %t && %run %t + +#include <assert.h> +#include <pthread.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sanitizer/msan_interface.h> + +constexpr uintptr_t kMagicCookie = 0x12345678; + +static ssize_t cookie_read(void *cookie, char *buf, size_t size) { + assert((uintptr_t)cookie == kMagicCookie); + memset(buf, 0, size); + return 0; +} + +static ssize_t cookie_write(void *cookie, const char *buf, size_t size) { + assert((uintptr_t)cookie == kMagicCookie); + __msan_check_mem_is_initialized(buf, size); + return 0; +} + +static int cookie_seek(void *cookie, off64_t *offset, int whence) { + assert((uintptr_t)cookie == kMagicCookie); + __msan_check_mem_is_initialized(offset, sizeof(*offset)); + return 0; +} + +static int cookie_close(void *cookie) { + assert((uintptr_t)cookie == kMagicCookie); + return 0; +} + +void PoisonStack() { char a[8192]; } + +void TestPoisonStack() { + // Verify that PoisonStack has poisoned the stack - otherwise this test is not + // testing anything. + char a; + assert(__msan_test_shadow(&a - 1000, 1) == 0); +} + +int main() { + void *cookie = (void *)kMagicCookie; + FILE *f = fopencookie(cookie, "rw", + {cookie_read, cookie_write, cookie_seek, cookie_close}); + PoisonStack(); + TestPoisonStack(); + fseek(f, 100, SEEK_SET); + char buf[50]; + fread(buf, 50, 1, f); + fwrite(buf, 50, 1, f); + fclose(f); + + f = fopencookie(cookie, "rw", {nullptr, nullptr, nullptr, nullptr}); + fseek(f, 100, SEEK_SET); + fread(buf, 50, 1, f); + fwrite(buf, 50, 1, f); + fclose(f); +} diff --git a/test/msan/Linux/getresid.cc b/test/msan/Linux/getresid.cc index 385351dfdcde..f3c0914b52f5 100644 --- a/test/msan/Linux/getresid.cc +++ b/test/msan/Linux/getresid.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p 2>&1 #include <assert.h> #include <unistd.h> diff --git a/test/msan/Linux/glob.cc b/test/msan/Linux/glob.cc index 8604e4d76265..1481861a08e0 100644 --- a/test/msan/Linux/glob.cc +++ b/test/msan/Linux/glob.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s #include <assert.h> #include <glob.h> diff --git a/test/msan/Linux/glob_altdirfunc.cc b/test/msan/Linux/glob_altdirfunc.cc index 2c02e735e05e..cb7fe09cbc3d 100644 --- a/test/msan/Linux/glob_altdirfunc.cc +++ b/test/msan/Linux/glob_altdirfunc.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p 2>&1 | FileCheck %s #include <assert.h> #include <glob.h> diff --git a/test/msan/Linux/glob_nomatch.cc b/test/msan/Linux/glob_nomatch.cc index bc35c30d6d07..fa132c813727 100644 --- a/test/msan/Linux/glob_nomatch.cc +++ b/test/msan/Linux/glob_nomatch.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p #include <assert.h> #include <glob.h> diff --git a/test/msan/ioctl_sound.cc b/test/msan/Linux/ioctl_sound.cc index ed896f761181..fb36c52f2e7e 100644 --- a/test/msan/ioctl_sound.cc +++ b/test/msan/Linux/ioctl_sound.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O3 -g %s -o %t && %run %t #include <assert.h> #include <fcntl.h> diff --git a/test/msan/mallinfo.cc b/test/msan/Linux/mallinfo.cc index 3f308683077e..3c3692969852 100644 --- a/test/msan/mallinfo.cc +++ b/test/msan/Linux/mallinfo.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t #include <assert.h> #include <malloc.h> diff --git a/test/msan/obstack.cc b/test/msan/Linux/obstack.cc index 222f43b839dc..f1f53be4c9b2 100644 --- a/test/msan/obstack.cc +++ b/test/msan/Linux/obstack.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s #include <obstack.h> #include <sanitizer/msan_interface.h> diff --git a/test/msan/Linux/sunrpc.cc b/test/msan/Linux/sunrpc.cc index 78645a7dcbf2..c92ad632c095 100644 --- a/test/msan/Linux/sunrpc.cc +++ b/test/msan/Linux/sunrpc.cc @@ -1,14 +1,14 @@ -// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=int -DFN=xdr_int %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DTYPE=int -DFN=xdr_int %s -o %t && \ // RUN: %run %t 2>&1 -// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=int -DFN=xdr_int -DUNINIT=1 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DTYPE=int -DFN=xdr_int -DUNINIT=1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=double -DFN=xdr_double %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DTYPE=double -DFN=xdr_double %s -o %t && \ // RUN: %run %t 2>&1 -// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=double -DFN=xdr_double -DUNINIT=1 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DTYPE=double -DFN=xdr_double -DUNINIT=1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t %s -o %t && \ // RUN: %run %t 2>&1 -// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t -DUNINIT=1 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t -DUNINIT=1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s #include <assert.h> diff --git a/test/msan/Linux/sunrpc_bytes.cc b/test/msan/Linux/sunrpc_bytes.cc index f0c35746f34d..477637af2b63 100644 --- a/test/msan/Linux/sunrpc_bytes.cc +++ b/test/msan/Linux/sunrpc_bytes.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -g -O0 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 %s -o %t && \ // RUN: %run %t 2>&1 -// RUN: %clangxx_msan -m64 -g -O0 -DUNINIT=1 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DUNINIT=1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s #include <assert.h> diff --git a/test/msan/Linux/sunrpc_string.cc b/test/msan/Linux/sunrpc_string.cc index 3f44a96d114c..350222f5cc1e 100644 --- a/test/msan/Linux/sunrpc_string.cc +++ b/test/msan/Linux/sunrpc_string.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -g -O0 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 %s -o %t && \ // RUN: %run %t 2>&1 -// RUN: %clangxx_msan -m64 -g -O0 -DUNINIT=1 %s -o %t && \ +// RUN: %clangxx_msan -g -O0 -DUNINIT=1 %s -o %t && \ // RUN: not %run %t 2>&1 | FileCheck %s #include <assert.h> diff --git a/test/msan/Linux/syscalls.cc b/test/msan/Linux/syscalls.cc index 4dd97e745148..78dba36638a6 100644 --- a/test/msan/Linux/syscalls.cc +++ b/test/msan/Linux/syscalls.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t 2>&1 #include <assert.h> #include <errno.h> diff --git a/test/msan/Linux/tcgetattr.cc b/test/msan/Linux/tcgetattr.cc index e1425b84f550..454b7fd1537e 100644 --- a/test/msan/Linux/tcgetattr.cc +++ b/test/msan/Linux/tcgetattr.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p #include <assert.h> #include <glob.h> diff --git a/test/msan/Linux/xattr.cc b/test/msan/Linux/xattr.cc index 1beba117d574..86cc2cd474d5 100644 --- a/test/msan/Linux/xattr.cc +++ b/test/msan/Linux/xattr.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p 2>&1 #include <argz.h> #include <assert.h> diff --git a/test/msan/backtrace.cc b/test/msan/backtrace.cc index 473e0ae8f88b..9cb883c5cf7d 100644 --- a/test/msan/backtrace.cc +++ b/test/msan/backtrace.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <assert.h> #include <execinfo.h> @@ -17,7 +17,7 @@ void f() { char **s = backtrace_symbols(buf, sz); assert(s > 0); for (int i = 0; i < sz; ++i) - printf("%d\n", strlen(s[i])); + printf("%d\n", (int)strlen(s[i])); } int main(void) { diff --git a/test/msan/c-strdup.c b/test/msan/c-strdup.c index 059300e4205a..b1e02b914fcd 100644 --- a/test/msan/c-strdup.c +++ b/test/msan/c-strdup.c @@ -1,7 +1,7 @@ -// RUN: %clang_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clang_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clang_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clang_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -O3 %s -o %t && %run %t >%t.out 2>&1 // Test that strdup in C programs is intercepted. // GLibC headers translate strdup to __strdup at -O1 and higher. diff --git a/test/msan/chained_origin.cc b/test/msan/chained_origin.cc index 336bbd852cb3..ae72c10b6ac8 100644 --- a/test/msan/chained_origin.cc +++ b/test/msan/chained_origin.cc @@ -1,17 +1,17 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-STACK < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DHEAP=1 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DHEAP=1 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HEAP < %t.out -// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-STACK < %t.out -// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DHEAP=1 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DHEAP=1 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HEAP < %t.out diff --git a/test/msan/chained_origin_empty_stack.cc b/test/msan/chained_origin_empty_stack.cc index 36727e3d7aa7..f1ed66b75e42 100644 --- a/test/msan/chained_origin_empty_stack.cc +++ b/test/msan/chained_origin_empty_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O3 %s -o %t && \ // RUN: MSAN_OPTIONS=store_context_size=1 not %run %t 2>&1 | FileCheck %s // Test that stack trace for the intermediate store is not empty. diff --git a/test/msan/chained_origin_limits.cc b/test/msan/chained_origin_limits.cc index 0cc57f32a6ac..90fd09a86b29 100644 --- a/test/msan/chained_origin_limits.cc +++ b/test/msan/chained_origin_limits.cc @@ -1,7 +1,7 @@ // This test program creates a very large number of unique histories. // Heap origin. -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O3 %s -o %t // RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK7 < %t.out @@ -16,7 +16,7 @@ // RUN: FileCheck %s --check-prefix=CHECK7 < %t.out // Stack origin. -// RUN: %clangxx_msan -DSTACK -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t +// RUN: %clangxx_msan -DSTACK -fsanitize-memory-track-origins=2 -O3 %s -o %t // RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK7 < %t.out @@ -32,7 +32,7 @@ // Heap origin, with calls. -// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -O3 %s -o %t // RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK7 < %t.out @@ -48,7 +48,7 @@ // Stack origin, with calls. -// RUN: %clangxx_msan -DSTACK -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t +// RUN: %clangxx_msan -DSTACK -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -O3 %s -o %t // RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK7 < %t.out diff --git a/test/msan/chained_origin_memcpy.cc b/test/msan/chained_origin_memcpy.cc index f4c2f7fcac87..3fe0b77a0614 100644 --- a/test/msan/chained_origin_memcpy.cc +++ b/test/msan/chained_origin_memcpy.cc @@ -1,17 +1,17 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=0 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=10 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=10 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 < %t.out -// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=0 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 < %t.out -// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=10 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=10 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 < %t.out diff --git a/test/msan/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc index 2841e34a1f1d..43dbdcca76a9 100644 --- a/test/msan/chained_origin_with_signals.cc +++ b/test/msan/chained_origin_with_signals.cc @@ -2,11 +2,11 @@ // This is, in fact, undesired behavior caused by our chained origins // implementation being not async-signal-safe. -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -O3 %s -o %t && \ // RUN: not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out diff --git a/test/msan/check_mem_is_initialized.cc b/test/msan/check_mem_is_initialized.cc index 7d2328810d90..e1d3b117e42f 100644 --- a/test/msan/check_mem_is_initialized.cc +++ b/test/msan/check_mem_is_initialized.cc @@ -1,19 +1,19 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out #include <sanitizer/msan_interface.h> diff --git a/test/msan/coverage-levels.cc b/test/msan/coverage-levels.cc index 7c2e143d3ab8..d71bfecb2ebb 100644 --- a/test/msan/coverage-levels.cc +++ b/test/msan/coverage-levels.cc @@ -1,13 +1,13 @@ // Test various levels of coverage // -// RUN: %clangxx_msan -DINIT_VAR=1 -O1 -fsanitize-coverage=1 %s -o %t +// RUN: %clangxx_msan -DINIT_VAR=1 -O1 -fsanitize-coverage=func %s -o %t // RUN: mkdir -p %T/coverage-levels // RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN -// RUN: %clangxx_msan -O1 -fsanitize-coverage=1 %s -o %t +// RUN: %clangxx_msan -O1 -fsanitize-coverage=func %s -o %t // RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_WARN -// RUN: %clangxx_msan -O1 -fsanitize-coverage=2 %s -o %t +// RUN: %clangxx_msan -O1 -fsanitize-coverage=bb %s -o %t // RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 --check-prefix=CHECK_WARN -// RUN: %clangxx_msan -O1 -fsanitize-coverage=3 %s -o %t +// RUN: %clangxx_msan -O1 -fsanitize-coverage=edge %s -o %t // RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 --check-prefix=CHECK_WARN // volatile int sink; diff --git a/test/msan/cxa_atexit.cc b/test/msan/cxa_atexit.cc index 0aa36ecee011..70384b9c93f8 100644 --- a/test/msan/cxa_atexit.cc +++ b/test/msan/cxa_atexit.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p // PR17377: C++ module destructors get stale argument shadow. diff --git a/test/msan/death-callback.cc b/test/msan/death-callback.cc index 6d0488339998..08cf2911b0f9 100644 --- a/test/msan/death-callback.cc +++ b/test/msan/death-callback.cc @@ -1,10 +1,10 @@ -// RUN: %clangxx_msan -m64 -DERROR %s -o %t && not %run %t 2>&1 | \ +// RUN: %clangxx_msan -DERROR %s -o %t && not %run %t 2>&1 | \ // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB -// RUN: %clangxx_msan -m64 -DERROR -DMSANCB_SET %s -o %t && not %run %t 2>&1 | \ +// RUN: %clangxx_msan -DERROR -DMSANCB_SET %s -o %t && not %run %t 2>&1 | \ // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-CB -// RUN: %clangxx_msan -m64 -DERROR -DMSANCB_SET -DMSANCB_CLEAR %s -o %t && not %run %t 2>&1 | \ +// RUN: %clangxx_msan -DERROR -DMSANCB_SET -DMSANCB_CLEAR %s -o %t && not %run %t 2>&1 | \ // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB -// RUN: %clangxx_msan -m64 -DMSANCB_SET %s -o %t && %run %t 2>&1 | \ +// RUN: %clangxx_msan -DMSANCB_SET %s -o %t && %run %t 2>&1 | \ // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB #include <sanitizer/msan_interface.h> diff --git a/test/msan/dlerror.cc b/test/msan/dlerror.cc index 2c726d36041e..d5510b65c4a5 100644 --- a/test/msan/dlerror.cc +++ b/test/msan/dlerror.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <assert.h> #include <dlfcn.h> diff --git a/test/msan/dso-origin.cc b/test/msan/dso-origin.cc index ba008c00718d..7f62449842ce 100644 --- a/test/msan/dso-origin.cc +++ b/test/msan/dso-origin.cc @@ -1,7 +1,7 @@ // Build a library with origin tracking and an executable w/o origin tracking. // Test that origin tracking is enabled at runtime. -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -DBUILD_SO -fPIC -shared -o %t-so.so -// RUN: %clangxx_msan -m64 -O0 %s %t-so.so -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_msan -O0 %s %t-so.so -o %t && not %run %t 2>&1 | FileCheck %s #ifdef BUILD_SO diff --git a/test/msan/dtls_test.c b/test/msan/dtls_test.c index cb88ede2f0a6..4036f71a7f8e 100644 --- a/test/msan/dtls_test.c +++ b/test/msan/dtls_test.c @@ -1,5 +1,5 @@ -/* RUN: %clang_msan -g -m64 %s -o %t - RUN: %clang_msan -g -m64 %s -DBUILD_SO -fPIC -o %t-so.so -shared +/* RUN: %clang_msan -g %s -o %t + RUN: %clang_msan -g %s -DBUILD_SO -fPIC -o %t-so.so -shared RUN: %run %t 2>&1 Regression test for a bug in msan/glibc integration, diff --git a/test/msan/errno.cc b/test/msan/errno.cc index 8af8eb5ee6f9..6ed0fd4108e6 100644 --- a/test/msan/errno.cc +++ b/test/msan/errno.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <assert.h> #include <errno.h> diff --git a/test/msan/fork.cc b/test/msan/fork.cc index 10de8a9379d8..78a62d549ec3 100644 --- a/test/msan/fork.cc +++ b/test/msan/fork.cc @@ -2,7 +2,7 @@ // Run a number of threads that create new chained origins, then fork // and verify that origin reads do not deadlock in the child process. -// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -m64 -O3 %s -o %t +// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -O3 %s -o %t // RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s // Fun fact: if test output is redirected to a file (as opposed to diff --git a/test/msan/ftime.cc b/test/msan/ftime.cc index 2d0935d18037..7a5a2fbf7dce 100644 --- a/test/msan/ftime.cc +++ b/test/msan/ftime.cc @@ -1,4 +1,7 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t + +// ftime() is deprecated on FreeBSD. +// XFAIL: freebsd #include <assert.h> #include <sys/timeb.h> diff --git a/test/msan/getaddrinfo-positive.cc b/test/msan/getaddrinfo-positive.cc index 7658cd504dba..45c1b604d1e3 100644 --- a/test/msan/getaddrinfo-positive.cc +++ b/test/msan/getaddrinfo-positive.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out #include <sys/types.h> diff --git a/test/msan/getaddrinfo.cc b/test/msan/getaddrinfo.cc index abab8bd78f75..c9dcf3ecbae5 100644 --- a/test/msan/getaddrinfo.cc +++ b/test/msan/getaddrinfo.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <sys/types.h> #include <sys/socket.h> diff --git a/test/msan/getc_unlocked.c b/test/msan/getc_unlocked.c index 7df958ad657a..8468fed5ccfa 100644 --- a/test/msan/getc_unlocked.c +++ b/test/msan/getc_unlocked.c @@ -1,12 +1,12 @@ -// RUN: %clangxx_msan -DGETC -m64 -O0 -g -xc++ %s -o %t && %run %t -// RUN: %clangxx_msan -DGETC -m64 -O3 -g -xc++ %s -o %t && %run %t -// RUN: %clang_msan -DGETC -m64 -O0 -g %s -o %t && %run %t -// RUN: %clang_msan -DGETC -m64 -O3 -g %s -o %t && %run %t +// RUN: %clangxx_msan -DGETC -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx_msan -DGETC -O3 -g -xc++ %s -o %t && %run %t +// RUN: %clang_msan -DGETC -O0 -g %s -o %t && %run %t +// RUN: %clang_msan -DGETC -O3 -g %s -o %t && %run %t -// RUN: %clangxx_msan -DGETCHAR -m64 -O0 -g -xc++ %s -o %t && %run %t -// RUN: %clangxx_msan -DGETCHAR -m64 -O3 -g -xc++ %s -o %t && %run %t -// RUN: %clang_msan -DGETCHAR -m64 -O0 -g %s -o %t && %run %t -// RUN: %clang_msan -DGETCHAR -m64 -O3 -g %s -o %t && %run %t +// RUN: %clangxx_msan -DGETCHAR -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx_msan -DGETCHAR -O3 -g -xc++ %s -o %t && %run %t +// RUN: %clang_msan -DGETCHAR -O0 -g %s -o %t && %run %t +// RUN: %clang_msan -DGETCHAR -O3 -g %s -o %t && %run %t #include <assert.h> #include <stdio.h> diff --git a/test/msan/getline.cc b/test/msan/getline.cc index 51e105e0be5c..ee12d4ddc529 100644 --- a/test/msan/getline.cc +++ b/test/msan/getline.cc @@ -7,6 +7,10 @@ // RUN: %clang_msan -O0 -xc -D_GNU_SOURCE=1 %s -o %t && %run %t %t-testdata // RUN: %clang_msan -O2 -xc -D_GNU_SOURCE=1 %s -o %t && %run %t %t-testdata +#if defined(__FreeBSD__) +#define _WITH_GETLINE // To declare getline(). +#endif + #include <assert.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/msan/heap-origin.cc b/test/msan/heap-origin.cc index 0920001beed7..4f170f0d7377 100644 --- a/test/msan/heap-origin.cc +++ b/test/msan/heap-origin.cc @@ -1,19 +1,19 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out #include <stdlib.h> diff --git a/test/msan/iconv.cc b/test/msan/iconv.cc index ea6958b79b96..c2da938169b3 100644 --- a/test/msan/iconv.cc +++ b/test/msan/iconv.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s #include <assert.h> #include <iconv.h> @@ -15,7 +15,12 @@ int main(void) { char inbuf_[100]; strcpy(inbuf_, "sample text"); char outbuf_[100]; +#if defined(__FreeBSD__) + // FreeBSD's iconv() expects the 2nd argument be of type 'const char**'. + const char *inbuf = inbuf_; +#else char *inbuf = inbuf_; +#endif char *outbuf = outbuf_; size_t inbytesleft = strlen(inbuf_); size_t outbytesleft = sizeof(outbuf_); diff --git a/test/msan/if_indextoname.cc b/test/msan/if_indextoname.cc index b74aec16c98a..7b1b989e57d5 100644 --- a/test/msan/if_indextoname.cc +++ b/test/msan/if_indextoname.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t 2>&1 -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t 2>&1 #include <assert.h> #include <errno.h> @@ -18,6 +18,6 @@ int main(int argc, char *argv[]) { printf("No network interfaces found.\n"); return 0; } - assert(strlen(ifname) + 1 == __msan_test_shadow(ifname, sizeof(ifname))); + assert(strlen(ifname) + 1 <= __msan_test_shadow(ifname, sizeof(ifname))); return 0; } diff --git a/test/msan/ifaddrs.cc b/test/msan/ifaddrs.cc index 6a0db3a5b71f..6f5eb935ab52 100644 --- a/test/msan/ifaddrs.cc +++ b/test/msan/ifaddrs.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p 2>&1 #include <assert.h> #include <errno.h> @@ -10,6 +10,10 @@ #include <vector> +#if defined(__FreeBSD__) +#include <sys/socket.h> // To define 'struct sockaddr'. +#endif + #include <sanitizer/msan_interface.h> #define CHECK_AND_PUSH(addr, size) \ diff --git a/test/msan/initgroups.cc b/test/msan/initgroups.cc index 94f6cd8252f3..974b1cbaa666 100644 --- a/test/msan/initgroups.cc +++ b/test/msan/initgroups.cc @@ -1,7 +1,8 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <sys/types.h> #include <grp.h> +#include <unistd.h> // FreeBSD declares initgroups() here. int main(void) { initgroups("root", 0); diff --git a/test/msan/insertvalue_origin.cc b/test/msan/insertvalue_origin.cc index 545debffaad5..a0c70023f2f6 100644 --- a/test/msan/insertvalue_origin.cc +++ b/test/msan/insertvalue_origin.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out // Test origin propagation through insertvalue IR instruction. diff --git a/test/msan/ioctl.cc b/test/msan/ioctl.cc index caa5c274f5eb..e21ef636c604 100644 --- a/test/msan/ioctl.cc +++ b/test/msan/ioctl.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O3 -g %s -o %t && %run %t #include <assert.h> #include <stdlib.h> diff --git a/test/msan/ioctl_custom.cc b/test/msan/ioctl_custom.cc index 7c7fde4bd5d0..6df22d75e95e 100644 --- a/test/msan/ioctl_custom.cc +++ b/test/msan/ioctl_custom.cc @@ -1,8 +1,8 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O3 -g %s -o %t && %run %t -// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_msan -DPOSITIVE -m64 -O3 -g %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -DPOSITIVE -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -DPOSITIVE -O3 -g %s -o %t && not %run %t 2>&1 | FileCheck %s #include <assert.h> #include <stdlib.h> diff --git a/test/msan/keep-going-dso.cc b/test/msan/keep-going-dso.cc index 7975306c557f..f32a513ea9e7 100644 --- a/test/msan/keep-going-dso.cc +++ b/test/msan/keep-going-dso.cc @@ -1,15 +1,15 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out // Test how -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going affect reports diff --git a/test/msan/keep-going.cc b/test/msan/keep-going.cc index 6426574a9e50..57729756357d 100644 --- a/test/msan/keep-going.cc +++ b/test/msan/keep-going.cc @@ -1,19 +1,19 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=1 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=1 not %run %t >%t.out 2>&1 // FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=0 not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=0 not %run %t >%t.out 2>&1 // FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out // Test behaviour of -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going. diff --git a/test/msan/lit.cfg b/test/msan/lit.cfg index f425e25957ac..011ccd2fae74 100644 --- a/test/msan/lit.cfg +++ b/test/msan/lit.cfg @@ -14,6 +14,9 @@ clang_msan_cflags = ["-fsanitize=memory", "-fno-omit-frame-pointer", "-fno-optimize-sibling-calls", "-m64"] + config.debug_info_flags +# Some Msan tests leverage backtrace() which requires libexecinfo on FreeBSD. +if config.host_os == 'FreeBSD': + clang_msan_cflags += ["-lexecinfo"] clang_msan_cxxflags = config.cxx_mode_flags + clang_msan_cflags def build_invocation(compile_flags): diff --git a/test/msan/mktime.cc b/test/msan/mktime.cc index c419057c3907..997fb0ddbbab 100644 --- a/test/msan/mktime.cc +++ b/test/msan/mktime.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s #include <assert.h> #include <time.h> diff --git a/test/msan/mmap.cc b/test/msan/mmap.cc new file mode 100644 index 000000000000..c09fcb76a827 --- /dev/null +++ b/test/msan/mmap.cc @@ -0,0 +1,45 @@ +// Test that mmap (without MAP_FIXED) always returns valid application addresses. +// RUN: %clangxx_msan -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -fsanitize-memory-track-origins %s -o %t && %run %t + +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <sys/mman.h> +#include <stdio.h> + +bool AddrIsApp(void *p) { + uintptr_t addr = (uintptr_t)p; +#if defined(__FreeBSD__) && defined(__x86_64__) + return addr < 0x010000000000ULL || addr >= 0x600000000000ULL; +#elif defined(__x86_64__) + return addr >= 0x600000000000ULL; +#elif defined(__mips64) + return addr >= 0x00e000000000ULL; +#elif defined(__powerpc64__) + return addr < 0x000100000000ULL || addr >= 0x300000000000ULL; +#endif +} + +int main() { + // Large enough to quickly exhaust the entire address space. +#if defined(__mips64) + const size_t kMapSize = 0x100000000ULL; +#else + const size_t kMapSize = 0x1000000000ULL; +#endif + int success_count = 0; + while (true) { + void *p = mmap(0, kMapSize, PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + printf("%p\n", p); + if (p == MAP_FAILED) { + assert(errno == ENOMEM); + break; + } + assert(AddrIsApp(p)); + success_count++; + } + printf("successful mappings: %d\n", success_count); + assert(success_count > 5); +} diff --git a/test/msan/mmap_below_shadow.cc b/test/msan/mmap_below_shadow.cc index 0b982d58930d..563d8774f525 100644 --- a/test/msan/mmap_below_shadow.cc +++ b/test/msan/mmap_below_shadow.cc @@ -3,10 +3,10 @@ // Without MAP_FIXED, we ignore the address hint and map somewhere in // application range. -// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -DFIXED=0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -DFIXED=1 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -DFIXED=0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -DFIXED=1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t #include <assert.h> #include <errno.h> @@ -15,12 +15,18 @@ int main(void) { // Hint address just below shadow. -#if defined(__x86_64__) +#if defined(__FreeBSD__) && defined(__x86_64__) + uintptr_t hint = 0x0f0000000000ULL; + const uintptr_t app_start = 0x000000000000ULL; +#elif defined(__x86_64__) uintptr_t hint = 0x4f0000000000ULL; const uintptr_t app_start = 0x600000000000ULL; #elif defined (__mips64) uintptr_t hint = 0x4f00000000ULL; const uintptr_t app_start = 0x6000000000ULL; +#elif defined (__powerpc64__) + uintptr_t hint = 0x2f0000000000ULL; + const uintptr_t app_start = 0x300000000000ULL; #endif uintptr_t p = (uintptr_t)mmap( (void *)hint, 4096, PROT_WRITE, diff --git a/test/msan/msan_check_mem_is_initialized.cc b/test/msan/msan_check_mem_is_initialized.cc index 33558cd2feb2..599cf2d7dd46 100644 --- a/test/msan/msan_check_mem_is_initialized.cc +++ b/test/msan/msan_check_mem_is_initialized.cc @@ -1,9 +1,9 @@ -// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t +// RUN: %clangxx_msan -O0 -g -DPOSITIVE %s -o %t // RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK // RUN: MSAN_OPTIONS=verbosity=1 not %run %t 2>&1 | \ // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VERBOSE -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t #include <sanitizer/msan_interface.h> diff --git a/test/msan/msan_dump_shadow.cc b/test/msan/msan_dump_shadow.cc index 08371e306f32..543fa7013223 100644 --- a/test/msan/msan_dump_shadow.cc +++ b/test/msan/msan_dump_shadow.cc @@ -1,10 +1,10 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out #include <sanitizer/msan_interface.h> diff --git a/test/msan/msan_print_shadow.cc b/test/msan/msan_print_shadow.cc index f4894596a0e2..9668be2c240f 100644 --- a/test/msan/msan_print_shadow.cc +++ b/test/msan/msan_print_shadow.cc @@ -1,10 +1,10 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NO-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS --check-prefix=CHECK-ORIGINS-2 < %t.out #include <sanitizer/msan_interface.h> diff --git a/test/msan/msan_print_shadow2.cc b/test/msan/msan_print_shadow2.cc index 343ee9e5c3de..5095081c9655 100644 --- a/test/msan/msan_print_shadow2.cc +++ b/test/msan/msan_print_shadow2.cc @@ -1,10 +1,10 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NO-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out #include <sanitizer/msan_interface.h> diff --git a/test/msan/msan_print_shadow3.cc b/test/msan/msan_print_shadow3.cc index c605ef18886d..b29f3222dc33 100644 --- a/test/msan/msan_print_shadow3.cc +++ b/test/msan/msan_print_shadow3.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out #include <stdint.h> diff --git a/test/msan/mul_by_const.cc b/test/msan/mul_by_const.cc index a975bb92167e..8240fdbedb5b 100644 --- a/test/msan/mul_by_const.cc +++ b/test/msan/mul_by_const.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t +// RUN: %clangxx_msan -O2 %s -o %t && %run %t #include <sanitizer/msan_interface.h> diff --git a/test/msan/no_sanitize_memory.cc b/test/msan/no_sanitize_memory.cc index c5643816c281..5c53ee4f3482 100644 --- a/test/msan/no_sanitize_memory.cc +++ b/test/msan/no_sanitize_memory.cc @@ -1,12 +1,12 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O0 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O1 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O2 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 // Test that (no_sanitize_memory) functions // * don't check shadow values (-DCHECK_IN_F) diff --git a/test/msan/no_sanitize_memory_prop.cc b/test/msan/no_sanitize_memory_prop.cc index 4275ebbf78e1..bfd4194dd266 100644 --- a/test/msan/no_sanitize_memory_prop.cc +++ b/test/msan/no_sanitize_memory_prop.cc @@ -1,7 +1,7 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && %run %t >%t.out 2>&1 // Test that (no_sanitize_memory) functions DO NOT propagate shadow. diff --git a/test/msan/origin-store-long.cc b/test/msan/origin-store-long.cc index a7c2b7a7d578..5c9fe04b7203 100644 --- a/test/msan/origin-store-long.cc +++ b/test/msan/origin-store-long.cc @@ -1,7 +1,7 @@ // Check that 8-byte store updates origin for the full store range. -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out #include <sanitizer/msan_interface.h> diff --git a/test/msan/param_tls_limit.cc b/test/msan/param_tls_limit.cc index 869afc935773..982ae1ebdc2e 100644 --- a/test/msan/param_tls_limit.cc +++ b/test/msan/param_tls_limit.cc @@ -1,9 +1,9 @@ // ParamTLS has limited size. Everything that does not fit is considered fully // initialized. -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && %run %t -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O0 %s -o %t && %run %t #include <sanitizer/msan_interface.h> #include <assert.h> diff --git a/test/msan/print_stats.cc b/test/msan/print_stats.cc index 74943835b367..39af504179d6 100644 --- a/test/msan/print_stats.cc +++ b/test/msan/print_stats.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g %s -o %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -g %s -o %t // RUN: %run %t 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s // RUN: MSAN_OPTIONS=print_stats=1 %run %t 2>&1 | \ @@ -6,13 +6,13 @@ // RUN: MSAN_OPTIONS=print_stats=1,atexit=1 %run %t 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS %s -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g -DPOSITIVE=1 %s -o %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -g -DPOSITIVE=1 %s -o %t // RUN: not %run %t 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s // RUN: MSAN_OPTIONS=print_stats=1 not %run %t 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS %s -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g -DPOSITIVE=1 -mllvm -msan-keep-going=1 %s -o %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -g -DPOSITIVE=1 -mllvm -msan-keep-going=1 %s -o %t // RUN: not %run %t 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS --check-prefix=CHECK-KEEPGOING %s // RUN: MSAN_OPTIONS=print_stats=1 not %run %t 2>&1 | \ diff --git a/test/msan/pthread_getattr_np_deadlock.cc b/test/msan/pthread_getattr_np_deadlock.cc index 07f19cb61b6c..0f52280856cc 100644 --- a/test/msan/pthread_getattr_np_deadlock.cc +++ b/test/msan/pthread_getattr_np_deadlock.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -fsanitize-memory-track-origins -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && %run %t // Regression test for a deadlock in pthread_getattr_np diff --git a/test/msan/rand_r.cc b/test/msan/rand_r.cc index d6bdb1deaa68..f40cf9f9632a 100644 --- a/test/msan/rand_r.cc +++ b/test/msan/rand_r.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s #include <assert.h> #include <stdio.h> diff --git a/test/msan/readdir64.cc b/test/msan/readdir64.cc index 4f00d1838794..e77ada6ac8f2 100644 --- a/test/msan/readdir64.cc +++ b/test/msan/readdir64.cc @@ -1,12 +1,12 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O1 %s -o %t && %run %t +// RUN: %clangxx_msan -O2 %s -o %t && %run %t +// RUN: %clangxx_msan -O3 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t -// RUN: %clangxx_msan -m64 -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t // Test that readdir64 is intercepted as well as readdir. diff --git a/test/msan/realloc-large-origin.cc b/test/msan/realloc-large-origin.cc index de39394cbb79..ce25ad8c47fd 100644 --- a/test/msan/realloc-large-origin.cc +++ b/test/msan/realloc-large-origin.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out // This is a regression test: there used to be broken "stored to memory at" diff --git a/test/msan/realloc-origin.cc b/test/msan/realloc-origin.cc index 6707cc9049a4..e6893713ea46 100644 --- a/test/msan/realloc-origin.cc +++ b/test/msan/realloc-origin.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out // This test relies on realloc from 100 to 101 being done in-place. diff --git a/test/msan/report-demangling.cc b/test/msan/report-demangling.cc index e6d5c27ec85d..fbb6554470f2 100644 --- a/test/msan/report-demangling.cc +++ b/test/msan/report-demangling.cc @@ -1,7 +1,7 @@ // Test that function name is mangled in the "created by an allocation" line, // and demangled in the single-frame "stack trace" that follows. -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out __attribute__((noinline)) @@ -15,5 +15,5 @@ int main(int argc, char **argv) { return f(); // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function '_Z1fv' - // CHECK: #0 {{.*}} in f() {{.*}}report-demangling.cc:[[@LINE-10]] + // CHECK: #0 {{.*}} in f{{.*}} {{.*}}report-demangling.cc:[[@LINE-10]] } diff --git a/test/msan/scandir.cc b/test/msan/scandir.cc index 571ba4f603d2..8adfe006602a 100644 --- a/test/msan/scandir.cc +++ b/test/msan/scandir.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p #include <assert.h> #include <glob.h> diff --git a/test/msan/scandir_null.cc b/test/msan/scandir_null.cc index e7663dc43a74..94ac31a4143b 100644 --- a/test/msan/scandir_null.cc +++ b/test/msan/scandir_null.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p +// RUN: %clangxx_msan -O3 %s -o %t && %run %t %p #include <assert.h> #include <glob.h> diff --git a/test/msan/select.cc b/test/msan/select.cc index 89de75ebaaef..7b8c34785c23 100644 --- a/test/msan/select.cc +++ b/test/msan/select.cc @@ -1,10 +1,10 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out #include <stdlib.h> diff --git a/test/msan/setlocale.cc b/test/msan/setlocale.cc index b7007f78da7c..796f4454e6aa 100644 --- a/test/msan/setlocale.cc +++ b/test/msan/setlocale.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <assert.h> #include <locale.h> diff --git a/test/msan/stack-origin.cc b/test/msan/stack-origin.cc index c39c3a8cf6d0..e69c072242c5 100644 --- a/test/msan/stack-origin.cc +++ b/test/msan/stack-origin.cc @@ -1,19 +1,19 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out #include <stdlib.h> diff --git a/test/msan/stack-origin2.cc b/test/msan/stack-origin2.cc index 0ba57607dadb..6c7428c2dec8 100644 --- a/test/msan/stack-origin2.cc +++ b/test/msan/stack-origin2.cc @@ -1,21 +1,21 @@ // Test that on the second entry to a function the origins are still right. -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out #include <stdlib.h> diff --git a/test/msan/strlen_of_shadow.cc b/test/msan/strlen_of_shadow.cc index 8f1b4e1fc6bb..763b3a1c73c0 100644 --- a/test/msan/strlen_of_shadow.cc +++ b/test/msan/strlen_of_shadow.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t // Check that strlen() and similar intercepted functions can be called on shadow // memory. @@ -13,6 +13,10 @@ const char *mem_to_shadow(const char *p) { return (char *)((uintptr_t)p & ~0x400000000000ULL); #elif defined (__mips64) return (char *)((uintptr_t)p & ~0x4000000000ULL); +#elif defined(__powerpc64__) +#define LINEARIZE_MEM(mem) \ + (((uintptr_t)(mem) & ~0x200000000000ULL) ^ 0x100000000000ULL) + return (char *)(LINEARIZE_MEM(p) + 0x080000000000ULL); #endif } diff --git a/test/msan/strxfrm.cc b/test/msan/strxfrm.cc index b930a6af69c4..9a30d03c33ae 100644 --- a/test/msan/strxfrm.cc +++ b/test/msan/strxfrm.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t #include <assert.h> #include <sanitizer/msan_interface.h> diff --git a/test/msan/sync_lock_set_and_test.cc b/test/msan/sync_lock_set_and_test.cc index b6d344a61340..11fd14fae0f1 100644 --- a/test/msan/sync_lock_set_and_test.cc +++ b/test/msan/sync_lock_set_and_test.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t int main(void) { int i; diff --git a/test/msan/textdomain.cc b/test/msan/textdomain.cc index 47e991e8c16b..760debd68c33 100644 --- a/test/msan/textdomain.cc +++ b/test/msan/textdomain.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t #include <libintl.h> #include <stdio.h> diff --git a/test/msan/times.cc b/test/msan/times.cc index a042f548dc0c..7c81884118bc 100644 --- a/test/msan/times.cc +++ b/test/msan/times.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t #include <assert.h> #include <stdlib.h> diff --git a/test/msan/tls_reuse.cc b/test/msan/tls_reuse.cc index e024a5a8d13f..78a328fa3ce0 100644 --- a/test/msan/tls_reuse.cc +++ b/test/msan/tls_reuse.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t // Check that when TLS block is reused between threads, its shadow is cleaned. diff --git a/test/msan/tzset.cc b/test/msan/tzset.cc index ed61d7bcc449..05915e047e15 100644 --- a/test/msan/tzset.cc +++ b/test/msan/tzset.cc @@ -1,4 +1,4 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -O0 %s -o %t && %run %t #include <stdlib.h> #include <string.h> diff --git a/test/msan/unaligned_read_origin.cc b/test/msan/unaligned_read_origin.cc index e5618efbde24..b04800a139f2 100644 --- a/test/msan/unaligned_read_origin.cc +++ b/test/msan/unaligned_read_origin.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s < %t.out #include <sanitizer/msan_interface.h> diff --git a/test/msan/unpoison_string.cc b/test/msan/unpoison_string.cc index ac947b9cf6fd..84d952833539 100644 --- a/test/msan/unpoison_string.cc +++ b/test/msan/unpoison_string.cc @@ -1,6 +1,6 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t // RUN: %run %t -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t // RUN: %run %t #include <assert.h> diff --git a/test/msan/use-after-free.cc b/test/msan/use-after-free.cc index 869bad98e455..c505124479b0 100644 --- a/test/msan/use-after-free.cc +++ b/test/msan/use-after-free.cc @@ -1,19 +1,19 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O3 %s -o %t && not %run %t >%t.out 2>&1 // RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out #include <stdlib.h> diff --git a/test/msan/vector_cvt.cc b/test/msan/vector_cvt.cc index bd9b6a8b8000..633a8b15c444 100644 --- a/test/msan/vector_cvt.cc +++ b/test/msan/vector_cvt.cc @@ -1,5 +1,6 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t -// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -DPOSITIVE -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// REQUIRES: x86_64-supported-target #include <emmintrin.h> diff --git a/test/msan/vector_select.cc b/test/msan/vector_select.cc index afeb1ad50c8b..0cf116497801 100644 --- a/test/msan/vector_select.cc +++ b/test/msan/vector_select.cc @@ -1,5 +1,5 @@ -// RUN: %clangxx_msan -m64 -O0 %s -c -o %t -// RUN: %clangxx_msan -m64 -O3 %s -c -o %t +// RUN: %clangxx_msan -O0 %s -c -o %t +// RUN: %clangxx_msan -O3 %s -c -o %t // Regression test for MemorySanitizer instrumentation of a select instruction // with vector arguments. @@ -11,7 +11,7 @@ __m128d select(bool b, __m128d c, __m128d d) { return b ? c : d; } -#elif defined (__mips64) +#elif defined (__mips64) || defined (__powerpc64__) typedef double __w64d __attribute__ ((vector_size(16))); __w64d select(bool b, __w64d c, __w64d d) diff --git a/test/profile/Inputs/gcc-flag-compatibility.c b/test/profile/Inputs/gcc-flag-compatibility.c new file mode 100644 index 000000000000..1c07bb1d0f30 --- /dev/null +++ b/test/profile/Inputs/gcc-flag-compatibility.c @@ -0,0 +1,8 @@ +int X = 0; + +int main() { + int i; + for (i = 0; i < 100; i++) + X += i; + return 0; +} diff --git a/test/profile/Inputs/instrprof-dynamic-a.cpp b/test/profile/Inputs/instrprof-dynamic-a.cpp index 67de263c4d88..2ec484a5b381 100644 --- a/test/profile/Inputs/instrprof-dynamic-a.cpp +++ b/test/profile/Inputs/instrprof-dynamic-a.cpp @@ -1,7 +1,7 @@ #include "instrprof-dynamic-header.h" void a() { if (true) { - bar<void>(); - bar<char>(); + bar<void>(1); + bar<char>(1); } } diff --git a/test/profile/Inputs/instrprof-dynamic-b.cpp b/test/profile/Inputs/instrprof-dynamic-b.cpp index c8fb75ef52ed..5c2d9bae8dba 100644 --- a/test/profile/Inputs/instrprof-dynamic-b.cpp +++ b/test/profile/Inputs/instrprof-dynamic-b.cpp @@ -1,7 +1,7 @@ #include "instrprof-dynamic-header.h" void b() { if (true) { - bar<void>(); - bar<int>(); + bar<void>(1); + bar<int>(1); } } diff --git a/test/profile/Inputs/instrprof-dynamic-header.h b/test/profile/Inputs/instrprof-dynamic-header.h index 1dc2e37ef82c..7a57b13c67f9 100644 --- a/test/profile/Inputs/instrprof-dynamic-header.h +++ b/test/profile/Inputs/instrprof-dynamic-header.h @@ -1,5 +1,7 @@ -template <class T> void bar() { - if (true) {} +template <class T> void bar(int X) { + if (X) { + X *= 4; + } } void a(); void b(); diff --git a/test/profile/Inputs/instrprof-dynamic-main.cpp b/test/profile/Inputs/instrprof-dynamic-main.cpp index 0dd60213926e..2cf37c8b6d5e 100644 --- a/test/profile/Inputs/instrprof-dynamic-main.cpp +++ b/test/profile/Inputs/instrprof-dynamic-main.cpp @@ -2,7 +2,7 @@ void foo(int K) { if (K) {} } int main(int argc, char *argv[]) { foo(5); - bar<void>(); + bar<void>(1); a(); b(); return 0; diff --git a/test/profile/gcc-flag-compatibility.test b/test/profile/gcc-flag-compatibility.test new file mode 100644 index 000000000000..8e8b55dafe23 --- /dev/null +++ b/test/profile/gcc-flag-compatibility.test @@ -0,0 +1,17 @@ +RUN: mkdir -p %t.d +RUN: %clang_profgen_gcc=%t.d/d1/d2 -o %t.d/code %S/Inputs/gcc-flag-compatibility.c + +# Test that the instrumented code writes to %t.d/d1/d2/default.profraw +RUN: %run %t.d/code +RUN: llvm-profdata merge -o %t.profdata %t.d/d1/d2/default.profraw + +# Test that we can override the directory and file name with LLVM_PROFILE_FILE. +RUN: env LLVM_PROFILE_FILE=%t.d/x1/prof.raw %run %t.d/code +RUN: llvm-profdata merge -o %t.profdata %t.d/x1/prof.raw + +# Test that we can specify a directory with -fprofile-use. +RUN: llvm-profdata merge -o %t.d/default.profdata %t.d/x1/prof.raw +RUN: %clang_profuse_gcc=%t.d -o %t.d/code %S/Inputs/gcc-flag-compatibility.c + +# Test that we can specify a file with -fprofile-use. +RUN: %clang_profuse_gcc=%t.profdata -o %t.d/code %S/Inputs/gcc-flag-compatibility.c diff --git a/test/profile/instrprof-override-filename-then-reset-default.c b/test/profile/instrprof-override-filename-then-reset-default.c new file mode 100644 index 000000000000..137a3b2f2291 --- /dev/null +++ b/test/profile/instrprof-override-filename-then-reset-default.c @@ -0,0 +1,19 @@ +// RUN: rm -rf %t.d +// RUN: mkdir -p %t.d +// RUN: cd %t.d +// RUN: %clang_profgen -O3 %s -o %t.out +// RUN: %run %t.out %t.d/bad.profraw +// RUN: llvm-profdata merge -o %t.d/default.profdata %t.d/default.profraw +// RUN: %clang_profuse=%t.d/default.profdata -o - -S -emit-llvm %s | FileCheck %s + + +void __llvm_profile_override_default_filename(const char *); +int main(int argc, const char *argv[]) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + __llvm_profile_override_default_filename(argv[1]); + __llvm_profile_override_default_filename(0); + return 0; +} +// CHECK: ![[PD1]] = !{!"branch_weights", i32 1, i32 2} diff --git a/test/profile/instrprof-override-filename-with-env.c b/test/profile/instrprof-override-filename-with-env.c new file mode 100644 index 000000000000..cce83891663a --- /dev/null +++ b/test/profile/instrprof-override-filename-with-env.c @@ -0,0 +1,14 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: env LLVM_PROFILE_FILE=%t.good.profraw %run %t %t.bad.profraw +// RUN: llvm-profdata merge -o %t.profdata %t.good.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +void __llvm_profile_override_default_filename(const char *); +int main(int argc, const char *argv[]) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + __llvm_profile_override_default_filename(argv[1]); + return 0; +} +// CHECK: ![[PD1]] = !{!"branch_weights", i32 1, i32 2} diff --git a/test/profile/instrprof-override-filename.c b/test/profile/instrprof-override-filename.c new file mode 100644 index 000000000000..59dea29e3b88 --- /dev/null +++ b/test/profile/instrprof-override-filename.c @@ -0,0 +1,14 @@ +// RUN: %clang_profgen -o %t -O3 %s +// RUN: %run %t %t.profraw +// RUN: llvm-profdata merge -o %t.profdata %t.profraw +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s + +void __llvm_profile_override_default_filename(const char *); +int main(int argc, const char *argv[]) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + __llvm_profile_override_default_filename(argv[1]); + return 0; +} +// CHECK: ![[PD1]] = !{!"branch_weights", i32 1, i32 2} diff --git a/test/profile/instrprof-set-filename-then-reset-default.c b/test/profile/instrprof-set-filename-then-reset-default.c new file mode 100644 index 000000000000..6c07994f08c6 --- /dev/null +++ b/test/profile/instrprof-set-filename-then-reset-default.c @@ -0,0 +1,18 @@ +// RUN: rm -rf %t.d +// RUN: mkdir -p %t.d +// RUN: cd %t.d +// RUN: %clang_profgen -O3 %s -o %t.out +// RUN: %run %t.out %t.d/bad.profraw +// RUN: llvm-profdata merge -o %t.d/default.profdata %t.d/default.profraw +// RUN: %clang_profuse=%t.d/default.profdata -o - -S -emit-llvm %s | FileCheck %s + +void __llvm_profile_set_filename(const char *); +int main(int argc, const char *argv[]) { + // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + if (argc < 2) + return 1; + __llvm_profile_set_filename(argv[1]); + __llvm_profile_set_filename(0); + return 0; +} +// CHECK: ![[PD1]] = !{!"branch_weights", i32 1, i32 2} diff --git a/test/profile/lit.cfg b/test/profile/lit.cfg index e4910abbe7a4..b1b44a1a0665 100644 --- a/test/profile/lit.cfg +++ b/test/profile/lit.cfg @@ -45,6 +45,8 @@ def build_invocation(compile_flags): config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) config.substitutions.append( ("%clang_profgen ", build_invocation(clang_cflags) + " -fprofile-instr-generate ") ) config.substitutions.append( ("%clang_profuse=", build_invocation(clang_cflags) + " -fprofile-instr-use=") ) +config.substitutions.append( ("%clang_profgen_gcc=", build_invocation(clang_cflags) + " -fprofile-generate=") ) +config.substitutions.append( ("%clang_profuse_gcc=", build_invocation(clang_cflags) + " -fprofile-use=") ) if config.host_os not in ['Darwin', 'FreeBSD', 'Linux']: config.unsupported = True diff --git a/test/safestack/CMakeLists.txt b/test/safestack/CMakeLists.txt new file mode 100644 index 000000000000..6f5c2f9b0af4 --- /dev/null +++ b/test/safestack/CMakeLists.txt @@ -0,0 +1,29 @@ +set(SAFESTACK_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(SAFESTACK_LIT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +set(SAFESTACK_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND SAFESTACK_TEST_DEPS safestack) + + # Some tests require LTO, so add a dependency on the relevant LTO plugin. + if(LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR) + list(APPEND SAFESTACK_TEST_DEPS + LLVMgold + ) + endif() + if(APPLE) + list(APPEND SAFESTACK_TEST_DEPS + LTO + ) + endif() +endif() + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + ) + +add_lit_testsuite(check-safestack "Running the SafeStack tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${SAFESTACK_TEST_DEPS}) +set_target_properties(check-safestack PROPERTIES FOLDER "SafeStack tests") diff --git a/test/safestack/buffer-copy-vla.c b/test/safestack/buffer-copy-vla.c new file mode 100644 index 000000000000..356a1ac5c65c --- /dev/null +++ b/test/safestack/buffer-copy-vla.c @@ -0,0 +1,26 @@ +// RUN: %clang_safestack %s -o %t +// RUN: %run %t + +#include "utils.h" + +// Test that loads/stores work correctly for VLAs on the unsafe stack. + +int main(int argc, char **argv) +{ + int i = 128; + break_optimization(&i); + char buffer[i]; + + // check that we can write to a buffer + for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i) + buffer[i] = argv[0][i]; + buffer[i] = '\0'; + + break_optimization(buffer); + + // check that we can read from a buffer + for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i) + if (buffer[i] != argv[0][i]) + return 1; + return 0; +} diff --git a/test/safestack/buffer-copy.c b/test/safestack/buffer-copy.c new file mode 100644 index 000000000000..96b2302be1b1 --- /dev/null +++ b/test/safestack/buffer-copy.c @@ -0,0 +1,25 @@ +// RUN: %clang_safestack %s -o %t +// RUN: %run %t + +#include "utils.h" + +// Test that loads/stores work correctly for variables on the unsafe stack. + +int main(int argc, char **argv) +{ + int i; + char buffer[128]; + + // check that we can write to a buffer + for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i) + buffer[i] = argv[0][i]; + buffer[i] = '\0'; + + break_optimization(buffer); + + // check that we can read from a buffer + for (i = 0; argv[0][i] && i < sizeof (buffer) - 1; ++i) + if (buffer[i] != argv[0][i]) + return 1; + return 0; +} diff --git a/test/safestack/init.c b/test/safestack/init.c new file mode 100644 index 000000000000..cff75f5f8939 --- /dev/null +++ b/test/safestack/init.c @@ -0,0 +1,9 @@ +// RUN: %clang_safestack %s -o %t +// RUN: %run %t + +// Basic smoke test for the runtime library. + +int main(int argc, char **argv) +{ + return 0; +} diff --git a/test/safestack/lit.cfg b/test/safestack/lit.cfg new file mode 100644 index 000000000000..13fc92fa4b2b --- /dev/null +++ b/test/safestack/lit.cfg @@ -0,0 +1,24 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'SafeStack' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Test suffixes. +config.suffixes = ['.c', '.cc', '.cpp', '.m', '.mm', '.ll', '.test'] + +# Add clang substitutions. +config.substitutions.append( ("%clang_nosafestack ", config.clang + " -O0 -fno-sanitize=safe-stack ") ) +config.substitutions.append( ("%clang_safestack ", config.clang + " -O0 -fsanitize=safe-stack ") ) + +if config.lto_supported: + config.available_features.add('lto') + config.substitutions.append((r"%clang_lto_safestack ", ' '.join(config.lto_launch + [config.clang] + config.lto_flags + ['-flto -fsanitize=safe-stack ']))) + +# SafeStack tests are currently supported on Linux, FreeBSD and Darwin only. +if config.host_os not in ['Linux', 'FreeBSD', 'Darwin']: + config.unsupported = True diff --git a/test/safestack/lit.site.cfg.in b/test/safestack/lit.site.cfg.in new file mode 100644 index 000000000000..cb1e7292e5f2 --- /dev/null +++ b/test/safestack/lit.site.cfg.in @@ -0,0 +1,8 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@SAFESTACK_LIT_SOURCE_DIR@/lit.cfg") diff --git a/test/safestack/lto.c b/test/safestack/lto.c new file mode 100644 index 000000000000..6ee23a1c72f2 --- /dev/null +++ b/test/safestack/lto.c @@ -0,0 +1,12 @@ +// REQUIRES: lto + +// RUN: %clang_lto_safestack %s -o %t +// RUN: %run %t + +// Test that safe stack works with LTO. + +int main() { + char c[] = "hello world"; + puts(c); + return 0; +} diff --git a/test/safestack/overflow.c b/test/safestack/overflow.c new file mode 100644 index 000000000000..14e29823cd99 --- /dev/null +++ b/test/safestack/overflow.c @@ -0,0 +1,23 @@ +// RUN: %clang_safestack %s -o %t +// RUN: %run %t + +// RUN: %clang_nosafestack -fno-stack-protector %s -o %t +// RUN: not %run %t + +// Test that buffer overflows on the unsafe stack do not affect variables on the +// safe stack. + +__attribute__((noinline)) +void fct(volatile int *buffer) +{ + memset(buffer - 1, 0, 7 * sizeof(int)); +} + +int main(int argc, char **argv) +{ + int value1 = 42; + int buffer[5]; + int value2 = 42; + fct(buffer); + return value1 != 42 || value2 != 42; +} diff --git a/test/safestack/pthread-cleanup.c b/test/safestack/pthread-cleanup.c new file mode 100644 index 000000000000..805366c9f1eb --- /dev/null +++ b/test/safestack/pthread-cleanup.c @@ -0,0 +1,31 @@ +// RUN: %clang_safestack %s -pthread -o %t +// RUN: not --crash %run %t + +// Test that unsafe stacks are deallocated correctly on thread exit. + +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +enum { kBufferSize = (1 << 15) }; + +void *t1_start(void *ptr) +{ + char buffer[kBufferSize]; + return buffer; +} + +int main(int argc, char **argv) +{ + pthread_t t1; + char *buffer = NULL; + + if (pthread_create(&t1, NULL, t1_start, NULL)) + abort(); + if (pthread_join(t1, &buffer)) + abort(); + + // should segfault here + memset(buffer, 0, kBufferSize); + return 0; +} diff --git a/test/safestack/pthread.c b/test/safestack/pthread.c new file mode 100644 index 000000000000..416586ee13e7 --- /dev/null +++ b/test/safestack/pthread.c @@ -0,0 +1,42 @@ +// RUN: %clang_safestack %s -pthread -o %t +// RUN: %run %t + +// XFAIL: darwin + +// Test that pthreads receive their own unsafe stack. + +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include "utils.h" + +static int ptr_test = 42; + +void *t1_start(void *ptr) +{ + if (ptr != &ptr_test) + abort(); + + // safe stack + int val = ptr_test * 5; + + // unsafe stack + char buffer[8096]; // two pages + memset(buffer, val, sizeof (buffer)); + break_optimization(buffer); + + return ptr; +} + +int main(int argc, char **argv) +{ + pthread_t t1; + void *ptr = NULL; + if (pthread_create(&t1, NULL, t1_start, &ptr_test)) + abort(); + if (pthread_join(t1, &ptr)) + abort(); + if (ptr != &ptr_test) + abort(); + return 0; +} diff --git a/test/safestack/utils.h b/test/safestack/utils.h new file mode 100644 index 000000000000..b04e3bdc127e --- /dev/null +++ b/test/safestack/utils.h @@ -0,0 +1,8 @@ +#ifndef UTILS_H +#define UTILS_H + +static inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +#endif diff --git a/test/sanitizer_common/CMakeLists.txt b/test/sanitizer_common/CMakeLists.txt index 13eecbdc1b2b..7eca7f7672d0 100644 --- a/test/sanitizer_common/CMakeLists.txt +++ b/test/sanitizer_common/CMakeLists.txt @@ -16,16 +16,27 @@ endif() # Create a separate config for each tool we support. foreach(tool ${SUPPORTED_TOOLS}) string(TOUPPER ${tool} tool_toupper) - if(${tool_toupper}_SUPPORTED_ARCH) + if(${tool_toupper}_SUPPORTED_ARCH AND NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND SANITIZER_COMMON_TEST_DEPS ${tool}) + endif() + foreach(arch ${${tool_toupper}_SUPPORTED_ARCH}) set(SANITIZER_COMMON_LIT_TEST_MODE ${tool}) + set(SANITIZER_COMMON_TEST_TARGET_ARCH ${arch}) + if(${arch} MATCHES "arm|aarch64") + # This is only true if we're cross-compiling. + set(SANITIZER_COMMON_TEST_TARGET_CFLAGS + ${COMPILER_RT_TEST_COMPILER_CFLAGS}) + else() + get_target_flags_for_arch(${arch} SANITIZER_COMMON_TEST_TARGET_CFLAGS) + string(REPLACE ";" " " SANITIZER_COMMON_TEST_TARGET_CFLAGS "${SANITIZER_COMMON_TEST_TARGET_CFLAGS}") + endif() + set(CONFIG_NAME ${tool}-${arch}-${OS_NAME}) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/${tool}/lit.site.cfg) - list(APPEND SANITIZER_COMMON_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${tool}) - if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND SANITIZER_COMMON_TEST_DEPS ${tool}) - endif() - endif() + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) + list(APPEND SANITIZER_COMMON_TESTSUITES + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) + endforeach() endforeach() # Unit tests. @@ -37,7 +48,9 @@ if(COMPILER_RT_INCLUDE_TESTS) list(APPEND SANITIZER_COMMON_TEST_DEPS SanitizerUnitTests) endif() -if(SANITIZER_COMMON_TESTSUITES) +# FIXME: Re-enable on 64-bit Windows. +if(SANITIZER_COMMON_TESTSUITES AND + (NOT OS_NAME MATCHES "Windows" OR CMAKE_SIZEOF_VOID_P EQUAL 4)) add_lit_testsuite(check-sanitizer "Running sanitizer_common tests" ${SANITIZER_COMMON_TESTSUITES} DEPENDS ${SANITIZER_COMMON_TEST_DEPS}) diff --git a/test/sanitizer_common/TestCases/Linux/assert.cc b/test/sanitizer_common/TestCases/Linux/assert.cc new file mode 100644 index 000000000000..7f9b0a061da0 --- /dev/null +++ b/test/sanitizer_common/TestCases/Linux/assert.cc @@ -0,0 +1,24 @@ +// Test the handle_abort option. +// RUN: %clang %s -o %t +// RUN: not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s +// RUN: %tool_options=handle_abort=0 not --crash %run %t 2>&1 | FileCheck --check-prefix=CHECK0 %s +// RUN: %tool_options=handle_abort=1 not %run %t 2>&1 | FileCheck --check-prefix=CHECK1 %s +// FIXME: implement in other sanitizers, not just asan. +// XFAIL: msan +// XFAIL: lsan +// XFAIL: tsan +#include <assert.h> +#include <stdio.h> +#include <sanitizer/asan_interface.h> + +void death() { + fprintf(stderr, "DEATH CALLBACK\n"); +} + +int main(int argc, char **argv) { + __sanitizer_set_death_callback(death); + assert(argc == 100); +} +// CHECK1: ERROR: {{.*}}Sanitizer: +// CHECK1: DEATH CALLBACK +// CHECK0-NOT: Sanitizer diff --git a/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc b/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc index a8b51d7a99c0..c0d6cfea1fbe 100644 --- a/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc +++ b/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cc @@ -2,6 +2,7 @@ // RUN: %clangxx -O0 -g %s -o %t && %run %t #include <assert.h> +#include <errno.h> #include <pwd.h> #include <signal.h> #include <stdio.h> @@ -13,7 +14,7 @@ int main(void) { struct passwd *pwdres; char buf[10000]; int res = getpwnam_r("no-such-user", &pwd, buf, sizeof(buf), &pwdres); - assert(res == 0); + assert(res == 0 || res == ENOENT); assert(pwdres == 0); return 0; } diff --git a/test/sanitizer_common/TestCases/Linux/open_memstream.cc b/test/sanitizer_common/TestCases/Linux/open_memstream.cc index 6abe0bfb1483..69097c094a93 100644 --- a/test/sanitizer_common/TestCases/Linux/open_memstream.cc +++ b/test/sanitizer_common/TestCases/Linux/open_memstream.cc @@ -1,5 +1,6 @@ // RUN: %clangxx -m64 -O0 -g -xc++ %s -o %t && %run %t // RUN: %clangxx -m64 -O3 -g -xc++ %s -o %t && %run %t +// REQUIRES: x86_64-supported-target #include <assert.h> #include <stdio.h> diff --git a/test/tsan/signal_segv_handler.cc b/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc index 2d806eef6764..643fb48ae773 100644 --- a/test/tsan/signal_segv_handler.cc +++ b/test/sanitizer_common/TestCases/Linux/signal_segv_handler.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" ASAN_OPTIONS="handle_segv=0 allow_user_segv_handler=1" %run %t 2>&1 | FileCheck %s // JVM uses SEGV to preempt threads. All threads do a load from a known address // periodically. When runtime needs to preempt threads, it unmaps the page. @@ -13,26 +13,35 @@ // "benign" SEGVs that are handled by signal handler, and ensures that // the process survive. -#include "test.h" +#include <stdio.h> +#include <stdlib.h> #include <signal.h> #include <sys/mman.h> +#include <string.h> +#include <unistd.h> +unsigned long page_size; void *guard; void handler(int signo, siginfo_t *info, void *uctx) { - mprotect(guard, 4096, PROT_READ | PROT_WRITE); + mprotect(guard, page_size, PROT_READ | PROT_WRITE); } int main() { - struct sigaction a; + page_size = sysconf(_SC_PAGESIZE); + struct sigaction a, old; + memset(&a, 0, sizeof(a)); + memset(&old, 0, sizeof(old)); a.sa_sigaction = handler; a.sa_flags = SA_SIGINFO; - sigaction(SIGSEGV, &a, 0); - guard = mmap(0, 4096, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + sigaction(SIGSEGV, &a, &old); + guard = mmap(0, 3 * page_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); + guard = (char*)guard + page_size; // work around a kernel bug for (int i = 0; i < 1000000; i++) { - mprotect(guard, 4096, PROT_NONE); + mprotect(guard, page_size, PROT_NONE); *(int*)guard = 1; } + sigaction(SIGSEGV, &old, 0); fprintf(stderr, "DONE\n"); } diff --git a/test/sanitizer_common/TestCases/Posix/decorate_proc_maps.cc b/test/sanitizer_common/TestCases/Posix/decorate_proc_maps.cc new file mode 100644 index 000000000000..622471767655 --- /dev/null +++ b/test/sanitizer_common/TestCases/Posix/decorate_proc_maps.cc @@ -0,0 +1,60 @@ +// RUN: %clangxx -g %s -o %t +// RUN: %tool_options=decorate_proc_maps=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%tool_name +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +bool CopyFdToFd(int in_fd, int out_fd) { + const size_t kBufSize = 0x10000; + static char buf[kBufSize]; + while (true) { + ssize_t got = read(in_fd, buf, kBufSize); + if (got > 0) { + write(out_fd, buf, got); + } else if (got == 0) { + break; + } else if (errno != EAGAIN || errno != EWOULDBLOCK || errno != EINTR) { + fprintf(stderr, "error reading file, errno %d\n", errno); + return false; + } + } + return true; +} + +void *ThreadFn(void *arg) { + (void)arg; + int fd = open("/proc/self/maps", O_RDONLY); + bool res = CopyFdToFd(fd, 2); + close(fd); + return (void *)!res; +} + +int main(void) { + pthread_t t; + void *res; + pthread_create(&t, 0, ThreadFn, 0); + pthread_join(t, &res); + return (int)(size_t)res; +} + +// CHECK-asan: rw-p {{.*}} [low shadow] +// CHECK-asan: ---p {{.*}} [shadow gap] +// CHECK-asan: rw-p {{.*}} [high shadow] + +// CHECK-msan: ---p {{.*}} [invalid] +// CHECK-msan: rw-p {{.*}} [shadow] +// CHECK-msan: ---p {{.*}} [origin] + +// CHECK-tsan: rw-p {{.*}} [shadow] +// CHECK-tsan: rw-p {{.*}} [meta shadow] +// CHECK-tsan: rw-p {{.*}} [trace 0] +// CHECK-tsan: rw-p {{.*}} [trace header 0] +// CHECK-tsan: rw-p {{.*}} [trace 1] +// CHECK-tsan: rw-p {{.*}} [trace header 1] + +// Nothing interesting with standalone LSan. +// CHECK-lsan: decorate_proc_maps diff --git a/test/sanitizer_common/TestCases/Posix/lit.local.cfg b/test/sanitizer_common/TestCases/Posix/lit.local.cfg new file mode 100644 index 000000000000..a6d96d3054cf --- /dev/null +++ b/test/sanitizer_common/TestCases/Posix/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os in ['Windows', 'Darwin']: + config.unsupported = True diff --git a/test/sanitizer_common/TestCases/strcasestr.c b/test/sanitizer_common/TestCases/strcasestr.c new file mode 100644 index 000000000000..4de3cac7e253 --- /dev/null +++ b/test/sanitizer_common/TestCases/strcasestr.c @@ -0,0 +1,16 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +// There's no interceptor for strcasestr on Windows +// XFAIL: win32 + +#define _GNU_SOURCE +#include <assert.h> +#include <string.h> +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "aB"; + char s2[] = "b"; + r = strcasestr(s1, s2); + assert(r == s1 + 1); + return 0; +} diff --git a/test/sanitizer_common/TestCases/strcspn.c b/test/sanitizer_common/TestCases/strcspn.c new file mode 100644 index 000000000000..066a27bbdf2c --- /dev/null +++ b/test/sanitizer_common/TestCases/strcspn.c @@ -0,0 +1,13 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t r; + char s1[] = "ad"; + char s2[] = "cd"; + r = strcspn(s1, s2); + assert(r == 1); + return 0; +} diff --git a/test/sanitizer_common/TestCases/strpbrk.c b/test/sanitizer_common/TestCases/strpbrk.c new file mode 100644 index 000000000000..318e3a4977f2 --- /dev/null +++ b/test/sanitizer_common/TestCases/strpbrk.c @@ -0,0 +1,14 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <string.h> + + +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ad"; + char s2[] = "cd"; + r = strpbrk(s1, s2); + assert(r == s1 + 1); + return 0; +} diff --git a/test/sanitizer_common/TestCases/strspn.c b/test/sanitizer_common/TestCases/strspn.c new file mode 100644 index 000000000000..a9a24305c6f1 --- /dev/null +++ b/test/sanitizer_common/TestCases/strspn.c @@ -0,0 +1,13 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <string.h> + +int main(int argc, char **argv) { + size_t r; + char s1[] = "ab"; + char s2[] = "ac"; + r = strspn(s1, s2); + assert(r == 1); + return 0; +} diff --git a/test/sanitizer_common/TestCases/strstr.c b/test/sanitizer_common/TestCases/strstr.c new file mode 100644 index 000000000000..2089ac7b5fcb --- /dev/null +++ b/test/sanitizer_common/TestCases/strstr.c @@ -0,0 +1,12 @@ +// RUN: %clang %s -o %t && %run %t 2>&1 + +#include <assert.h> +#include <string.h> +int main(int argc, char **argv) { + char *r = 0; + char s1[] = "ab"; + char s2[] = "b"; + r = strstr(s1, s2); + assert(r == s1 + 1); + return 0; +} diff --git a/test/sanitizer_common/lit.common.cfg b/test/sanitizer_common/lit.common.cfg index fb37815ff472..f2d3fec6baa2 100644 --- a/test/sanitizer_common/lit.common.cfg +++ b/test/sanitizer_common/lit.common.cfg @@ -30,6 +30,7 @@ def build_invocation(compile_flags): config.substitutions.append( ("%clang ", build_invocation(clang_cflags)) ) config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) +config.substitutions.append( ("%tool_name", config.tool_name) ) config.substitutions.append( ("%tool_options", tool_options) ) config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/test/sanitizer_common/lit.site.cfg.in b/test/sanitizer_common/lit.site.cfg.in index 1e94aa567632..64a3edf6c682 100644 --- a/test/sanitizer_common/lit.site.cfg.in +++ b/test/sanitizer_common/lit.site.cfg.in @@ -3,6 +3,8 @@ lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configu # Tool-specific config options. config.tool_name = "@SANITIZER_COMMON_LIT_TEST_MODE@" +config.target_cflags = "@SANITIZER_COMMON_TEST_TARGET_CFLAGS@" +config.target_arch = "@SANITIZER_COMMON_TEST_TARGET_ARCH@" # Load tool-specific config that would do the real work. lit_config.load_config(config, "@SANITIZER_COMMON_LIT_SOURCE_DIR@/lit.common.cfg") diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt index 5a9542fd76fc..2996c1d80fbd 100644 --- a/test/tsan/CMakeLists.txt +++ b/test/tsan/CMakeLists.txt @@ -1,5 +1,7 @@ set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -list(APPEND TSAN_TEST_DEPS GotsanRuntimeCheck) +if(NOT ${LLVM_NATIVE_ARCH} STREQUAL "Mips") + list(APPEND TSAN_TEST_DEPS GotsanRuntimeCheck) +endif() if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND TSAN_TEST_DEPS tsan) endif() diff --git a/test/tsan/cond_cancel.c b/test/tsan/cond_cancel.c index e744570b12fc..ddfb745174f6 100644 --- a/test/tsan/cond_cancel.c +++ b/test/tsan/cond_cancel.c @@ -8,9 +8,14 @@ pthread_mutex_t m; pthread_cond_t c; int x; +static void my_cleanup(void *arg) { + printf("my_cleanup\n"); + pthread_mutex_unlock((pthread_mutex_t*)arg); +} + void *thr1(void *p) { pthread_mutex_lock(&m); - pthread_cleanup_push((void(*)(void *arg))pthread_mutex_unlock, &m); + pthread_cleanup_push(my_cleanup, &m); barrier_wait(&barrier); while (x == 0) pthread_cond_wait(&c, &m); diff --git a/test/tsan/cond_destruction.cc b/test/tsan/cond_destruction.cc new file mode 100644 index 000000000000..f56b30c4f0a2 --- /dev/null +++ b/test/tsan/cond_destruction.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %run %t arg 2>&1 | FileCheck %s +// RUN: %run %t arg arg 2>&1 | FileCheck %s +#include "test.h" + +// Test for destruction of pthread_cond_t. +// POSIX states that it is safe to destroy a condition variable upon which no +// threads are currently blocked. That is, it is not necessary to wait untill +// other threads return from pthread_cond_wait, they just need to be unblocked. + +pthread_mutex_t m; +pthread_cond_t c; +bool done1, done2; + +void *thr(void *p) { + pthread_mutex_lock(&m); + done1 = true; + pthread_cond_signal(&c); + while (!done2) + pthread_cond_wait(&c, &m); + pthread_mutex_unlock(&m); + return 0; +} + +int main(int argc, char **argv) { + pthread_t th; + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th, 0, thr, 0); + pthread_mutex_lock(&m); + while (!done1) + pthread_cond_wait(&c, &m); + done2 = true; + // Any of these sequences is legal. + if (argc == 1) { + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + pthread_cond_destroy(&c); + } else if (argc == 2) { + pthread_mutex_unlock(&m); + pthread_cond_signal(&c); + pthread_cond_destroy(&c); + } else { + pthread_cond_signal(&c); + pthread_cond_destroy(&c); + pthread_mutex_unlock(&m); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/test/tsan/cond_race.cc b/test/tsan/cond_race.cc index 52654f16e85c..4daf37f85414 100644 --- a/test/tsan/cond_race.cc +++ b/test/tsan/cond_race.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s // CHECK-NOT: unlock of unlocked mutex // CHECK: ThreadSanitizer: data race // CHECK: pthread_cond_signal diff --git a/test/tsan/deadlock_detector_stress_test.cc b/test/tsan/deadlock_detector_stress_test.cc index e02a9123f5af..c77ffe555ce5 100644 --- a/test/tsan/deadlock_detector_stress_test.cc +++ b/test/tsan/deadlock_detector_stress_test.cc @@ -1,6 +1,6 @@ // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex // RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND -// TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND +// RUN: TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock // RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock diff --git a/test/tsan/dl_iterate_phdr.cc b/test/tsan/dl_iterate_phdr.cc new file mode 100644 index 000000000000..b230a920ac4f --- /dev/null +++ b/test/tsan/dl_iterate_phdr.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script. + +#ifdef BUILD_SO + +#include "test.h" + +int exported_var = 0; + +#else // BUILD_SO + +#include "test.h" +#include <dlfcn.h> +#include <link.h> +#include <string.h> +#include <string> + +static int callback(struct dl_phdr_info *info, size_t size, void *data) { + if (info->dlpi_name[0] == '\0') + info->dlpi_name = "/proc/self/exe"; + return !strcmp(info->dlpi_name, "non existent module"); +} + +void *thread(void *unused) { + for (int i = 0; i < 1000; i++) { + barrier_wait(&barrier); + dl_iterate_phdr(callback, 0); + } + return 0; +} + +int main(int argc, char *argv[]) { + barrier_init(&barrier, 2); + std::string path = std::string(argv[0]) + std::string("-so.so"); + pthread_t th; + pthread_create(&th, 0, thread, 0); + for (int i = 0; i < 1000; i++) { + barrier_wait(&barrier); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen: %s\n", dlerror()); + return 1; + } + dlclose(lib); + } + pthread_join(th, 0); + printf("DONE\n"); + return 0; +} + +#endif // BUILD_SO + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/fd_dup_norace2.cc b/test/tsan/fd_dup_norace2.cc new file mode 100644 index 000000000000..662c686f33a8 --- /dev/null +++ b/test/tsan/fd_dup_norace2.cc @@ -0,0 +1,60 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +// dup2(oldfd, newfd) races with read(newfd). +// This is not reported as race because: +// 1. Some software dups a closed pipe in place of a socket before closing +// the socket (to prevent races actually). +// 2. Some daemons dup /dev/null in place of stdin/stdout. + +int fd; + +void *Thread(void *x) { + char buf; + int n = read(fd, &buf, 1); + if (n != 1) { + // This read can "legitimately" fail regadless of the fact that glibc claims + // that "there is no instant in the middle of calling dup2 at which new is + // closed and not yet a duplicate of old". Strace of the failing runs + // looks as follows: + // + // [pid 122196] open("/dev/urandom", O_RDONLY) = 3 + // [pid 122196] open("/dev/urandom", O_RDONLY) = 4 + // Process 122382 attached + // [pid 122382] read(3, <unfinished ...> + // [pid 122196] dup2(4, 3 <unfinished ...> + // [pid 122382] <... read resumed> 0x7fcd139960b7, 1) = -1 EBADF (Bad file descriptor) + // [pid 122196] <... dup2 resumed> ) = 3 + // read failed: n=-1 errno=9 + // + // The failing read does not interfere with what this test tests, + // so we just ignore the failure. + // + // exit(printf("read failed: n=%d errno=%d\n", n, errno)); + } + return 0; +} + +int main() { + fd = open("/dev/urandom", O_RDONLY); + int fd2 = open("/dev/urandom", O_RDONLY); + if (fd == -1 || fd2 == -1) + exit(printf("open failed\n")); + pthread_t th; + pthread_create(&th, 0, Thread, 0); + if (dup2(fd2, fd) == -1) + exit(printf("dup2 failed\n")); + pthread_join(th, 0); + if (close(fd) == -1) + exit(printf("close failed\n")); + if (close(fd2) == -1) + exit(printf("close failed\n")); + printf("DONE\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/fd_dup_race.cc b/test/tsan/fd_dup_race.cc new file mode 100644 index 000000000000..a1aee5500753 --- /dev/null +++ b/test/tsan/fd_dup_race.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +// dup2(oldfd, newfd) races with close(newfd). + +int fd; + +void *Thread(void *x) { + barrier_wait(&barrier); + if (close(fd) == -1) + exit(printf("close failed\n")); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + fd = open("/dev/random", O_RDONLY); + int fd2 = open("/dev/random", O_RDONLY); + if (fd == -1 || fd2 == -1) + exit(printf("open failed\n")); + pthread_t th; + pthread_create(&th, 0, Thread, 0); + if (dup2(fd2, fd) == -1) + exit(printf("dup2 failed\n")); + barrier_wait(&barrier); + pthread_join(th, 0); + printf("DONE\n"); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/heap_race.cc b/test/tsan/heap_race.cc index c3da68f42658..0201ea9a2e7f 100644 --- a/test/tsan/heap_race.cc +++ b/test/tsan/heap_race.cc @@ -1,17 +1,21 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "test.h" #include <pthread.h> #include <stdio.h> #include <stddef.h> void *Thread(void *a) { ((int*)a)[0]++; + barrier_wait(&barrier); return NULL; } int main() { + barrier_init(&barrier, 2); int *p = new int(42); pthread_t t; pthread_create(&t, NULL, Thread, p); + barrier_wait(&barrier); p[0]++; pthread_join(t, NULL); delete p; diff --git a/test/tsan/ignore_free.cc b/test/tsan/ignore_free.cc index bb6c6ee14364..4e678952c7aa 100644 --- a/test/tsan/ignore_free.cc +++ b/test/tsan/ignore_free.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s #include "test.h" extern "C" { diff --git a/test/tsan/ignore_malloc.cc b/test/tsan/ignore_malloc.cc index 1f633f062d0e..100b4e5dc808 100644 --- a/test/tsan/ignore_malloc.cc +++ b/test/tsan/ignore_malloc.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s #include "test.h" extern "C" { diff --git a/test/tsan/java.h b/test/tsan/java.h index 35fdbc1e7bb1..565a7a7fdabf 100644 --- a/test/tsan/java.h +++ b/test/tsan/java.h @@ -18,4 +18,9 @@ int __tsan_java_mutex_unlock_rec(jptr addr); int __tsan_java_acquire(jptr addr); int __tsan_java_release(jptr addr); int __tsan_java_release_store(jptr addr); + +void __tsan_read1_pc(jptr addr, jptr pc); +void __tsan_write1_pc(jptr addr, jptr pc); } + +const jptr kExternalPCBit = 1ULL << 60; diff --git a/test/tsan/java_heap_init.cc b/test/tsan/java_heap_init.cc new file mode 100644 index 000000000000..bb7357c25b42 --- /dev/null +++ b/test/tsan/java_heap_init.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" +#include <errno.h> +#include <sys/mman.h> + +int main() { + // Test that munmap interceptor resets meta shadow for the memory range. + // Previously __tsan_java_move failed because it encountered non-zero meta + // shadow for the destination. + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)mmap(0, kHeapSize, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (jheap == (jptr)MAP_FAILED) + return printf("mmap failed with %d\n", errno); + __atomic_store_n((int*)jheap, 1, __ATOMIC_RELEASE); + munmap((void*)jheap, kHeapSize); + jheap = (jptr)mmap((void*)jheap, kHeapSize, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (jheap == (jptr)MAP_FAILED) + return printf("second mmap failed with %d\n", errno); + __tsan_java_init(jheap, kHeapSize); + __tsan_java_move(jheap + 16, jheap, 16); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_race.cc b/test/tsan/java_race.cc index ede058e85d8a..140a2a3c19d3 100644 --- a/test/tsan/java_race.cc +++ b/test/tsan/java_race.cc @@ -2,11 +2,13 @@ #include "java.h" void *Thread(void *p) { + barrier_wait(&barrier); *(int*)p = 42; return 0; } int main() { + barrier_init(&barrier, 2); int const kHeapSize = 1024 * 1024; jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; __tsan_java_init(jheap, kHeapSize); @@ -15,6 +17,7 @@ int main() { pthread_t th; pthread_create(&th, 0, Thread, (void*)jheap); *(int*)jheap = 43; + barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(jheap, kBlockSize); fprintf(stderr, "DONE\n"); diff --git a/test/tsan/java_race_pc.cc b/test/tsan/java_race_pc.cc new file mode 100644 index 000000000000..015a0b1f43c6 --- /dev/null +++ b/test/tsan/java_race_pc.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" + +void foobar() { +} + +void barbaz() { +} + +void *Thread(void *p) { + barrier_wait(&barrier); + __tsan_read1_pc((jptr)p, (jptr)foobar + 1); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)jheap); + __tsan_write1_pc((jptr)jheap, (jptr)barbaz + 1); + barrier_wait(&barrier); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 foobar +// CHECK: #0 barbaz +// CHECK: DONE diff --git a/test/tsan/java_symbolization.cc b/test/tsan/java_symbolization.cc new file mode 100644 index 000000000000..aa5ec0c37558 --- /dev/null +++ b/test/tsan/java_symbolization.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" +#include <memory.h> + +extern "C" bool __tsan_symbolize_external(jptr pc, + char *func_buf, jptr func_siz, + char *file_buf, jptr file_siz, + int *line, int *col) { + if (pc == (1234 | kExternalPCBit)) { + memcpy(func_buf, "MyFunc", sizeof("MyFunc")); + memcpy(file_buf, "MyFile.java", sizeof("MyFile.java")); + *line = 1234; + *col = 56; + return true; + } + return false; +} + +void *Thread(void *p) { + barrier_wait(&barrier); + __tsan_write1_pc((jptr)p, 1234 | kExternalPCBit); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)jheap); + __tsan_write1_pc((jptr)jheap, 1234 | kExternalPCBit); + barrier_wait(&barrier); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 MyFunc MyFile.java:1234:56 +// CHECK: DONE diff --git a/test/tsan/large_malloc_meta.cc b/test/tsan/large_malloc_meta.cc new file mode 100644 index 000000000000..e83004824a3a --- /dev/null +++ b/test/tsan/large_malloc_meta.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/mman.h> + +// Test for previously unbounded memory consumption for large mallocs. +// Code allocates a large memory block (that is handled by LargeMmapAllocator), +// and forces allocation of meta shadow for the block. Then freed the block. +// But meta shadow was not unmapped. Then code occupies the virtual memory +// range of the block with something else (that does not need meta shadow). +// And repeats. As the result meta shadow growed infinitely. +// This program used to consume >2GB. Now it consumes <50MB. + +int main() { + for (int i = 0; i < 1000; i++) { + const int kSize = 1 << 20; + const int kPageSize = 4 << 10; + volatile int *p = new int[kSize]; + for (int j = 0; j < kSize; j += kPageSize / sizeof(*p)) + __atomic_store_n(&p[i], 1, __ATOMIC_RELEASE); + delete[] p; + mmap(0, kSize * sizeof(*p) + kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, + -1, 0); + } + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc index d7371c5e4069..d642067391fd 100644 --- a/test/tsan/longjmp.cc +++ b/test/tsan/longjmp.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <stdio.h> #include <stdlib.h> #include <setjmp.h> diff --git a/test/tsan/longjmp2.cc b/test/tsan/longjmp2.cc index 546019b2d11a..eee423dc5fbe 100644 --- a/test/tsan/longjmp2.cc +++ b/test/tsan/longjmp2.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <stdio.h> #include <stdlib.h> #include <setjmp.h> diff --git a/test/tsan/longjmp3.cc b/test/tsan/longjmp3.cc index 71d964dbbed9..79965c4193d3 100644 --- a/test/tsan/longjmp3.cc +++ b/test/tsan/longjmp3.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/longjmp4.cc b/test/tsan/longjmp4.cc index 15330f5d83c8..c8583997331e 100644 --- a/test/tsan/longjmp4.cc +++ b/test/tsan/longjmp4.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/malloc_stack.cc b/test/tsan/malloc_stack.cc index ba1d62bcd9e7..f0c6f9354a5f 100644 --- a/test/tsan/malloc_stack.cc +++ b/test/tsan/malloc_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s #include "test.h" _Atomic(int*) p; diff --git a/test/tsan/mmap_large.cc b/test/tsan/mmap_large.cc index 4ae4c0863501..098530475df5 100644 --- a/test/tsan/mmap_large.cc +++ b/test/tsan/mmap_large.cc @@ -4,6 +4,13 @@ #include <errno.h> #include <sys/mman.h> +#if defined(__FreeBSD__) +// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before +// that, it was never implemented. So just define it to zero. +#undef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif + int main() { #ifdef __x86_64__ const size_t kLog2Size = 39; diff --git a/test/tsan/mmap_stress.cc b/test/tsan/mmap_stress.cc new file mode 100644 index 000000000000..5e3904adf90b --- /dev/null +++ b/test/tsan/mmap_stress.cc @@ -0,0 +1,47 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <errno.h> +#include <sys/mman.h> + +void *SubWorker(void *arg) { + (void)arg; + const int kMmapSize = 65536; + for (int i = 0; i < 500; i++) { + int *ptr = (int*)mmap(0, kMmapSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + *ptr = 42; + munmap(ptr, kMmapSize); + } + return 0; +} + +void *Worker1(void *arg) { + (void)arg; + pthread_t th[4]; + for (int i = 0; i < 4; i++) + pthread_create(&th[i], 0, SubWorker, 0); + for (int i = 0; i < 4; i++) + pthread_join(th[i], 0); + return 0; +} + +void *Worker(void *arg) { + (void)arg; + pthread_t th[4]; + for (int i = 0; i < 4; i++) + pthread_create(&th[i], 0, Worker1, 0); + for (int i = 0; i < 4; i++) + pthread_join(th[i], 0); + return 0; +} + +int main() { + pthread_t th[4]; + for (int i = 0; i < 4; i++) + pthread_create(&th[i], 0, Worker, 0); + for (int i = 0; i < 4; i++) + pthread_join(th[i], 0); + fprintf(stderr, "DONE\n"); +} + +// CHECK: DONE diff --git a/test/tsan/mop1.c b/test/tsan/mop1.c new file mode 100644 index 000000000000..e61c5b8caac9 --- /dev/null +++ b/test/tsan/mop1.c @@ -0,0 +1,40 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "test.h" + +// We want to establish the following sequence of accesses to X: +// - main thread writes X +// - thread2 reads X, this read happens-before the write in main thread +// - thread1 reads X, this read is concurrent with the write in main thread +// Write in main thread and read in thread1 should be detected as a race. +// Previously tsan replaced write by main thread with read by thread1, +// as the result the race was not detected. + +volatile long X, Y, Z; + +void *Thread1(void *x) { + barrier_wait(&barrier); + barrier_wait(&barrier); + Y = X; + return NULL; +} + +void *Thread2(void *x) { + Z = X; + barrier_wait(&barrier); + return NULL; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + X = 42; + barrier_wait(&barrier); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/race_top_suppression.cc b/test/tsan/race_top_suppression.cc new file mode 100644 index 000000000000..7d42dbf3b4bf --- /dev/null +++ b/test/tsan/race_top_suppression.cc @@ -0,0 +1,29 @@ +// RUN: echo "race_top:TopFunction" > %t.supp +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%t.supp'" %run %t 2>&1 | FileCheck %s +// RUN: rm %t.supp +#include "test.h" + +int Global; + +void TopFunction(int *p) { + *p = 1; +} + +void *Thread(void *x) { + barrier_wait(&barrier); + TopFunction(&Global); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + Global--; + barrier_wait(&barrier); + pthread_join(t, 0); + fprintf(stderr, "DONE\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/race_top_suppression1.cc b/test/tsan/race_top_suppression1.cc new file mode 100644 index 000000000000..881e661ba789 --- /dev/null +++ b/test/tsan/race_top_suppression1.cc @@ -0,0 +1,32 @@ +// RUN: echo "race_top:TopFunction" > %t.supp +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%t.supp'" %deflake %run %t 2>&1 | FileCheck %s +// RUN: rm %t.supp +#include "test.h" + +int Global; + +void AnotherFunction(int *p) { + *p = 1; +} + +void TopFunction(int *p) { + AnotherFunction(p); +} + +void *Thread(void *x) { + barrier_wait(&barrier); + TopFunction(&Global); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + Global--; + barrier_wait(&barrier); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/setuid.c b/test/tsan/setuid.c new file mode 100644 index 000000000000..bc9c8ca3abaa --- /dev/null +++ b/test/tsan/setuid.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <unistd.h> + +// Setuid call used to hang because the background tsan thread did not handle +// SIGSETXID signal. Note that we don't care whether setuid call succeeds +// or not. + +static void *thread(void *arg) { + (void)arg; + sleep(1); + return 0; +} + +int main() { + // Create another thread just for completeness of the picture. + pthread_t th; + pthread_create(&th, 0, thread, 0); + setuid(0); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/setuid2.c b/test/tsan/setuid2.c new file mode 100644 index 000000000000..67a6fd14dbcb --- /dev/null +++ b/test/tsan/setuid2.c @@ -0,0 +1,21 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <unistd.h> +#include <time.h> + +// Test that setuid call works in presence of stoptheworld. + +int main() { + struct timespec tp0, tp1; + clock_gettime(CLOCK_MONOTONIC, &tp0); + clock_gettime(CLOCK_MONOTONIC, &tp1); + while (tp1.tv_sec - tp0.tv_sec < 3) { + clock_gettime(CLOCK_MONOTONIC, &tp1); + setuid(0); + } + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/signal_cond.cc b/test/tsan/signal_cond.cc new file mode 100644 index 000000000000..f5eae745d407 --- /dev/null +++ b/test/tsan/signal_cond.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <semaphore.h> + +// Test that signals can be delivered to blocked pthread_cond_wait. +// https://code.google.com/p/thread-sanitizer/issues/detail?id=91 + +int g_thread_run = 1; +pthread_mutex_t mutex; +pthread_cond_t cond; +sem_t sem; + +void sig_handler(int sig) { + (void)sig; + write(1, "SIGNAL\n", sizeof("SIGNAL\n") - 1); + sem_post(&sem); +} + +void* my_thread(void* arg) { + pthread_mutex_lock(&mutex); + while (g_thread_run) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + return 0; +} + +int main() { + sem_init(&sem, 0, 0); + signal(SIGUSR1, &sig_handler); + pthread_t thr; + pthread_create(&thr, 0, &my_thread, 0); + // wait for thread to get inside pthread_cond_wait + // (can't use barrier_wait for that) + sleep(1); + pthread_kill(thr, SIGUSR1); + while (sem_wait(&sem) == -1 && errno == EINTR) { + } + pthread_mutex_lock(&mutex); + g_thread_run = 0; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + pthread_join(thr, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: SIGNAL +// CHECK: DONE diff --git a/test/tsan/signal_longjmp.cc b/test/tsan/signal_longjmp.cc index 84b0682dcbaf..2525c898887b 100644 --- a/test/tsan/signal_longjmp.cc +++ b/test/tsan/signal_longjmp.cc @@ -3,6 +3,9 @@ // Test case for longjumping out of signal handler: // https://code.google.com/p/thread-sanitizer/issues/detail?id=75 +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <setjmp.h> #include <signal.h> #include <stdlib.h> diff --git a/test/tsan/signal_recursive.cc b/test/tsan/signal_recursive.cc index 825338de9fba..67fc9c0ec9a3 100644 --- a/test/tsan/signal_recursive.cc +++ b/test/tsan/signal_recursive.cc @@ -3,6 +3,8 @@ // Test case for recursive signal handlers, adopted from: // https://code.google.com/p/thread-sanitizer/issues/detail?id=71 +// REQUIRES: disabled + #include "test.h" #include <semaphore.h> #include <signal.h> diff --git a/test/tsan/test.h b/test/tsan/test.h index bb861b07745e..4e877f6d8dfd 100644 --- a/test/tsan/test.h +++ b/test/tsan/test.h @@ -11,10 +11,16 @@ __typeof(pthread_barrier_wait) *barrier_wait; void barrier_init(pthread_barrier_t *barrier, unsigned count) { +#if defined(__FreeBSD__) + static const char libpthread_name[] = "libpthread.so"; +#else + static const char libpthread_name[] = "libpthread.so.0"; +#endif + if (barrier_wait == 0) { - void *h = dlopen("libpthread.so.0", RTLD_LAZY); + void *h = dlopen(libpthread_name, RTLD_LAZY); if (h == 0) { - fprintf(stderr, "failed to dlopen libpthread.so.0, exiting\n"); + fprintf(stderr, "failed to dlopen %s, exiting\n", libpthread_name); exit(1); } barrier_wait = (__typeof(barrier_wait))dlsym(h, "pthread_barrier_wait"); diff --git a/test/tsan/thread_detach2.c b/test/tsan/thread_detach2.c new file mode 100644 index 000000000000..8133980ba5a1 --- /dev/null +++ b/test/tsan/thread_detach2.c @@ -0,0 +1,28 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" + +// Test for https://llvm.org/bugs/show_bug.cgi?id=23235 +// The bug was that synchronization between thread creation and thread start +// is not established if pthread_create is followed by pthread_detach. + +int x; + +void *Thread(void *a) { + x = 42; + barrier_wait(&barrier); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + x = 43; + pthread_create(&t, 0, Thread, 0); + pthread_detach(t); + barrier_wait(&barrier); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: PASS diff --git a/test/ubsan/CMakeLists.txt b/test/ubsan/CMakeLists.txt index 1c0c92903298..cd197c7aed46 100644 --- a/test/ubsan/CMakeLists.txt +++ b/test/ubsan/CMakeLists.txt @@ -1,25 +1,43 @@ set(UBSAN_LIT_TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(UBSAN_LIT_TEST_MODE "Standalone") -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig/lit.site.cfg) -set(UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/UbsanConfig) +set(UBSAN_TESTSUITES) +set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -if(COMPILER_RT_HAS_ASAN) - set(UBSAN_LIT_TEST_MODE "AddressSanitizer") +macro(add_ubsan_testsuite test_mode sanitizer arch) + set(UBSAN_LIT_TEST_MODE "${test_mode}") + set(CONFIG_NAME ${UBSAN_LIT_TEST_MODE}-${arch}) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig/lit.site.cfg) - list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/AsanConfig) -endif() + ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) + list(APPEND UBSAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND UBSAN_TEST_DEPS ${sanitizer}) + endif() +endmacro() -set(UBSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -if(NOT COMPILER_RT_STANDALONE_BUILD) - list(APPEND UBSAN_TEST_DEPS ubsan asan) -endif() +foreach(arch ${UBSAN_SUPPORTED_ARCH}) + set(UBSAN_TEST_TARGET_ARCH ${arch}) + if(${arch} MATCHES "arm|aarch64") + # This is only true if we're cross-compiling. + set(UBSAN_TEST_TARGET_CFLAGS ${COMPILER_RT_TEST_COMPILER_CFLAGS}) + else() + get_target_flags_for_arch(${arch} UBSAN_TEST_TARGET_CFLAGS) + string(REPLACE ";" " " UBSAN_TEST_TARGET_CFLAGS "${UBSAN_TEST_TARGET_CFLAGS}") + endif() + add_ubsan_testsuite("Standalone" ubsan ${arch}) + + if(COMPILER_RT_HAS_ASAN AND ";${ASAN_SUPPORTED_ARCH};" MATCHES ";${arch};") + add_ubsan_testsuite("AddressSanitizer" asan ${arch}) + endif() + if(COMPILER_RT_HAS_MSAN AND ";${MSAN_SUPPORTED_ARCH};" MATCHES ";${arch};") + add_ubsan_testsuite("MemorySanitizer" msan ${arch}) + endif() + if(COMPILER_RT_HAS_TSAN AND ";${TSAN_SUPPORTED_ARCH};" MATCHES ";${arch};") + add_ubsan_testsuite("ThreadSanitizer" tsan ${arch}) + endif() +endforeach() add_lit_testsuite(check-ubsan "Running UndefinedBehaviorSanitizer tests" ${UBSAN_TESTSUITES} DEPENDS ${UBSAN_TEST_DEPS}) -set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan unittests") +set_target_properties(check-ubsan PROPERTIES FOLDER "UBSan tests") diff --git a/test/ubsan/TestCases/Float/cast-overflow.cpp b/test/ubsan/TestCases/Float/cast-overflow.cpp index 526817153d02..eda087442450 100644 --- a/test/ubsan/TestCases/Float/cast-overflow.cpp +++ b/test/ubsan/TestCases/Float/cast-overflow.cpp @@ -1,7 +1,6 @@ -// FIXME: run this (and other) UBSan tests in both 32- and 64-bit modes (?). -// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t +// RUN: %clangxx -fsanitize=float-cast-overflow -g %s -o %t // RUN: %run %t _ -// RUN: %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: env UBSAN_OPTIONS=print_summary=1 %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 // RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 // RUN: %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 // RUN: %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 @@ -13,8 +12,6 @@ // RUN: not %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9 // This test assumes float and double are IEEE-754 single- and double-precision. -// XFAIL: armv7l-unknown-linux-gnueabihf -// XFAIL: aarch64 #if defined(__APPLE__) # include <machine/endian.h> @@ -26,6 +23,10 @@ # define BYTE_ORDER _BYTE_ORDER # define BIG_ENDIAN _BIG_ENDIAN # define LITTLE_ENDIAN _LITTLE_ENDIAN +#elif defined(_WIN32) +# define BYTE_ORDER 0 +# define BIG_ENDIAN 1 +# define LITTLE_ENDIAN 0 #else # include <endian.h> # define BYTE_ORDER __BYTE_ORDER @@ -82,40 +83,53 @@ int main(int argc, char **argv) { // FIXME: Produce a source location for these checks and test for it here. // Floating point -> integer overflow. - case '0': + case '0': { // Note that values between 0x7ffffe00 and 0x80000000 may or may not // successfully round-trip, depending on the rounding mode. // CHECK-0: runtime error: value 2.14748{{.*}} is outside the range of representable values of type 'int' - return MaxFloatRepresentableAsInt + 0x80; - case '1': + static int test_int = MaxFloatRepresentableAsInt + 0x80; + // CHECK-0: SUMMARY: {{.*}}Sanitizer: undefined-behavior {{.*}}cast-overflow.cpp:[[@LINE-1]] + return 0; + } + case '1': { // CHECK-1: runtime error: value -2.14748{{.*}} is outside the range of representable values of type 'int' - return MinFloatRepresentableAsInt - 0x100; + static int test_int = MinFloatRepresentableAsInt - 0x100; + return 0; + } case '2': { // CHECK-2: runtime error: value -1 is outside the range of representable values of type 'unsigned int' volatile float f = -1.0; volatile unsigned u = (unsigned)f; return 0; } - case '3': + case '3': { // CHECK-3: runtime error: value 4.2949{{.*}} is outside the range of representable values of type 'unsigned int' - return (unsigned)(MaxFloatRepresentableAsUInt + 0x100); + static int test_int = (unsigned)(MaxFloatRepresentableAsUInt + 0x100); + return 0; + } - case '4': + case '4': { // CHECK-4: runtime error: value {{.*}} is outside the range of representable values of type 'int' - return Inf; - case '5': + static int test_int = Inf; + return 0; + } + case '5': { // CHECK-5: runtime error: value {{.*}} is outside the range of representable values of type 'int' - return NaN; + static int test_int = NaN; + return 0; + } // Integer -> floating point overflow. - case '6': + case '6': { // CHECK-6: {{runtime error: value 0xffffff00000000000000000000000001 is outside the range of representable values of type 'float'|__int128 not supported}} -#ifdef __SIZEOF_INT128__ - return (float)(FloatMaxAsUInt128 + 1); +#if defined(__SIZEOF_INT128__) && !defined(_WIN32) + static int test_int = (float)(FloatMaxAsUInt128 + 1); + return 0; #else puts("__int128 not supported"); return 0; #endif + } // FIXME: The backend cannot lower __fp16 operations on x86 yet. //case '7': // (__fp16)65504; // ok diff --git a/test/ubsan/TestCases/Integer/add-overflow.cpp b/test/ubsan/TestCases/Integer/add-overflow.cpp index d3425828ec88..301941b852b4 100644 --- a/test/ubsan/TestCases/Integer/add-overflow.cpp +++ b/test/ubsan/TestCases/Integer/add-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 -// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 -// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 +// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 #include <stdint.h> #include <stdio.h> @@ -22,7 +22,7 @@ int main() { #endif #ifdef ADD_I128 -# ifdef __SIZEOF_INT128__ +# if defined(__SIZEOF_INT128__) && !defined(_WIN32) (void)((__int128_t(1) << 126) + (__int128_t(1) << 126)); # else puts("__int128 not supported"); diff --git a/test/ubsan/TestCases/Integer/div-zero.cpp b/test/ubsan/TestCases/Integer/div-zero.cpp index 9a223312e8e7..68b01afab218 100644 --- a/test/ubsan/TestCases/Integer/div-zero.cpp +++ b/test/ubsan/TestCases/Integer/div-zero.cpp @@ -3,7 +3,7 @@ // RUN: %clangxx -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %run %t 2>&1 | FileCheck %s // RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %run %t 2>&1 | FileCheck %s -#ifdef __SIZEOF_INT128__ +#if defined(__SIZEOF_INT128__) && !defined(_WIN32) typedef __int128 intmax; #else typedef long long intmax; diff --git a/test/ubsan/TestCases/Integer/incdec-overflow.cpp b/test/ubsan/TestCases/Integer/incdec-overflow.cpp index fc7141c803d8..06090300af3b 100644 --- a/test/ubsan/TestCases/Integer/incdec-overflow.cpp +++ b/test/ubsan/TestCases/Integer/incdec-overflow.cpp @@ -1,7 +1,7 @@ -// RUN: %clangxx -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s -// RUN: %clangxx -DOP=++n -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s -// RUN: %clangxx -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s -// RUN: %clangxx -DOP=--m -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -DOP=n++ -fsanitize=signed-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=PLUS +// RUN: %clangxx -DOP=++n -fsanitize=signed-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=PLUS +// RUN: %clangxx -DOP=m-- -fsanitize=signed-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=MINUS +// RUN: %clangxx -DOP=--m -fsanitize=signed-integer-overflow %s -o %t4 && %run %t4 2>&1 | FileCheck %s --check-prefix=MINUS #include <stdint.h> @@ -10,7 +10,7 @@ int main() { n++; n++; int m = -n - 1; - // CHECK: incdec-overflow.cpp:15:3: runtime error: signed integer overflow: [[MINUS:-?]]214748364 - // CHECK: + [[MINUS]]1 cannot be represented in type 'int' OP; + // PLUS: incdec-overflow.cpp:[[@LINE-1]]:3: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' + // MINUS: incdec-overflow.cpp:[[@LINE-2]]:3: runtime error: signed integer overflow: -2147483648 - 1 cannot be represented in type 'int' } diff --git a/test/ubsan/TestCases/Integer/negate-overflow.cpp b/test/ubsan/TestCases/Integer/negate-overflow.cpp index bde0bdabb292..628291eb4b95 100644 --- a/test/ubsan/TestCases/Integer/negate-overflow.cpp +++ b/test/ubsan/TestCases/Integer/negate-overflow.cpp @@ -1,5 +1,5 @@ -// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECKS -// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECKU +// RUN: %clangxx -fsanitize=signed-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECKS +// RUN: %clangxx -fsanitize=unsigned-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECKU int main() { // CHECKS-NOT: runtime error diff --git a/test/ubsan/TestCases/Integer/shift.cpp b/test/ubsan/TestCases/Integer/shift.cpp index e86fac8d574a..50db16dac18e 100644 --- a/test/ubsan/TestCases/Integer/shift.cpp +++ b/test/ubsan/TestCases/Integer/shift.cpp @@ -1,13 +1,20 @@ -// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW -// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW -// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW -// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW -// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW -// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW -// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH -// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH -// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH -// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t1 && not %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t2 && not %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t3 && not %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t4 && not %run %t4 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t5 && not %run %t5 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t6 && not %run %t6 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t7 && not %run %t7 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t8 && not %run %t8 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t9 && not %run %t9 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t10 && not %run %t10 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH + +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t12 && %run %t12 +// RUN: %clangxx -DLSH_OVERFLOW -DOP='>>' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t13 && %run %t13 +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t14 && %run %t14 +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t15 && %run %t15 +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t16 && %run %t16 +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t17 && %run %t17 #include <stdint.h> @@ -20,18 +27,19 @@ int main() { b <<= 1; // still ok, unsigned #ifdef LSH_OVERFLOW - // CHECK-LSH_OVERFLOW: shift.cpp:24:5: runtime error: left shift of negative value -2147483648 + // CHECK-LSH_OVERFLOW: shift.cpp:[[@LINE+1]]:5: runtime error: left shift of negative value -2147483648 a OP 1; #endif #ifdef TOO_LOW - // CHECK-TOO_LOW: shift.cpp:29:5: runtime error: shift exponent -3 is negative + a = 0; + // CHECK-TOO_LOW: shift.cpp:[[@LINE+1]]:5: runtime error: shift exponent -3 is negative a OP (-3); #endif #ifdef TOO_HIGH a = 0; - // CHECK-TOO_HIGH: shift.cpp:35:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + // CHECK-TOO_HIGH: shift.cpp:[[@LINE+1]]:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' a OP 32; #endif } diff --git a/test/ubsan/TestCases/Integer/sub-overflow.cpp b/test/ubsan/TestCases/Integer/sub-overflow.cpp index 15e64d951603..54ec4b5cd3c6 100644 --- a/test/ubsan/TestCases/Integer/sub-overflow.cpp +++ b/test/ubsan/TestCases/Integer/sub-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clangxx -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 -// RUN: %clangxx -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 -// RUN: %clangxx -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 +// RUN: %clangxx -DSUB_I32 -fsanitize=signed-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=signed-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=signed-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 #include <stdint.h> #include <stdio.h> @@ -21,7 +21,7 @@ int main() { #endif #ifdef SUB_I128 -# ifdef __SIZEOF_INT128__ +# if defined(__SIZEOF_INT128__) && !defined(_WIN32) (void)(-(__int128_t(1) << 126) - (__int128_t(1) << 126) - 1); # else puts("__int128 not supported"); diff --git a/test/ubsan/TestCases/Integer/summary.cpp b/test/ubsan/TestCases/Integer/summary.cpp index 6e9aec63ca74..21f537b92767 100644 --- a/test/ubsan/TestCases/Integer/summary.cpp +++ b/test/ubsan/TestCases/Integer/summary.cpp @@ -5,6 +5,6 @@ int main() { (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); - // CHECK: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]] + // CHECK: SUMMARY: AddressSanitizer: undefined-behavior {{.*}}summary.cpp:[[@LINE-1]]:44 return 0; } diff --git a/test/ubsan/TestCases/Integer/uadd-overflow.cpp b/test/ubsan/TestCases/Integer/uadd-overflow.cpp index 7a96880fe636..8ef8983b56e0 100644 --- a/test/ubsan/TestCases/Integer/uadd-overflow.cpp +++ b/test/ubsan/TestCases/Integer/uadd-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 -// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 -// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 +// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 #include <stdint.h> #include <stdio.h> @@ -22,7 +22,7 @@ int main() { #endif #ifdef ADD_I128 -# ifdef __SIZEOF_INT128__ +# if defined(__SIZEOF_INT128__) && !defined(_WIN32) (void)((__uint128_t(1) << 127) + (__uint128_t(1) << 127)); # else puts("__int128 not supported"); diff --git a/test/ubsan/TestCases/Integer/uincdec-overflow.cpp b/test/ubsan/TestCases/Integer/uincdec-overflow.cpp index a236d21fcf1f..4cc73972dacd 100644 --- a/test/ubsan/TestCases/Integer/uincdec-overflow.cpp +++ b/test/ubsan/TestCases/Integer/uincdec-overflow.cpp @@ -1,7 +1,7 @@ -// RUN: %clangxx -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s -// RUN: %clangxx -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-INC %s -// RUN: %clangxx -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s -// RUN: %clangxx -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck --check-prefix=CHECK-DEC %s +// RUN: %clangxx -DOP=n++ -fsanitize=unsigned-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=++n -fsanitize=unsigned-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck --check-prefix=CHECK-INC %s +// RUN: %clangxx -DOP=m-- -fsanitize=unsigned-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck --check-prefix=CHECK-DEC %s +// RUN: %clangxx -DOP=--m -fsanitize=unsigned-integer-overflow %s -o %t4 && %run %t4 2>&1 | FileCheck --check-prefix=CHECK-DEC %s #include <stdint.h> diff --git a/test/ubsan/TestCases/Integer/usub-overflow.cpp b/test/ubsan/TestCases/Integer/usub-overflow.cpp index e5de7de54eaa..fb671b003af7 100644 --- a/test/ubsan/TestCases/Integer/usub-overflow.cpp +++ b/test/ubsan/TestCases/Integer/usub-overflow.cpp @@ -1,6 +1,6 @@ -// RUN: %clangxx -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 -// RUN: %clangxx -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 -// RUN: %clangxx -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 +// RUN: %clangxx -DSUB_I32 -fsanitize=unsigned-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I32 +// RUN: %clangxx -DSUB_I64 -fsanitize=unsigned-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I64 +// RUN: %clangxx -DSUB_I128 -fsanitize=unsigned-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-SUB_I128 #include <stdint.h> #include <stdio.h> @@ -21,7 +21,7 @@ int main() { #endif #ifdef SUB_I128 -# ifdef __SIZEOF_INT128__ +# if defined(__SIZEOF_INT128__) && !defined(_WIN32) (void)((__uint128_t(1) << 126) - (__uint128_t(1) << 127)); # else puts("__int128 not supported\n"); diff --git a/test/ubsan/TestCases/Misc/Linux/coverage-levels.cc b/test/ubsan/TestCases/Misc/Linux/coverage-levels.cc new file mode 100644 index 000000000000..df6e835dd9df --- /dev/null +++ b/test/ubsan/TestCases/Misc/Linux/coverage-levels.cc @@ -0,0 +1,39 @@ +// Test various levels of coverage +// +// RUN: mkdir -p %T/coverage-levels +// RUN: OPT=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels +// RUN: %clangxx -fsanitize=shift -DGOOD_SHIFT=1 -O1 -fsanitize-coverage=func %s -o %t +// RUN: UBSAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN +// RUN: %clangxx -fsanitize=undefined -DGOOD_SHIFT=1 -O1 -fsanitize-coverage=func %s -o %t +// RUN: UBSAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN + +// RUN: %clangxx -fsanitize=shift -O1 -fsanitize-coverage=func %s -o %t +// RUN: UBSAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_WARN +// RUN: %clangxx -fsanitize=shift -O1 -fsanitize-coverage=bb %s -o %t +// RUN: UBSAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 --check-prefix=CHECK_WARN +// RUN: %clangxx -fsanitize=shift -O1 -fsanitize-coverage=edge %s -o %t +// RUN: UBSAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 --check-prefix=CHECK_WARN + +// Coverage is not yet implemented in TSan. +// XFAIL: ubsan-tsan + +volatile int sink; +int main(int argc, char **argv) { + int shift = argc * 32; +#if GOOD_SHIFT + shift = 3; +#endif + if ((argc << shift) == 16) // False. + return 1; + return 0; +} + +// CHECK_WARN: shift exponent 32 is too large +// CHECK_NOWARN-NOT: ERROR +// FIXME: Currently, coverage instrumentation kicks in after ubsan, so we get +// more than the minimal number of instrumented blocks. +// FIXME: Currently, ubsan with -fno-sanitize-recover and w/o asan will fail +// to dump coverage. +// CHECK1: 1 PCs written +// CHECK2: 3 PCs written +// CHECK3: 4 PCs written diff --git a/test/ubsan/TestCases/Misc/Linux/lit.local.cfg b/test/ubsan/TestCases/Misc/Linux/lit.local.cfg new file mode 100644 index 000000000000..57271b8078a4 --- /dev/null +++ b/test/ubsan/TestCases/Misc/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/ubsan/TestCases/Misc/Linux/ubsan_options.cc b/test/ubsan/TestCases/Misc/Linux/ubsan_options.cc new file mode 100644 index 000000000000..2be8792cce96 --- /dev/null +++ b/test/ubsan/TestCases/Misc/Linux/ubsan_options.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx -fsanitize=integer -fsanitize-recover=integer %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// __ubsan_default_options() doesn't work on Darwin. +// XFAIL: darwin + +#include <stdint.h> + +extern "C" const char *__ubsan_default_options() { + return "halt_on_error=1"; +} + +int main() { + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK: ubsan_options.cc:[[@LINE-1]]:44: runtime error: unsigned integer overflow + return 0; +} + diff --git a/test/ubsan/TestCases/Misc/bounds.cpp b/test/ubsan/TestCases/Misc/bounds.cpp index ffcac528be90..199690dad2a2 100644 --- a/test/ubsan/TestCases/Misc/bounds.cpp +++ b/test/ubsan/TestCases/Misc/bounds.cpp @@ -1,7 +1,7 @@ // RUN: %clangxx -fsanitize=bounds %s -O3 -o %t // RUN: %run %t 0 0 0 // RUN: %run %t 1 2 3 -// RUN: not --crash %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 +// RUN: %expect_crash %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 // RUN: %run %t 0 3 0 2>&1 | FileCheck %s --check-prefix=CHECK-B-3 // RUN: %run %t 0 0 4 2>&1 | FileCheck %s --check-prefix=CHECK-C-4 diff --git a/test/ubsan/TestCases/Misc/coverage-levels.cc b/test/ubsan/TestCases/Misc/coverage-levels.cc deleted file mode 100644 index 2fe12ffefd4b..000000000000 --- a/test/ubsan/TestCases/Misc/coverage-levels.cc +++ /dev/null @@ -1,38 +0,0 @@ -// Test various levels of coverage -// -// RUN: mkdir -p %T/coverage-levels -// RUN: OPT=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels -// RUN: %clangxx -fsanitize=shift -DGOOD_SHIFT=1 -O1 -fsanitize-coverage=1 %s -o %t -// RUN: UBSAN_OPTIONS=$OPT ASAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN -// RUN: %clangxx -fsanitize=undefined -DGOOD_SHIFT=1 -O1 -fsanitize-coverage=1 %s -o %t -// RUN: UBSAN_OPTIONS=$OPT ASAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN - -// RUN: %clangxx -fsanitize=shift -O1 -fsanitize-coverage=1 %s -o %t -// RUN: UBSAN_OPTIONS=$OPT ASAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_WARN -// RUN: %clangxx -fsanitize=shift -O1 -fsanitize-coverage=2 %s -o %t -// RUN: UBSAN_OPTIONS=$OPT ASAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 --check-prefix=CHECK_WARN -// RUN: %clangxx -fsanitize=shift -O1 -fsanitize-coverage=3 %s -o %t -// RUN: UBSAN_OPTIONS=$OPT ASAN_OPTIONS=$OPT %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 --check-prefix=CHECK_WARN - -// XFAIL: darwin - -volatile int sink; -int main(int argc, char **argv) { - int shift = argc * 32; -#if GOOD_SHIFT - shift = 3; -#endif - if ((argc << shift) == 16) // False. - return 1; - return 0; -} - -// CHECK_WARN: shift exponent 32 is too large -// CHECK_NOWARN-NOT: ERROR -// FIXME: Currently, coverage instrumentation kicks in after ubsan, so we get -// more than the minimal number of instrumented blocks. -// FIXME: Currently, ubsan with -fno-sanitize-recover and w/o asan will fail -// to dump coverage. -// CHECK1: 1 PCs written -// CHECK2: 3 PCs written -// CHECK3: 4 PCs written diff --git a/test/ubsan/TestCases/Misc/deduplication.cpp b/test/ubsan/TestCases/Misc/deduplication.cpp index 7d7b0bd58c6e..4b02590fa0ac 100644 --- a/test/ubsan/TestCases/Misc/deduplication.cpp +++ b/test/ubsan/TestCases/Misc/deduplication.cpp @@ -11,6 +11,7 @@ void overflow() { int main() { // CHECK: Start fprintf(stderr, "Start\n"); + fflush(stderr); // CHECK: runtime error // CHECK-NOT: runtime error diff --git a/test/ubsan/TestCases/Misc/enum.cpp b/test/ubsan/TestCases/Misc/enum.cpp index 49ac7c6bb187..5dbecf161262 100644 --- a/test/ubsan/TestCases/Misc/enum.cpp +++ b/test/ubsan/TestCases/Misc/enum.cpp @@ -2,6 +2,10 @@ // RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %run %t // RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL +// FIXME: UBSan fails to add the correct instrumentation code for some reason on +// Windows. +// XFAIL: win32 + enum E { a = 1 } e; #undef E diff --git a/test/ubsan/TestCases/Misc/log-path_test.cc b/test/ubsan/TestCases/Misc/log-path_test.cc new file mode 100644 index 000000000000..b39e1b077e27 --- /dev/null +++ b/test/ubsan/TestCases/Misc/log-path_test.cc @@ -0,0 +1,33 @@ +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=316 +// XFAIL: android + +// RUN: %clangxx -fsanitize=undefined %s -O1 -o %t + +// Regular run. +// RUN: %run %t -4 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out + +// Good log_path. +// RUN: rm -f %t.log.* +// RUN: env UBSAN_OPTIONS=log_path=%t.log %run %t -4 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.* + +// Run w/o errors should not produce any log. +// RUN: rm -f %t.log.* +// RUN: env UBSAN_OPTIONS=log_path=%t.log %run %t 4 +// RUN: not cat %t.log.* + +// FIXME: log_path is not supported on Windows yet. +// XFAIL: win32 + +#include <stdio.h> +#include <stdlib.h> +int main(int argc, char *argv[]) { + double a = atof(argv[1]); + unsigned int ai = (unsigned int) a; + printf("%f %u\n", a, ai); + return 0; +} + +// CHECK-ERROR: runtime error: value -4 is outside the range of representable values of type 'unsigned int' + diff --git a/test/ubsan/TestCases/Misc/missing_return.cpp b/test/ubsan/TestCases/Misc/missing_return.cpp index 5d3d54de17dd..75e26df536a6 100644 --- a/test/ubsan/TestCases/Misc/missing_return.cpp +++ b/test/ubsan/TestCases/Misc/missing_return.cpp @@ -1,15 +1,13 @@ // RUN: %clangxx -fsanitize=return -g %s -O3 -o %t // RUN: not %run %t 2>&1 | FileCheck %s -// RUN: UBSAN_OPTIONS=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os-STACKTRACE +// RUN: env UBSAN_OPTIONS=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%os-STACKTRACE // CHECK: missing_return.cpp:[[@LINE+1]]:5: runtime error: execution reached the end of a value-returning function without returning a value int f() { -// Slow stack unwinding is disabled on Darwin for now, see +// Slow stack unwinding is not available on Darwin for now, see // https://code.google.com/p/address-sanitizer/issues/detail?id=137 -// CHECK-Linux-STACKTRACE: #0 {{.*}} in f(){{.*}}missing_return.cpp:[[@LINE-3]] -// CHECK-FreeBSD-STACKTRACE: #0 {{.*}} in f(void){{.*}}missing_return.cpp:[[@LINE-4]] -// Check for already checked line to avoid lit error reports. -// CHECK-Darwin-STACKTRACE: missing_return.cpp +// CHECK-Linux-STACKTRACE: #0 {{.*}}f(){{.*}}missing_return.cpp:[[@LINE-3]] +// CHECK-FreeBSD-STACKTRACE: #0 {{.*}}f(void){{.*}}missing_return.cpp:[[@LINE-4]] } int main(int, char **argv) { diff --git a/test/ubsan/TestCases/TypeCheck/Function/function.cpp b/test/ubsan/TestCases/TypeCheck/Function/function.cpp index 2609c6a8f666..5a2fda4c9d46 100644 --- a/test/ubsan/TestCases/TypeCheck/Function/function.cpp +++ b/test/ubsan/TestCases/TypeCheck/Function/function.cpp @@ -1,7 +1,7 @@ // RUN: %clangxx -fsanitize=function %s -O3 -g -o %t // RUN: %run %t 2>&1 | FileCheck %s // Verify that we can disable symbolization if needed: -// RUN: UBSAN_OPTIONS=symbolize=0 ASAN_OPTIONS=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM +// RUN: UBSAN_OPTIONS=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM // -fsanitize=function is unsupported on Darwin yet. // XFAIL: darwin diff --git a/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/test/ubsan/TestCases/TypeCheck/misaligned.cpp index 9c8455d5f1c3..1b0abad747bc 100644 --- a/test/ubsan/TestCases/TypeCheck/misaligned.cpp +++ b/test/ubsan/TestCases/TypeCheck/misaligned.cpp @@ -7,7 +7,7 @@ // RUN: %run %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN // RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW // RUN: %run %t u1 2>&1 | FileCheck %s --check-prefix=CHECK-UPCAST -// RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD +// RUN: env UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD // RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover=alignment %s -O3 -o %t // RUN: not %run %t w1 2>&1 | FileCheck %s --check-prefix=CHECK-WILD @@ -38,19 +38,19 @@ int main(int, char **argv) { switch (argv[1][0]) { case 'l': - // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]:12: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-LOAD: misaligned.cpp:[[@LINE+4]]{{(:12)?}}: runtime error: load of misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment // CHECK-LOAD-NEXT: [[PTR]]: note: pointer points here // CHECK-LOAD-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-LOAD-NEXT: {{^ \^}} return *p && 0; // Slow stack unwinding is disabled on Darwin for now, see // https://code.google.com/p/address-sanitizer/issues/detail?id=137 - // CHECK-Linux-STACK-LOAD: #0 {{.*}} in main{{.*}}misaligned.cpp + // CHECK-Linux-STACK-LOAD: #0 {{.*}}main{{.*}}misaligned.cpp // Check for the already checked line to avoid lit error reports. // CHECK-Darwin-STACK-LOAD: {{ }} case 's': - // CHECK-STORE: misaligned.cpp:[[@LINE+4]]:5: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-STORE: misaligned.cpp:[[@LINE+4]]{{(:5)?}}: runtime error: store to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment // CHECK-STORE-NEXT: [[PTR]]: note: pointer points here // CHECK-STORE-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-STORE-NEXT: {{^ \^}} @@ -58,7 +58,7 @@ int main(int, char **argv) { break; case 'r': - // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]:15: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment + // CHECK-REFERENCE: misaligned.cpp:[[@LINE+4]]{{(:(5|15))?}}: runtime error: reference binding to misaligned address [[PTR:0x[0-9a-f]*]] for type 'int', which requires 4 byte alignment // CHECK-REFERENCE-NEXT: [[PTR]]: note: pointer points here // CHECK-REFERENCE-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-REFERENCE-NEXT: {{^ \^}} @@ -66,28 +66,28 @@ int main(int, char **argv) { break; case 'm': - // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]:15: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMBER: misaligned.cpp:[[@LINE+4]]{{(:15)?}}: runtime error: member access within misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment // CHECK-MEMBER-NEXT: [[PTR]]: note: pointer points here // CHECK-MEMBER-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-MEMBER-NEXT: {{^ \^}} return s->k && 0; case 'f': - // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]:12: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-MEMFUN: misaligned.cpp:[[@LINE+4]]{{(:12)?}}: runtime error: member call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment // CHECK-MEMFUN-NEXT: [[PTR]]: note: pointer points here // CHECK-MEMFUN-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-MEMFUN-NEXT: {{^ \^}} return s->f() && 0; case 'n': - // CHECK-NEW: misaligned.cpp:[[@LINE+4]]:21: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment + // CHECK-NEW: misaligned.cpp:[[@LINE+4]]{{(:21)?}}: runtime error: constructor call on misaligned address [[PTR:0x[0-9a-f]*]] for type 'S', which requires 4 byte alignment // CHECK-NEW-NEXT: [[PTR]]: note: pointer points here // CHECK-NEW-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-NEW-NEXT: {{^ \^}} return (new (s) S)->k && 0; case 'u': { - // CHECK-UPCAST: misaligned.cpp:[[@LINE+4]]:17: runtime error: upcast of misaligned address [[PTR:0x[0-9a-f]*]] for type 'T', which requires 4 byte alignment + // CHECK-UPCAST: misaligned.cpp:[[@LINE+4]]{{(:17)?}}: runtime error: upcast of misaligned address [[PTR:0x[0-9a-f]*]] for type 'T', which requires 4 byte alignment // CHECK-UPCAST-NEXT: [[PTR]]: note: pointer points here // CHECK-UPCAST-NEXT: {{^ 00 00 00 01 02 03 04 05}} // CHECK-UPCAST-NEXT: {{^ \^}} @@ -96,7 +96,7 @@ int main(int, char **argv) { } case 'w': - // CHECK-WILD: misaligned.cpp:[[@LINE+3]]:35: runtime error: member access within misaligned address 0x{{0+}}123 for type 'S', which requires 4 byte alignment + // CHECK-WILD: misaligned.cpp:[[@LINE+3]]{{(:35)?}}: runtime error: member access within misaligned address 0x{{0+}}123 for type 'S', which requires 4 byte alignment // CHECK-WILD-NEXT: 0x{{0+}}123: note: pointer points here // CHECK-WILD-NEXT: <memory cannot be printed> return static_cast<S*>(wild)->k; diff --git a/test/ubsan/TestCases/TypeCheck/null.cpp b/test/ubsan/TestCases/TypeCheck/null.cpp index 190dc30e5dfe..1e179559d5df 100644 --- a/test/ubsan/TestCases/TypeCheck/null.cpp +++ b/test/ubsan/TestCases/TypeCheck/null.cpp @@ -1,6 +1,6 @@ // RUN: %clangxx -fsanitize=null %s -O3 -o %t // RUN: %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD -// RUN: not --crash %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: %expect_crash %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE // RUN: %run %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE // RUN: %run %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER // RUN: %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp new file mode 100644 index 000000000000..dc27d9f39ce3 --- /dev/null +++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base-construction.cpp @@ -0,0 +1,13 @@ +// RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr %s -o %t +// RUN: %run %t + +// REQUIRES: cxxabi + +int volatile n; + +struct A { virtual ~A() {} }; +struct B: virtual A {}; +struct C: virtual A { ~C() { n = 0; } }; +struct D: virtual B, virtual C {}; + +int main() { delete new D; } diff --git a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp index 806e45c7d357..09deac143724 100644 --- a/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp +++ b/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp @@ -1,8 +1,7 @@ // RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr -g %s -O3 -o %t // RUN: not %run %t 2>&1 | FileCheck %s -// FIXME: This test produces linker errors on Darwin. -// XFAIL: darwin +// REQUIRES: cxxabi struct S { virtual int f() { return 0; } }; struct T : virtual S {}; diff --git a/test/ubsan/TestCases/TypeCheck/vptr.cpp b/test/ubsan/TestCases/TypeCheck/vptr.cpp index 1f8ee02641a8..a95edf918c95 100644 --- a/test/ubsan/TestCases/TypeCheck/vptr.cpp +++ b/test/ubsan/TestCases/TypeCheck/vptr.cpp @@ -1,37 +1,33 @@ -// RUN: %clangxx -frtti -fsanitize=vptr -g %s -O3 -o %t +// RUN: %clangxx -frtti -fsanitize=vptr -fno-sanitize-recover=vptr -g %s -O3 -o %t +// RUN: export UBSAN_OPTIONS=print_stacktrace=1 // RUN: %run %t rT && %run %t mT && %run %t fT && %run %t cT // RUN: %run %t rU && %run %t mU && %run %t fU && %run %t cU // RUN: %run %t rS && %run %t rV && %run %t oV -// RUN: %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace -// RUN: %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace -// RUN: %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace -// RUN: %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --strict-whitespace -// RUN: %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace -// RUN: %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --strict-whitespace -// RUN: %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --strict-whitespace -// RUN: %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --strict-whitespace +// RUN: not %run %t mS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --check-prefix=CHECK-%os-MEMBER --strict-whitespace +// RUN: not %run %t fS 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: not %run %t cS 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --check-prefix=CHECK-%os-DOWNCAST --strict-whitespace +// RUN: not %run %t mV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER --check-prefix=CHECK-%os-MEMBER --strict-whitespace +// RUN: not %run %t fV 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN --strict-whitespace +// RUN: not %run %t cV 2>&1 | FileCheck %s --check-prefix=CHECK-DOWNCAST --check-prefix=CHECK-%os-DOWNCAST --strict-whitespace +// RUN: not %run %t oU 2>&1 | FileCheck %s --check-prefix=CHECK-OFFSET --check-prefix=CHECK-%os-OFFSET --strict-whitespace +// RUN: not %run %t m0 2>&1 | FileCheck %s --check-prefix=CHECK-NULL-MEMBER --check-prefix=CHECK-%os-NULL-MEMBER --strict-whitespace // RUN: (echo "vptr_check:S"; echo "vptr_check:T"; echo "vptr_check:U") > %t.supp -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mS 2>&1 -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fS 2>&1 -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cS 2>&1 -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t mV 2>&1 -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t fV 2>&1 -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t cV 2>&1 -// RUN: UBSAN_OPTIONS="suppressions='%t.supp':halt_on_error=1" %run %t oU 2>&1 +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t mS +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t fS +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t cS +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t mV +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t fV +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t cV +// RUN: UBSAN_OPTIONS="suppressions='%t.supp'" %run %t oU // RUN: echo "vptr_check:S" > %t.loc-supp -// RUN: UBSAN_OPTIONS="suppressions='%t.loc-supp':halt_on_error=1" not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS +// RUN: UBSAN_OPTIONS="suppressions='%t.loc-supp'" not %run %t x- 2>&1 | FileCheck %s --check-prefix=CHECK-LOC-SUPPRESS -// FIXME: This test produces linker errors on Darwin. -// XFAIL: darwin -// REQUIRES: stable-runtime - -extern "C" { -const char *__ubsan_default_options() { - return "print_stacktrace=1"; -} -} +// REQUIRES: stable-runtime, cxxabi +#include <new> +#include <assert.h> +#include <stdio.h> struct S { S() : a(0) {} @@ -57,7 +53,9 @@ T *p = 0; int access_p(T *p, char type); -int main(int, char **argv) { +int main(int argc, char **argv) { + assert(argc > 1); + fprintf(stderr, "Test case: %s\n", argv[1]); T t; (void)t.a; (void)t.b; @@ -76,12 +74,15 @@ int main(int, char **argv) { (void)((T&)u).S::v(); char Buffer[sizeof(U)] = {}; + char TStorage[sizeof(T)]; switch (argv[1][1]) { case '0': p = reinterpret_cast<T*>(Buffer); break; case 'S': - p = reinterpret_cast<T*>(new S); + // Make sure p points to the memory chunk of sufficient size to prevent ASan + // reports about out-of-bounds access. + p = reinterpret_cast<T*>(new(TStorage) S); break; case 'T': p = new T; @@ -103,7 +104,7 @@ int access_p(T *p, char type) { case 'r': // Binding a reference to storage of appropriate size and alignment is OK. {T &r = *p;} - break; + return 0; case 'x': for (int i = 0; i < 2; i++) { @@ -124,7 +125,7 @@ int access_p(T *p, char type) { // CHECK-MEMBER-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-MEMBER-NEXT: {{^ vptr for}} [[DYN_TYPE]] - // CHECK-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + // CHECK-Linux-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] return p->b; // CHECK-NULL-MEMBER: vptr.cpp:[[@LINE-2]]:15: runtime error: member access within address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' @@ -132,7 +133,7 @@ int access_p(T *p, char type) { // CHECK-NULL-MEMBER-NEXT: {{^ ?.. .. .. .. ?00 00 00 00 ?00 00 00 00 ?}} // CHECK-NULL-MEMBER-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-NULL-MEMBER-NEXT: {{^ invalid vptr}} - // CHECK-NULL-MEMBER-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE-7]] + // CHECK-Linux-NULL-MEMBER: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE-7]] case 'f': // CHECK-MEMFUN: vptr.cpp:[[@LINE+6]]:12: runtime error: member call on address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' @@ -149,17 +150,18 @@ int access_p(T *p, char type) { // CHECK-OFFSET-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-OFFSET-NEXT: {{^ \^ ( ~~~~~~~~~~~~)?~~~~~~~~~~~ *$}} // CHECK-OFFSET-NEXT: {{^ ( )?vptr for}} 'T' base class of [[DYN_TYPE]] - // CHECK-OFFSET-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] + // CHECK-Linux-OFFSET: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] return reinterpret_cast<U*>(p)->v() - 2; case 'c': - // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:5: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-DOWNCAST: vptr.cpp:[[@LINE+6]]:11: runtime error: downcast of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' // CHECK-DOWNCAST-NEXT: [[PTR]]: note: object is of type [[DYN_TYPE:'S'|'U']] // CHECK-DOWNCAST-NEXT: {{^ .. .. .. .. .. .. .. .. .. .. .. .. }} // CHECK-DOWNCAST-NEXT: {{^ \^~~~~~~~~~~(~~~~~~~~~~~~)? *$}} // CHECK-DOWNCAST-NEXT: {{^ vptr for}} [[DYN_TYPE]] - // CHECK-DOWNCAST-NEXT: #0 {{.*}} in access_p{{.*}}vptr.cpp:[[@LINE+1]] - static_cast<T*>(reinterpret_cast<S*>(p)); + // CHECK-Linux-DOWNCAST: #0 {{.*}}access_p{{.*}}vptr.cpp:[[@LINE+1]] + (void)static_cast<T*>(reinterpret_cast<S*>(p)); return 0; } + return 0; } diff --git a/test/ubsan/lit.common.cfg b/test/ubsan/lit.common.cfg index d28733a61cf8..7ae078a8625c 100644 --- a/test/ubsan/lit.common.cfg +++ b/test/ubsan/lit.common.cfg @@ -17,18 +17,22 @@ config.test_source_root = os.path.dirname(__file__) # Choose between standalone and UBSan+ASan modes. ubsan_lit_test_mode = get_required_attr(config, 'ubsan_lit_test_mode') if ubsan_lit_test_mode == "Standalone": - config.name = 'UndefinedBehaviorSanitizer-Standalone' + config.name = 'UBSan-Standalone-' + config.target_arch config.available_features.add("ubsan-standalone") clang_ubsan_cflags = [] elif ubsan_lit_test_mode == "AddressSanitizer": - if config.host_os == 'Darwin': - # ubsan-asan doesn't yet work on Darwin, - # see http://llvm.org/bugs/show_bug.cgi?id=21112. - config.unsupported = True - config.name = 'UndefinedBehaviorSanitizer-AddressSanitizer' + config.name = 'UBSan-ASan-' + config.target_arch config.available_features.add("ubsan-asan") clang_ubsan_cflags = ["-fsanitize=address"] config.environment['ASAN_OPTIONS'] = 'detect_leaks=0' +elif ubsan_lit_test_mode == "MemorySanitizer": + config.name = 'UBSan-MSan-' + config.target_arch + config.available_features.add("ubsan-msan") + clang_ubsan_cflags = ["-fsanitize=memory"] +elif ubsan_lit_test_mode == "ThreadSanitizer": + config.name = 'UBSan-TSan-' + config.target_arch + config.available_features.add("ubsan-tsan") + clang_ubsan_cflags = ["-fsanitize=thread"] else: lit_config.fatal("Unknown UBSan test mode: %r" % ubsan_lit_test_mode) @@ -47,9 +51,14 @@ config.substitutions.append( ("%clangxx ", build_invocation(clang_ubsan_cxxflags config.suffixes = ['.c', '.cc', '.cpp'] # Check that the host supports UndefinedBehaviorSanitizer tests -if config.host_os not in ['Linux', 'Darwin', 'FreeBSD']: +if config.host_os not in ['Linux', 'Darwin', 'FreeBSD', 'Windows']: config.unsupported = True +if config.host_os == 'Windows': + # We do not currently support enough of the Microsoft ABI for UBSan to work on + # Windows. + config.available_features.remove('cxxabi') + # Allow tests to use REQUIRES=stable-runtime. For use when you cannot use XFAIL # because the test hangs or fails on one configuration and not the other. if config.target_arch.startswith('arm') == False: diff --git a/test/ubsan/lit.site.cfg.in b/test/ubsan/lit.site.cfg.in index ef72d2bbb42f..1b06881b3527 100644 --- a/test/ubsan/lit.site.cfg.in +++ b/test/ubsan/lit.site.cfg.in @@ -3,6 +3,8 @@ lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configu # Tool-specific config options. config.ubsan_lit_test_mode = "@UBSAN_LIT_TEST_MODE@" +config.target_cflags = "@UBSAN_TEST_TARGET_CFLAGS@" +config.target_arch = "@UBSAN_TEST_TARGET_ARCH@" # Load tool-specific config that would do the real work. lit_config.load_config(config, "@UBSAN_LIT_TESTS_DIR@/lit.common.cfg") diff --git a/www/index.html b/www/index.html index 875ce57a8267..5d21abf26a3b 100644 --- a/www/index.html +++ b/www/index.html @@ -133,10 +133,10 @@ <p>compiler-rt doesn't have its own mailing list, if you have questions please email the <a - href="http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev">llvmdev</a> mailing + href="http://lists.llvm.org/mailman/listinfo/llvm-dev">llvm-dev</a> mailing list. Commits to the compiler-rt SVN module are automatically sent to the <a - href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits">llvm-commits</a> + href="http://lists.llvm.org/mailman/listinfo/llvm-commits">llvm-commits</a> mailing list.</p> </div> </body> diff --git a/www/menu.html.incl b/www/menu.html.incl index cc0af560fd42..dfcb6f4ad1f4 100644 --- a/www/menu.html.incl +++ b/www/menu.html.incl @@ -10,8 +10,8 @@ <div class="submenu"> <label>Quick Links</label> - <a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev">llvmdev</a> - <a href="http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits">llvm-commits</a> + <a href="http://lists.llvm.org/mailman/listinfo/llvm-dev">llvm-dev</a> + <a href="http://lists.llvm.org/mailman/listinfo/llvm-commits">llvm-commits</a> <a href="http://llvm.org/bugs/">Bug Reports</a> <a href="http://llvm.org/svn/llvm-project/compiler-rt/trunk/">Browse SVN</a> <a href="http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/">Browse ViewVC</a> |