diff options
author | Alexey Samsonov <samsonov@google.com> | 2014-02-14 14:06:10 +0000 |
---|---|---|
committer | Alexey Samsonov <samsonov@google.com> | 2014-02-14 14:06:10 +0000 |
commit | ba2d0d7b386f38c97eee11749f2fc75449c2b253 (patch) | |
tree | 645ce9b32dc3f7727d3bac94f8c3387d051b9c01 /test/asan | |
parent | 5811f0e1e955866b76b0d9ebeccd2e4755c1dac8 (diff) |
Move ASan lit-tests under test/asan
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@201413 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/asan')
151 files changed, 4427 insertions, 0 deletions
diff --git a/test/asan/CMakeLists.txt b/test/asan/CMakeLists.txt new file mode 100644 index 000000000..c47d04876 --- /dev/null +++ b/test/asan/CMakeLists.txt @@ -0,0 +1,64 @@ +set(ASAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +set(ASAN_TESTSUITES) + +if(CAN_TARGET_arm_android) + # This is only true if we are cross-compiling. + # Build all tests with host compiler and use host tools. + set(ASAN_TEST_TARGET_CC ${CMAKE_C_COMPILER}) + get_filename_component(ASAN_TEST_LLVM_TOOLS_DIR ${CMAKE_C_COMPILER} PATH) + set(ASAN_TEST_CONFIG_SUFFIX "-arm-android") + set(ASAN_TEST_BITS "32") + get_target_flags_for_arch(arm_android ASAN_TEST_TARGET_CFLAGS) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/GenericConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/ARMAndroidConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/ARMAndroidConfig) +endif() + +if(CAN_TARGET_x86_64 OR CAN_TARGET_powerpc64) + set(ASAN_TEST_CONFIG_SUFFIX "64") + set(ASAN_TEST_BITS "64") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS}) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/GenericConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) +endif() + +if(CAN_TARGET_i386) + set(ASAN_TEST_CONFIG_SUFFIX "32") + set(ASAN_TEST_BITS "32") + set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS}) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/GenericConfig/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg + ) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) +endif() + +if(LLVM_INCLUDE_TESTS) +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg + ) +endif() + +# Run ASan tests only if we're sure we may produce working binaries. +set(ASAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + asan_runtime_libraries) +set(ASAN_TEST_PARAMS + asan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) +# FIXME: support unit test in the android test runner +if(LLVM_INCLUDE_TESTS AND NOT CAN_TARGET_arm_android) + list(APPEND ASAN_TEST_DEPS AsanUnitTests) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) +endif() +add_lit_testsuite(check-asan "Running the AddressSanitizer tests" + ${ASAN_TESTSUITES} + PARAMS ${ASAN_TEST_PARAMS} + DEPENDS ${ASAN_TEST_DEPS}) +set_target_properties(check-asan PROPERTIES FOLDER "ASan tests") diff --git a/test/asan/GenericConfig/lit.site.cfg.in b/test/asan/GenericConfig/lit.site.cfg.in new file mode 100644 index 000000000..48ea81252 --- /dev/null +++ b/test/asan/GenericConfig/lit.site.cfg.in @@ -0,0 +1,17 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Tool-specific config options. +config.name_suffix = "@ASAN_TEST_CONFIG_SUFFIX@" +config.asan_lit_source_dir = "@ASAN_LIT_SOURCE_DIR@" +config.target_cflags = "@ASAN_TEST_TARGET_CFLAGS@" +config.clang = "@ASAN_TEST_TARGET_CC@" +config.llvm_tools_dir = "@ASAN_TEST_LLVM_TOOLS_DIR@" +config.bits = "@ASAN_TEST_BITS@" +config.android = "@CAN_TARGET_arm_android@" + +# 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, "@ASAN_LIT_SOURCE_DIR@/lit.cfg") diff --git a/test/asan/TestCases/Darwin/asan_gen_prefixes.cc b/test/asan/TestCases/Darwin/asan_gen_prefixes.cc new file mode 100644 index 000000000..13363ac47 --- /dev/null +++ b/test/asan/TestCases/Darwin/asan_gen_prefixes.cc @@ -0,0 +1,14 @@ +// Make sure __asan_gen_* strings have the correct prefixes on Darwin +// ("L" in __TEXT,__cstring, "l" in __TEXT,__const + +// RUN: %clang_asan %s -S -o %t.s +// RUN: cat %t.s | FileCheck %s || exit 1 + +int x, y, z; +int main() { return 0; } +// CHECK: .section{{.*}}__TEXT,__const +// CHECK: l___asan_gen_ +// CHECK: .section{{.*}}__TEXT,__cstring,cstring_literals +// CHECK: L___asan_gen_ +// CHECK: L___asan_gen_ +// CHECK: L___asan_gen_ diff --git a/test/asan/TestCases/Darwin/interface_symbols_darwin.c b/test/asan/TestCases/Darwin/interface_symbols_darwin.c new file mode 100644 index 000000000..03042d62a --- /dev/null +++ b/test/asan/TestCases/Darwin/interface_symbols_darwin.c @@ -0,0 +1,42 @@ +// Check the presense of interface symbols in the ASan runtime dylib. +// If you're changing this file, please also change +// ../Linux/interface_symbols.c + +// RUN: %clang_asan -dead_strip -O2 %s -o %t.exe +// RUN: rm -f %t.symbols %t.interface + +// RUN: nm -g `otool -L %t.exe | grep "asan_osx_dynamic.dylib" | \ +// RUN: tr -d '\011' | \ +// RUN: sed "s/.dylib.*/.dylib/"` \ +// RUN: | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | grep -v "__asan_malloc_hook" \ +// RUN: | grep -v "__asan_free_hook" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols + +// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// 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_get_current_fake_stack >> %t.interface +// RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: for i in `jot - 0 10`; do echo __asan_stack_malloc_$i >> %t.interface; done +// RUN: for i in `jot - 0 10`; do echo __asan_stack_free_$i >> %t.interface; done + +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +int main() { return 0; } diff --git a/test/asan/TestCases/Darwin/lit.local.cfg b/test/asan/TestCases/Darwin/lit.local.cfg new file mode 100644 index 000000000..a85dfcd24 --- /dev/null +++ b/test/asan/TestCases/Darwin/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 ['Darwin']: + config.unsupported = True diff --git a/test/asan/TestCases/Darwin/malloc_set_zone_name-mprotect.cc b/test/asan/TestCases/Darwin/malloc_set_zone_name-mprotect.cc new file mode 100644 index 000000000..807a8283e --- /dev/null +++ b/test/asan/TestCases/Darwin/malloc_set_zone_name-mprotect.cc @@ -0,0 +1,51 @@ +// Regression test for a bug in malloc_create_zone() +// (https://code.google.com/p/address-sanitizer/issues/detail?id=203) +// The old implementation of malloc_create_zone() didn't always return a +// page-aligned address, so we can only test on a best-effort basis. + +// RUN: %clangxx_asan %s -o %t +// RUN: %t 2>&1 + +#include <malloc/malloc.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +const int kNumIter = 4096; +const int kNumZones = 100; +int main() { + char *mem[kNumIter * 2]; + // Allocate memory chunks from different size classes up to 1 page. + // (For the case malloc() returns memory chunks in descending order) + for (int i = 0; i < kNumIter; i++) { + mem[i] = (char*)malloc(8 * i); + } + // Try to allocate a page-aligned malloc zone. Otherwise the mprotect() call + // in malloc_set_zone_name() will silently fail. + malloc_zone_t *zone = NULL; + bool aligned = false; + for (int i = 0; i < kNumZones; i++) { + zone = malloc_create_zone(0, 0); + if (((uintptr_t)zone & (~0xfff)) == (uintptr_t)zone) { + aligned = true; + break; + } + } + if (!aligned) { + printf("Warning: couldn't allocate a page-aligned zone."); + return 0; + } + // malloc_set_zone_name() calls mprotect(zone, 4096, PROT_READ | PROT_WRITE), + // modifies the zone contents and then calls mprotect(zone, 4096, PROT_READ). + malloc_set_zone_name(zone, "foobar"); + // Allocate memory chunks from different size classes again. + for (int i = 0; i < kNumIter; i++) { + mem[i + kNumIter] = (char*)malloc(8 * i); + } + // Access the allocated memory chunks and free them. + for (int i = 0; i < kNumIter * 2; i++) { + memset(mem[i], 'a', 8 * (i % kNumIter)); + free(mem[i]); + } + return 0; +} diff --git a/test/asan/TestCases/Darwin/malloc_zone-protected.cc b/test/asan/TestCases/Darwin/malloc_zone-protected.cc new file mode 100644 index 000000000..d5f6c7c12 --- /dev/null +++ b/test/asan/TestCases/Darwin/malloc_zone-protected.cc @@ -0,0 +1,20 @@ +// Make sure the zones created by malloc_create_zone() are write-protected. +#include <malloc/malloc.h> +#include <stdio.h> + +// RUN: %clangxx_asan %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + + +void *pwn(malloc_zone_t *unused_zone, size_t unused_size) { + printf("PWNED\n"); + return NULL; +} + +int main() { + malloc_zone_t *zone = malloc_create_zone(0, 0); + zone->malloc = pwn; + void *v = malloc_zone_malloc(zone, 1); + // CHECK-NOT: PWNED + return 0; +} diff --git a/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc b/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc new file mode 100644 index 000000000..208fe43ac --- /dev/null +++ b/test/asan/TestCases/Darwin/reexec-insert-libraries-env.cc @@ -0,0 +1,20 @@ +// Make sure ASan doesn't hang in an exec loop if DYLD_INSERT_LIBRARIES is set. +// This is a regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=159 + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// 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: %t 2>&1 | FileCheck %s || exit 1 +#include <stdio.h> +#include <stdlib.h> + +int main() { + const char kEnvName[] = "DYLD_INSERT_LIBRARIES"; + printf("%s=%s\n", kEnvName, getenv(kEnvName)); + // CHECK: {{DYLD_INSERT_LIBRARIES=.*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc b/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc new file mode 100644 index 000000000..fa0dd4f9d --- /dev/null +++ b/test/asan/TestCases/Darwin/unset-insert-libraries-on-exec.cc @@ -0,0 +1,20 @@ +// Make sure ASan removes the runtime library from DYLD_INSERT_LIBRARIES before +// executing other programs. + +// RUN: %clangxx_asan %s -o %t +// RUN: %clangxx %p/../Helpers/echo-env.cc -o %T/echo-env +// RUN: %clangxx %p/../SharedLibs/darwin-dummy-shared-lib-so.cc \ +// RUN: -dynamiclib -o %t-darwin-dummy-shared-lib-so.dylib + +// Make sure DYLD_INSERT_LIBRARIES doesn't contain the runtime library before +// execl(). + +// RUN: %t %T/echo-env >/dev/null 2>&1 +// RUN: DYLD_INSERT_LIBRARIES=%t-darwin-dummy-shared-lib-so.dylib \ +// RUN: %t %T/echo-env 2>&1 | FileCheck %s || exit 1 +#include <unistd.h> +int main(int argc, char *argv[]) { + execl(argv[1], argv[1], "DYLD_INSERT_LIBRARIES", NULL); + // CHECK: {{DYLD_INSERT_LIBRARIES = .*darwin-dummy-shared-lib-so.dylib.*}} + return 0; +} diff --git a/test/asan/TestCases/Helpers/blacklist-extra.cc b/test/asan/TestCases/Helpers/blacklist-extra.cc new file mode 100644 index 000000000..627115cdd --- /dev/null +++ b/test/asan/TestCases/Helpers/blacklist-extra.cc @@ -0,0 +1,5 @@ +// This function is broken, but this file is blacklisted +int externalBrokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} diff --git a/test/asan/TestCases/Helpers/echo-env.cc b/test/asan/TestCases/Helpers/echo-env.cc new file mode 100644 index 000000000..65e91c155 --- /dev/null +++ b/test/asan/TestCases/Helpers/echo-env.cc @@ -0,0 +1,19 @@ +// Helper binary for +// lit_tests/TestCases/Darwin/unset-insert-libraries-on-exec.cc +// Prints the environment variable with the given name. +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + if (argc != 2) { + printf("Usage: %s ENVNAME\n", argv[0]); + exit(1); + } + const char *value = getenv(argv[1]); + if (value) { + printf("%s = %s\n", argv[1], value); + } else { + printf("%s not set.\n", argv[1]); + } + return 0; +} diff --git a/test/asan/TestCases/Helpers/init-order-atexit-extra.cc b/test/asan/TestCases/Helpers/init-order-atexit-extra.cc new file mode 100644 index 000000000..e4189d19d --- /dev/null +++ b/test/asan/TestCases/Helpers/init-order-atexit-extra.cc @@ -0,0 +1,16 @@ +#include <stdio.h> + +class C { + public: + C() { value = 42; } + ~C() { } + int value; +}; + +C c; + +void AccessC() { + printf("C value: %d\n", c.value); +} + +int main() { return 0; } diff --git a/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc b/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc new file mode 100644 index 000000000..d4606f0af --- /dev/null +++ b/test/asan/TestCases/Helpers/init-order-pthread-create-extra.cc @@ -0,0 +1,2 @@ +void *bar(void *input); +void *glob2 = bar((void*)0x2345); diff --git a/test/asan/TestCases/Helpers/initialization-blacklist-extra.cc b/test/asan/TestCases/Helpers/initialization-blacklist-extra.cc new file mode 100644 index 000000000..09aed2112 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-blacklist-extra.cc @@ -0,0 +1,15 @@ +int zero_init() { return 0; } +int badGlobal = zero_init(); +int readBadGlobal() { return badGlobal; } + +namespace badNamespace { +class BadClass { + public: + BadClass() { value = 0; } + int value; +}; +// Global object with non-trivial constructor. +BadClass bad_object; +} // namespace badNamespace + +int accessBadObject() { return badNamespace::bad_object.value; } diff --git a/test/asan/TestCases/Helpers/initialization-blacklist-extra2.cc b/test/asan/TestCases/Helpers/initialization-blacklist-extra2.cc new file mode 100644 index 000000000..69455a0a6 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-blacklist-extra2.cc @@ -0,0 +1,4 @@ +int zero_init(); +int badSrcGlobal = zero_init(); +int readBadSrcGlobal() { return badSrcGlobal; } + diff --git a/test/asan/TestCases/Helpers/initialization-blacklist.txt b/test/asan/TestCases/Helpers/initialization-blacklist.txt new file mode 100644 index 000000000..832946356 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-blacklist.txt @@ -0,0 +1,3 @@ +global:*badGlobal*=init +type:*badNamespace::BadClass*=init +src:*initialization-blacklist-extra2.cc=init diff --git a/test/asan/TestCases/Helpers/initialization-bug-extra.cc b/test/asan/TestCases/Helpers/initialization-bug-extra.cc new file mode 100644 index 000000000..3c4cb411d --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-bug-extra.cc @@ -0,0 +1,5 @@ +// This file simply declares a dynamically initialized var by the name of 'y'. +int initY() { + return 5; +} +int y = initY(); diff --git a/test/asan/TestCases/Helpers/initialization-bug-extra2.cc b/test/asan/TestCases/Helpers/initialization-bug-extra2.cc new file mode 100644 index 000000000..a3d8f190e --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-bug-extra2.cc @@ -0,0 +1,6 @@ +// 'z' is dynamically initialized global from different TU. +extern int z; +int __attribute__((noinline)) initY() { + return z + 1; +} +int y = initY(); diff --git a/test/asan/TestCases/Helpers/initialization-constexpr-extra.cc b/test/asan/TestCases/Helpers/initialization-constexpr-extra.cc new file mode 100644 index 000000000..b32466a98 --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-constexpr-extra.cc @@ -0,0 +1,3 @@ +// Constexpr: +int getCoolestInteger(); +static int coolest_integer = getCoolestInteger(); diff --git a/test/asan/TestCases/Helpers/initialization-nobug-extra.cc b/test/asan/TestCases/Helpers/initialization-nobug-extra.cc new file mode 100644 index 000000000..886165aff --- /dev/null +++ b/test/asan/TestCases/Helpers/initialization-nobug-extra.cc @@ -0,0 +1,9 @@ +// Linker initialized: +int getAB(); +static int ab = getAB(); +// Function local statics: +int countCalls(); +static int one = countCalls(); +// Trivial constructor, non-trivial destructor: +int getStructWithDtorValue(); +static int val = getStructWithDtorValue(); diff --git a/test/asan/TestCases/Helpers/lit.local.cfg b/test/asan/TestCases/Helpers/lit.local.cfg new file mode 100644 index 000000000..2fc4d9945 --- /dev/null +++ b/test/asan/TestCases/Helpers/lit.local.cfg @@ -0,0 +1,3 @@ +# Sources in this directory are helper files for tests which test functionality +# involving multiple translation units. +config.suffixes = [] diff --git a/test/asan/TestCases/Linux/asan_prelink_test.cc b/test/asan/TestCases/Linux/asan_prelink_test.cc new file mode 100644 index 000000000..0f158c1bb --- /dev/null +++ b/test/asan/TestCases/Linux/asan_prelink_test.cc @@ -0,0 +1,28 @@ +// Test if asan works with prelink. +// It does not actually use prelink, but relies on ld's flag -Ttext-segment +// or gold's flag -Ttext (we try the first flag first, if that fails we +// try the second flag). +// +// RUN: %clangxx_asan -c %s -o %t.o +// 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 %t 2>&1 | FileCheck %s + +// REQUIRES: x86_64-supported-target, asan-64-bits +#if BUILD_SO +int G; +int *getG() { + return &G; +} +#else +#include <stdio.h> +extern int *getG(); +int main(int argc, char **argv) { + long p = (long)getG(); + printf("SO mapped at %lx\n", p & ~0xffffffffUL); + *getG() = 0; +} +#endif +// CHECK: 0x003000000000, 0x004fffffffff{{.*}} MidMem +// CHECK: SO mapped at 3600000000 diff --git a/test/asan/TestCases/Linux/clone_test.cc b/test/asan/TestCases/Linux/clone_test.cc new file mode 100644 index 000000000..432190a18 --- /dev/null +++ b/test/asan/TestCases/Linux/clone_test.cc @@ -0,0 +1,44 @@ +// Regression test for: +// http://code.google.com/p/address-sanitizer/issues/detail?id=37 + +// RUN: %clangxx_asan -O0 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t | FileCheck %s + +#include <stdio.h> +#include <sched.h> +#include <sys/syscall.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +int Child(void *arg) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + _exit(1); // NoReturn, stack will remain unpoisoned unless we do something. +} + +int main(int argc, char **argv) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + char *sp = child_stack + kStackSize; // Stack grows down. + printf("Parent: %p\n", sp); + pid_t clone_pid = clone(Child, sp, CLONE_FILES | CLONE_VM, NULL); + int status; + pid_t wait_result = waitpid(clone_pid, &status, __WCLONE); + if (wait_result < 0) { + perror("waitpid"); + return 0; + } + if (wait_result == clone_pid && WIFEXITED(status)) { + // Make sure the child stack was indeed unpoisoned. + for (int i = 0; i < kStackSize; i++) + child_stack[i] = i; + int ret = child_stack[argc - 1]; + printf("PASSED\n"); + // CHECK: PASSED + return ret; + } + return 0; +} diff --git a/test/asan/TestCases/Linux/coverage.cc b/test/asan/TestCases/Linux/coverage.cc new file mode 100644 index 000000000..449e1c446 --- /dev/null +++ b/test/asan/TestCases/Linux/coverage.cc @@ -0,0 +1,55 @@ +// RUN: %clangxx_asan -mllvm -asan-coverage=1 -DSHARED %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -mllvm -asan-coverage=1 %s -o %t -Wl,-R. %t.so +// RUN: export ASAN_OPTIONS=coverage=1:verbosity=1 +// RUN: %t 2>&1 | FileCheck %s --check-prefix=CHECK-main +// RUN: %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo +// RUN: %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar +// RUN: %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar +// RUN: not %t foo bar 1 2 2>&1 | FileCheck %s --check-prefix=CHECK-report +// +// https://code.google.com/p/address-sanitizer/issues/detail?id=263 +// XFAIL: android + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifdef SHARED +void bar() { printf("bar\n"); } +#else +__attribute__((noinline)) +void foo() { printf("foo\n"); } +extern void bar(); + +int G[4]; + +int main(int argc, char **argv) { + fprintf(stderr, "PID: %d\n", getpid()); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "foo")) + foo(); + if (!strcmp(argv[i], "bar")) + bar(); + } + return G[argc]; // Buffer overflow if argc >= 4. +} +#endif + +// CHECK-main: PID: [[PID:[0-9]+]] +// CHECK-main: [[PID]].sancov: 1 PCs written +// CHECK-main-NOT: .so.[[PID]] +// +// CHECK-foo: PID: [[PID:[0-9]+]] +// CHECK-foo: [[PID]].sancov: 2 PCs written +// 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-foo-bar: PID: [[PID:[0-9]+]] +// CHECK-foo-bar: [[PID]].sancov: 2 PCs written +// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written +// +// CHECK-report: AddressSanitizer: global-buffer-overflow +// CHECK-report: PCs written diff --git a/test/asan/TestCases/Linux/function-sections-are-bad.cc b/test/asan/TestCases/Linux/function-sections-are-bad.cc new file mode 100644 index 000000000..cccd6ca1c --- /dev/null +++ b/test/asan/TestCases/Linux/function-sections-are-bad.cc @@ -0,0 +1,39 @@ +// Check that --gc-sections does not throw away (or localize) parts of sanitizer +// interface. +// RUN: %clang_asan -m64 %s -Wl,--gc-sections -o %t +// RUN: %clang_asan -m64 %s -DBUILD_SO -fPIC -o %t-so.so -shared +// RUN: %t 2>&1 + +#ifndef BUILD_SO +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + + typedef void (*F)(); + F f = (F)dlsym(handle, "call_rtl_from_dso"); + printf("%s\n", dlerror()); + assert(dlerror() == 0); + f(); + + dlclose(handle); + return 0; +} + +#else // BUILD_SO + +#include <sanitizer/msan_interface.h> +extern "C" void call_rtl_from_dso() { + volatile int32_t x; + volatile int32_t y = __sanitizer_unaligned_load32((void *)&x); +} + +#endif // BUILD_SO diff --git a/test/asan/TestCases/Linux/glob.cc b/test/asan/TestCases/Linux/glob.cc new file mode 100644 index 000000000..123768b09 --- /dev/null +++ b/test/asan/TestCases/Linux/glob.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <string> + + +int main(int argc, char *argv[]) { + std::string path = argv[1]; + std::string pattern = path + "/glob_test_root/*a"; + printf("pattern: %s\n", pattern.c_str()); + + glob_t globbuf; + int res = glob(pattern.c_str(), 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + globfree(&globbuf); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/test/asan/TestCases/Linux/glob_test_root/aa b/test/asan/TestCases/Linux/glob_test_root/aa new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/asan/TestCases/Linux/glob_test_root/aa diff --git a/test/asan/TestCases/Linux/glob_test_root/ab b/test/asan/TestCases/Linux/glob_test_root/ab new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/asan/TestCases/Linux/glob_test_root/ab diff --git a/test/asan/TestCases/Linux/glob_test_root/ba b/test/asan/TestCases/Linux/glob_test_root/ba new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/asan/TestCases/Linux/glob_test_root/ba diff --git a/test/asan/TestCases/Linux/globals-gc-sections.cc b/test/asan/TestCases/Linux/globals-gc-sections.cc new file mode 100644 index 000000000..72a9e9498 --- /dev/null +++ b/test/asan/TestCases/Linux/globals-gc-sections.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan %s -o %t -Wl,--gc-sections -ffunction-sections -mllvm -asan-globals=0 +// RUN: %clangxx_asan %s -o %t -Wl,--gc-sections -ffunction-sections -mllvm -asan-globals=1 + +// https://code.google.com/p/address-sanitizer/issues/detail?id=260 +// XFAIL: * + +int undefined(); + +int (*unused)() = undefined; + +int main() { + return 0; +} diff --git a/test/asan/TestCases/Linux/heap-overflow-large.cc b/test/asan/TestCases/Linux/heap-overflow-large.cc new file mode 100644 index 000000000..67e9c3718 --- /dev/null +++ b/test/asan/TestCases/Linux/heap-overflow-large.cc @@ -0,0 +1,23 @@ +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=183 + +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %t 12 2>&1 | FileCheck %s +// RUN: not %t 100 2>&1 | FileCheck %s +// RUN: not %t 10000 2>&1 | FileCheck %s + +#include <stdlib.h> +#include <string.h> + +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]; + // CHECK: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}} + // CHECK: #0 0x{{.*}} in main {{.*}}heap-overflow-large.cc:[[@LINE-2]] + // CHECK: AddressSanitizer can not {{(provide additional info|describe address in more detail \(wild memory access suspected\))}} + // CHECK: SUMMARY: AddressSanitizer: {{(heap-buffer-overflow|SEGV)}} + delete[] x; + return res; +} diff --git a/test/asan/TestCases/Linux/heavy_uar_test.cc b/test/asan/TestCases/Linux/heavy_uar_test.cc new file mode 100644 index 000000000..0e2bf2fee --- /dev/null +++ b/test/asan/TestCases/Linux/heavy_uar_test.cc @@ -0,0 +1,54 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +__attribute__((noinline)) +char *pretend_to_do_something(char *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); + return x; +} + +__attribute__((noinline)) +char *LeakStack() { + char x[1024]; + memset(x, 0, sizeof(x)); + return pretend_to_do_something(x); +} + +template<size_t kFrameSize> +__attribute__((noinline)) +void RecuriveFunctionWithStackFrame(int depth) { + if (depth <= 0) return; + char x[kFrameSize]; + x[0] = depth; + pretend_to_do_something(x); + RecuriveFunctionWithStackFrame<kFrameSize>(depth - 1); +} + +int main(int argc, char **argv) { + int n_iter = argc >= 2 ? atoi(argv[1]) : 1000; + int depth = argc >= 3 ? atoi(argv[2]) : 500; + for (int i = 0; i < n_iter; i++) { + RecuriveFunctionWithStackFrame<10>(depth); + RecuriveFunctionWithStackFrame<100>(depth); + RecuriveFunctionWithStackFrame<500>(depth); + RecuriveFunctionWithStackFrame<1024>(depth); + RecuriveFunctionWithStackFrame<2000>(depth); + RecuriveFunctionWithStackFrame<5000>(depth); + RecuriveFunctionWithStackFrame<10000>(depth); + } + char *stale_stack = LeakStack(); + RecuriveFunctionWithStackFrame<1024>(10); + stale_stack[100]++; + // CHECK: ERROR: AddressSanitizer: stack-use-after-return on address + // CHECK: is located in stack of thread T0 at offset {{116|132}} in frame + // CHECK: in LeakStack(){{.*}}heavy_uar_test.cc: + // CHECK: [{{16|32}}, {{1040|1056}}) 'x' + return 0; +} diff --git a/test/asan/TestCases/Linux/initialization-bug-any-order.cc b/test/asan/TestCases/Linux/initialization-bug-any-order.cc new file mode 100644 index 000000000..042a07e42 --- /dev/null +++ b/test/asan/TestCases/Linux/initialization-bug-any-order.cc @@ -0,0 +1,36 @@ +// Test to make sure basic initialization order errors are caught. +// Check that on Linux initialization order bugs are caught +// independently on order in which we list source files (if we specify +// 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 %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 %t 2>&1 | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +#include <cstdio> + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // CHECK: {{AddressSanitizer: initialization-order-fiasco}} + // CHECK: {{READ of size .* at 0x.* thread T0}} + // CHECK: {{#0 0x.* in .*initX.* .*initialization-bug-any-order.cc:}}[[@LINE-3]] + // CHECK: {{0x.* is located 0 bytes inside of global variable .*y.*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/test/asan/TestCases/Linux/interception_failure_test.cc b/test/asan/TestCases/Linux/interception_failure_test.cc new file mode 100644 index 000000000..9d161aa2d --- /dev/null +++ b/test/asan/TestCases/Linux/interception_failure_test.cc @@ -0,0 +1,22 @@ +// If user provides his own libc functions, ASan doesn't +// intercept these functions. + +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return 0; +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK-NOT: heap-use-after-free +} diff --git a/test/asan/TestCases/Linux/interception_malloc_test.cc b/test/asan/TestCases/Linux/interception_malloc_test.cc new file mode 100644 index 000000000..cdd7239ab --- /dev/null +++ b/test/asan/TestCases/Linux/interception_malloc_test.cc @@ -0,0 +1,23 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +extern "C" void *__interceptor_malloc(size_t size); +extern "C" void *malloc(size_t size) { + write(2, "malloc call\n", sizeof("malloc call\n") - 1); + return __interceptor_malloc(size); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: malloc call + // CHECK: heap-use-after-free +} diff --git a/test/asan/TestCases/Linux/interception_readdir_r_test.cc b/test/asan/TestCases/Linux/interception_readdir_r_test.cc new file mode 100644 index 000000000..198e1f388 --- /dev/null +++ b/test/asan/TestCases/Linux/interception_readdir_r_test.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -O0 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// +// RUN: %clangxx_asan -O0 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -D_FILE_OFFSET_BITS=64 -DTEMP_DIR='"'"%T"'"' -o %t && %t 2>&1 | FileCheck %s + +#include <dirent.h> +#include <memory.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + + +int main() { + // Ensure the readdir_r interceptor doesn't erroneously mark the entire dirent + // as written when the end of the directory pointer is reached. + fputs("test1: reading the " TEMP_DIR " directory...\n", stderr); + DIR *d = opendir(TEMP_DIR); + struct dirent *result = (struct dirent *)(0xfeedbeef); + // We assume the temp dir for this test doesn't have crazy long file names. + char entry_buffer[4096]; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + unsigned count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent *)entry_buffer)->d_reclen = 9999; + if (readdir_r(d, (struct dirent *)entry_buffer, &result) != 0) + abort(); + ++count; + } while (result != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test1: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries + + // Ensure the readdir64_r interceptor doesn't have the bug either. + fputs("test2: reading the " TEMP_DIR " directory...\n", stderr); + d = opendir(TEMP_DIR); + struct dirent64 *result64; + memset(entry_buffer, 0xab, sizeof(entry_buffer)); + count = 0; + do { + // Stamp the entry struct to try to trick the interceptor. + ((struct dirent64 *)entry_buffer)->d_reclen = 9999; + if (readdir64_r(d, (struct dirent64 *)entry_buffer, &result64) != 0) + abort(); + ++count; + } while (result64 != NULL); + fprintf(stderr, "read %d entries\n", count); + closedir(d); + // CHECK: test2: reading the {{.*}} directory... + // CHECK-NOT: stack-buffer-overflow + // CHECK: read {{.*}} entries +} diff --git a/test/asan/TestCases/Linux/interception_test.cc b/test/asan/TestCases/Linux/interception_test.cc new file mode 100644 index 000000000..2b3316d7d --- /dev/null +++ b/test/asan/TestCases/Linux/interception_test.cc @@ -0,0 +1,22 @@ +// ASan interceptor can be accessed with __interceptor_ prefix. + +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +extern "C" long __interceptor_strtol(const char *nptr, char **endptr, int base); +extern "C" long strtol(const char *nptr, char **endptr, int base) { + fprintf(stderr, "my_strtol_interceptor\n"); + return __interceptor_strtol(nptr, endptr, base); +} + +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return (int)strtol(x, 0, 10); + // CHECK: my_strtol_interceptor + // CHECK: heap-use-after-free +} diff --git a/test/asan/TestCases/Linux/interface_symbols_linux.c b/test/asan/TestCases/Linux/interface_symbols_linux.c new file mode 100644 index 000000000..f67a12476 --- /dev/null +++ b/test/asan/TestCases/Linux/interface_symbols_linux.c @@ -0,0 +1,36 @@ +// Check the presense of interface symbols in compiled file. + +// RUN: %clang_asan -O2 %s -o %t.exe +// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | grep -v "__asan_malloc_hook" \ +// RUN: | grep -v "__asan_free_hook" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_stack_" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols +// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// 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_get_current_fake_stack >> %t.interface +// RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing +// in "initialized data section". +// REQUIRES: x86_64-supported-target,i386-supported-target + +int main() { return 0; } diff --git a/test/asan/TestCases/Linux/lit.local.cfg b/test/asan/TestCases/Linux/lit.local.cfg new file mode 100644 index 000000000..57271b807 --- /dev/null +++ b/test/asan/TestCases/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/asan/TestCases/Linux/malloc-in-qsort.cc b/test/asan/TestCases/Linux/malloc-in-qsort.cc new file mode 100644 index 000000000..3251b35e1 --- /dev/null +++ b/test/asan/TestCases/Linux/malloc-in-qsort.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_malloc=0 not %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). +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only avaliable on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +// REQUIRES: compiler-rt-optimized + +#include <stdlib.h> +#include <stdio.h> + +int *GlobalPtr; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + GlobalPtr = new int[10]; + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); + return GlobalPtr[10]; +} + +// Fast unwind: can not unwind through qsort. +// FIXME: this test does not properly work with slow unwind yet. + +// CHECK-FAST: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-FAST: is located 0 bytes to the right +// CHECK-FAST: #0{{.*}}operator new +// CHECK-FAST-NEXT: #1{{.*}}QsortCallback +// CHECK-FAST-NOT: MyQsort +// +// CHECK-SLOW: ERROR: AddressSanitizer: heap-buffer-overflow +// CHECK-SLOW: is located 0 bytes to the right +// CHECK-SLOW: #0{{.*}}operator new +// CHECK-SLOW-NEXT: #1{{.*}}QsortCallback +// CHECK-SLOW: #{{.*}}MyQsort +// CHECK-SLOW-NEXT: #{{.*}}main diff --git a/test/asan/TestCases/Linux/malloc_delete_mismatch.cc b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc new file mode 100644 index 000000000..7010eb2de --- /dev/null +++ b/test/asan/TestCases/Linux/malloc_delete_mismatch.cc @@ -0,0 +1,31 @@ +// Check that we detect malloc/delete mismatch only if the approptiate flag +// is set. + +// RUN: %clangxx_asan -g %s -o %t 2>&1 + +// Find error and provide malloc context. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=ALLOC-STACK + +// No error here. +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=0 %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 %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=alloc_dealloc_mismatch=1:malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s +#include <stdlib.h> + +static volatile char *x; + +int main() { + x = (char*)malloc(10); + x[0] = 0; + delete x; +} +// CHECK: ERROR: AddressSanitizer: alloc-dealloc-mismatch (malloc vs operator delete) on 0x +// CHECK-NEXT: #0{{.*}}operator delete +// CHECK: #{{.*}}main +// CHECK: is located 0 bytes inside of 10-byte region +// CHECK-NEXT: allocated by thread T0 here: +// ALLOC-STACK-NEXT: #0{{.*}}malloc +// ALLOC-STACK: #{{.*}}main +// CHECK: HINT: {{.*}} you may set ASAN_OPTIONS=alloc_dealloc_mismatch=0 diff --git a/test/asan/TestCases/Linux/overflow-in-qsort.cc b/test/asan/TestCases/Linux/overflow-in-qsort.cc new file mode 100644 index 000000000..139977261 --- /dev/null +++ b/test/asan/TestCases/Linux/overflow-in-qsort.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-FAST +// RUN: ASAN_OPTIONS=fast_unwind_on_fatal=0 not %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). +// https://code.google.com/p/address-sanitizer/issues/detail?id=137 + +// Fast unwinder is only avaliable on x86_64 and i386. +// REQUIRES: x86_64-supported-target + +#include <stdlib.h> +#include <stdio.h> + +int global_array[10]; +volatile int one = 1; + +extern "C" { +int QsortCallback(const void *a, const void *b) { + char *x = (char*)a; + char *y = (char*)b; + printf("Calling QsortCallback\n"); + global_array[one * 10] = 0; // BOOM + return (int)*x - (int)*y; +} + +__attribute__((noinline)) +void MyQsort(char *a, size_t size) { + printf("Calling qsort\n"); + qsort(a, size, sizeof(char), QsortCallback); + printf("Done\n"); // Avoid tail call. +} +} // extern "C" + +int main() { + char a[2] = {1, 2}; + MyQsort(a, 2); +} + +// Fast unwind: can not unwind through qsort. + +// CHECK-FAST: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-FAST: #0{{.*}} in QsortCallback +// CHECK-FAST-NOT: MyQsort +// CHECK-FAST: is located 0 bytes to the right of global variable 'global_array + +// CHECK-SLOW: ERROR: AddressSanitizer: global-buffer-overflow +// CHECK-SLOW: #0{{.*}} in QsortCallback +// CHECK-SLOW: #{{.*}} in MyQsort +// CHECK-SLOW: #{{.*}} in main +// CHECK-SLOW: is located 0 bytes to the right of global variable 'global_array diff --git a/test/asan/TestCases/Linux/preinit_test.cc b/test/asan/TestCases/Linux/preinit_test.cc new file mode 100644 index 000000000..28e509472 --- /dev/null +++ b/test/asan/TestCases/Linux/preinit_test.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx -DFUNC=zzzz %s -shared -o %t.so -fPIC +// RUN: %clangxx_asan -DFUNC=main %s -o %t -Wl,-R. %t.so +// RUN: %t + +// This test ensures that we call __asan_init early enough. +// We build a shared library w/o asan instrumentation +// and the binary with asan instrumentation. +// Both files include the same header (emulated by -DFUNC here) +// with C++ template magic which runs global initializer at library load time. +// The function get() is instrumented with asan, but called +// before the usual constructors are run. +// So, we must make sure that __asan_init is executed even earlier. +// +// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56393 + +struct A { + int foo() const { return 0; } +}; +A get () { return A(); } +template <class> struct O { + static A const e; +}; +template <class T> A const O <T>::e = get(); +int FUNC() { + return O<int>::e.foo(); +} + diff --git a/test/asan/TestCases/Linux/ptrace.cc b/test/asan/TestCases/Linux/ptrace.cc new file mode 100644 index 000000000..8831b81ef --- /dev/null +++ b/test/asan/TestCases/Linux/ptrace.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -DPOSITIVE -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdio.h> +#include <sys/ptrace.h> +#include <sys/types.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(void) { + pid_t pid; + pid = fork(); + if (pid == 0) { // child + ptrace(PTRACE_TRACEME, 0, NULL, NULL); + execl("/bin/true", "true", NULL); + } else { + wait(NULL); + user_regs_struct regs; + int res; + user_regs_struct * volatile pregs = ®s; +#ifdef POSITIVE + ++pregs; +#endif + res = ptrace(PTRACE_GETREGS, pid, NULL, pregs); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{.*ptrace.cc:}}[[@LINE-2]] + assert(!res); +#if __WORDSIZE == 64 + printf("%zx\n", regs.rip); +#else + printf("%lx\n", regs.eip); +#endif + + user_fpregs_struct fpregs; + res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); + assert(!res); + printf("%lx\n", (unsigned long)fpregs.cwd); + +#if __WORDSIZE == 32 + user_fpxregs_struct fpxregs; + res = ptrace(PTRACE_GETFPXREGS, pid, NULL, &fpxregs); + assert(!res); + printf("%lx\n", (unsigned long)fpxregs.mxcsr); +#endif + + ptrace(PTRACE_CONT, pid, NULL, NULL); + wait(NULL); + } + return 0; +} diff --git a/test/asan/TestCases/Linux/rlimit_mmap_test.cc b/test/asan/TestCases/Linux/rlimit_mmap_test.cc new file mode 100644 index 000000000..0d1d4baa7 --- /dev/null +++ b/test/asan/TestCases/Linux/rlimit_mmap_test.cc @@ -0,0 +1,16 @@ +// Check that we properly report mmap failure. +// RUN: %clangxx_asan %s -o %t && not %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <assert.h> +#include <sys/time.h> +#include <sys/resource.h> + +static volatile void *x; + +int main(int argc, char **argv) { + struct rlimit mmap_resource_limit = { 0, 0 }; + assert(0 == setrlimit(RLIMIT_AS, &mmap_resource_limit)); + x = malloc(10000000); +// CHECK: ERROR: Failed to mmap + return 0; +} diff --git a/test/asan/TestCases/Linux/shmctl.cc b/test/asan/TestCases/Linux/shmctl.cc new file mode 100644 index 000000000..c2e650a40 --- /dev/null +++ b/test/asan/TestCases/Linux/shmctl.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 +// Regression test for +// https://code.google.com/p/address-sanitizer/issues/detail?id=250 +#include <stdio.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <assert.h> + +int main() { + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + assert(id > -1); + struct shmid_ds ds; + int res = shmctl(id, IPC_STAT, &ds); + assert(res > -1); + printf("shm_segsz: %zd\n", ds.shm_segsz); + assert(ds.shm_segsz == 4096); + assert(-1 != shmctl(id, IPC_RMID, 0)); + + struct shm_info shmInfo; + res = shmctl(0, SHM_INFO, (struct shmid_ds *)&shmInfo); + assert(res > -1); + + return 0; +} diff --git a/test/asan/TestCases/Linux/stress_dtls.c b/test/asan/TestCases/Linux/stress_dtls.c new file mode 100644 index 000000000..4e22f8ae9 --- /dev/null +++ b/test/asan/TestCases/Linux/stress_dtls.c @@ -0,0 +1,105 @@ +// REQUIRES: asan-64-bits +// Stress test dynamic TLS + dlopen + threads. +// +// Note that glibc 2.15 seems utterly broken on this test, +// it fails with ~17 DSOs dlopen-ed. +// glibc 2.19 seems fine. +// +// +// RUN: %clangxx_asan -x c -DSO_NAME=f0 %s -shared -o %t-f0.so -fPIC +// RUN: %clangxx_asan -x c -DSO_NAME=f1 %s -shared -o %t-f1.so -fPIC +// RUN: %clangxx_asan -x c -DSO_NAME=f2 %s -shared -o %t-f2.so -fPIC +// RUN: %clangxx_asan %s -o %t +// RUN: %t 0 3 +// RUN: %t 2 3 +// RUN: ASAN_OPTIONS=verbosity=2 %t 2 2 2>&1 | FileCheck %s +// CHECK: __tls_get_addr +// CHECK: __tls_get_addr +// CHECK: __tls_get_addr +// CHECK: __tls_get_addr +// CHECK: __tls_get_addr +/* +cc=your-compiler + +$cc stress_dtls.c -lpthread -ldl +for((i=0;i<100;i++)); do + $cc -fPIC -shared -DSO_NAME=f$i -o a.out-f$i.so stress_dtls.c; +done +./a.out 2 4 # <<<<<< 2 threads, 4 libs +./a.out 3 50 # <<<<<< 3 threads, 50 libs +*/ +#ifndef SO_NAME +#define _GNU_SOURCE +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <stdint.h> + +typedef void **(*f_t)(); + +__thread int my_tls; + +#define MAX_N_FUNCTIONS 1000 +f_t Functions[MAX_N_FUNCTIONS]; + +void *PrintStuff(void *unused) { + uintptr_t stack; + // fprintf(stderr, "STACK: %p TLS: %p SELF: %p\n", &stack, &my_tls, + // (void *)pthread_self()); + int i; + for (i = 0; i < MAX_N_FUNCTIONS; i++) { + if (!Functions[i]) break; + uintptr_t dtls = (uintptr_t)Functions[i](); + fprintf(stderr, " dtls[%03d]: %lx\n", i, dtls); + *(long*)dtls = 42; // check that this is writable. + } + return NULL; +} + +int main(int argc, char *argv[]) { + int num_threads = 1; + int num_libs = 1; + if (argc >= 2) + num_threads = atoi(argv[1]); + if (argc >= 3) + num_libs = atoi(argv[2]); + assert(num_libs <= MAX_N_FUNCTIONS); + + int lib; + for (lib = 0; lib < num_libs; lib++) { + char buf[4096]; + snprintf(buf, sizeof(buf), "%s-f%d.so", argv[0], lib); + void *handle = dlopen(buf, RTLD_LAZY); + if (!handle) { + fprintf(stderr, "%s\n", dlerror()); + exit(1); + } + snprintf(buf, sizeof(buf), "f%d", lib); + Functions[lib] = (f_t)dlsym(handle, buf); + if (!Functions[lib]) { + fprintf(stderr, "%s\n", dlerror()); + exit(1); + } + fprintf(stderr, "LIB[%03d] %s: %p\n", lib, buf, Functions[lib]); + PrintStuff(0); + + int i; + for (i = 0; i < num_threads; i++) { + pthread_t t; + pthread_create(&t, 0, PrintStuff, 0); + pthread_join(t, 0); + } + } + return 0; +} +#else // SO_NAME +#ifndef DTLS_SIZE +# define DTLS_SIZE (1 << 17) +#endif +__thread void *huge_thread_local_array[DTLS_SIZE]; +void **SO_NAME() { + return &huge_thread_local_array[0]; +} +#endif diff --git a/test/asan/TestCases/Linux/swapcontext_test.cc b/test/asan/TestCases/Linux/swapcontext_test.cc new file mode 100644 index 000000000..6cbb69a35 --- /dev/null +++ b/test/asan/TestCases/Linux/swapcontext_test.cc @@ -0,0 +1,90 @@ +// Check that ASan plays well with easy cases of makecontext/swapcontext. + +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s +// +// This test is too sublte to try on non-x86 arch for now. +// REQUIRES: x86_64-supported-target,i386-supported-target + +#include <stdio.h> +#include <ucontext.h> +#include <unistd.h> + +ucontext_t orig_context; +ucontext_t child_context; + +const int kStackSize = 1 << 20; + +__attribute__((noinline)) +void Throw() { + throw 1; +} + +__attribute__((noinline)) +void ThrowAndCatch() { + try { + Throw(); + } catch(int a) { + printf("ThrowAndCatch: %d\n", a); + } +} + +void Child(int mode) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + ThrowAndCatch(); // Simulate __asan_handle_no_return(). + // (a) Do nothing, just return to parent function. + // (b) Jump into the original function. Stack remains poisoned unless we do + // something. + if (mode == 1) { + if (swapcontext(&child_context, &orig_context) < 0) { + perror("swapcontext"); + _exit(0); + } + } +} + +int Run(int arg, int mode, char *child_stack) { + printf("Child stack: %p\n", child_stack); + // Setup child context. + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + if (mode == 0) { + child_context.uc_link = &orig_context; + } + makecontext(&child_context, (void (*)())Child, 1, mode); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 0; + } + // Touch childs's stack to make sure it's unpoisoned. + for (int i = 0; i < kStackSize; i++) { + child_stack[i] = i; + } + return child_stack[arg]; +} + +int main(int argc, char **argv) { + char stack[kStackSize + 1]; + // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext + int ret = 0; + ret += Run(argc - 1, 0, stack); + printf("Test1 passed\n"); + // CHECK: Test1 passed + ret += Run(argc - 1, 1, stack); + printf("Test2 passed\n"); + // CHECK: Test2 passed + char *heap = new char[kStackSize + 1]; + ret += Run(argc - 1, 0, heap); + printf("Test3 passed\n"); + // CHECK: Test3 passed + ret += Run(argc - 1, 1, heap); + printf("Test4 passed\n"); + // CHECK: Test4 passed + + delete [] heap; + return ret; +} diff --git a/test/asan/TestCases/Linux/syscalls.cc b/test/asan/TestCases/Linux/syscalls.cc new file mode 100644 index 000000000..4bcbe4461 --- /dev/null +++ b/test/asan/TestCases/Linux/syscalls.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <errno.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> + +#include <sanitizer/linux_syscall_hooks.h> + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + __sanitizer_syscall_pre_recvmsg(0, buf - 1, 0); + // CHECK: AddressSanitizer: stack-buffer-{{.*}}erflow + // CHECK: READ of size {{.*}} at {{.*}} thread T0 + // CHECK: #0 {{.*}} in __sanitizer_syscall{{.*}}recvmsg + return 0; +} diff --git a/test/asan/TestCases/Linux/tsd_dtor_leak.cc b/test/asan/TestCases/Linux/tsd_dtor_leak.cc new file mode 100644 index 000000000..a1d89ee43 --- /dev/null +++ b/test/asan/TestCases/Linux/tsd_dtor_leak.cc @@ -0,0 +1,39 @@ +// Regression test for a leak in tsd: +// https://code.google.com/p/address-sanitizer/issues/detail?id=233 +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: ASAN_OPTIONS=quarantine_size=1 %t +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +extern "C" size_t __asan_get_heap_size(); +static pthread_key_t tsd_key; + +void *Thread(void *) { + pthread_setspecific(tsd_key, malloc(10)); + return 0; +} + +static volatile void *v; + +void Dtor(void *tsd) { + v = malloc(10000); + free(tsd); + free((void*)v); // The bug was that this was leaking. +} + +int main() { + assert(0 == pthread_key_create(&tsd_key, Dtor)); + size_t old_heap_size = 0; + for (int i = 0; i < 10; i++) { + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); + size_t new_heap_size = __asan_get_heap_size(); + fprintf(stderr, "heap size: new: %zd old: %zd\n", new_heap_size, old_heap_size); + if (old_heap_size) + assert(old_heap_size == new_heap_size); + old_heap_size = new_heap_size; + } +} diff --git a/test/asan/TestCases/Linux/uar_signals.cc b/test/asan/TestCases/Linux/uar_signals.cc new file mode 100644 index 000000000..9663859df --- /dev/null +++ b/test/asan/TestCases/Linux/uar_signals.cc @@ -0,0 +1,70 @@ +// This test shows that the current implementation of use-after-return is +// not signal-safe. +// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t +// RUN: %clangxx_asan -O1 %s -o %t -lpthread && %t +#include <signal.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/time.h> +#include <pthread.h> + +int *g; +int n_signals; + +typedef void (*Sigaction)(int, siginfo_t *, void *); + +void SignalHandler(int, siginfo_t*, void*) { + int local; + g = &local; + n_signals++; + // printf("s: %p\n", &local); +} + +static void EnableSigprof(Sigaction SignalHandler) { + struct sigaction sa; + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGPROF, &sa, NULL) != 0) { + perror("sigaction"); + abort(); + } + struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 1; + timer.it_value = timer.it_interval; + if (setitimer(ITIMER_PROF, &timer, 0) != 0) { + perror("setitimer"); + abort(); + } +} + +void RecursiveFunction(int depth) { + if (depth == 0) return; + int local; + g = &local; + // printf("r: %p\n", &local); + // printf("[%2d] n_signals: %d\n", depth, n_signals); + RecursiveFunction(depth - 1); + RecursiveFunction(depth - 1); +} + +void *Thread(void *) { + RecursiveFunction(18); + return NULL; +} + +int main(int argc, char **argv) { + EnableSigprof(SignalHandler); + + for (int i = 0; i < 4; i++) { + fprintf(stderr, "."); + const int kNumThread = sizeof(void*) == 8 ? 16 : 8; + pthread_t t[kNumThread]; + for (int i = 0; i < kNumThread; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThread; i++) + pthread_join(t[i], 0); + } + fprintf(stderr, "\n"); +} diff --git a/test/asan/TestCases/Linux/unpoison_tls.cc b/test/asan/TestCases/Linux/unpoison_tls.cc new file mode 100644 index 000000000..d67c4f954 --- /dev/null +++ b/test/asan/TestCases/Linux/unpoison_tls.cc @@ -0,0 +1,35 @@ +// Test that TLS is unpoisoned on thread death. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> + +#include <sanitizer/asan_interface.h> + +__thread int64_t tls_var[2]; + +volatile int64_t *p_tls_var; + +void *first(void *arg) { + ASAN_POISON_MEMORY_REGION(&tls_var, sizeof(tls_var)); + p_tls_var = tls_var; + return 0; +} + +void *second(void *arg) { + assert(tls_var == p_tls_var); + *p_tls_var = 1; + return 0; +} + +int main(int argc, char *argv[]) { + pthread_t p; + assert(0 == pthread_create(&p, 0, first, 0)); + assert(0 == pthread_join(p, 0)); + assert(0 == pthread_create(&p, 0, second, 0)); + assert(0 == pthread_join(p, 0)); + return 0; +} diff --git a/test/asan/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc b/test/asan/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc new file mode 100644 index 000000000..5d9399914 --- /dev/null +++ b/test/asan/TestCases/SharedLibs/darwin-dummy-shared-lib-so.cc @@ -0,0 +1,13 @@ +//===----------- darwin-dummy-shared-lib-so.cc ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +void foo() {} diff --git a/test/asan/TestCases/SharedLibs/dlclose-test-so.cc b/test/asan/TestCases/SharedLibs/dlclose-test-so.cc new file mode 100644 index 000000000..73e005073 --- /dev/null +++ b/test/asan/TestCases/SharedLibs/dlclose-test-so.cc @@ -0,0 +1,33 @@ +//===----------- dlclose-test-so.cc -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +// Regression test for +// http://code.google.com/p/address-sanitizer/issues/detail?id=19 +//===----------------------------------------------------------------------===// +#include <stdio.h> + +static int pad1; +static int static_var; +static int pad2; + +extern "C" +int *get_address_of_static_var() { + return &static_var; +} + +__attribute__((constructor)) +void at_dlopen() { + printf("%s: I am being dlopened\n", __FILE__); +} +__attribute__((destructor)) +void at_dlclose() { + printf("%s: I am being dlclosed\n", __FILE__); +} diff --git a/test/asan/TestCases/SharedLibs/init-order-dlopen-so.cc b/test/asan/TestCases/SharedLibs/init-order-dlopen-so.cc new file mode 100644 index 000000000..dc097a520 --- /dev/null +++ b/test/asan/TestCases/SharedLibs/init-order-dlopen-so.cc @@ -0,0 +1,12 @@ +#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(); diff --git a/test/asan/TestCases/SharedLibs/lit.local.cfg b/test/asan/TestCases/SharedLibs/lit.local.cfg new file mode 100644 index 000000000..b3677c17a --- /dev/null +++ b/test/asan/TestCases/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# Sources in this directory are compiled as shared libraries and used by +# tests in parent directory. + +config.suffixes = [] diff --git a/test/asan/TestCases/SharedLibs/shared-lib-test-so.cc b/test/asan/TestCases/SharedLibs/shared-lib-test-so.cc new file mode 100644 index 000000000..6ef565ce4 --- /dev/null +++ b/test/asan/TestCases/SharedLibs/shared-lib-test-so.cc @@ -0,0 +1,26 @@ +//===----------- shared-lib-test-so.cc --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of AddressSanitizer, an address sanity checker. +// +//===----------------------------------------------------------------------===// +#include <stdio.h> + +int pad[10]; +int GLOB[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +extern "C" +void inc(int index) { + GLOB[index]++; +} + +extern "C" +void inc2(int *a, int index) { + a[index]++; +} diff --git a/test/asan/TestCases/SharedLibs/start-deactivated-so.cc b/test/asan/TestCases/SharedLibs/start-deactivated-so.cc new file mode 100644 index 000000000..9611fa5ba --- /dev/null +++ b/test/asan/TestCases/SharedLibs/start-deactivated-so.cc @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <stdlib.h> + +extern "C" void do_another_bad_thing() { + char *volatile p = (char *)malloc(100); + printf("%hhx\n", p[105]); +} diff --git a/test/asan/TestCases/allocator_returns_null.cc b/test/asan/TestCases/allocator_returns_null.cc new file mode 100644 index 000000000..595c9e252 --- /dev/null +++ b/test/asan/TestCases/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0. +// +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: ASAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: ASAN_OPTIONS=allocator_may_return_null=1 %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL + +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <assert.h> +#include <limits> +int main(int argc, char **argv) { + volatile size_t size = std::numeric_limits<size_t>::max() - 10000; + assert(argc == 2); + char *x = 0; + if (!strcmp(argv[1], "malloc")) { + fprintf(stderr, "malloc:\n"); + x = (char*)malloc(size); + } + if (!strcmp(argv[1], "calloc")) { + fprintf(stderr, "calloc:\n"); + x = (char*)calloc(size / 4, 4); + } + + if (!strcmp(argv[1], "calloc-overflow")) { + fprintf(stderr, "calloc-overflow:\n"); + volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = (char*)calloc(kArraySize, kArraySize2); + } + + if (!strcmp(argv[1], "realloc")) { + fprintf(stderr, "realloc:\n"); + x = (char*)realloc(0, size); + } + if (!strcmp(argv[1], "realloc-after-malloc")) { + fprintf(stderr, "realloc-after-malloc:\n"); + char *t = (char*)malloc(100); + *t = 42; + x = (char*)realloc(t, size); + assert(*t == 42); + } + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + return x != 0; +} +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: AddressSanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 diff --git a/test/asan/TestCases/allow_user_segv.cc b/test/asan/TestCases/allow_user_segv.cc new file mode 100644 index 000000000..55cf6044e --- /dev/null +++ b/test/asan/TestCases/allow_user_segv.cc @@ -0,0 +1,48 @@ +// 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 %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && ASAN_OPTIONS=allow_user_segv_handler=true not %t 2>&1 | FileCheck %s + +#include <signal.h> +#include <stdio.h> + +struct sigaction user_sigaction; +struct sigaction original_sigaction; + +void User_OnSIGSEGV(int signum, siginfo_t *siginfo, void *context) { + fprintf(stderr, "User sigaction called\n"); + if (original_sigaction.sa_flags | SA_SIGINFO) + original_sigaction.sa_sigaction(signum, siginfo, context); + else + original_sigaction.sa_handler(signum); +} + +int DoSEGV() { + volatile int *x = 0; + return *x; +} + +int main() { + user_sigaction.sa_sigaction = User_OnSIGSEGV; + user_sigaction.sa_flags = SA_SIGINFO; +#if defined(__APPLE__) && !defined(__LP64__) + // On 32-bit Darwin KERN_PROTECTION_FAILURE (SIGBUS) is delivered. + int signum = SIGBUS; +#else + // On 64-bit Darwin KERN_INVALID_ADDRESS (SIGSEGV) is delivered. + // On Linux SIGSEGV is delivered as well. + int signum = SIGSEGV; +#endif + if (sigaction(signum, &user_sigaction, &original_sigaction)) { + perror("sigaction"); + return 1; + } + fprintf(stderr, "User sigaction installed\n"); + return DoSEGV(); +} + +// CHECK: User sigaction installed +// CHECK-NEXT: User sigaction called +// CHECK-NEXT: ASAN:SIGSEGV +// CHECK: AddressSanitizer: SEGV on unknown address diff --git a/test/asan/TestCases/asan-symbolize-sanity-test.cc b/test/asan/TestCases/asan-symbolize-sanity-test.cc new file mode 100644 index 000000000..0efe245bb --- /dev/null +++ b/test/asan/TestCases/asan-symbolize-sanity-test.cc @@ -0,0 +1,39 @@ +// Check that asan_symbolize.py script works (for binaries, ASan RTL and +// shared object files. + +// RUN: %clangxx_asan -O0 %p/SharedLibs/shared-lib-test-so.cc -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_SYMBOLIZER_PATH= not %t 2>&1 | %asan_symbolize | FileCheck %s +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int*, int); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc2 = (fun_t*)dlsym(lib, "inc2"); + if (!inc2) return 1; + printf("ok\n"); + int *array = (int*)malloc(40); + inc2(array, 1); + inc2(array, -1); // BOOM + // CHECK: ERROR: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 4 at 0x{{.*}} + // CHECK: #0 {{.*}} in inc2 {{.*}}shared-lib-test-so.cc:25 + // CHECK: #1 {{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-4]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #{{.*}} in {{(wrap_|__interceptor_)?}}malloc + // CHECK: #{{.*}} in main {{.*}}asan-symbolize-sanity-test.cc:[[@LINE-9]] + return 0; +} diff --git a/test/asan/TestCases/assign_large_valloc_to_global.cc b/test/asan/TestCases/assign_large_valloc_to_global.cc new file mode 100644 index 000000000..b0a501576 --- /dev/null +++ b/test/asan/TestCases/assign_large_valloc_to_global.cc @@ -0,0 +1,8 @@ +// Make sure we don't report a leak nor hang. +// RUN: %clangxx_asan -O3 %s -o %t && %t +#include <stdlib.h> +#ifndef __APPLE__ +#include <malloc.h> +#endif // __APPLE__ +int *p = (int*)valloc(1 << 20); +int main() { } diff --git a/test/asan/TestCases/atexit_stats.cc b/test/asan/TestCases/atexit_stats.cc new file mode 100644 index 000000000..e3b1269d2 --- /dev/null +++ b/test/asan/TestCases/atexit_stats.cc @@ -0,0 +1,13 @@ +// Make sure we report atexit stats. +// RUN: %clangxx_asan -O3 %s -o %t +// RUN: ASAN_OPTIONS=atexit=1:print_stats=1 %t 2>&1 | FileCheck %s +#include <stdlib.h> +#if !defined(__APPLE__) +#include <malloc.h> +#endif +int *p1 = (int*)malloc(900); +int *p2 = (int*)malloc(90000); +int *p3 = (int*)malloc(9000000); +int main() { } + +// CHECK: AddressSanitizer exit stats: diff --git a/test/asan/TestCases/blacklist.cc b/test/asan/TestCases/blacklist.cc new file mode 100644 index 000000000..46625ee7b --- /dev/null +++ b/test/asan/TestCases/blacklist.cc @@ -0,0 +1,38 @@ +// Test the blacklist functionality of ASan + +// RUN: echo "fun:*brokenFunction*" > %tmp +// RUN: echo "global:*badGlobal*" >> %tmp +// RUN: echo "src:*blacklist-extra.cc" >> %tmp +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O0 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O1 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O2 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 +// RUN: %clangxx_asan -fsanitize-blacklist=%tmp -O3 %s -o %t \ +// RUN: %p/Helpers/blacklist-extra.cc && %t 2>&1 + +// badGlobal is accessed improperly, but we blacklisted it. Align +// it to make sure memory past the end of badGlobal will be in +// the same page. +__attribute__((aligned(16))) int badGlobal; +int readBadGlobal() { + return (&badGlobal)[1]; +} + +// A function which is broken, but excluded in the blacklist. +int brokenFunction(int argc) { + char x[10] = {0}; + return x[argc * 10]; // BOOM +} + +// This function is defined in Helpers/blacklist-extra.cc, a source file which +// is blacklisted by name +int externalBrokenFunction(int x); + +int main(int argc, char **argv) { + brokenFunction(argc); + int x = readBadGlobal(); + externalBrokenFunction(argc); + return 0; +} diff --git a/test/asan/TestCases/contiguous_container.cc b/test/asan/TestCases/contiguous_container.cc new file mode 100644 index 000000000..ebcd7c9a6 --- /dev/null +++ b/test/asan/TestCases/contiguous_container.cc @@ -0,0 +1,74 @@ +// RUN: %clangxx_asan -O %s -o %t && %t +// +// Test __sanitizer_annotate_contiguous_container. + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +extern "C" { +void __sanitizer_annotate_contiguous_container(void *beg, void *end, + void *old_mid, void *new_mid); +bool __asan_address_is_poisoned(void *addr); +} // extern "C" + +void TestContainer(size_t capacity) { + char *beg = new char[capacity]; + char *end = beg + capacity; + char *mid = beg + capacity; + char *old_mid = 0; + unsigned seed = 0; + + for (int i = 0; i < 10000; i++) { + size_t size = rand_r(&seed) % (capacity + 1); + assert(size <= capacity); + old_mid = mid; + mid = beg + size; + __sanitizer_annotate_contiguous_container(beg, end, old_mid, mid); + + for (size_t idx = 0; idx < size; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + for (size_t idx = size; idx < capacity; idx++) + assert(__asan_address_is_poisoned(beg + idx)); + } + + // Don't forget to unpoison the whole thing before destroing/reallocating. + __sanitizer_annotate_contiguous_container(beg, end, mid, end); + for (size_t idx = 0; idx < capacity; idx++) + assert(!__asan_address_is_poisoned(beg + idx)); + delete[] beg; +} + +__attribute__((noinline)) +void Throw() { throw 1; } + +__attribute__((noinline)) +void ThrowAndCatch() { + try { + Throw(); + } catch(...) { + } +} + +void TestThrow() { + char x[32]; + __sanitizer_annotate_contiguous_container(x, x + 32, x + 32, x + 14); + assert(!__asan_address_is_poisoned(x + 13)); + assert(__asan_address_is_poisoned(x + 14)); + ThrowAndCatch(); + assert(!__asan_address_is_poisoned(x + 13)); + // FIXME: invert the assertion below once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 14)); + __sanitizer_annotate_contiguous_container(x, x + 32, x + 14, x + 32); + assert(!__asan_address_is_poisoned(x + 13)); + assert(!__asan_address_is_poisoned(x + 14)); +} + +int main(int argc, char **argv) { + int n = argc == 1 ? 128 : atoi(argv[1]); + for (int i = 0; i <= n; i++) + TestContainer(i); + TestThrow(); +} diff --git a/test/asan/TestCases/contiguous_container_crash.cc b/test/asan/TestCases/contiguous_container_crash.cc new file mode 100644 index 000000000..6be9ad5f6 --- /dev/null +++ b/test/asan/TestCases/contiguous_container_crash.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_asan -O %s -o %t +// RUN: not %t crash 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s +// RUN: not %t bad-bounds 2>&1 | FileCheck --check-prefix=CHECK-BAD %s +// Test crash due to __sanitizer_annotate_contiguous_container. + +#include <assert.h> +#include <string.h> + +extern "C" { +void __sanitizer_annotate_contiguous_container(const void *beg, const void *end, + const void *old_mid, + const void *new_mid); +} // extern "C" + +static volatile int one = 1; + +int TestCrash() { + long t[100]; + __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 100, + &t[0] + 50); + return (int)t[60 * one]; // Touches the poisoned memory. +} + +void BadBounds() { + long t[100]; + __sanitizer_annotate_contiguous_container(&t[0], &t[0] + 100, &t[0] + 101, + &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(); +} +// CHECK-CRASH: AddressSanitizer: container-overflow +// CHECK-BAD: ERROR: AddressSanitizer: bad parameters to __sanitizer_annotate_contiguous_container diff --git a/test/asan/TestCases/current_allocated_bytes.cc b/test/asan/TestCases/current_allocated_bytes.cc new file mode 100644 index 000000000..669cf150b --- /dev/null +++ b/test/asan/TestCases/current_allocated_bytes.cc @@ -0,0 +1,43 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O2 %s -o %t && %t + +#include <assert.h> +#include <pthread.h> +#include <sanitizer/asan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +const size_t kLargeAlloc = 1UL << 20; + +void* allocate(void *arg) { + volatile void *ptr = malloc(kLargeAlloc); + free((void*)ptr); + return 0; +} + +void* check_stats(void *arg) { + assert(__asan_get_current_allocated_bytes() > 0); + return 0; +} + +int main() { + size_t used_mem = __asan_get_current_allocated_bytes(); + printf("Before: %zu\n", used_mem); + const int kNumIterations = 1000; + for (int iter = 0; iter < kNumIterations; iter++) { + pthread_t thr[4]; + for (int j = 0; j < 4; j++) { + assert(0 == + pthread_create(&thr[j], 0, (j < 2) ? allocate : check_stats, 0)); + } + for (int j = 0; j < 4; j++) + assert(0 == pthread_join(thr[j], 0)); + used_mem = __asan_get_current_allocated_bytes(); + if (used_mem > kLargeAlloc) { + printf("After iteration %d: %zu\n", iter, used_mem); + return 1; + } + } + printf("Success after %d iterations\n", kNumIterations); + return 0; +} diff --git a/test/asan/TestCases/deep_call_stack.cc b/test/asan/TestCases/deep_call_stack.cc new file mode 100644 index 000000000..e24704b90 --- /dev/null +++ b/test/asan/TestCases/deep_call_stack.cc @@ -0,0 +1,25 @@ +// Check that UAR mode can handle very deep recusrion. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: %t 2>&1 | FileCheck %s +// Also check that use_sigaltstack+verbosity doesn't crash. +// RUN: ASAN_OPTIONS=verbosity=1:use_sigaltstack=1 %t | FileCheck %s +#include <stdio.h> + +__attribute__((noinline)) +void RecursiveFunc(int depth, int *ptr) { + if ((depth % 1000) == 0) + printf("[%05d] ptr: %p\n", depth, ptr); + if (depth == 0) + return; + int local; + RecursiveFunc(depth - 1, &local); +} + +int main(int argc, char **argv) { + RecursiveFunc(40000, 0); + return 0; +} +// CHECK: [40000] ptr: +// CHECK: [20000] ptr: +// CHECK: [00000] ptr diff --git a/test/asan/TestCases/deep_stack_uaf.cc b/test/asan/TestCases/deep_stack_uaf.cc new file mode 100644 index 000000000..920411c4a --- /dev/null +++ b/test/asan/TestCases/deep_stack_uaf.cc @@ -0,0 +1,31 @@ +// Check that we can store lots of stack frames if asked to. + +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: ASAN_OPTIONS=malloc_context_size=120:redzone=512 not %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <stdio.h> + +template <int depth> +struct DeepFree { + static void free(char *x) { + DeepFree<depth - 1>::free(x); + } +}; + +template<> +struct DeepFree<0> { + static void free(char *x) { + ::free(x); + } +}; + +int main() { + char *x = (char*)malloc(10); + // deep_free(x); + DeepFree<200>::free(x); + return x[5]; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: DeepFree<36> + // CHECK: DeepFree<98> + // CHECK: DeepFree<115> +} diff --git a/test/asan/TestCases/deep_tail_call.cc b/test/asan/TestCases/deep_tail_call.cc new file mode 100644 index 000000000..2e7aa8e02 --- /dev/null +++ b/test/asan/TestCases/deep_tail_call.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// CHECK: AddressSanitizer: global-buffer-overflow +int global[10]; +// CHECK: {{#0.*call4}} +void __attribute__((noinline)) call4(int i) { global[i+10]++; } +// CHECK: {{#1.*call3}} +void __attribute__((noinline)) call3(int i) { call4(i); } +// CHECK: {{#2.*call2}} +void __attribute__((noinline)) call2(int i) { call3(i); } +// CHECK: {{#3.*call1}} +void __attribute__((noinline)) call1(int i) { call2(i); } +// CHECK: {{#4.*main}} +int main(int argc, char **argv) { + call1(argc); + return global[0]; +} diff --git a/test/asan/TestCases/deep_thread_stack.cc b/test/asan/TestCases/deep_thread_stack.cc new file mode 100644 index 000000000..92e0d66c8 --- /dev/null +++ b/test/asan/TestCases/deep_thread_stack.cc @@ -0,0 +1,57 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <pthread.h> + +int *x; + +void *AllocThread(void *arg) { + x = new int; + *x = 42; + return NULL; +} + +void *FreeThread(void *arg) { + delete x; + return NULL; +} + +void *AccessThread(void *arg) { + *x = 43; // BOOM + return NULL; +} + +typedef void* (*callback_type)(void* arg); + +void *RunnerThread(void *function) { + pthread_t thread; + pthread_create(&thread, NULL, (callback_type)function, NULL); + pthread_join(thread, NULL); + return NULL; +} + +void RunThread(callback_type function) { + pthread_t runner; + pthread_create(&runner, NULL, RunnerThread, (void*)function); + pthread_join(runner, NULL); +} + +int main(int argc, char *argv[]) { + RunThread(AllocThread); + RunThread(FreeThread); + RunThread(AccessThread); + return (x != 0); +} + +// CHECK: AddressSanitizer: heap-use-after-free +// CHECK: WRITE of size 4 at 0x{{.*}} thread T[[ACCESS_THREAD:[0-9]+]] +// CHECK: freed by thread T[[FREE_THREAD:[0-9]+]] here: +// CHECK: previously allocated by thread T[[ALLOC_THREAD:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_THREAD]] created by T[[ACCESS_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ACCESS_RUNNER]] created by T0 here: +// CHECK: Thread T[[FREE_THREAD]] created by T[[FREE_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[FREE_RUNNER]] created by T0 here: +// CHECK: Thread T[[ALLOC_THREAD]] created by T[[ALLOC_RUNNER:[0-9]+]] here: +// CHECK: Thread T[[ALLOC_RUNNER]] created by T0 here: diff --git a/test/asan/TestCases/default_blacklist.cc b/test/asan/TestCases/default_blacklist.cc new file mode 100644 index 000000000..25a1ae175 --- /dev/null +++ b/test/asan/TestCases/default_blacklist.cc @@ -0,0 +1,3 @@ +// Test that ASan uses the default blacklist from resource directory. +// RUN: %clangxx_asan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}asan_blacklist.txt diff --git a/test/asan/TestCases/default_options.cc b/test/asan/TestCases/default_options.cc new file mode 100644 index 000000000..84b80557b --- /dev/null +++ b/test/asan/TestCases/default_options.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s + +const char *kAsanDefaultOptions="verbosity=1 foo=bar"; + +extern "C" +__attribute__((no_sanitize_address)) +const char *__asan_default_options() { + // CHECK: Using the defaults from __asan_default_options: {{.*}} foo=bar + return kAsanDefaultOptions; +} + +int main() { + return 0; +} diff --git a/test/asan/TestCases/dlclose-test.cc b/test/asan/TestCases/dlclose-test.cc new file mode 100644 index 000000000..03ed16016 --- /dev/null +++ b/test/asan/TestCases/dlclose-test.cc @@ -0,0 +1,81 @@ +// Regression test for +// http://code.google.com/p/address-sanitizer/issues/detail?id=19 +// Bug description: +// 1. application dlopens foo.so +// 2. asan registers all globals from foo.so +// 3. application dlcloses foo.so +// 4. application mmaps some memory to the location where foo.so was before +// 5. application starts using this mmaped memory, but asan still thinks there +// are globals. +// 6. BOOM + +// This sublte test assumes that after a foo.so is dlclose-d +// we can mmap the region of memory that has been occupied by the library. +// It works on i368/x86_64 Linux, but not necessary anywhere else. +// REQUIRES: x86_64-supported-target,i386-supported-target + +// RUN: %clangxx_asan -O0 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %p/SharedLibs/dlclose-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <unistd.h> + +#include <string> + +using std::string; + +typedef int *(fun_t)(); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + size_t PageSize = sysconf(_SC_PAGESIZE); + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *get = (fun_t*)dlsym(lib, "get_address_of_static_var"); + if (!get) { + printf("failed dlsym\n"); + return 1; + } + int *addr = get(); + assert(((size_t)addr % 32) == 0); // should be 32-byte aligned. + printf("addr: %p\n", addr); + addr[0] = 1; // make sure we can write there. + + // Now dlclose the shared library. + printf("attempting to dlclose\n"); + if (dlclose(lib)) { + printf("failed to dlclose\n"); + return 1; + } + // Now, the page where 'addr' is unmapped. Map it. + size_t page_beg = ((size_t)addr) & ~(PageSize - 1); + void *res = mmap((void*)(page_beg), PageSize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED | MAP_NORESERVE, 0, 0); + if (res == (char*)-1L) { + printf("failed to mmap\n"); + return 1; + } + addr[1] = 2; // BOOM (if the bug is not fixed). + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/test/asan/TestCases/double-free.cc b/test/asan/TestCases/double-free.cc new file mode 100644 index 000000000..6bfd4fa2c --- /dev/null +++ b/test/asan/TestCases/double-free.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O0 %s -o %t 2>&1 +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %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: #0 0x{{.*}} in {{.*}}free + // CHECK: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-3]] + // CHECK: freed by thread T0 here: + // MALLOC-CTX: #0 0x{{.*}} in {{.*}}free + // MALLOC-CTX: #1 0x{{.*}} in main {{.*}}double-free.cc:[[@LINE-7]] + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: double-free.cc:[[@LINE-12]] + return res; +} diff --git a/test/asan/TestCases/force_inline_opt0.cc b/test/asan/TestCases/force_inline_opt0.cc new file mode 100644 index 000000000..775a66dfe --- /dev/null +++ b/test/asan/TestCases/force_inline_opt0.cc @@ -0,0 +1,14 @@ +// This test checks that we are no instrumenting a memory access twice +// (before and after inlining) +// RUN: %clangxx_asan -O1 %s -o %t && %t +// RUN: %clangxx_asan -O0 %s -o %t && %t +__attribute__((always_inline)) +void foo(int *x) { + *x = 0; +} + +int main() { + int x; + foo(&x); + return x; +} diff --git a/test/asan/TestCases/free_hook_realloc.cc b/test/asan/TestCases/free_hook_realloc.cc new file mode 100644 index 000000000..7a71964b0 --- /dev/null +++ b/test/asan/TestCases/free_hook_realloc.cc @@ -0,0 +1,32 @@ +// Check that free hook doesn't conflict with Realloc. +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <unistd.h> + +static void *glob_ptr; + +extern "C" { +void __asan_free_hook(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/gc-test.cc b/test/asan/TestCases/gc-test.cc new file mode 100644 index 000000000..984146481 --- /dev/null +++ b/test/asan/TestCases/gc-test.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_asan %s -o %t +// RUN: ASAN_OPTIONS=detect_stack_use_after_return=1 %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: ASAN_OPTIONS=detect_stack_use_after_return=0 %t 2>&1 | FileCheck %s --check-prefix=CHECK0 + +#include <assert.h> +#include <stdio.h> +#include <pthread.h> +#include <sanitizer/asan_interface.h> + +static const int kNumThreads = 2; + +void *Thread(void *unused) { + void *fake_stack = __asan_get_current_fake_stack(); + char var[15]; + if (fake_stack) { + fprintf(stderr, "fake stack found: %p; var: %p\n", fake_stack, var); + // CHECK1: fake stack found + // CHECK1: fake stack found + void *beg, *end; + void *real_stack = + __asan_addr_is_in_fake_stack(fake_stack, &var[0], &beg, &end); + assert(real_stack); + assert((char*)beg <= (char*)&var[0]); + assert((char*)end > (char*)&var[0]); + for (int i = -32; i < 15; i++) { + void *beg1, *end1; + char *ptr = &var[0] + i; + void *real_stack1 = + __asan_addr_is_in_fake_stack(fake_stack, ptr, &beg1, &end1); + assert(real_stack == real_stack1); + assert(beg == beg1); + assert(end == end1); + } + } else { + fprintf(stderr, "no fake stack\n"); + // CHECK0: no fake stack + // CHECK0: no fake stack + } + return NULL; +} + +int main(int argc, char **argv) { + pthread_t t[kNumThreads]; + for (int i = 0; i < kNumThreads; i++) + pthread_create(&t[i], 0, Thread, 0); + for (int i = 0; i < kNumThreads; i++) + pthread_join(t[i], 0); + return 0; +} diff --git a/test/asan/TestCases/global-demangle.cc b/test/asan/TestCases/global-demangle.cc new file mode 100644 index 000000000..d050b70f0 --- /dev/null +++ b/test/asan/TestCases/global-demangle.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +namespace XXX { +class YYY { + public: + static char ZZZ[]; +}; +char YYY::ZZZ[] = "abc"; +} + +int main(int argc, char **argv) { + return (int)XXX::YYY::ZZZ[argc + 5]; // BOOM + // CHECK: {{READ of size 1 at 0x.*}} + // CHECK: {{0x.* is located 2 bytes to the right of global variable}} + // CHECK: 'XXX::YYY::ZZZ' {{.*}} of size 4 + // CHECK: 'XXX::YYY::ZZZ' is ascii string 'abc' +} diff --git a/test/asan/TestCases/global-overflow.cc b/test/asan/TestCases/global-overflow.cc new file mode 100644 index 000000000..0f080f55f --- /dev/null +++ b/test/asan/TestCases/global-overflow.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> +int main(int argc, char **argv) { + static char XXX[10]; + static char YYY[10]; + static char ZZZ[10]; + memset(XXX, 0, 10); + memset(YYY, 0, 10); + memset(ZZZ, 0, 10); + int res = YYY[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*global-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of global variable}} + // CHECK: {{.*YYY.* of size 10}} + res += XXX[argc] + ZZZ[argc]; + return res; +} diff --git a/test/asan/TestCases/heap-overflow.cc b/test/asan/TestCases/heap-overflow.cc new file mode 100644 index 000000000..2c943a360 --- /dev/null +++ b/test/asan/TestCases/heap-overflow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#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 * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*heap-overflow.cc:}}[[@LINE-2]] + // CHECK: {{0x.* is located 0 bytes to the right of 10-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*heap-overflow.cc:9}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*heap-overflow.cc:9}} + free(x); + return res; +} diff --git a/test/asan/TestCases/huge_negative_hea_oob.cc b/test/asan/TestCases/huge_negative_hea_oob.cc new file mode 100644 index 000000000..58a44c5fb --- /dev/null +++ b/test/asan/TestCases/huge_negative_hea_oob.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O %s -o %t && not %t 2>&1 | FileCheck %s +// Check that we can find huge buffer overflows to the left. +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char *x = (char*)malloc(1 << 20); + memset(x, 0, 10); + int res = x[-argc * 4000]; // BOOOM + // CHECK: is located 4000 bytes to the left of + free(x); + return res; +} diff --git a/test/asan/TestCases/init-order-atexit.cc b/test/asan/TestCases/init-order-atexit.cc new file mode 100644 index 000000000..e38cdd273 --- /dev/null +++ b/test/asan/TestCases/init-order-atexit.cc @@ -0,0 +1,31 @@ +// Test for the following situation: +// (1) global A is constructed. +// (2) exit() is called during construction of global B. +// (3) destructor of A reads uninitialized global C from another module. +// 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: ASAN_OPTIONS=strict_init_order=true not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +void AccessC(); + +class A { + public: + A() { } + ~A() { AccessC(); printf("PASSED\n"); } + // CHECK-NOT: AddressSanitizer + // CHECK: PASSED +}; + +A a; + +class B { + public: + B() { exit(1); } + ~B() { } +}; + +B b; diff --git a/test/asan/TestCases/init-order-dlopen.cc b/test/asan/TestCases/init-order-dlopen.cc new file mode 100644 index 000000000..d30d11999 --- /dev/null +++ b/test/asan/TestCases/init-order-dlopen.cc @@ -0,0 +1,57 @@ +// 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 %p/SharedLibs/init-order-dlopen-so.cc \ +// RUN: -fPIC -shared -o %t-so.so -Wl,-U,_inc_global || \ +// RUN: %clangxx_asan -O0 %p/SharedLibs/init-order-dlopen-so.cc \ +// 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 -o %t -Wl,--export-dynamic || \ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %t 2>&1 | FileCheck %s +#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; +} diff --git a/test/asan/TestCases/init-order-pthread-create.cc b/test/asan/TestCases/init-order-pthread-create.cc new file mode 100644 index 000000000..52031216d --- /dev/null +++ b/test/asan/TestCases/init-order-pthread-create.cc @@ -0,0 +1,32 @@ +// Check that init-order checking is properly disabled if pthread_create is +// called. + +// RUN: %clangxx_asan %s %p/Helpers/init-order-pthread-create-extra.cc -o %t +// RUN: ASAN_OPTIONS=strict_init_order=true %t + +#include <stdio.h> +#include <pthread.h> + +void *run(void *arg) { + return arg; +} + +void *foo(void *input) { + pthread_t t; + pthread_create(&t, 0, run, input); + void *res; + pthread_join(t, &res); + return res; +} + +void *bar(void *input) { + return input; +} + +void *glob = foo((void*)0x1234); +extern void *glob2; + +int main() { + printf("%p %p\n", glob, glob2); + return 0; +} diff --git a/test/asan/TestCases/initialization-blacklist.cc b/test/asan/TestCases/initialization-blacklist.cc new file mode 100644 index 000000000..f40fcc082 --- /dev/null +++ b/test/asan/TestCases/initialization-blacklist.cc @@ -0,0 +1,32 @@ +// Test for blacklist functionality of initialization-order checker. + +// 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 \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %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 \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %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 \ +// RUN: -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +// Function is defined in another TU. +int readBadGlobal(); +int x = readBadGlobal(); // init-order bug. + +// Function is defined in another TU. +int accessBadObject(); +int y = accessBadObject(); // init-order bug. + +int readBadSrcGlobal(); +int z = readBadSrcGlobal(); // init-order bug. + +int main(int argc, char **argv) { + return argc + x + y + z - 1; +} diff --git a/test/asan/TestCases/initialization-bug.cc b/test/asan/TestCases/initialization-bug.cc new file mode 100644 index 000000000..fb289b1c7 --- /dev/null +++ b/test/asan/TestCases/initialization-bug.cc @@ -0,0 +1,45 @@ +// Test to make sure basic initialization order errors are caught. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-bug-extra2.cc -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true not %t 2>&1 | FileCheck %s + +// Do not test with optimization -- the error may be optimized away. + +// FIXME: https://code.google.com/p/address-sanitizer/issues/detail?id=186 +// XFAIL: darwin + +#include <cstdio> + +// The structure of the test is: +// "x", "y", "z" are dynamically initialized globals. +// Value of "x" depends on "y", value of "y" depends on "z". +// "x" and "z" are defined in this TU, "y" is defined in another one. +// Thus we shoud stably report initialization order fiasco independently of +// the translation unit order. + +int initZ() { + return 5; +} +int z = initZ(); + +// 'y' is a dynamically initialized global residing in a different TU. This +// dynamic initializer will read the value of 'y' before main starts. The +// result is undefined behavior, which should be caught by initialization order +// checking. +extern int y; +int __attribute__((noinline)) initX() { + return y + 1; + // 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).*}} +} + +// This initializer begins our initialization order problems. +static int x = initX(); + +int main() { + // ASan should have caused an exit before main runs. + printf("PASS\n"); + // CHECK-NOT: PASS + return 0; +} diff --git a/test/asan/TestCases/initialization-constexpr.cc b/test/asan/TestCases/initialization-constexpr.cc new file mode 100644 index 000000000..65c95edd5 --- /dev/null +++ b/test/asan/TestCases/initialization-constexpr.cc @@ -0,0 +1,31 @@ +// Constexpr: +// We need to check that a global variable initialized with a constexpr +// constructor can be accessed during dynamic initialization (as a constexpr +// constructor implies that it was initialized during constant initialization, +// not dynamic initialization). + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-constexpr-extra.cc\ +// RUN: --std=c++11 -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +class Integer { + private: + int value; + + public: + constexpr Integer(int x = 0) : value(x) {} + int getValue() {return value;} +}; +Integer coolestInteger(42); +int getCoolestInteger() { return coolestInteger.getValue(); } + +int main() { return 0; } diff --git a/test/asan/TestCases/initialization-nobug.cc b/test/asan/TestCases/initialization-nobug.cc new file mode 100644 index 000000000..ed37d137f --- /dev/null +++ b/test/asan/TestCases/initialization-nobug.cc @@ -0,0 +1,48 @@ +// A collection of various initializers which shouldn't trip up initialization +// order checking. If successful, this will just return 0. + +// RUN: %clangxx_asan -O0 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O1 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O2 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 +// RUN: %clangxx_asan -O3 %s %p/Helpers/initialization-nobug-extra.cc -fsanitize=init-order -o %t +// RUN: ASAN_OPTIONS=check_initialization_order=true %t 2>&1 + +// Simple access: +// Make sure that accessing a global in the same TU is safe + +bool condition = true; +int initializeSameTU() { + return condition ? 0x2a : 052; +} +int sameTU = initializeSameTU(); + +// Linker initialized: +// Check that access to linker initialized globals originating from a different +// TU's initializer is safe. + +int A = (1 << 1) + (1 << 3) + (1 << 5), B; +int getAB() { + return A * B; +} + +// Function local statics: +// Check that access to function local statics originating from a different +// TU's initializer is safe. + +int countCalls() { + static int calls; + return ++calls; +} + +// Trivial constructor, non-trivial destructor. +struct StructWithDtor { + ~StructWithDtor() { } + int value; +}; +StructWithDtor struct_with_dtor; +int getStructWithDtorValue() { return struct_with_dtor.value; } + +int main() { return 0; } diff --git a/test/asan/TestCases/inline.cc b/test/asan/TestCases/inline.cc new file mode 100644 index 000000000..792aff59f --- /dev/null +++ b/test/asan/TestCases/inline.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O3 %s -o %t && %t + +// Test that no_sanitize_address attribute applies even when the function would +// be normally inlined. + +#include <stdlib.h> + +__attribute__((no_sanitize_address)) +int f(int *p) { + return *p; // BOOOM?? Nope! +} + +int main(int argc, char **argv) { + int * volatile x = (int*)malloc(2*sizeof(int) + 2); + int res = f(x + 2); + if (res) + exit(0); + return 0; +} diff --git a/test/asan/TestCases/interface_test.cc b/test/asan/TestCases/interface_test.cc new file mode 100644 index 000000000..297b5526e --- /dev/null +++ b/test/asan/TestCases/interface_test.cc @@ -0,0 +1,8 @@ +// Check that user may include ASan interface header. +// RUN: %clang_asan %s -o %t && %t +// RUN: %clang %s -o %t && %t +#include <sanitizer/asan_interface.h> + +int main() { + return 0; +} diff --git a/test/asan/TestCases/invalid-free.cc b/test/asan/TestCases/invalid-free.cc new file mode 100644 index 000000000..f940b5012 --- /dev/null +++ b/test/asan/TestCases/invalid-free.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=MALLOC-CTX + +// Also works if no malloc context is available. +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %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 + 5); // BOOM + // CHECK: AddressSanitizer: attempting free on address{{.*}}in thread T0 + // CHECK: invalid-free.cc:[[@LINE-2]] + // CHECK: is located 5 bytes inside of 10-byte region + // CHECK: allocated by thread T0 here: + // MALLOC-CTX: invalid-free.cc:[[@LINE-8]] + return res; +} diff --git a/test/asan/TestCases/ioctl.cc b/test/asan/TestCases/ioctl.cc new file mode 100644 index 000000000..08ca688d3 --- /dev/null +++ b/test/asan/TestCases/ioctl.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_asan -O0 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 -g %s -o %t && ASAN_OPTIONS=handle_ioctl=1 not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -O0 -g %s -o %t && %t +// RUN: %clangxx_asan -O3 -g %s -o %t && %t + +#include <assert.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +int main(int argc, char **argv) { + int fd = socket(AF_INET, SOCK_DGRAM, 0); + + int nonblock; + int res = ioctl(fd, FIONBIO, &nonblock + 1); + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 4 at + // CHECK: {{#.* in main .*ioctl.cc:}}[[@LINE-3]] + assert(res == 0); + close(fd); + return 0; +} diff --git a/test/asan/TestCases/large_allocator_unpoisons_on_free.cc b/test/asan/TestCases/large_allocator_unpoisons_on_free.cc new file mode 100644 index 000000000..d1499d206 --- /dev/null +++ b/test/asan/TestCases/large_allocator_unpoisons_on_free.cc @@ -0,0 +1,25 @@ +// Test that LargeAllocator unpoisons memory before releasing it to the OS. +// 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=1 %t + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> + +int main() { + const int kPageSize = 4096; + void *p = NULL; + posix_memalign(&p, kPageSize, 1024 * 1024); + free(p); + + char *q = (char *)mmap(p, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, 0, 0); + assert(q == p); + + memset(q, 42, kPageSize); + + munmap(q, kPageSize); + return 0; +} diff --git a/test/asan/TestCases/large_func_test.cc b/test/asan/TestCases/large_func_test.cc new file mode 100644 index 000000000..0534bcd31 --- /dev/null +++ b/test/asan/TestCases/large_func_test.cc @@ -0,0 +1,51 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#include <stdlib.h> +__attribute__((noinline)) +static void LargeFunction(int *x, int zero) { + x[0]++; + x[1]++; + x[2]++; + x[3]++; + x[4]++; + x[5]++; + x[6]++; + x[7]++; + x[8]++; + x[9]++; + + // CHECK: {{.*ERROR: AddressSanitizer: heap-buffer-overflow on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + x[zero + 103]++; // we should report this exact line + // atos incorrectly extracts the symbol name for the static functions on + // Darwin. + // CHECK-Linux: {{#0 0x.* in LargeFunction.*large_func_test.cc:}}[[@LINE-3]] + // CHECK-Darwin: {{#0 0x.* in .*LargeFunction.*large_func_test.cc}}:[[@LINE-4]] + + x[10]++; + x[11]++; + x[12]++; + x[13]++; + x[14]++; + x[15]++; + x[16]++; + x[17]++; + x[18]++; + x[19]++; +} + +int main(int argc, char **argv) { + int *x = new int[100]; + LargeFunction(x, argc - 1); + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-1]] + // CHECK: {{0x.* is located 12 bytes to the right of 400-byte region}} + // CHECK: {{allocated by thread T0 here:}} + // CHECK-Linux: {{ #0 0x.* in operator new.*}} + // CHECK-Darwin: {{ #0 0x.* in .*_Zna.*}} + // CHECK: {{ #1 0x.* in main .*large_func_test.cc:}}[[@LINE-7]] + delete x; +} diff --git a/test/asan/TestCases/log-path_test.cc b/test/asan/TestCases/log-path_test.cc new file mode 100644 index 000000000..1072670fb --- /dev/null +++ b/test/asan/TestCases/log-path_test.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_asan %s -o %t + +// Regular run. +// RUN: not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.out + +// Good log_path. +// RUN: rm -f %t.log.* +// RUN: ASAN_OPTIONS=log_path=%t.log not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-ERROR < %t.log.* + +// Invalid log_path. +// RUN: ASAN_OPTIONS=log_path=/INVALID not %t 2> %t.out +// RUN: FileCheck %s --check-prefix=CHECK-INVALID < %t.out + +// Too long log_path. +// RUN: ASAN_OPTIONS=log_path=`for((i=0;i<10000;i++)); do echo -n $i; done` \ +// RUN: not %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: ASAN_OPTIONS=log_path=%t.log %t ARG ARG ARG +// RUN: not cat %t.log.* + + +#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 +// CHECK-INVALID: ERROR: Can't open file: /INVALID +// CHECK-LONG: ERROR: Path is too long: 01234 diff --git a/test/asan/TestCases/log_path_fork_test.cc.disabled b/test/asan/TestCases/log_path_fork_test.cc.disabled new file mode 100644 index 000000000..c6c1b49e9 --- /dev/null +++ b/test/asan/TestCases/log_path_fork_test.cc.disabled @@ -0,0 +1,22 @@ +// 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: ASAN_OPTIONS="log_path=%t.log verbosity=1" not %t 2> %t.out +// RUN: for f in %t.log.* ; do FileCheck %s < $f; done +// RUN: [ `ls %t.log.* | wc -l` == 2 ] + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char **argv) { + void *x = malloc(10); + free(x); + if (fork() == -1) return 1; + // There are two processes at this point, thus there should be two distinct + // error logs. + free(x); + return 0; +} + +// CHECK: ERROR: AddressSanitizer diff --git a/test/asan/TestCases/lsan_annotations.cc b/test/asan/TestCases/lsan_annotations.cc new file mode 100644 index 000000000..c55ab8692 --- /dev/null +++ b/test/asan/TestCases/lsan_annotations.cc @@ -0,0 +1,16 @@ +// Check that LSan annotations work fine. +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O3 %s -o %t && %t + +#include <sanitizer/lsan_interface.h> +#include <stdlib.h> + +int main() { + int *x = new int; + __lsan_ignore_object(x); + { + __lsan::ScopedDisabler disabler; + double *y = new double; + } + return 0; +} diff --git a/test/asan/TestCases/malloc_context_size.cc b/test/asan/TestCases/malloc_context_size.cc new file mode 100644 index 000000000..266ce66f5 --- /dev/null +++ b/test/asan/TestCases/malloc_context_size.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=0:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=0 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os +// RUN: ASAN_OPTIONS=malloc_context_size=1:fast_unwind_on_malloc=1 not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os + +int main() { + char *x = new char[20]; + delete[] x; + return x[0]; + // We need to keep duplicate lines with different 'CHECK-%os' prefixes, + // otherwise FileCheck barks on missing 'CHECK-%os' before 'CHECK-%os-NEXT'. + + // CHECK-Linux: freed by thread T{{.*}} here: + // CHECK-Linux-NEXT: #0 0x{{.*}} in operator delete[] + // CHECK-Darwin: freed by thread T{{.*}} here: + // CHECK-Darwin-NEXT: #0 0x{{.*}} in wrap__ZdaPv + // CHECK-NOT: #1 0x{{.*}} + + // CHECK-Linux: previously allocated by thread T{{.*}} here: + // CHECK-Linux-NEXT: #0 0x{{.*}} in operator new[] + // CHECK-Darwin: previously allocated by thread T{{.*}} here: + // CHECK-Darwin-NEXT: #0 0x{{.*}} in wrap__Znam + // CHECK-NOT: #1 0x{{.*}} + + // CHECK: SUMMARY: AddressSanitizer: heap-use-after-free +} diff --git a/test/asan/TestCases/malloc_fill.cc b/test/asan/TestCases/malloc_fill.cc new file mode 100644 index 000000000..57f50d143 --- /dev/null +++ b/test/asan/TestCases/malloc_fill.cc @@ -0,0 +1,22 @@ +// Check that we fill malloc-ed memory correctly. +// RUN: %clangxx_asan %s -o %t +// RUN: %t | FileCheck %s +// RUN: ASAN_OPTIONS=max_malloc_fill_size=10:malloc_fill_byte=8 %t | FileCheck %s --check-prefix=CHECK-10-8 +// RUN: ASAN_OPTIONS=max_malloc_fill_size=20:malloc_fill_byte=171 %t | FileCheck %s --check-prefix=CHECK-20-ab + +#include <stdio.h> +int main(int argc, char **argv) { + // With asan allocator this makes sure we get memory from mmap. + static const int kSize = 1 << 25; + unsigned char *x = new unsigned char[kSize]; + printf("-"); + for (int i = 0; i <= 32; i++) { + printf("%02x", x[i]); + } + printf("-\n"); + delete [] x; +} + +// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe- +// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000- +// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000- diff --git a/test/asan/TestCases/malloc_hook.cc b/test/asan/TestCases/malloc_hook.cc new file mode 100644 index 000000000..83be1020e --- /dev/null +++ b/test/asan/TestCases/malloc_hook.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: %t 2>&1 | FileCheck %s +#include <stdlib.h> +#include <unistd.h> + +extern "C" { +bool __asan_get_ownership(const void *p); + +void *global_ptr; + +// Note: avoid calling functions that allocate memory in malloc/free +// to avoid infinite recursion. +void __asan_malloc_hook(void *ptr, size_t sz) { + if (__asan_get_ownership(ptr)) { + write(1, "MallocHook\n", sizeof("MallocHook\n")); + global_ptr = ptr; + } +} +void __asan_free_hook(void *ptr) { + if (__asan_get_ownership(ptr) && ptr == global_ptr) + write(1, "FreeHook\n", sizeof("FreeHook\n")); +} +} // extern "C" + +int main() { + volatile int *x = new int; + // CHECK: MallocHook + // Check that malloc hook was called with correct argument. + if (global_ptr != (void*)x) { + _exit(1); + } + *x = 0; + delete x; + // CHECK: FreeHook + return 0; +} diff --git a/test/asan/TestCases/max_redzone.cc b/test/asan/TestCases/max_redzone.cc new file mode 100644 index 000000000..dbcedd044 --- /dev/null +++ b/test/asan/TestCases/max_redzone.cc @@ -0,0 +1,26 @@ +// Test max_redzone runtime option. + +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=max_redzone=16 %t 0 2>&1 +// RUN: %clangxx_asan -O0 %s -o %t && %t 1 2>&1 +// RUN: %clangxx_asan -O3 %s -o %t && ASAN_OPTIONS=max_redzone=16 %t 0 2>&1 +// RUN: %clangxx_asan -O3 %s -o %t && %t 1 2>&1 + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <sanitizer/asan_interface.h> + +int main(int argc, char **argv) { + if (argc < 2) + return 1; + bool large_redzone = atoi(argv[1]); + size_t before = __asan_get_heap_size(); + void *pp[10000]; + for (int i = 0; i < 10000; ++i) + pp[i] = malloc(4096 - 64); + size_t after = __asan_get_heap_size(); + for (int i = 0; i < 10000; ++i) + free(pp[i]); + size_t diff = after - before; + return !(large_redzone ? diff > 46000000 : diff < 46000000); +} diff --git a/test/asan/TestCases/memcmp_strict_test.cc b/test/asan/TestCases/memcmp_strict_test.cc new file mode 100644 index 000000000..e06a8c7e9 --- /dev/null +++ b/test/asan/TestCases/memcmp_strict_test.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=0 %t +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS=strict_memcmp=1 not %t 2>&1 | FileCheck %s +// Default to strict_memcmp=1. +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <string.h> +int main() { + char kFoo[] = "foo"; + char kFubar[] = "fubar"; + int res = memcmp(kFoo, kFubar, strlen(kFubar)); + printf("res: %d\n", res); + // CHECK: AddressSanitizer: stack-buffer-overflow + return 0; +} diff --git a/test/asan/TestCases/memcmp_test.cc b/test/asan/TestCases/memcmp_test.cc new file mode 100644 index 000000000..758311ddc --- /dev/null +++ b/test/asan/TestCases/memcmp_test.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// REQUIRES: compiler-rt-optimized + +#include <string.h> +int main(int argc, char **argv) { + char a1[] = {argc, 2, 3, 4}; + char a2[] = {1, 2*argc, 3, 4}; + int res = memcmp(a1, a2, 4 + argc); // BOOM + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{#0.*memcmp}} + // CHECK: {{#1.*main}} + return res; +} diff --git a/test/asan/TestCases/no_asan_gen_globals.c b/test/asan/TestCases/no_asan_gen_globals.c new file mode 100644 index 000000000..a747d7a36 --- /dev/null +++ b/test/asan/TestCases/no_asan_gen_globals.c @@ -0,0 +1,8 @@ +// Make sure __asan_gen_* strings do not end up in the symbol table. + +// RUN: %clang_asan %s -o %t.exe +// RUN: nm %t.exe | FileCheck %s + +int x, y, z; +int main() { return 0; } +// CHECK-NOT: __asan_gen_ diff --git a/test/asan/TestCases/null_deref.cc b/test/asan/TestCases/null_deref.cc new file mode 100644 index 000000000..bae356ae1 --- /dev/null +++ b/test/asan/TestCases/null_deref.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +__attribute__((noinline)) +static void NullDeref(int *ptr) { + // CHECK: ERROR: AddressSanitizer: SEGV on unknown address + // CHECK: {{0x0*000.. .*pc 0x.*}} + ptr[10]++; // BOOM + // atos on Mac cannot extract the symbol name correctly. + // CHECK-Linux: {{ #0 0x.* in NullDeref.*null_deref.cc:}}[[@LINE-2]] + // CHECK-Darwin: {{ #0 0x.* in .*NullDeref.*null_deref.cc:}}[[@LINE-3]] +} +int main() { + NullDeref((int*)0); + // CHECK: {{ #1 0x.* in main.*null_deref.cc:}}[[@LINE-1]] + // CHECK: {{AddressSanitizer can not provide additional info.}} +} diff --git a/test/asan/TestCases/on_error_callback.cc b/test/asan/TestCases/on_error_callback.cc new file mode 100644 index 000000000..d0cec2eb2 --- /dev/null +++ b/test/asan/TestCases/on_error_callback.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <stdlib.h> + +extern "C" +void __asan_on_error() { + fprintf(stderr, "__asan_on_error called"); +} + +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/partial_right.cc b/test/asan/TestCases/partial_right.cc new file mode 100644 index 000000000..a000a913d --- /dev/null +++ b/test/asan/TestCases/partial_right.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main(int argc, char **argv) { + volatile int *x = (int*)malloc(2*sizeof(int) + 2); + int res = x[2]; // BOOOM + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: [[ADDR:0x[01-9a-fa-f]+]] is located 0 bytes to the right of {{.*}}-byte region [{{.*}},{{.*}}[[ADDR]]) + return res; +} diff --git a/test/asan/TestCases/poison_partial.cc b/test/asan/TestCases/poison_partial.cc new file mode 100644 index 000000000..f7c48bf59 --- /dev/null +++ b/test/asan/TestCases/poison_partial.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s +// RUN: not %t heap 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=poison_partial=0 %t +// RUN: ASAN_OPTIONS=poison_partial=0 %t heap +#include <string.h> +char g[21]; +char *x; + +int main(int argc, char **argv) { + if (argc >= 2) + x = new char[21]; + else + x = &g[0]; + memset(x, 0, 21); + int *y = (int*)x; + return y[5]; +} +// CHECK: 0 bytes to the right diff --git a/test/asan/TestCases/print-stack-trace.cc b/test/asan/TestCases/print-stack-trace.cc new file mode 100644 index 000000000..923fa6580 --- /dev/null +++ b/test/asan/TestCases/print-stack-trace.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && %t 2>&1 | FileCheck %s + +#include <sanitizer/asan_interface.h> + +void FooBarBaz() { + __sanitizer_print_stack_trace(); +} + +int main() { + FooBarBaz(); + return 0; +} +// CHECK: {{ #0 0x.* in __sanitizer_print_stack_trace}} +// CHECK: {{ #1 0x.* in FooBarBaz\(\) .*print-stack-trace.cc:7}} +// CHECK: {{ #2 0x.* in main .*print-stack-trace.cc:11}} diff --git a/test/asan/TestCases/print_summary.cc b/test/asan/TestCases/print_summary.cc new file mode 100644 index 000000000..949c9b54f --- /dev/null +++ b/test/asan/TestCases/print_summary.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s --check-prefix=YES +// RUN: ASAN_OPTIONS=print_summary=false not %t 2>&1 | FileCheck %s --check-prefix=NO + +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 +} + diff --git a/test/asan/TestCases/printf-1.c b/test/asan/TestCases/printf-1.c new file mode 100644 index 000000000..20618a250 --- /dev/null +++ b/test/asan/TestCases/printf-1.c @@ -0,0 +1,21 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck %s +// RUN: %t 2>&1 | FileCheck %s + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + // Check that printf works fine under Asan. + printf("%c %d %.3f %s\n", c, x, f, s); + // CHECK: 0 12 1.239 34 + // Check that snprintf works fine under Asan. + char buf[4]; + snprintf(buf, 1000, "qwe"); + printf("%s\n", buf); + // CHECK: qwe + return 0; +} diff --git a/test/asan/TestCases/printf-2.c b/test/asan/TestCases/printf-2.c new file mode 100644 index 000000000..a31d2e56a --- /dev/null +++ b/test/asan/TestCases/printf-2.c @@ -0,0 +1,24 @@ +// 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: ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: ASAN_OPTIONS=replace_str=0:replace_intrin=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + char *p = strdup((const char *)s); + free(p); + printf("%c %d %.3f %s\n", c, x, f, p); + return 0; + // Check that %s is sanitized. + // CHECK-ON: heap-use-after-free + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 +} diff --git a/test/asan/TestCases/printf-3.c b/test/asan/TestCases/printf-3.c new file mode 100644 index 000000000..12ecc6485 --- /dev/null +++ b/test/asan/TestCases/printf-3.c @@ -0,0 +1,19 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS=check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile int n[1]; + printf("%c %d %.3f %s%n\n", c, x, f, s, &n[1]); + return 0; + // Check that %n is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 34 +} diff --git a/test/asan/TestCases/printf-4.c b/test/asan/TestCases/printf-4.c new file mode 100644 index 000000000..e93c60ca9 --- /dev/null +++ b/test/asan/TestCases/printf-4.c @@ -0,0 +1,22 @@ +// 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: ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=replace_str=0:replace_intrin=0:check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: ASAN_OPTIONS=replace_str=0:replace_intrin=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include <stdio.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile char buf[2]; + sprintf((char *)buf, "%c %d %.3f %s\n", c, x, f, s); + puts((const char *)buf); + return 0; + // Check that size of output buffer is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 12 1.239 34 +} diff --git a/test/asan/TestCases/printf-5.c b/test/asan/TestCases/printf-5.c new file mode 100644 index 000000000..5e90e734f --- /dev/null +++ b/test/asan/TestCases/printf-5.c @@ -0,0 +1,22 @@ +// RUN: %clang_asan -O2 %s -o %t +// We need replace_intrin=0 to avoid reporting errors in memcpy. +// RUN: ASAN_OPTIONS=replace_intrin=0:check_printf=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-ON %s +// RUN: ASAN_OPTIONS=replace_intrin=0:check_printf=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: ASAN_OPTIONS=replace_intrin=0 %t 2>&1 | FileCheck --check-prefix=CHECK-OFF %s + +#include <stdio.h> +#include <string.h> +int main() { + volatile char c = '0'; + volatile int x = 12; + volatile float f = 1.239; + volatile char s[] = "34"; + volatile char fmt[2]; + memcpy((char *)fmt, "%c %d %f %s\n", sizeof(fmt)); + printf((char *)fmt, c, x, f, s); + return 0; + // Check that format string is sanitized. + // CHECK-ON: stack-buffer-overflow + // CHECK-ON-NOT: 0 12 1.239 34 + // CHECK-OFF: 0 +} diff --git a/test/asan/TestCases/readv.cc b/test/asan/TestCases/readv.cc new file mode 100644 index 000000000..ba17505f3 --- /dev/null +++ b/test/asan/TestCases/readv.cc @@ -0,0 +1,32 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t +// RUN: %clangxx_asan -O0 %s -DPOSITIVE -o %t && not %t 2>&1 | FileCheck %s + +// Test the readv() interceptor. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <time.h> + +int main() { + char buf[2011]; + struct iovec iov[2]; +#ifdef POSITIVE + char * volatile buf_ = buf; + iov[0].iov_base = buf_ - 1; +#else + iov[0].iov_base = buf + 1; +#endif + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/etc/hosts", O_RDONLY); + assert(fd > 0); + readv(fd, iov, 2); + // CHECK: WRITE of size 5 at + close(fd); + return 0; +} diff --git a/test/asan/TestCases/sanity_check_pure_c.c b/test/asan/TestCases/sanity_check_pure_c.c new file mode 100644 index 000000000..df150675b --- /dev/null +++ b/test/asan/TestCases/sanity_check_pure_c.c @@ -0,0 +1,19 @@ +// Sanity checking a test in pure C. +// RUN: %clang_asan -O2 %s -o %t +// RUN: not %t 2>&1 | FileCheck %s + +// Sanity checking a test in pure C with -pie. +// RUN: %clang_asan -O2 %s -pie -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: heap-use-after-free + // CHECK: free + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-4]] + // CHECK: malloc + // CHECK: main{{.*}}sanity_check_pure_c.c:[[@LINE-7]] +} diff --git a/test/asan/TestCases/shared-lib-test.cc b/test/asan/TestCases/shared-lib-test.cc new file mode 100644 index 000000000..126903a55 --- /dev/null +++ b/test/asan/TestCases/shared-lib-test.cc @@ -0,0 +1,42 @@ +// RUN: %clangxx_asan -O0 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %p/SharedLibs/shared-lib-test-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> + +#include <string> + +using std::string; + +typedef void (fun_t)(int x); + +int main(int argc, char *argv[]) { + string path = string(argv[0]) + "-so.so"; + printf("opening %s ... \n", path.c_str()); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + fun_t *inc = (fun_t*)dlsym(lib, "inc"); + if (!inc) return 1; + printf("ok\n"); + inc(1); + inc(-1); // BOOM + // CHECK: {{.*ERROR: AddressSanitizer: global-buffer-overflow}} + // CHECK: {{READ of size 4 at 0x.* thread T0}} + // CHECK: {{ #0 0x.*}} + // CHECK: {{ #1 0x.* in main .*shared-lib-test.cc:}}[[@LINE-4]] + return 0; +} diff --git a/test/asan/TestCases/sleep_before_dying.c b/test/asan/TestCases/sleep_before_dying.c new file mode 100644 index 000000000..8dee9f277 --- /dev/null +++ b/test/asan/TestCases/sleep_before_dying.c @@ -0,0 +1,10 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS="sleep_before_dying=1" not %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // CHECK: Sleeping for 1 second +} diff --git a/test/asan/TestCases/stack-buffer-overflow-with-position.cc b/test/asan/TestCases/stack-buffer-overflow-with-position.cc new file mode 100644 index 000000000..7fbe5c595 --- /dev/null +++ b/test/asan/TestCases/stack-buffer-overflow-with-position.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_asan -O2 %s -o %t +// RUN: not %t -2 2>&1 | FileCheck --check-prefix=CHECK-m2 %s +// RUN: not %t -1 2>&1 | FileCheck --check-prefix=CHECK-m1 %s +// RUN: %t 0 +// RUN: %t 8 +// RUN: not %t 9 2>&1 | FileCheck --check-prefix=CHECK-9 %s +// RUN: not %t 10 2>&1 | FileCheck --check-prefix=CHECK-10 %s +// RUN: not %t 30 2>&1 | FileCheck --check-prefix=CHECK-30 %s +// RUN: not %t 31 2>&1 | FileCheck --check-prefix=CHECK-31 %s +// RUN: not %t 41 2>&1 | FileCheck --check-prefix=CHECK-41 %s +// RUN: not %t 42 2>&1 | FileCheck --check-prefix=CHECK-42 %s +// RUN: not %t 62 2>&1 | FileCheck --check-prefix=CHECK-62 %s +// RUN: not %t 63 2>&1 | FileCheck --check-prefix=CHECK-63 %s +// RUN: not %t 73 2>&1 | FileCheck --check-prefix=CHECK-73 %s +// RUN: not %t 74 2>&1 | FileCheck --check-prefix=CHECK-74 %s +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +int main(int argc, char **argv) { + assert(argc >= 2); + int idx = atoi(argv[1]); + char AAA[10], BBB[10], CCC[10]; + memset(AAA, 0, sizeof(AAA)); + memset(BBB, 0, sizeof(BBB)); + memset(CCC, 0, sizeof(CCC)); + int res = 0; + char *p = AAA + idx; + printf("AAA: %p\ny: %p\nz: %p\np: %p\n", AAA, BBB, CCC, p); + // make sure BBB and CCC are not removed; + return *(short*)(p) + BBB[argc % 2] + CCC[argc % 2]; +} +// CHECK-m2: 'AAA' <== {{.*}}underflows this variable +// CHECK-m1: 'AAA' <== {{.*}}partially underflows this variable +// CHECK-9: 'AAA' <== {{.*}}partially overflows this variable +// CHECK-10: 'AAA' <== {{.*}}overflows this variable +// CHECK-30: 'BBB' <== {{.*}}underflows this variable +// CHECK-31: 'BBB' <== {{.*}}partially underflows this variable +// CHECK-41: 'BBB' <== {{.*}}partially overflows this variable +// CHECK-42: 'BBB' <== {{.*}}overflows this variable +// CHECK-62: 'CCC' <== {{.*}}underflows this variable +// CHECK-63: 'CCC' <== {{.*}}partially underflows this variable +// CHECK-73: 'CCC' <== {{.*}}partially overflows this variable +// CHECK-74: 'CCC' <== {{.*}}overflows this variable diff --git a/test/asan/TestCases/stack-frame-demangle.cc b/test/asan/TestCases/stack-frame-demangle.cc new file mode 100644 index 000000000..2b83ecc29 --- /dev/null +++ b/test/asan/TestCases/stack-frame-demangle.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> + +namespace XXX { +struct YYY { + static int ZZZ(int x) { + char array[10]; + memset(array, 0, 10); + return array[x]; // BOOOM + // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 1 at + // CHECK: is located in stack of thread T0 at offset + // CHECK: XXX::YYY::ZZZ + } +}; +} // namespace XXX + +int main(int argc, char **argv) { + int res = XXX::YYY::ZZZ(argc + 10); + return res; +} diff --git a/test/asan/TestCases/stack-oob-frames.cc b/test/asan/TestCases/stack-oob-frames.cc new file mode 100644 index 000000000..909e700b3 --- /dev/null +++ b/test/asan/TestCases/stack-oob-frames.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -O1 %s -o %t +// RUN: not %t 0 2>&1 | FileCheck %s --check-prefix=CHECK0 +// RUN: not %t 1 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: not %t 2 2>&1 | FileCheck %s --check-prefix=CHECK2 +// RUN: not %t 3 2>&1 | FileCheck %s --check-prefix=CHECK3 + +#define NOINLINE __attribute__((noinline)) +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { + char s[4] = {0}; + char *d = s; + break_optimization(&d); + switch (frame) { + case 3: a[5]++; break; + case 2: b[5]++; break; + case 1: c[5]++; break; + case 0: d[5]++; break; + } +} +NOINLINE static void Frame1(int frame, char *a, char *b) { + char c[4] = {0}; Frame0(frame, a, b, c); + break_optimization(0); +} +NOINLINE static void Frame2(int frame, char *a) { + char b[4] = {0}; Frame1(frame, a, b); + break_optimization(0); +} +NOINLINE static void Frame3(int frame) { + char a[4] = {0}; Frame2(frame, a); + break_optimization(0); +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + Frame3(argv[1][0] - '0'); +} + +// CHECK0: AddressSanitizer: stack-buffer-overflow +// CHECK0: #0{{.*}}Frame0 +// CHECK0: #1{{.*}}Frame1 +// CHECK0: #2{{.*}}Frame2 +// CHECK0: #3{{.*}}Frame3 +// CHECK0: is located in stack of thread T0 at offset +// CHECK0-NEXT: #0{{.*}}Frame0 +// +// CHECK1: AddressSanitizer: stack-buffer-overflow +// CHECK1: is located in stack of thread T0 at offset +// CHECK1-NEXT: #0{{.*}}Frame1 +// +// CHECK2: AddressSanitizer: stack-buffer-overflow +// CHECK2: is located in stack of thread T0 at offset +// CHECK2-NEXT: #0{{.*}}Frame2 +// +// CHECK3: AddressSanitizer: stack-buffer-overflow +// CHECK3: is located in stack of thread T0 at offset +// CHECK3-NEXT: #0{{.*}}Frame3 diff --git a/test/asan/TestCases/stack-overflow.cc b/test/asan/TestCases/stack-overflow.cc new file mode 100644 index 000000000..adf1c0784 --- /dev/null +++ b/test/asan/TestCases/stack-overflow.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> +int main(int argc, char **argv) { + char x[10]; + memset(x, 0, 10); + int res = x[argc * 10]; // BOOOM + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*stack-overflow.cc:}}[[@LINE-2]] + // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} + // CHECK-NEXT: in{{.*}}main{{.*}}stack-overflow.cc + return res; +} diff --git a/test/asan/TestCases/stack-use-after-return.cc b/test/asan/TestCases/stack-use-after-return.cc new file mode 100644 index 000000000..75313d4c7 --- /dev/null +++ b/test/asan/TestCases/stack-use-after-return.cc @@ -0,0 +1,77 @@ +// RUN: export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS=detect_stack_use_after_return=0 %t +// Regression test for a CHECK failure with small stack size and large frame. +// RUN: %clangxx_asan -O3 %s -o %t -DkSize=10000 && \ +// RUN: (ulimit -s 65; not %t) 2>&1 | FileCheck %s +// +// Test that we can find UAR in a thread other than main: +// RUN: %clangxx_asan -DUseThread -O2 %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck --check-prefix=THREAD %s +// +// Test the max_uar_stack_size_log/min_uar_stack_size_log flag. +// +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:max_uar_stack_size_log=20:verbosity=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-20 %s +// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:min_uar_stack_size_log=24:max_uar_stack_size_log=24:verbosity=1 not %t 2>&1 | FileCheck --check-prefix=CHECK-24 %s + +#include <stdio.h> +#include <pthread.h> + +#ifndef kSize +# define kSize 1 +#endif + +#ifndef UseThread +# define UseThread 0 +#endif + +__attribute__((noinline)) +char *Ident(char *x) { + fprintf(stderr, "1: %p\n", x); + return x; +} + +__attribute__((noinline)) +char *Func1() { + char local[kSize]; + return Ident(local); +} + +__attribute__((noinline)) +void Func2(char *x) { + fprintf(stderr, "2: %p\n", x); + *x = 1; + // CHECK: WRITE of size 1 {{.*}} thread T0 + // CHECK: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-2]] + // CHECK: is located in stack of thread T0 at offset + // CHECK: 'local' <== Memory access at offset {{16|32}} is inside this variable + // THREAD: WRITE of size 1 {{.*}} thread T{{[1-9]}} + // THREAD: #0{{.*}}Func2{{.*}}stack-use-after-return.cc:[[@LINE-6]] + // THREAD: is located in stack of thread T{{[1-9]}} at offset + // THREAD: 'local' <== Memory access at offset {{16|32}} is inside this variable + // CHECK-20: T0: FakeStack created:{{.*}} stack_size_log: 20 + // CHECK-24: T0: FakeStack created:{{.*}} stack_size_log: 24 +} + +void *Thread(void *unused) { + Func2(Func1()); + return NULL; +} + +int main(int argc, char **argv) { +#if UseThread + pthread_t t; + pthread_create(&t, 0, Thread, 0); + pthread_join(t, 0); +#else + Func2(Func1()); +#endif + return 0; +} diff --git a/test/asan/TestCases/start-deactivated.cc b/test/asan/TestCases/start-deactivated.cc new file mode 100644 index 000000000..a06976649 --- /dev/null +++ b/test/asan/TestCases/start-deactivated.cc @@ -0,0 +1,58 @@ +// Test for ASAN_OPTIONS=start_deactivated=1 mode. +// Main executable is uninstrumented, but linked to ASan runtime. The shared +// library is instrumented. Memory errors before dlopen are not detected. + +// RUN: %clangxx_asan -O0 %p/SharedLibs/start-deactivated-so.cc \ +// RUN: -fPIC -shared -o %t-so.so +// RUN: %clangxx -O0 %s -c -o %t.o +// RUN: %clangxx_asan -O0 %t.o -o %t +// RUN: ASAN_OPTIONS=start_deactivated=1 not %t 2>&1 | FileCheck %s + +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <string> + +#include "sanitizer/asan_interface.h" + +void test_malloc_shadow() { + char *p = (char *)malloc(100); + char *q = (char *)__asan_region_is_poisoned(p + 95, 8); + fprintf(stderr, "=%zd=\n", q ? q - (p + 95) : -1); + free(p); +} + +typedef void (*Fn)(); + +int main(int argc, char *argv[]) { + test_malloc_shadow(); + // CHECK: =-1= + + std::string path = std::string(argv[0]) + "-so.so"; + void *dso = dlopen(path.c_str(), RTLD_NOW); + if (!dso) { + fprintf(stderr, "dlopen failed: %s\n", dlerror()); + return 1; + } + + test_malloc_shadow(); + // CHECK: =5= + + void *fn = dlsym(dso, "do_another_bad_thing"); + if (!fn) { + fprintf(stderr, "dlsym failed: %s\n", dlerror()); + return 1; + } + + ((Fn)fn)(); + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: READ of size 1 + // CHECK: {{#0 .* in do_another_bad_thing}} + // CHECK: is located 5 bytes to the right of 100-byte region + // CHECK: in do_another_bad_thing + + return 0; +} diff --git a/test/asan/TestCases/strdup_oob_test.cc b/test/asan/TestCases/strdup_oob_test.cc new file mode 100644 index 000000000..e92afd3ca --- /dev/null +++ b/test/asan/TestCases/strdup_oob_test.cc @@ -0,0 +1,19 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <string.h> + +char kString[] = "foo"; + +int main(int argc, char **argv) { + char *copy = strdup(kString); + int x = copy[4 + argc]; // BOOM + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: #0 {{.*}}main {{.*}}strdup_oob_test.cc:[[@LINE-2]] + // CHECK: allocated by thread T{{.*}} here: + // CHECK: #0 {{.*}}strdup + // CHECK: strdup_oob_test.cc:[[@LINE-6]] + return x; +} diff --git a/test/asan/TestCases/strerror_r_test.cc b/test/asan/TestCases/strerror_r_test.cc new file mode 100644 index 000000000..d91ad3320 --- /dev/null +++ b/test/asan/TestCases/strerror_r_test.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 %s -o %t && %t + +// Regression test for PR17138. + +#include <assert.h> +#include <string.h> +#include <stdio.h> + +int main() { + char buf[1024]; + char *res = (char *)strerror_r(300, buf, sizeof(buf)); + printf("%p\n", res); + return 0; +} diff --git a/test/asan/TestCases/strip_path_prefix.c b/test/asan/TestCases/strip_path_prefix.c new file mode 100644 index 000000000..c4d6ba49d --- /dev/null +++ b/test/asan/TestCases/strip_path_prefix.c @@ -0,0 +1,12 @@ +// RUN: %clang_asan -O2 %s -o %t +// RUN: ASAN_OPTIONS="strip_path_prefix='/'" not %t 2>&1 | FileCheck %s + +#include <stdlib.h> +int main() { + char *x = (char*)malloc(10 * sizeof(char)); + free(x); + return x[5]; + // Check that paths in error report don't start with slash. + // CHECK: heap-use-after-free + // CHECK-NOT: #0 0x{{.*}} ({{[/].*}}) +} diff --git a/test/asan/TestCases/strncpy-overflow.cc b/test/asan/TestCases/strncpy-overflow.cc new file mode 100644 index 000000000..f91e191fd --- /dev/null +++ b/test/asan/TestCases/strncpy-overflow.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// REQUIRES: compiler-rt-optimized + +#include <string.h> +#include <stdlib.h> +int main(int argc, char **argv) { + char *hello = (char*)malloc(6); + strcpy(hello, "hello"); + char *short_buffer = (char*)malloc(9); + strncpy(short_buffer, hello, 10); // BOOM + // CHECK: {{WRITE of size 10 at 0x.* thread T0}} + // CHECK-Linux: {{ #0 0x.* in .*strncpy}} + // CHECK-Darwin: {{ #0 0x.* in wrap_strncpy}} + // CHECK: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-4]] + // CHECK: {{0x.* is located 0 bytes to the right of 9-byte region}} + // CHECK: {{allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-10]] + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*strncpy-overflow.cc:}}[[@LINE-13]] + return short_buffer[8]; +} diff --git a/test/asan/TestCases/throw_call_test.cc b/test/asan/TestCases/throw_call_test.cc new file mode 100644 index 000000000..3a97a94b6 --- /dev/null +++ b/test/asan/TestCases/throw_call_test.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_asan %s -o %t && %t +// http://code.google.com/p/address-sanitizer/issues/detail?id=147 (not fixed). +// BROKEN: %clangxx_asan %s -o %t -static-libstdc++ && %t +// +// Android builds with static libstdc++ by default. +// XFAIL: android + +#include <stdio.h> +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline, no_sanitize_address)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + if (zero == 0) + throw 42; +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} diff --git a/test/asan/TestCases/throw_catch.cc b/test/asan/TestCases/throw_catch.cc new file mode 100644 index 000000000..593a26981 --- /dev/null +++ b/test/asan/TestCases/throw_catch.cc @@ -0,0 +1,75 @@ +// RUN: %clangxx_asan -O %s -o %t && %t + +#include <assert.h> +#include <setjmp.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sanitizer/asan_interface.h> + +__attribute__((noinline)) +void Throw() { + int local; + fprintf(stderr, "Throw: %p\n", &local); + throw 1; +} + +__attribute__((noinline)) +void ThrowAndCatch() { + int local; + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch: %p\n", &local); + } +} + +void TestThrow() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + ThrowAndCatch(); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} + +void TestThrowInline() { + char x[32]; + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + try { + Throw(); + } catch(...) { + fprintf(stderr, "Catch\n"); + } + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} + +static jmp_buf buf; + +void TestLongJmp() { + char x[32]; + fprintf(stderr, "\nTestLongJmp\n"); + fprintf(stderr, "Before: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + if (0 == setjmp(buf)) + longjmp(buf, 1); + fprintf(stderr, "After: %p poisoned: %d\n", &x, + __asan_address_is_poisoned(x + 32)); + // FIXME: Invert this assertion once we fix + // https://code.google.com/p/address-sanitizer/issues/detail?id=258 + assert(!__asan_address_is_poisoned(x + 32)); +} + +int main(int argc, char **argv) { + TestThrow(); + TestThrowInline(); + TestLongJmp(); +} diff --git a/test/asan/TestCases/throw_invoke_test.cc b/test/asan/TestCases/throw_invoke_test.cc new file mode 100644 index 000000000..077a940e8 --- /dev/null +++ b/test/asan/TestCases/throw_invoke_test.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_asan %s -o %t && %t +// RUN: %clangxx_asan %s -o %t -static-libstdc++ && %t +#include <stdio.h> +static volatile int zero = 0; +inline void pretend_to_do_something(void *x) { + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline)) +void ReallyThrow() { + fprintf(stderr, "ReallyThrow\n"); + try { + if (zero == 0) + throw 42; + else if (zero == 1) + throw 1.; + } catch(double x) { + } +} + +__attribute__((noinline)) +void Throw() { + int a, b, c, d, e; + pretend_to_do_something(&a); + pretend_to_do_something(&b); + pretend_to_do_something(&c); + pretend_to_do_something(&d); + pretend_to_do_something(&e); + fprintf(stderr, "Throw stack = %p\n", &a); + ReallyThrow(); +} + +__attribute__((noinline)) +void CheckStack() { + int ar[100]; + pretend_to_do_something(ar); + for (int i = 0; i < 100; i++) + ar[i] = i; + fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); +} + +int main(int argc, char** argv) { + try { + Throw(); + } catch(int a) { + fprintf(stderr, "a = %d\n", a); + } + CheckStack(); +} + diff --git a/test/asan/TestCases/time_interceptor.cc b/test/asan/TestCases/time_interceptor.cc new file mode 100644 index 000000000..3be00d60c --- /dev/null +++ b/test/asan/TestCases/time_interceptor.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s + +// Test the time() interceptor. + +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +int main() { + time_t *tm = (time_t*)malloc(sizeof(time_t)); + free(tm); + time_t t = time(tm); + printf("Time: %s\n", ctime(&t)); // NOLINT + // CHECK: use-after-free + return 0; +} diff --git a/test/asan/TestCases/uar_and_exceptions.cc b/test/asan/TestCases/uar_and_exceptions.cc new file mode 100644 index 000000000..c967531c2 --- /dev/null +++ b/test/asan/TestCases/uar_and_exceptions.cc @@ -0,0 +1,40 @@ +// Test that use-after-return works with exceptions. +// export ASAN_OPTIONS=detect_stack_use_after_return=1 +// RUN: %clangxx_asan -O0 %s -o %t && %t + +#include <stdio.h> + +volatile char *g; + +#ifndef FRAME_SIZE +# define FRAME_SIZE 100 +#endif + +#ifndef NUM_ITER +# define NUM_ITER 4000 +#endif + +#ifndef DO_THROW +# define DO_THROW 1 +#endif + +void Func(int depth) { + char frame[FRAME_SIZE]; + g = &frame[0]; + if (depth) + Func(depth - 1); + else if (DO_THROW) + throw 1; +} + +int main(int argc, char **argv) { + for (int i = 0; i < NUM_ITER; i++) { + try { + Func(argc * 100); + } catch(...) { + } + if ((i % (NUM_ITER / 10)) == 0) + fprintf(stderr, "done [%d]\n", i); + } + return 0; +} diff --git a/test/asan/TestCases/unaligned_loads_and_stores.cc b/test/asan/TestCases/unaligned_loads_and_stores.cc new file mode 100644 index 000000000..d50566c44 --- /dev/null +++ b/test/asan/TestCases/unaligned_loads_and_stores.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_asan -O0 %s -o %t +// RUN: not %t A 2>&1 | FileCheck --check-prefix=CHECK-A %s +// RUN: not %t B 2>&1 | FileCheck --check-prefix=CHECK-B %s +// RUN: not %t C 2>&1 | FileCheck --check-prefix=CHECK-C %s +// RUN: not %t D 2>&1 | FileCheck --check-prefix=CHECK-D %s +// RUN: not %t E 2>&1 | FileCheck --check-prefix=CHECK-E %s + +// RUN: not %t K 2>&1 | FileCheck --check-prefix=CHECK-K %s +// RUN: not %t L 2>&1 | FileCheck --check-prefix=CHECK-L %s +// RUN: not %t M 2>&1 | FileCheck --check-prefix=CHECK-M %s +// RUN: not %t N 2>&1 | FileCheck --check-prefix=CHECK-N %s +// RUN: not %t O 2>&1 | FileCheck --check-prefix=CHECK-O %s + +#include <sanitizer/asan_interface.h> + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + if (argc != 2) return 1; + char *x = new char[16]; + memset(x, 0xab, 16); + int res = 1; + switch (argv[1][0]) { + case 'A': res = __sanitizer_unaligned_load16(x + 15); break; +// CHECK-A ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-A: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-A: is located 0 bytes to the right of 16-byte region + case 'B': res = __sanitizer_unaligned_load32(x + 14); break; +// CHECK-B: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'C': res = __sanitizer_unaligned_load32(x + 13); break; +// CHECK-C: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'D': res = __sanitizer_unaligned_load64(x + 15); break; +// CHECK-D: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'E': res = __sanitizer_unaligned_load64(x + 9); break; +// CHECK-E: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + + case 'K': __sanitizer_unaligned_store16(x + 15, 0); break; +// CHECK-K ERROR: AddressSanitizer: heap-buffer-overflow on address +// CHECK-K: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-2]] +// CHECK-K: is located 0 bytes to the right of 16-byte region + case 'L': __sanitizer_unaligned_store32(x + 15, 0); break; +// CHECK-L: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'M': __sanitizer_unaligned_store32(x + 13, 0); break; +// CHECK-M: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'N': __sanitizer_unaligned_store64(x + 10, 0); break; +// CHECK-N: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + case 'O': __sanitizer_unaligned_store64(x + 14, 0); break; +// CHECK-O: main{{.*}}unaligned_loads_and_stores.cc:[[@LINE-1]] + } + delete x; + return res; +} diff --git a/test/asan/TestCases/use-after-free-right.cc b/test/asan/TestCases/use-after-free-right.cc new file mode 100644 index 000000000..88d91f53d --- /dev/null +++ b/test/asan/TestCases/use-after-free-right.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +// Test use-after-free report in the case when access is at the right border of +// the allocation. + +#include <stdlib.h> +int main() { + volatile char *x = (char*)malloc(sizeof(char)); + free((void*)x); + *x = 42; + // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}} + // CHECK: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{WRITE of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free-right.cc:13}} + // CHECK: {{0x.* is located 0 bytes inside of 1-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:12}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:12}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free-right.cc:11}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free-right.cc:11}} +} diff --git a/test/asan/TestCases/use-after-free.cc b/test/asan/TestCases/use-after-free.cc new file mode 100644 index 000000000..4f31422be --- /dev/null +++ b/test/asan/TestCases/use-after-free.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O1 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O2 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s --check-prefix=CHECK-%os --check-prefix=CHECK + +#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: {{0x.* at pc 0x.* bp 0x.* sp 0x.*}} + // CHECK: {{READ of size 1 at 0x.* thread T0}} + // CHECK: {{ #0 0x.* in main .*use-after-free.cc:10}} + // CHECK: {{0x.* is located 5 bytes inside of 10-byte region .0x.*,0x.*}} + // CHECK: {{freed by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*free}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:9}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_free}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:9}} + + // CHECK: {{previously allocated by thread T0 here:}} + + // CHECK-Linux: {{ #0 0x.* in .*malloc}} + // CHECK-Linux: {{ #1 0x.* in main .*use-after-free.cc:8}} + + // CHECK-Darwin: {{ #0 0x.* in wrap_malloc.*}} + // CHECK-Darwin: {{ #1 0x.* in main .*use-after-free.cc:8}} + // CHECK: Shadow byte legend (one shadow byte represents 8 application bytes): + // CHECK: Global redzone: + // CHECK: ASan internal: +} diff --git a/test/asan/TestCases/use-after-poison.cc b/test/asan/TestCases/use-after-poison.cc new file mode 100644 index 000000000..e3bc6ecee --- /dev/null +++ b/test/asan/TestCases/use-after-poison.cc @@ -0,0 +1,20 @@ +// Check that __asan_poison_memory_region works. +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// +// Check that we can disable it +// RUN: ASAN_OPTIONS=allow_user_poisoning=0 %t + +#include <stdlib.h> + +extern "C" void __asan_poison_memory_region(void *, size_t); + +int main(int argc, char **argv) { + char *x = new char[16]; + x[10] = 0; + __asan_poison_memory_region(x, 16); + int res = x[argc * 10]; // BOOOM + // CHECK: ERROR: AddressSanitizer: use-after-poison + // CHECK: main{{.*}}use-after-poison.cc:[[@LINE-2]] + delete [] x; + return res; +} diff --git a/test/asan/TestCases/use-after-scope-dtor-order.cc b/test/asan/TestCases/use-after-scope-dtor-order.cc new file mode 100644 index 000000000..32fa6ad8a --- /dev/null +++ b/test/asan/TestCases/use-after-scope-dtor-order.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +#include <stdio.h> + +struct IntHolder { + explicit IntHolder(int *val = 0) : val_(val) { } + ~IntHolder() { + printf("Value: %d\n", *val_); // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in IntHolder::~IntHolder{{.*}}use-after-scope-dtor-order.cc:[[@LINE-2]] + } + void set(int *val) { val_ = val; } + int *get() { return val_; } + + int *val_; +}; + +int main(int argc, char *argv[]) { + // It is incorrect to use "x" int IntHolder destructor, because "x" is + // "destroyed" earlier as it's declared later. + IntHolder holder; + int x = argc; + holder.set(&x); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope-inlined.cc b/test/asan/TestCases/use-after-scope-inlined.cc new file mode 100644 index 000000000..0bad048e3 --- /dev/null +++ b/test/asan/TestCases/use-after-scope-inlined.cc @@ -0,0 +1,27 @@ +// Test with "-O2" only to make sure inlining (leading to use-after-scope) +// happens. "always_inline" is not enough, as Clang doesn't emit +// llvm.lifetime intrinsics at -O0. +// +// RUN: %clangxx_asan -O2 -fsanitize=use-after-scope %s -o %t && not %t 2>&1 | FileCheck %s + +int *arr; + +__attribute__((always_inline)) +void inlined(int arg) { + int x[5]; + for (int i = 0; i < arg; i++) x[i] = i; + arr = x; +} + +int main(int argc, char *argv[]) { + inlined(argc); + return arr[argc - 1]; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: READ of size 4 at 0x{{.*}} thread T0 + // CHECK: #0 0x{{.*}} in main + // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] + // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset + // CHECK: [[OFFSET:[^ ]*]] in frame + // CHECK: main + // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' +} diff --git a/test/asan/TestCases/use-after-scope-nobug.cc b/test/asan/TestCases/use-after-scope-nobug.cc new file mode 100644 index 000000000..c23acf76e --- /dev/null +++ b/test/asan/TestCases/use-after-scope-nobug.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && %t + +#include <stdio.h> + +int main() { + int *p = 0; + // Variable goes in and out of scope. + for (int i = 0; i < 3; i++) { + int x = 0; + p = &x; + } + printf("PASSED\n"); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope-temp.cc b/test/asan/TestCases/use-after-scope-temp.cc new file mode 100644 index 000000000..13d714f9d --- /dev/null +++ b/test/asan/TestCases/use-after-scope-temp.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: %t 2>&1 | FileCheck %s +// +// Lifetime for temporaries is not emitted yet. +// XFAIL: * + +#include <stdio.h> + +struct IntHolder { + explicit IntHolder(int val) : val(val) { + printf("IntHolder: %d\n", val); + } + int val; +}; + +const IntHolder *saved; + +void save(const IntHolder &holder) { + saved = &holder; +} + +int main(int argc, char *argv[]) { + save(IntHolder(10)); + int x = saved->val; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope-temp.cc:[[@LINE-2]] + printf("saved value: %d\n", x); + return 0; +} diff --git a/test/asan/TestCases/use-after-scope.cc b/test/asan/TestCases/use-after-scope.cc new file mode 100644 index 000000000..c46c9594c --- /dev/null +++ b/test/asan/TestCases/use-after-scope.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_asan -O0 -fsanitize=use-after-scope %s -o %t && \ +// RUN: not %t 2>&1 | FileCheck %s +// RUN: ASAN_OPTIONS="detect_stack_use_after_return=1" not %t 2>&1 | FileCheck %s + +int main() { + int *p = 0; + { + int x = 0; + p = &x; + } + return *p; // BOOM + // CHECK: ERROR: AddressSanitizer: stack-use-after-scope + // CHECK: #0 0x{{.*}} in main {{.*}}use-after-scope.cc:[[@LINE-2]] + // CHECK: Address 0x{{.*}} is located in stack of thread T{{.*}} at offset [[OFFSET:[^ ]+]] in frame + // {{\[}}[[OFFSET]], {{[0-9]+}}) 'x' +} diff --git a/test/asan/TestCases/wait.cc b/test/asan/TestCases/wait.cc new file mode 100644 index 000000000..730221b32 --- /dev/null +++ b/test/asan/TestCases/wait.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_asan -DWAIT -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAITPID -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAITPID -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3 -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3 -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4 -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4 -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT3_RUSAGE -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -DWAIT4_RUSAGE -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; +#if defined(WAIT) + res = wait(status); +#elif defined(WAITPID) + res = waitpid(pid, status, WNOHANG); +#elif defined(WAIT3) + res = wait3(status, WNOHANG, NULL); +#elif defined(WAIT4) + res = wait4(pid, status, WNOHANG, NULL); +#elif defined(WAIT3_RUSAGE) || defined(WAIT4_RUSAGE) + struct rusage *ru = (struct rusage*)(x + argc * 3); + int good_status; +# if defined(WAIT3_RUSAGE) + res = wait3(&good_status, WNOHANG, ru); +# elif defined(WAIT4_RUSAGE) + res = wait4(pid, &good_status, WNOHANG, ru); +# endif +#endif + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*wait}} + // CHECK: {{in main .*wait.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res == -1 ? 1 : 0; + } + // child + return 0; +} diff --git a/test/asan/TestCases/waitid.cc b/test/asan/TestCases/waitid.cc new file mode 100644 index 000000000..386e7108e --- /dev/null +++ b/test/asan/TestCases/waitid.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %t 2>&1 | FileCheck %s + +// No waitid on Android. +// XFAIL: android + +#include <assert.h> +#include <sys/wait.h> +#include <unistd.h> + +int main(int argc, char **argv) { + pid_t pid = fork(); + if (pid) { // parent + int x[3]; + int *status = x + argc * 3; + int res; + + siginfo_t *si = (siginfo_t*)(x + argc * 3); + res = waitid(P_ALL, 0, si, WEXITED | WNOHANG); + // CHECK: stack-buffer-overflow + // CHECK: {{WRITE of size .* at 0x.* thread T0}} + // CHECK: {{in .*waitid}} + // CHECK: {{in main .*waitid.cc:}} + // CHECK: is located in stack of thread T0 at offset + // CHECK: {{in main}} + return res != -1; + } + // child + return 0; +} diff --git a/test/asan/Unit/lit.site.cfg.in b/test/asan/Unit/lit.site.cfg.in new file mode 100644 index 000000000..96cfc386a --- /dev/null +++ b/test/asan/Unit/lit.site.cfg.in @@ -0,0 +1,18 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'AddressSanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with ASan unit tests. +# FIXME: De-hardcode this path. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/asan/tests" +config.test_source_root = config.test_exec_root + +# Enable leak detection in ASan unit tests on x86_64-linux. +if config.host_os == 'Linux' and config.host_arch == 'x86_64': + config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' diff --git a/test/asan/android_commands/android_common.py b/test/asan/android_commands/android_common.py new file mode 100644 index 000000000..43ac7b48d --- /dev/null +++ b/test/asan/android_commands/android_common.py @@ -0,0 +1,29 @@ +import os, subprocess, tempfile +import time + +ANDROID_TMPDIR = '/data/local/tmp/Output' +ADB = os.environ.get('ADB', 'adb') + +verbose = False +if os.environ.get('ANDROID_RUN_VERBOSE') == '1': + verbose = True + +def adb(args): + if verbose: + print args + devnull = open(os.devnull, 'w') + return subprocess.call([ADB] + args, stdout=devnull, stderr=subprocess.STDOUT) + +def pull_from_device(path): + tmp = tempfile.mktemp() + adb(['pull', path, tmp]) + text = open(tmp, 'r').read() + os.unlink(tmp) + return text + +def push_to_device(path): + # Workaround for https://code.google.com/p/android/issues/detail?id=65857 + dst_path = os.path.join(ANDROID_TMPDIR, os.path.basename(path)) + tmp_path = dst_path + '.push' + adb(['push', path, tmp_path]) + adb(['shell', 'cp "%s" "%s" 2>&1' % (tmp_path, dst_path)]) diff --git a/test/asan/android_commands/android_compile.py b/test/asan/android_commands/android_compile.py new file mode 100755 index 000000000..4b880886b --- /dev/null +++ b/test/asan/android_commands/android_compile.py @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import os, sys, subprocess +from android_common import * + + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +android_run = os.path.join(here, 'android_run.py') + +output = None +output_type = 'executable' + +args = sys.argv[1:] +while args: + arg = args.pop(0) + if arg == '-shared': + output_type = 'shared' + elif arg == '-c': + output_type = 'object' + elif arg == '-o': + output = args.pop(0) + +if output == None: + print "No output file name!" + sys.exit(1) + +ret = subprocess.call(sys.argv[1:]) +if ret != 0: + sys.exit(ret) + +if output_type in ['executable', 'shared']: + push_to_device(output) + +if output_type == 'executable': + os.rename(output, output + '.real') + os.symlink(android_run, output) diff --git a/test/asan/android_commands/android_run.py b/test/asan/android_commands/android_run.py new file mode 100755 index 000000000..a6ceeb427 --- /dev/null +++ b/test/asan/android_commands/android_run.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import os, sys, subprocess, tempfile +from android_common import * + +ANDROID_TMPDIR = '/data/local/tmp/Output' + +here = os.path.abspath(os.path.dirname(sys.argv[0])) +device_binary = os.path.join(ANDROID_TMPDIR, os.path.basename(sys.argv[0])) + +def build_env(): + args = [] + # Android linker ignores RPATH. Set LD_LIBRARY_PATH to Output dir. + args.append('LD_LIBRARY_PATH=%s:%s' % + (ANDROID_TMPDIR, os.environ.get('LD_LIBRARY_PATH', ''))) + for (key, value) in os.environ.items(): + if key in ['ASAN_OPTIONS']: + args.append('%s="%s"' % (key, value)) + return ' '.join(args) + +device_env = build_env() +device_args = ' '.join(sys.argv[1:]) # FIXME: escape? +device_stdout = device_binary + '.stdout' +device_stderr = device_binary + '.stderr' +device_exitcode = device_binary + '.exitcode' +ret = adb(['shell', 'cd %s && %s %s %s >%s 2>%s ; echo $? >%s' % + (ANDROID_TMPDIR, device_env, device_binary, device_args, + device_stdout, device_stderr, device_exitcode)]) +if ret != 0: + sys.exit(ret) + +sys.stdout.write(pull_from_device(device_stdout)) +sys.stderr.write(pull_from_device(device_stderr)) +sys.exit(int(pull_from_device(device_exitcode))) diff --git a/test/asan/lit.cfg b/test/asan/lit.cfg new file mode 100644 index 000000000..4fa796bfb --- /dev/null +++ b/test/asan/lit.cfg @@ -0,0 +1,73 @@ +# -*- Python -*- + +import os + +def get_required_attr(config, attr_name): + attr_value = getattr(config, attr_name, None) + if not attr_value: + lit_config.fatal( + "No attribute %r in test configuration! You may need to run " + "tests from your build directory or add this attribute " + "to lit.site.cfg " % attr_name) + return attr_value + +# Setup config name. +config.name = 'AddressSanitizer' + config.name_suffix + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags used with -fsanitize=address option. +# FIXME: Review the set of required flags and check if it can be reduced. +target_cflags = " " + config.target_cflags +clang_asan_cflags = (" -fsanitize=address" + + " -mno-omit-leaf-frame-pointer" + + " -fno-omit-frame-pointer" + + " -fno-optimize-sibling-calls" + + " -g" + + target_cflags) +clang_asan_cxxflags = " --driver-mode=g++" + clang_asan_cflags + +asan_lit_source_dir = get_required_attr(config, "asan_lit_source_dir") +if config.android == "TRUE": + config.available_features.add('android') + clang_wrapper = os.path.join(asan_lit_source_dir, + "android_commands", "android_compile.py") + " " +else: + clang_wrapper = "" + +config.substitutions.append( ("%clang ", " " + clang_wrapper + config.clang + target_cflags + " ")) +config.substitutions.append( ("%clangxx ", (" " + clang_wrapper + config.clang + + " --driver-mode=g++" + + target_cflags + " ")) ) +config.substitutions.append( ("%clang_asan ", (" " + clang_wrapper + config.clang + " " + + clang_asan_cflags + " ")) ) +config.substitutions.append( ("%clangxx_asan ", (" " + clang_wrapper + config.clang + " " + + clang_asan_cxxflags + " ")) ) + + +# FIXME: De-hardcode this path. +asan_source_dir = os.path.join( + get_required_attr(config, "compiler_rt_src_root"), "lib", "asan") +# Setup path to asan_symbolize.py script. +asan_symbolize = os.path.join(asan_source_dir, "scripts", "asan_symbolize.py") +if not os.path.exists(asan_symbolize): + lit_config.fatal("Can't find script on path %r" % asan_symbolize) +python_exec = get_required_attr(config, "python_executable") +config.substitutions.append( ("%asan_symbolize", python_exec + " " + asan_symbolize + " ") ) + +# Define CHECK-%os to check for OS-dependent output. +config.substitutions.append( ('CHECK-%os', ("CHECK-" + config.host_os))) + +config.available_features.add("asan-" + config.bits + "-bits") + +# Turn on leak detection on 64-bit Linux. +if config.host_os == 'Linux' and config.bits == '64': + config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# AddressSanitizer tests are currently supported on Linux and Darwin only. +if config.host_os not in ['Linux', 'Darwin']: + config.unsupported = True |