diff --git a/ndk-test/arm-neon/CMakeLists.txt b/ndk-test/arm-neon/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..75b2d3812a076c46ad27c40213af9fa9522bfa68 --- /dev/null +++ b/ndk-test/arm-neon/CMakeLists.txt @@ -0,0 +1,34 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 3.15) + +project(arm-neon-test) + +SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/output) + +enable_language(C CXX ASM) + +set(BUILD_FLAGS + -mfloat-abi=hard + -march=armv7a) + +function(NEON_TEST WITH_FP16) + + add_executable(arm-neon-fp16 src/arm-neon-fp16.cpp) + if(WITH_FP16) + list(APPEND BUILD_FLAGS -mfpu=neon-fp16) + else() + list(APPEND BUILD_FLAGS -mfpu=neon) + endif() + target_compile_options(arm-neon-fp16 PUBLIC "${BUILD_FLAGS}") + +endfunction() + +function(ASM_TEST) + add_library(asm-test OBJECT src/asm-test.s) + target_compile_options(asm-test PUBLIC "${BUILD_FLAGS}") +endfunction() + + + +NEON_TEST(ON) +# NEON_TEST(OFF) +ASM_TEST() \ No newline at end of file diff --git a/ndk-test/arm-neon/build.py b/ndk-test/arm-neon/build.py new file mode 100755 index 0000000000000000000000000000000000000000..a5689d6d88c695f2000f0d984f7554b5da26853e --- /dev/null +++ b/ndk-test/arm-neon/build.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import os +import sys +import shutil + +arg_path=os.path.join(os.path.abspath(os.path.dirname(os.getcwd())),'script') +sys.path.append(arg_path) + +from config_args import linux_args + +def build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + os.mkdir("build") + os.chdir("build") + build_cmd = "cmake -G 'Ninja' " + (" ".join(linux_args))+ " .." + print(build_cmd) + os.system(build_cmd) + os.system("ninja -f build.ninja") + # os.chdir(os.path.abspath(os.path.dirname(os.getcwd()))) + # if os.path.exists("build") and os.path.isdir("build"): + # shutil.rmtree("build") + + +if __name__ == '__main__': + build() + + diff --git a/ndk-test/arm-neon/src/arm-neon-fp16.cpp b/ndk-test/arm-neon/src/arm-neon-fp16.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e7eb6ab61b65e420e1977c37b4598e532c1c6bd --- /dev/null +++ b/ndk-test/arm-neon/src/arm-neon-fp16.cpp @@ -0,0 +1,11 @@ +#include + +uint32x2_t test_vadd_u32(uint32x2_t a, uint32x2_t b) { + return vadd_u32(a,b); +} + +float16x8_t test_vld1q_f16(float16_t const *a) { + return vld1q_f16(a); +} + +int main(){} \ No newline at end of file diff --git a/ndk-test/arm-neon/src/asm-test.s b/ndk-test/arm-neon/src/asm-test.s new file mode 100644 index 0000000000000000000000000000000000000000..5ec03122210e68ed6fdb3f2d415491eaf3a9b91d --- /dev/null +++ b/ndk-test/arm-neon/src/asm-test.s @@ -0,0 +1,3 @@ + +LDRD r4,[r8,#28] +blx r0 \ No newline at end of file diff --git a/ndk-test/sanitize/asan/CMakeLists.txt b/ndk-test/sanitize/asan/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..27b51709352598bd6ce5c0d942d700bcd6865fdc --- /dev/null +++ b/ndk-test/sanitize/asan/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.5) + +# project name +project (asan_test) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../exe) + +# Add executable +file(GLOB TESTCASES src/*.cpp) +foreach(srcfile ${TESTCASES}) + get_filename_component(testname ${srcfile} NAME_WE) + add_executable( ${testname} ${srcfile}) +endforeach(add_subdirectory(src/init-order)) + +add_subdirectory(src/init-order) diff --git a/ndk-test/sanitize/asan/build.py b/ndk-test/sanitize/asan/build.py new file mode 100755 index 0000000000000000000000000000000000000000..3b27a56998b0fb966611ae7b5acba4cc35baa11a --- /dev/null +++ b/ndk-test/sanitize/asan/build.py @@ -0,0 +1,41 @@ +#!/usr/bin/python + +import os +import subprocess +import sys +import shutil + +pre_dir = os.path.abspath(os.path.dirname(os.getcwd())) +config_dir = os.path.join(os.path.dirname(pre_dir),'script') +sys.path.append(config_dir) + +from config_args import linux_args + +# Add asan compile options +asan_flags = [ "-DCMAKE_CXX_FLAGS=-fsanitize=address" ] + +build_args=[] +build_args.extend(linux_args) +build_args.extend(asan_flags) + +def rm_build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + +def build_linux(): + rm_build() + os.mkdir("build") + # os.chdir("build") + build_cmd = "cmake " + (" ".join(build_args))+ " .." + print(build_cmd) + res1 = subprocess.Popen(build_cmd,cwd='build',shell=True).wait() + # print(res1) + res2 = subprocess.Popen('make',cwd='build',shell=True).wait() + # print(res2) + rm_build() + +if __name__ == '__main__': + build_linux() diff --git a/ndk-test/sanitize/asan/src/global-buffer-overflow.cpp b/ndk-test/sanitize/asan/src/global-buffer-overflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1eaca6fdfef6045c82cbbd79721f23dc19ef09f0 --- /dev/null +++ b/ndk-test/sanitize/asan/src/global-buffer-overflow.cpp @@ -0,0 +1,7 @@ +//CHECK: AddressSanitizer:Global-buffer-overflow + +int array[100]; + +int main(int argc, char *argv[]) { + return array[100]; +} \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/heap-buffer-overflow.cpp b/ndk-test/sanitize/asan/src/heap-buffer-overflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2f6bddaeed2ed026f90ddc9ba58de2f8308cedbd --- /dev/null +++ b/ndk-test/sanitize/asan/src/heap-buffer-overflow.cpp @@ -0,0 +1,10 @@ +// BUILD: clang -fsanitize=address %s -o %t +// CHECK: AddressSanitizer:Heap-buffer-overflow + +int main(int argc, char *argv[]) +{ + int *array = new int[100]; + int res = array[100]; + delete[] array; + return res; +} \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/init-order/CMakeLists.txt b/ndk-test/sanitize/asan/src/init-order/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..53e8019ff92521959b83dcfa5e65f264ec614d1d --- /dev/null +++ b/ndk-test/sanitize/asan/src/init-order/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.5) + +add_executable(IO-a a.cpp b.cpp) +add_executable(IO-b b.cpp a.cpp) \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/init-order/a.cpp b/ndk-test/sanitize/asan/src/init-order/a.cpp new file mode 100644 index 0000000000000000000000000000000000000000..17120325b8f7d479cdd3ee3dc41fa455b93ec490 --- /dev/null +++ b/ndk-test/sanitize/asan/src/init-order/a.cpp @@ -0,0 +1,23 @@ +// BUILD: clang -fsanitize=address a.cpp b.cpp -o IO-a +// BUILD: clang -fsanitize=address b.cpp a.cpp -o IO-b (noexpect) + +// ASAN_OPTIONS=check_initialization_order=true IO-a +// ASAN_OPTIONS=check_initialization_order=true IO-b (noexpect) +// CHECK: AddressSanitizer: Initialization-order-fiasco + +#include + +extern int extern_global; + +int __attribute__((noinline)) read_extern_global() +{ + return extern_global; +} + +int x = read_extern_global() + 1; + +int main() +{ + printf("%d\n", x); + return 0; +} diff --git a/ndk-test/sanitize/asan/src/init-order/b.cpp b/ndk-test/sanitize/asan/src/init-order/b.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f192335cdcdcd23d51ca5de9e96c4d12dff6a668 --- /dev/null +++ b/ndk-test/sanitize/asan/src/init-order/b.cpp @@ -0,0 +1,8 @@ +// BUILD: clang -fsanitize=address a.cpp b.cpp -o IO-a +// BUILD: clang -fsanitize=address b.cpp a.cpp -o IO-b (noexpect) + +// ASAN_OPTIONS=check_initialization_order=true IO-b +// CHECK: AddressSanitizer: initialization-order-fiasco + +int foo() { return 42; } +int extern_global = foo(); \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/memory-leaks.cpp b/ndk-test/sanitize/asan/src/memory-leaks.cpp new file mode 100644 index 0000000000000000000000000000000000000000..243ce9283e24acfdc9f9c4a81cb308bc9a7738c0 --- /dev/null +++ b/ndk-test/sanitize/asan/src/memory-leaks.cpp @@ -0,0 +1,12 @@ +// BUILD: clang -fsanitize=address %s -o %t +// CHECK: AddressSanitizer:Memory-leaks + +#include + +void* p; + +int main(int argc, char *argv[]) { + p = malloc(7); + p = 0; + return 0; +} \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/sanitizer_coverage_trace_pc_guard.cpp b/ndk-test/sanitize/asan/src/sanitizer_coverage_trace_pc_guard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b151fc247dcf970714f738943145f2019787ee24 --- /dev/null +++ b/ndk-test/sanitize/asan/src/sanitizer_coverage_trace_pc_guard.cpp @@ -0,0 +1,41 @@ +// Tests trace pc guard coverage collection. + +// REQUIRES: has_sancovcc,stable-runtime +// UNSUPPORTED: ubsan,i386-darwin +// XFAIL: tsan,powerpc64,s390x,mips +// XFAIL: android && asan + +// RUN: DIR=%t_workdir +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-SANCOV %s +// RUN: %env_tool_opts=coverage=0 %t 2>&1 | FileCheck --check-prefix=CHECK-NOCOV %s +// RUN: rm -rf $DIR +// Make some room to stabilize line numbers + +#include + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + foo(); +} + +// CHECK: main +// CHECK-NEXT: foo +// CHECK-NEXT: foo +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard.{{.*}}.sancov: 2 PCs written +// +// CHECK-SANCOV: sanitizer_coverage_trace_pc_guard.cpp:[[@LINE-16]] foo +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard.cpp:[[@LINE-12]] main +// +// CHECK-NOCOV-NOT: SanitizerCoverage diff --git a/ndk-test/sanitize/asan/src/stack-buffer-overflow.cpp b/ndk-test/sanitize/asan/src/stack-buffer-overflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71f7b3b2a4044921cb97301e7e68c502d550bfb8 --- /dev/null +++ b/ndk-test/sanitize/asan/src/stack-buffer-overflow.cpp @@ -0,0 +1,8 @@ +// BUILD: clang -fsanitize=address %s -o %t +// CHECK: AddressSanitizer:Stack-buffer-overflow + +int main(int argc, char *argv[]) +{ + int array[100]; + return array[100]; +} \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/use-after-free.cpp b/ndk-test/sanitize/asan/src/use-after-free.cpp new file mode 100644 index 0000000000000000000000000000000000000000..88f46b10fd6c82388726269ed10c52fb46812307 --- /dev/null +++ b/ndk-test/sanitize/asan/src/use-after-free.cpp @@ -0,0 +1,15 @@ +// BUILD: clang -fsanitize=address %s -o %t +// CHECK: AddressSanitizer:Use-after-free + +#include + +volatile void *buf; +volatile char sink; + +int main(int argc, char *argv[]) { + void *ptr = malloc(1); + buf = ptr; + free(ptr); + sink = *static_cast(ptr); + return 0; +} \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/use-after-return.cpp b/ndk-test/sanitize/asan/src/use-after-return.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4103345f2653803c16d37951f17407a9baf01a18 --- /dev/null +++ b/ndk-test/sanitize/asan/src/use-after-return.cpp @@ -0,0 +1,15 @@ +// BUILD: clang -fsanitize=address %s -o %t +// ASAN_OPTIONS=detect_stack_use_after_return=true %t +// CHECK: AddressSanitizer:Use-after-return + +int *ptr; + +void FunctionThatEscapesLocalObject() { + int local[100]; + ptr = &local[0]; +} + +int main(int argc, char **argv) { + FunctionThatEscapesLocalObject(); + return ptr[argc]; +} \ No newline at end of file diff --git a/ndk-test/sanitize/asan/src/use-after-scope.cpp b/ndk-test/sanitize/asan/src/use-after-scope.cpp new file mode 100644 index 0000000000000000000000000000000000000000..108ab5bb7ae1cb5834d5a9b0da8bb055be96e73e --- /dev/null +++ b/ndk-test/sanitize/asan/src/use-after-scope.cpp @@ -0,0 +1,13 @@ +// BUILD: clang -fsanitize=address %s -o %t +// CHECK: AddressSanitizer:Use-after-scope + +volatile int *p = 0; + +int main() { + { + int x = 0; + p = &x; + } + *p = 5; + return 0; +} \ No newline at end of file diff --git a/ndk-test/sanitize/build.py b/ndk-test/sanitize/build.py new file mode 100755 index 0000000000000000000000000000000000000000..1bce1fe1a23862bc87218e8c29c4ceec61cbf8ac --- /dev/null +++ b/ndk-test/sanitize/build.py @@ -0,0 +1,56 @@ +#!/bin/env python + +import os +import shutil +import stat +import subprocess + +target_list = [ + "asan", + # "cfi", #no expection + # "fuzz", #undefined symbol, not build + "scudo", + "ubsan", + # "shadowcallstack", # __aarch64__ + # "trace-pc-guard", # build_error +] + +def check_out_dir(): + if os.path.exists("output") and os.path.isdir("output"): + # print("Directory 'out' exist.") + shutil.rmtree("output") + else: + os.mkdir("output") + shutil.copy("runtest.sh",os.path.join(os.getcwd(),"output")) + +def copy_output(tar): + print("Start copying output executable...") + tar_out = os.path.join(tar, "exe") + copy_tar_path = os.path.join("output", tar) + ret = os.path.exists(tar_out) and os.path.isdir(tar_out) + if not ret: + print(tar_out + " not exists") + else: + shutil.move(tar_out,copy_tar_path) + +def start_build(): + check_out_dir() + for target in target_list: + tar_dir = os.path.join(os.getcwd(), target) + build_file = os.path.join(tar_dir, "build.py") + # build_file = 'build.py' + if os.path.exists(build_file) and os.path.isfile(build_file): + if os.access(build_file, os.X_OK): + # print("[Permission Check]" + build_file + " X_OK") + p = subprocess.Popen(build_file, cwd=tar_dir).wait() + # if p == 0: + copy_output(target) + else: + print(build_file + " permission denied.") + break + else: + print(build_file + " not found.") + break + +if __name__ == "__main__": + start_build() diff --git a/ndk-test/sanitize/cfi/CMakeLists.txt b/ndk-test/sanitize/cfi/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2ad4d809ebd347d18b8f3f96baaf8b0a1c506dd6 --- /dev/null +++ b/ndk-test/sanitize/cfi/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.5) + +# project name +project (asan_test) + +set(EXECUTABLE_OUTPUT_PATH build/out) + +# Add executable +file(GLOB TESTCASES src/*.cpp ) +foreach(srcfile IN LISTS ${TESTCASES}) + get_filename_component(testname ${srcfile} NAME_WE) + add_executable( ${testname} ${srcfile}) +endforeach() diff --git a/ndk-test/sanitize/cfi/build.py b/ndk-test/sanitize/cfi/build.py new file mode 100755 index 0000000000000000000000000000000000000000..fb801aa63aaaa6764928bf3432e41ab851cdb578 --- /dev/null +++ b/ndk-test/sanitize/cfi/build.py @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import os +import subprocess +import sys +import shutil + +pre_dir = os.path.abspath(os.path.dirname(os.getcwd())) +config_dir = os.path.join(os.path.dirname(pre_dir),'script') +sys.path.append(config_dir) + +from config_args import linux_args + +build_args=[] +build_args.extend(linux_args) + +def rm_build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + +def build_linux(): + rm_build() + os.mkdir("build") + build_cmd = "cmake " + (" ".join(build_args))+ " .." + print(build_cmd) + subprocess.Popen(build_cmd,cwd='build',shell=True).wait() + subprocess.Popen('make',cwd='build',shell=True).wait() + rm_build() + +if __name__ == '__main__': + build_linux() diff --git a/ndk-test/sanitize/cfi/src/simple-pass.cpp b/ndk-test/sanitize/cfi/src/simple-pass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de791fc1073ff0f16bac8bd82d96f09e8a03c282 --- /dev/null +++ b/ndk-test/sanitize/cfi/src/simple-pass.cpp @@ -0,0 +1,126 @@ +// -mretpoline does not work yet on Darwin. +// XFAIL: darwin + +// RUN: %clangxx_cfi -o %t %s +// RUN: %run %t +// RUN: %clangxx_cfi -mretpoline -o %t2 %s +// RUN: %run %t2 + +// Tests that the CFI mechanism does not crash the program when making various +// kinds of valid calls involving classes with various different linkages and +// types of inheritance, and both virtual and non-virtual member functions. + +#include "utils.h" + +struct A { + virtual void f(); + void g(); +}; + +void A::f() {} +void A::g() {} + +struct A2 : A { + virtual void f(); + void g(); +}; + +void A2::f() {} +void A2::g() {} + +struct B { + virtual void f() {} + void g() {} +}; + +struct B2 : B { + virtual void f() {} + void g() {} +}; + +namespace { + +struct C { + virtual void f(); + void g(); +}; + +void C::f() {} +void C::g() {} + +struct C2 : C { + virtual void f(); + void g(); +}; + +void C2::f() {} +void C2::g() {} + +struct D { + virtual void f() {} + void g() {} +}; + +struct D2 : D { + virtual void f() {} + void g() {} +}; + +} + +struct E { + virtual void f() {} + void g() {} +}; + +struct E2 : virtual E { + virtual void f() {} + void g() {} +}; + +int main() { + A *a = new A; + break_optimization(a); + a->f(); + a->g(); + a = new A2; + break_optimization(a); + a->f(); + a->g(); + + B *b = new B; + break_optimization(b); + b->f(); + b->g(); + b = new B2; + break_optimization(b); + b->f(); + b->g(); + + C *c = new C; + break_optimization(c); + c->f(); + c->g(); + c = new C2; + break_optimization(c); + c->f(); + c->g(); + + D *d = new D; + break_optimization(d); + d->f(); + d->g(); + d = new D2; + break_optimization(d); + d->f(); + d->g(); + + E *e = new E; + break_optimization(e); + e->f(); + e->g(); + e = new E2; + break_optimization(e); + e->f(); + e->g(); +} diff --git a/ndk-test/sanitize/cfi/src/stats.cpp b/ndk-test/sanitize/cfi/src/stats.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca6b3bf0df48146ef03932eb0d9f04899b66a167 --- /dev/null +++ b/ndk-test/sanitize/cfi/src/stats.cpp @@ -0,0 +1,55 @@ +// RUN: %clangxx_cfi %debug_info_flags -fsanitize-stats -o %t %s +// RUN: env SANITIZER_STATS_PATH=%t.stats %run %t +// RUN: sanstats %t.stats | FileCheck %s + +// FIXME: We currently emit the wrong debug info under devirtualization. +// UNSUPPORTED: devirt + +// FIXME: %t.stats must be transferred from device to host for this to work on Android. +// XFAIL: android + +struct ABase {}; + +struct A : ABase { + virtual void vf() {} + void nvf() {} +}; + +extern "C" __attribute__((noinline)) void vcall(A *a) { + // CHECK: stats.cpp:[[@LINE+1]] {{_?}}vcall cfi-vcall 37 + a->vf(); +} + +extern "C" __attribute__((noinline)) void nvcall(A *a) { + // CHECK: stats.cpp:[[@LINE+1]] {{_?}}nvcall cfi-nvcall 51 + a->nvf(); +} + +extern "C" __attribute__((noinline)) A *dcast(A *a) { + // CHECK: stats.cpp:[[@LINE+1]] {{_?}}dcast cfi-derived-cast 24 + return (A *)(ABase *)a; +} + +extern "C" __attribute__((noinline)) A *ucast(A *a) { + // CHECK: stats.cpp:[[@LINE+1]] {{_?}}ucast cfi-unrelated-cast 81 + return (A *)(char *)a; +} + +extern "C" __attribute__((noinline)) void unreachable(A *a) { + // CHECK-NOT: unreachable + a->vf(); +} + +int main() { + A a; + for (unsigned i = 0; i != 37; ++i) + vcall(&a); + for (unsigned i = 0; i != 51; ++i) + nvcall(&a); + for (unsigned i = 0; i != 24; ++i) + dcast(&a); + for (unsigned i = 0; i != 81; ++i) + ucast(&a); + for (unsigned i = 0; i != 0; ++i) + unreachable(&a); +} diff --git a/ndk-test/sanitize/cfi/src/target_uninstrumented.cpp b/ndk-test/sanitize/cfi/src/target_uninstrumented.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c2db9d609f47b9e1f0f9fc83a8ef8d4e0f670098 --- /dev/null +++ b/ndk-test/sanitize/cfi/src/target_uninstrumented.cpp @@ -0,0 +1,47 @@ +// RUN: %clangxx -g -DSHARED_LIB %s -fPIC -shared -o %dynamiclib %ld_flags_rpath_so +// RUN: %clangxx_cfi_diag -g %s -o %t %ld_flags_rpath_exe +// RUN: %run %t 2>&1 | FileCheck %s + +// REQUIRES: cxxabi +// UNSUPPORTED: windows-msvc + +#include +#include + +struct A { + virtual void f(); +}; + +void *create_B(); + +#ifdef SHARED_LIB + +struct B { + virtual void f(); +}; +void B::f() {} + +void *create_B() { + return (void *)(new B()); +} + +#else + +void A::f() {} + +int main(int argc, char *argv[]) { + void *p = create_B(); + // CHECK: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type + // CHECK: invalid vtable + // CHECK: check failed in {{.*}}, vtable located in {{.*}}libtarget_uninstrumented.cpp.dynamic.so + A *a = (A *)p; + memset(p, 0, sizeof(A)); + + // CHECK: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type + // CHECK: invalid vtable + // CHECK: check failed in {{.*}}, vtable located in (unknown) + a = (A *)p; + // CHECK: done + fprintf(stderr, "done %p\n", a); +} +#endif diff --git a/ndk-test/sanitize/cfi/src/two-vcalls.cpp b/ndk-test/sanitize/cfi/src/two-vcalls.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff823c498027dce5cbf21c3fbd8f3f44e4453d1e --- /dev/null +++ b/ndk-test/sanitize/cfi/src/two-vcalls.cpp @@ -0,0 +1,60 @@ +// RUN: %clangxx_cfi_diag -o %t %s +// RUN: %run %t 2>&1 | FileCheck %s + +// This test checks that we don't generate two type checks, +// if two virtual calls are in the same function. + +// UNSUPPORTED: windows-msvc +// REQUIRES: cxxabi + +// TODO(krasin): implement the optimization to not emit two type checks. +// XFAIL: * +#include + +class Base { + public: + virtual void Foo() { + fprintf(stderr, "Base::Foo\n"); + } + + virtual void Bar() { + fprintf(stderr, "Base::Bar\n"); + } +}; + +class Derived : public Base { + public: + void Foo() override { + fprintf(stderr, "Derived::Foo\n"); + } + + void Bar() override { + printf("Derived::Bar\n"); + } +}; + +__attribute__((noinline)) void print(Base* ptr) { + ptr->Foo(); + // Corrupt the vtable pointer. We expect that the optimization will + // check vtable before the first vcall then store it in a local + // variable, and reuse it for the second vcall. With no optimization, + // CFI will complain about the virtual table being corrupted. + *reinterpret_cast(ptr) = 0; + ptr->Bar(); +} + + +int main() { + Base b; + Derived d; + // CHECK: Base::Foo + // CHECK: Base::Bar + print(&b); + + // CHECK: Derived::Foo + // CHECK-NOT: runtime error + // CHECK: Derived::Bar + print(&d); + + return 0; +} diff --git a/ndk-test/sanitize/fuzz/CMakeLists.txt b/ndk-test/sanitize/fuzz/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..927bdb9c860c5c823807069572b77f80dd21fac0 --- /dev/null +++ b/ndk-test/sanitize/fuzz/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.5) + +# project name +project (fuzz_test) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../exe) +set(CMAKE_EXE_LINKER_FLAGS -fsanitize=fuzzer) + +# Add executable +file(GLOB TESTCASES src/*.cpp) +foreach(srcfile ${TESTCASES}) + get_filename_component(testname ${srcfile} NAME_WE) + add_executable(${testname} ${srcfile}) + # target_compile_options(${testname} PRIVATE -fsanitize=fuzzer) +endforeach() \ No newline at end of file diff --git a/ndk-test/sanitize/fuzz/build.py b/ndk-test/sanitize/fuzz/build.py new file mode 100755 index 0000000000000000000000000000000000000000..60b11db573533f850120ef8167ae57a0dcf46144 --- /dev/null +++ b/ndk-test/sanitize/fuzz/build.py @@ -0,0 +1,38 @@ +#!/bin/env python + +import os +import sys +import subprocess +import shutil + +pre_dir = os.path.abspath(os.path.dirname(os.getcwd())) +config_dir = os.path.join(os.path.dirname(pre_dir),'script') +sys.path.append(config_dir) + +from config_args import linux_args + +# Add libFuzz compile options +fuzz_flags = [ "-DCMAKE_EXE_LINKER_FLAGS=-fsanitize=fuzzer" ] + +build_args=[] +build_args.extend(linux_args) +# build_args.extend(fuzz_flags) + +def rm_build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + +def build_linux(): + rm_build() + os.mkdir("build") + build_cmd = "cmake " + (" ".join(build_args))+ " .." + print(build_cmd) + subprocess.Popen(build_cmd,cwd='build',shell=True).wait() + subprocess.Popen('make',cwd='build',shell=True).wait() + rm_build() + +if __name__ == '__main__': + build_linux() \ No newline at end of file diff --git a/ndk-test/sanitize/fuzz/src/PrintFuncTest.cpp b/ndk-test/sanitize/fuzz/src/PrintFuncTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1520e82b936cba0dfae35d9c566e328cc1d96006 --- /dev/null +++ b/ndk-test/sanitize/fuzz/src/PrintFuncTest.cpp @@ -0,0 +1,40 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include +#include + +extern "C" { +__attribute__((noinline)) +void FunctionC(const uint8_t *Data, size_t Size) { + if (Size > 3 && Data[3] == 'Z') { + static bool PrintedOnce = false; + if (!PrintedOnce) { + std::cout << "BINGO\n"; + PrintedOnce = true; + } + } +} + +__attribute__((noinline)) +void FunctionB(const uint8_t *Data, size_t Size) { + if (Size > 2 && Data[2] == 'Z') + FunctionC(Data, Size); +} +__attribute__((noinline)) +void FunctionA(const uint8_t *Data, size_t Size) { + if (Size > 1 && Data[1] == 'U') + FunctionB(Data, Size); +} +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size > 0 && Data[0] == 'F') + FunctionA(Data, Size); + return 0; +} + diff --git a/ndk-test/sanitize/fuzz/src/SimpleTest.cpp b/ndk-test/sanitize/fuzz/src/SimpleTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c51227ba2a441bfc80c66e2c872fc011a503a54f --- /dev/null +++ b/ndk-test/sanitize/fuzz/src/SimpleTest.cpp @@ -0,0 +1,29 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Simple test for a fuzzer. The fuzzer must find the string "Hi!". +#include +#include +#include +#include +#include +#include + +static volatile int Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + assert(Data); + if (Size > 0 && Data[0] == 'H') { + Sink = 1; + if (Size > 1 && Data[1] == 'i') { + Sink = 2; + if (Size > 2 && Data[2] == '!') { + std::cout << "BINGO; Found the target, exiting\n" << std::flush; + exit(0); + } + } + } + return 0; +} + diff --git a/ndk-test/sanitize/runtest.sh b/ndk-test/sanitize/runtest.sh new file mode 100644 index 0000000000000000000000000000000000000000..9722599e343e44f83476624319ccc6e50849a87a --- /dev/null +++ b/ndk-test/sanitize/runtest.sh @@ -0,0 +1,261 @@ +#! /bin/sh + +touch REPORT +rm REPORT +chmod +x sanitize/asan/* + +num_s=0 +num_f=0 +num_skip=0 + +function skip { + echo skip + let num_skip=$num_skip+1 +} + + +for case in `ls sanitize/asan` +do + echo [Runing test] : sanitize/asan/$case + if [ "$case" = "use-after-return" ]; then + ASAN_OPTIONS=detect_stack_use_after_return=1 sanitize/asan/$case 2>tmp + elif [ "$case" = "IO-a" ] | [ "$case" = "IO-b" ]; then + ASAN_OPTIONS=check_initialization_order=true sanitize/asan/$case 2>tmp + else + ASAN_OPTIONS='' sanitize/asan/$case 2>tmp + fi + + # check result + if [ "$case" = "memory-leaks" ];then + grep -nr 'SUMMARY: ' tmp | grep "leaked in" + else + grep -nr 'SUMMARY: ' tmp | grep $case + fi + res=$? + + # Specially + if [ res -ne 0 ] && [ "$case" = "IO-b" ];then + echo "$case succeeded." + let num_s=$num_s+1 + elif [ res -ne 0 ];then + echo "[ERROR] asan/$case failed." | tee -a REPORT + sanitize/asan/$case >> REPORT + let num_f=$num_f+1 + else + echo "$case succeeded." + let num_s=$num_s+1 + fi +done + +for case in `ls sanitize/scudo` +do + cat /dev/null > tmp + echo [Runing test] : sanitize/scudo/$case + if [ "$case" = "aligned-new" ];then + sanitize/scudo/$case valid 2>tmp + r1=$? + SCUDO_OPTIONS=allocator_may_return_null=1 sanitize/scudo/$case invalid 2>tmp + r2=$? + SCUDO_OPTIONS=allocator_may_return_null=0 sanitize/scudo/$case invalid 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r3=$? + let res=$r1+$r2+$r3 + res2=$r1+$r2+$r3 + elif [ "$case" = "alignment" ];then + sanitize/scudo/$case pointers 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + res=$? + elif [ "$case" = "dealloc-race" ];then + SCUDO_OPTIONS="QuarantineChunksUpToSize=0" sanitize/scudo/$case 2>tmp + res=$? + elif [ "$case" = "double-free" ];then + sanitize/scudo/$case malloc 2>tmp + grep -nr "ERROR: invalid chunk state" tmp >/dev/null + r1=$? + sanitize/scudo/$case new 2>tmp + grep -nr "ERROR: invalid chunk state" tmp >/dev/null + r2=$? + sanitize/scudo/$case newarray 2>tmp + grep -nr "ERROR: invalid chunk state" tmp >/dev/null + r3=$? + let res=$r1+$r2+$r3 + res2=$r1+$r2+$r3 + elif [ "$case" = "fsanitize" ];then + skip #build error + continue + elif [ "$case" = "interface" ];then + sanitize/scudo/$case ownership 2>tmp + r1=$? + sanitize/scudo/$case ownership-and-size 2>tmp + r2=$? + sanitize/scudo/$case heap-size 2>tmp + r3=$? + SCUDO_OPTIONS="allocator_may_return_null=1" sanitize/scudo/$case soft-limit 2>tmp + r4=$? + SCUDO_OPTIONS="allocator_may_return_null=1" sanitize/scudo/$case hard-limit 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r5=$? + let res=$r1+$r2+$r3+$r4+$r5 + res2=$r1+$r2+$r3+$r4+$r5 + elif [ "$case" = "memalign" ];then + skip + continue + # sanitize/scudo/$case valid 2>tmp + # sanitize/scudo/$case invalid 2>tmp + # scudo_opts=allocator_may_return_null=1 sanitize/scudo/$case invalid 2>tmp + # sanitize/scudo/$case double-free 2>tmp + # scudo_opts=DeallocationTypeMismatch=1 sanitize/scudo/$case realloc 2>tmp + # scudo_opts=DeallocationTypeMismatch=0 sanitize/scudo/$case realloc 2>tmp + elif [ "$case" = "mismatch" ];then + SCUDO_OPTIONS=DeallocationTypeMismatch=1 sanitize/scudo/$case mallocdel 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r1=$? + SCUDO_OPTIONS=DeallocationTypeMismatch=0 sanitize/scudo/$case mallocdel 2>tmp + r2=$? + SCUDO_OPTIONS=DeallocationTypeMismatch=1 sanitize/scudo/$case newfree 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r3=$? + SCUDO_OPTIONS=DeallocationTypeMismatch=0 sanitize/scudo/$case newfree 2>tmp + r4=$? + let res=$r1+$r2+$r3+$r4 + res2=$r1+$r2+$r3+$r4 + elif [ "$case" = "preinit" ];then + skip # UNSUPPORTED + continue + elif [ "$case" = "options" ];then + sanitize/scudo/$case 2>tmp + r1=$? + SCUDO_OPTIONS=DeallocationTypeMismatch=0 sanitize/scudo/$case 2>tmp + r2=$? + SCUDO_OPTIONS=DeallocationTypeMismatch=1 sanitize/scudo/$case 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r3=$? + let res=$r1+$r2+$r3 + res2=$r1+$r2+$r3 + elif [ "$case" = "preload" ];then + skip + continue + # env LD_PRELOAD=%shared_libscudo not %run %t 2>tmp | FileCheck %s + # env LD_PRELOAD=%shared_minlibscudo not %run %t 2>tmp | FileCheck %s + elif [ "$case" = "realloc" ];then + skip + continue + # sanitize/scudo/$case pointers 2>tmp + # sanitize/scudo/$case contents 2>tmp + # sanitize/scudo/$case usablesize 2>tmp + elif [ "$case" = "rss" ];then + sanitize/scudo/$case 2>tmp + r1=$? + SCUDO_OPTIONS="soft_rss_limit_mb=128" sanitize/scudo/$case 2>tmp + r2=$? + SCUDO_OPTIONS="hard_rss_limit_mb=128" sanitize/scudo/$case 2>tmp + r3=$? + SCUDO_OPTIONS="soft_rss_limit_mb=32:allocator_may_return_null=0" sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r4=$? #not + SCUDO_OPTIONS="soft_rss_limit_mb=32:allocator_may_return_null=1" sanitize/scudo/$case 2>tmp + r5=$? + SCUDO_OPTIONS="soft_rss_limit_mb=32:allocator_may_return_null=0:can_use_proc_maps_statm=0" sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r6=$? #not + SCUDO_OPTIONS="soft_rss_limit_mb=32:allocator_may_return_null=1:can_use_proc_maps_statm=0" sanitize/scudo/$case 2>tmp + r7=$? + SCUDO_OPTIONS="hard_rss_limit_mb=32:allocator_may_return_null=0" sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r8=$? #not + SCUDO_OPTIONS="hard_rss_limit_mb=32:allocator_may_return_null=1" sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r9=$? #not + SCUDO_OPTIONS="hard_rss_limit_mb=32:allocator_may_return_null=0:can_use_proc_maps_statm=0" sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r10=$? #not + SCUDO_OPTIONS="hard_rss_limit_mb=32:allocator_may_return_null=1:can_use_proc_maps_statm=0" sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r11=$? #not + let res=$r1+$r2+$r3+$r4+$r5+$r6+$r7+$r8+$r9+$r10+$r11 + res2=$r1+$r2+$r3+$r4+$r5+$r6+$r7+$r8+$r9+$r10+$r11 + elif [ "$case" = "sized-delete" ];then + SCUDO_OPTIONS=DeleteSizeMismatch=1 sanitize/scudo/$case gooddel 2>tmp + r1=$? + SCUDO_OPTIONS=DeleteSizeMismatch=1 sanitize/scudo/$case baddel 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r2=$? + SCUDO_OPTIONS=DeleteSizeMismatch=0 sanitize/scudo/$case baddel 2>tmp + r3=$? + SCUDO_OPTIONS=DeleteSizeMismatch=1 sanitize/scudo/$case gooddelarr 2>tmp + r4=$? + SCUDO_OPTIONS=DeleteSizeMismatch=1 sanitize/scudo/$case baddelarr 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r5=$? + SCUDO_OPTIONS=DeleteSizeMismatch=0 sanitize/scudo/$case baddelarr 2>tmp + r6=$? + let res=$r1+$r2+$r3+$r4+$r5+$r6 + res2=$r1+$r2+$r3+$r4+$r5+$r6 + elif [ "$case" = "sizes" ];then + SCUDO_OPTIONS=allocator_may_return_null=0 sanitize/scudo/$case malloc 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r1=$? + SCUDO_OPTIONS=allocator_may_return_null=1 sanitize/scudo/$case malloc 2>tmp + r2=$? + SCUDO_OPTIONS=allocator_may_return_null=0 sanitize/scudo/$case calloc 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r3=$? + SCUDO_OPTIONS=allocator_may_return_null=1 sanitize/scudo/$case calloc 2>tmp + r4=$? + SCUDO_OPTIONS=allocator_may_return_null=0 sanitize/scudo/$case new 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r5=$? + SCUDO_OPTIONS=allocator_may_return_null=1 sanitize/scudo/$case new 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r6=$? + SCUDO_OPTIONS=allocator_may_return_null=0 sanitize/scudo/$case new-nothrow 2>tmp #not + grep -nr 'Scudo ERROR: ' tmp >/dev/null + r7=$? + SCUDO_OPTIONS=allocator_may_return_null=1 sanitize/scudo/$case new-nothrow 2>tmp + r8=$? + sanitize/scudo/$case usable 2>tmp + r9=$? + let res=$r1+$r2+$r3+$r4+$r5+$r6+$r7+$r8+$r9 + res2=$r1+$r2+$r3+$r4+$r5+$r6+$r7+$r8+$r9 + else + sanitize/scudo/$case 2>tmp + grep -nr 'Scudo ERROR: ' tmp >/dev/null + res=$? + fi + + if [ res -ne 0 ];then + # echo res + echo "[ERROR] scudo/$case failed." | tee -a REPORT + let num_f=$num_f+1 + continue + else + echo "scudo/$case succeeded." + let num_s=$num_s+1 + continue + fi + echo +done + +for case in `ls sanitize/ubsan` +do + echo [Runing test] : sanitize/ubsan/$case + sanitize/ubsan/$case 2>tmp + # check result + grep -nr 'SUMMARY: UndefinedBehaviorSanitizer' tmp + res=$? + if [ res -ne 0 ];then + echo "[ERROR] ubsan/$case failed." | tee -a REPORT + sanitize/ubsan/$case >> REPORT + let num_f=$num_f+1 + else + echo "ubsan/$case succeeded." + let num_s=$num_s+1 + fi +done + +let sum_case=$num_s+$num_f+$num_skip +echo "All:$sum_case Succeeded:$num_s Failed:$num_f Skiped:$num_skip" +if [ $num_f -eq 0 ];then + echo "No test failed, report not create." +fi +rm tmp diff --git a/ndk-test/sanitize/scudo/CMakeLists.txt b/ndk-test/sanitize/scudo/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8949af2accd6935ee1eeca4639d1611771bfa46f --- /dev/null +++ b/ndk-test/sanitize/scudo/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.5) + +# project name +project (scudo_test) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../exe) + +# Add executable +file(GLOB TESTCASES src/*.cpp ) +foreach(srcfile ${TESTCASES}) + get_filename_component(testname ${srcfile} NAME_WE) + add_executable(${testname} ${srcfile}) +endforeach() + +file(GLOB TESTCASES_C src/*.c ) +foreach(srcfile_c ${TESTCASES_C}) + get_filename_component(testname_c ${srcfile_c} NAME_WE) + add_executable(${testname_c} ${srcfile_c}) + # include_directories("${PROJECT_SOURCE_DIR}/include") +endforeach() \ No newline at end of file diff --git a/ndk-test/sanitize/scudo/build.py b/ndk-test/sanitize/scudo/build.py new file mode 100755 index 0000000000000000000000000000000000000000..d188c9db0873356425302b19895cd6b5ff7fa11f --- /dev/null +++ b/ndk-test/sanitize/scudo/build.py @@ -0,0 +1,40 @@ +#!/usr/bin/python + +import os +import subprocess +import sys +import shutil + +pre_dir = os.path.abspath(os.path.dirname(os.getcwd())) +config_dir = os.path.join(os.path.dirname(pre_dir),'script') +sys.path.append(config_dir) + +from config_args import linux_args + +# Add asan compile options +scudo_flags = [ "-DCMAKE_CXX_FLAGS=-fsanitize=scudo" ] + +build_args=[] +build_args.extend(linux_args) +build_args.extend(scudo_flags) + +def rm_build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + +def build_linux(): + rm_build() + os.mkdir("build") + build_cmd = "cmake " + (" ".join(build_args))+ " .." + print(build_cmd) + res1 = subprocess.Popen(build_cmd,cwd='build',shell=True).wait() + # print(res1) + res2 = subprocess.Popen('make',cwd='build',shell=True).wait() + # print(res2) + rm_build() + +if __name__ == '__main__': + build_linux() diff --git a/ndk-test/sanitize/scudo/src/aligned-new.cpp b/ndk-test/sanitize/scudo/src/aligned-new.cpp new file mode 100644 index 0000000000000000000000000000000000000000..771dd3b828971141f12d4cb3ec76f1c3d8d7c66a --- /dev/null +++ b/ndk-test/sanitize/scudo/src/aligned-new.cpp @@ -0,0 +1,99 @@ +// RUN: %clangxx_scudo -std=c++1z -faligned-allocation %s -o %t +// RUN: %run %t valid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t invalid 2>&1 | FileCheck %s + +// Tests that the C++17 aligned new/delete operators are working as expected. +// Currently we do not check the consistency of the alignment on deallocation, +// so this just tests that the APIs work. + +#include +#include +#include +#include + +// Define all new/delete to not depend on the version provided by the platform. + +namespace std { +struct nothrow_t {}; +static const nothrow_t nothrow; +enum class align_val_t : size_t {}; +} // namespace std + +void *operator new(size_t); +void *operator new[](size_t); +void *operator new(size_t, std::nothrow_t const &); +void *operator new[](size_t, std::nothrow_t const &); +void *operator new(size_t, std::align_val_t); +void *operator new[](size_t, std::align_val_t); +void *operator new(size_t, std::align_val_t, std::nothrow_t const &); +void *operator new[](size_t, std::align_val_t, std::nothrow_t const &); + +void operator delete(void *) throw(); +void operator delete[](void *) throw(); +void operator delete(void *, std::nothrow_t const &); +void operator delete[](void *, std::nothrow_t const &); +void operator delete(void *, size_t) throw(); +void operator delete[](void *, size_t) throw(); +void operator delete(void *, std::align_val_t) throw(); +void operator delete[](void *, std::align_val_t) throw(); +void operator delete(void *, std::align_val_t, std::nothrow_t const &); +void operator delete[](void *, std::align_val_t, std::nothrow_t const &); +void operator delete(void *, size_t, std::align_val_t) throw(); +void operator delete[](void *, size_t, std::align_val_t) throw(); + +template +inline T *break_optimization(T *arg) { + __asm__ __volatile__("" + : + : "r"(arg) + : "memory"); + return arg; +} + +struct S12 { + int a, b, c; +}; +struct alignas(128) S12_128 { + int a, b, c; +}; +struct alignas(256) S12_256 { + int a, b, c; +}; +struct alignas(512) S1024_512 { + char a[1024]; +}; +struct alignas(1024) S1024_1024 { + char a[1024]; +}; + +int main(int argc, char **argv) { + assert(argc == 2); + + if (!strcmp(argv[1], "valid")) { + // Standard use case. + delete break_optimization(new S12); + delete break_optimization(new S12_128); + delete[] break_optimization(new S12_128[4]); + delete break_optimization(new S12_256); + delete break_optimization(new S1024_512); + delete[] break_optimization(new S1024_512[4]); + delete break_optimization(new S1024_1024); + + // Call directly the aligned versions of the operators. + const size_t alignment = 1U << 8; + void *p = operator new(1, static_cast(alignment)); + assert((reinterpret_cast(p) & (alignment - 1)) == 0); + operator delete(p, static_cast(alignment)); + } + if (!strcmp(argv[1], "invalid")) { + // Alignment must be a power of 2. + const size_t alignment = (1U << 8) - 1; + void *p = operator new(1, static_cast(alignment), + std::nothrow); + // CHECK: Scudo ERROR: invalid allocation alignment + assert(!p); + } + + return 0; +} diff --git a/ndk-test/sanitize/scudo/src/alignment.c b/ndk-test/sanitize/scudo/src/alignment.c new file mode 100644 index 0000000000000000000000000000000000000000..f5cc4f0299e48284b397ede9c149136c25e0af97 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/alignment.c @@ -0,0 +1,22 @@ +// RUN: %clang_scudo %s -o %t +// RUN: not %run %t pointers 2>&1 | FileCheck %s + +// Tests that a non MinAlignment aligned pointer will trigger the associated +// error on deallocation. + +#include +#include +#include +#include + +int main(int argc, char **argv) { + assert(argc == 2); + if (!strcmp(argv[1], "pointers")) { + void *p = malloc(1U << 16); + assert(p); + free((void *)((uintptr_t)p | 1)); + } + return 0; +} + +// CHECK: ERROR: misaligned pointer when deallocating address diff --git a/ndk-test/sanitize/scudo/src/dealloc-race.c b/ndk-test/sanitize/scudo/src/dealloc-race.c new file mode 100644 index 0000000000000000000000000000000000000000..cfac8f912692e4267dfb88ccac77290b1b9da940 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/dealloc-race.c @@ -0,0 +1,69 @@ +// RUN: %clang_scudo %s -O2 -o %t +// RUN: %env_scudo_opts="QuarantineChunksUpToSize=0" %run %t 2>&1 + +// This test attempts to reproduce a race condition in the deallocation path +// when bypassing the Quarantine. The old behavior was to zero-out the chunk +// header after checking its checksum, state & various other things, but that +// left a window during which 2 (or more) threads could deallocate the same +// chunk, with a net result of having said chunk present in those distinct +// thread caches. + +// A passing test means all the children died with an error. The failing +// scenario involves winning a race, so repro can be scarce. + +#include +#include +#include +#include +#include + +const int kNumThreads = 2; +pthread_t tid[kNumThreads]; + +pthread_cond_t cond = PTHREAD_COND_INITIALIZER; +pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +char go = 0; + +// Frees the pointer passed when signaled to. +void *thread_free(void *p) { + pthread_mutex_lock(&mutex); + while (!go) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + free(p); + return 0; +} + +// Allocates a chunk, and attempts to free it "simultaneously" by 2 threads. +void child(void) { + void *p = malloc(16); + for (int i = 0; i < kNumThreads; i++) + pthread_create(&tid[i], 0, thread_free, p); + pthread_mutex_lock(&mutex); + go = 1; + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); + for (int i = 0; i < kNumThreads; i++) + pthread_join(tid[i], 0); +} + +int main(int argc, char **argv) { + const int kChildren = 40; + pid_t pid; + for (int i = 0; i < kChildren; ++i) { + pid = fork(); + if (pid < 0) { + exit(1); + } else if (pid == 0) { + child(); + exit(0); + } else { + int status; + wait(&status); + // A 0 status means the child didn't die with an error. The race was won. + if (status == 0) + exit(1); + } + } + return 0; +} diff --git a/ndk-test/sanitize/scudo/src/double-free.cpp b/ndk-test/sanitize/scudo/src/double-free.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ce6ef5119ec471ff5468f48e1258fd93f40c87a7 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/double-free.cpp @@ -0,0 +1,36 @@ +// RUN: %clangxx_scudo %s -o %t +// RUN: not %run %t malloc 2>&1 | FileCheck %s +// RUN: not %run %t new 2>&1 | FileCheck %s +// RUN: not %run %t newarray 2>&1 | FileCheck %s + +// Tests double-free error on pointers allocated with different allocation +// functions. + +#include +#include +#include + +int main(int argc, char **argv) { + assert(argc == 2); + if (!strcmp(argv[1], "malloc")) { + void *p = malloc(sizeof(int)); + assert(p); + free(p); + free(p); + } + if (!strcmp(argv[1], "new")) { + int *p = new int; + assert(p); + delete p; + delete p; + } + if (!strcmp(argv[1], "newarray")) { + int *p = new int[8]; + assert(p); + delete[] p; + delete[] p; + } + return 0; +} + +// CHECK: ERROR: invalid chunk state diff --git a/ndk-test/sanitize/scudo/src/interface.cpp b/ndk-test/sanitize/scudo/src/interface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1e9f7d31243c38dbf387742cac768f89de9e068 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/interface.cpp @@ -0,0 +1,91 @@ +// RUN: %clangxx_scudo %s -lstdc++ -o %t +// RUN: %run %t ownership 2>&1 +// RUN: %run %t ownership-and-size 2>&1 +// RUN: %run %t heap-size 2>&1 +// RUN: %env_scudo_opts="allocator_may_return_null=1" %run %t soft-limit 2>&1 +// RUN: %env_scudo_opts="allocator_may_return_null=1" not %run %t hard-limit 2>&1 + +// Tests that the sanitizer interface functions behave appropriately. + +#include +#include +#include +#include + +#include + +#include +#include + +int main(int argc, char **argv) { + assert(argc == 2); + + if (!strcmp(argv[1], "ownership")) { + // Ensures that __sanitizer_get_ownership can be called before any other + // allocator function, and that it behaves properly on a pointer not owned + // by us. + assert(!__sanitizer_get_ownership(argv)); + } + if (!strcmp(argv[1], "ownership-and-size")) { + // Tests that __sanitizer_get_ownership and __sanitizer_get_allocated_size + // behave properly on chunks allocated by the Primary and Secondary. + void *p; + std::vector sizes{1, 8, 16, 32, 1024, 32768, + 1 << 16, 1 << 17, 1 << 20, 1 << 24}; + for (size_t size : sizes) { + p = malloc(size); + assert(p); + assert(__sanitizer_get_ownership(p)); + assert(__sanitizer_get_allocated_size(p) >= size); + free(p); + } + } + if (!strcmp(argv[1], "heap-size")) { + // Ensures that __sanitizer_get_heap_size can be called before any other + // allocator function. + assert(__sanitizer_get_heap_size() >= 0); + } + if (!strcmp(argv[1], "soft-limit")) { + // Verifies that setting the soft RSS limit at runtime works as expected. + std::vector pointers; + size_t size = 1 << 19; // 512Kb + for (int i = 0; i < 5; i++) { + void *p = malloc(size); + memset(p, 0, size); + pointers.push_back(p); + } + // Set the soft RSS limit to 1Mb. + __scudo_set_rss_limit(1, 0); + usleep(20000); + // The following allocation should return NULL. + void *p = malloc(size); + assert(!p); + // Remove the soft RSS limit. + __scudo_set_rss_limit(0, 0); + // The following allocation should succeed. + p = malloc(size); + assert(p); + free(p); + while (!pointers.empty()) { + free(pointers.back()); + pointers.pop_back(); + } + } + if (!strcmp(argv[1], "hard-limit")) { + // Verifies that setting the hard RSS limit at runtime works as expected. + std::vector pointers; + size_t size = 1 << 19; // 512Kb + for (int i = 0; i < 5; i++) { + void *p = malloc(size); + memset(p, 0, size); + pointers.push_back(p); + } + // Set the hard RSS limit to 1Mb + __scudo_set_rss_limit(1, 1); + usleep(20000); + // The following should trigger our death. + void *p = malloc(size); + } + + return 0; +} diff --git a/ndk-test/sanitize/scudo/src/memalign.c b/ndk-test/sanitize/scudo/src/memalign.c new file mode 100644 index 0000000000000000000000000000000000000000..8c6cdf612c23cf2548874834b0b79432190ff5a5 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/memalign.c @@ -0,0 +1,102 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t valid 2>&1 +// RUN: not %run %t invalid 2>&1 | FileCheck --check-prefix=CHECK-align %s +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t invalid 2>&1 +// RUN: not %run %t double-free 2>&1 | FileCheck --check-prefix=CHECK-double-free %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t realloc 2>&1 | FileCheck --check-prefix=CHECK-realloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t realloc 2>&1 + +// Tests that the various aligned allocation functions work as intended. Also +// tests for the condition where the alignment is not a power of 2. + +#include +#include +#include +#include +#include +#include +#include + +// Sometimes the headers may not have this... +void *aligned_alloc(size_t alignment, size_t size); + +int main(int argc, char **argv) { + void *p = NULL; + size_t alignment = 1U << 12; + size_t size = 1U << 12; + int err; + + assert(argc == 2); + + if (!strcmp(argv[1], "valid")) { + posix_memalign(&p, alignment, size); + assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); + free(p); + p = aligned_alloc(alignment, size); + assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); + free(p); + // Tests various combinations of alignment and sizes + for (int i = (sizeof(void *) == 4) ? 3 : 4; i < 19; i++) { + alignment = 1U << i; + for (int j = 1; j < 33; j++) { + size = 0x800 * j; + for (int k = 0; k < 3; k++) { + p = memalign(alignment, size - (2 * sizeof(void *) * k)); + assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); + free(p); + } + } + } + // For larger alignment, reduce the number of allocations to avoid running + // out of potential addresses (on 32-bit). + for (int i = 19; i <= 24; i++) { + alignment = 1U << i; + for (int k = 0; k < 3; k++) { + p = memalign(alignment, 0x1000 - (2 * sizeof(void *) * k)); + assert(p); + assert(((uintptr_t)p & (alignment - 1)) == 0); + free(p); + } + } + } + if (!strcmp(argv[1], "invalid")) { + // Alignment is not a power of 2. + p = memalign(alignment - 1, size); + // CHECK-align: Scudo ERROR: invalid allocation alignment + assert(!p); + // Size is not a multiple of alignment. + p = aligned_alloc(alignment, size >> 1); + assert(!p); + void *p_unchanged = (void *)0x42UL; + p = p_unchanged; + // Alignment is not a power of 2. + err = posix_memalign(&p, 3, size); + assert(p == p_unchanged); + assert(err == EINVAL); + // Alignment is a power of 2, but not a multiple of size(void *). + err = posix_memalign(&p, 2, size); + assert(p == p_unchanged); + assert(err == EINVAL); + } + if (!strcmp(argv[1], "double-free")) { + void *p = NULL; + posix_memalign(&p, 0x100, sizeof(int)); + assert(p); + free(p); + free(p); + } + if (!strcmp(argv[1], "realloc")) { + // We cannot reallocate a memalign'd chunk. + void *p = memalign(16, 16); + assert(p); + p = realloc(p, 32); + free(p); + } + return 0; +} + +// CHECK-double-free: ERROR: invalid chunk state +// CHECK-realloc: ERROR: allocation type mismatch when reallocating address diff --git a/ndk-test/sanitize/scudo/src/mismatch.cpp b/ndk-test/sanitize/scudo/src/mismatch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73da76c5b43e10aeb5dc9ca5e9585fccfc906caa --- /dev/null +++ b/ndk-test/sanitize/scudo/src/mismatch.cpp @@ -0,0 +1,30 @@ +// RUN: %clangxx_scudo %s -o %t +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t mallocdel 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t mallocdel 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t newfree 2>&1 | FileCheck --check-prefix=CHECK-dealloc %s +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t newfree 2>&1 + +// Tests that type mismatches between allocation and deallocation functions are +// caught when the related option is set. + +#include +#include +#include + +int main(int argc, char **argv) { + assert(argc == 2); + if (!strcmp(argv[1], "mallocdel")) { + int *p = (int *)malloc(16); + assert(p); + delete p; + } + if (!strcmp(argv[1], "newfree")) { + int *p = new int; + assert(p); + free((void *)p); + } + return 0; +} + +// CHECK-dealloc: ERROR: allocation type mismatch when deallocating address +// CHECK-realloc: ERROR: allocation type mismatch when reallocating address diff --git a/ndk-test/sanitize/scudo/src/options.cpp b/ndk-test/sanitize/scudo/src/options.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5fba18258e0987a5f9ad587ed94ceac6f657e719 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/options.cpp @@ -0,0 +1,24 @@ +// RUN: %clangxx_scudo %s -o %t +// RUN: %run %t 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=0 %run %t 2>&1 +// RUN: %env_scudo_opts=DeallocationTypeMismatch=1 not %run %t 2>&1 | FileCheck %s + +// Tests that the options can be passed using getScudoDefaultOptions, and that +// the environment ones take precedence over them. + +#include +#include +#include + +extern "C" const char *__scudo_default_options() { + return "DeallocationTypeMismatch=0"; // Defaults to true in scudo_flags.inc. +} + +int main(int argc, char **argv) { + int *p = (int *)malloc(16); + assert(p); + delete p; + return 0; +} + +// CHECK: ERROR: allocation type mismatch when deallocating address diff --git a/ndk-test/sanitize/scudo/src/preinit.c b/ndk-test/sanitize/scudo/src/preinit.c new file mode 100644 index 0000000000000000000000000000000000000000..f3aaf42e1d605824d9a4696f45272817d42aa415 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/preinit.c @@ -0,0 +1,37 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 + +// Verifies that calling malloc in a preinit_array function succeeds, and that +// the resulting pointer can be freed at program termination. + +// On some Android versions, calling mmap() from a preinit function segfaults. +// It looks like __mmap2.S ends up calling a NULL function pointer. +// UNSUPPORTED: android + +#include +#include +#include + +static void *global_p = NULL; + +void __init(void) { + global_p = malloc(1); + if (!global_p) + exit(1); +} + +void __fini(void) { + if (global_p) + free(global_p); +} + +int main(int argc, char **argv) { + void *p = malloc(1); + assert(p); + free(p); + + return 0; +} + +__attribute__((section(".preinit_array"), used)) void (*__local_preinit)(void) = __init; +__attribute__((section(".fini_array"), used)) void (*__local_fini)(void) = __fini; diff --git a/ndk-test/sanitize/scudo/src/preload.cpp b/ndk-test/sanitize/scudo/src/preload.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7fa8df4c693140dceb709d26cd412f2c8f09ce15 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/preload.cpp @@ -0,0 +1,21 @@ +// Test that the preloaded runtime works without linking the static library. + +// RUN: %clang %s -lstdc++ -o %t +// RUN: env LD_PRELOAD=%shared_libscudo not %run %t 2>&1 | FileCheck %s +// RUN: env LD_PRELOAD=%shared_minlibscudo not %run %t 2>&1 | FileCheck %s + +// This way of setting LD_PRELOAD does not work with Android test runner. +// REQUIRES: !android + +#include + +int main(int argc, char *argv[]) { + int *p = new int; + assert(p); + *p = 0; + delete p; + delete p; + return 0; +} + +// CHECK: ERROR: invalid chunk state diff --git a/ndk-test/sanitize/scudo/src/realloc.cpp b/ndk-test/sanitize/scudo/src/realloc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c4d459df9b0ca2e5ae25257cc72d07de9b39483 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/realloc.cpp @@ -0,0 +1,92 @@ +// RUN: %clangxx_scudo %s -lstdc++ -o %t +// RUN: %run %t pointers 2>&1 +// RUN: %run %t contents 2>&1 +// RUN: %run %t usablesize 2>&1 + +// Tests that our reallocation function returns the same pointer when the +// requested size can fit into the previously allocated chunk. Also tests that +// a new chunk is returned if the size is greater, and that the contents of the +// chunk are left unchanged. Finally, checks that realloc copies the usable +// size of the old chunk to the new one (as opposed to the requested size). + +#include +#include +#include + +#include + +#include + +int main(int argc, char **argv) { + void *p, *old_p; + // Those sizes will exercise both allocators (Primary & Secondary). + std::vector sizes{1, 16, 1024, 32768, 1 << 16, 1 << 17, 1 << 20}; + + assert(argc == 2); + + if (!strcmp(argv[1], "usablesize")) { + // This tests a sketchy behavior inherited from poorly written libraries + // that have become somewhat standard. When realloc'ing a chunk, the + // copied contents should span the usable size of the chunk, not the + // requested size. + size_t size = 496, usable_size; + p = nullptr; + // Make sure we get a chunk with a usable size actually larger than size. + do { + if (p) + free(p); + size += 16; + p = malloc(size); + usable_size = __sanitizer_get_allocated_size(p); + assert(usable_size >= size); + } while (usable_size == size); + for (int i = 0; i < usable_size; i++) + reinterpret_cast(p)[i] = 'A'; + old_p = p; + // Make sure we get a different chunk so that the data is actually copied. + do { + size *= 2; + p = realloc(p, size); + assert(p); + } while (p == old_p); + // The contents of the new chunk must match the old one up to usable_size. + for (int i = 0; i < usable_size; i++) + assert(reinterpret_cast(p)[i] == 'A'); + free(p); + } else { + for (size_t size : sizes) { + if (!strcmp(argv[1], "pointers")) { + old_p = p = realloc(nullptr, size); + assert(p); + size = __sanitizer_get_allocated_size(p); + // Our realloc implementation will return the same pointer if the size + // requested is lower than or equal to the usable size of the associated + // chunk. + p = realloc(p, size - 1); + assert(p == old_p); + p = realloc(p, size); + assert(p == old_p); + // And a new one if the size is greater. + p = realloc(p, size + 1); + assert(p != old_p); + // A size of 0 will free the chunk and return nullptr. + p = realloc(p, 0); + assert(!p); + old_p = nullptr; + } + if (!strcmp(argv[1], "contents")) { + p = realloc(nullptr, size); + assert(p); + for (int i = 0; i < size; i++) + reinterpret_cast(p)[i] = 'A'; + p = realloc(p, size + 1); + // The contents of the reallocated chunk must match the original one. + for (int i = 0; i < size; i++) + assert(reinterpret_cast(p)[i] == 'A'); + } + } + } + return 0; +} + +// CHECK: ERROR: invalid chunk type when reallocating address diff --git a/ndk-test/sanitize/scudo/src/rss.c b/ndk-test/sanitize/scudo/src/rss.c new file mode 100644 index 0000000000000000000000000000000000000000..7f182b6dd6e5e504381b4dbc95365dca2e4e8136 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/rss.c @@ -0,0 +1,57 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=128" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=128" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-nolimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=32:allocator_may_return_null=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=32:allocator_may_return_null=1" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit-returnnull +// RUN: %env_scudo_opts="soft_rss_limit_mb=32:allocator_may_return_null=0:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit +// RUN: %env_scudo_opts="soft_rss_limit_mb=32:allocator_may_return_null=1:can_use_proc_maps_statm=0" %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-softlimit-returnnull +// RUN: %env_scudo_opts="hard_rss_limit_mb=32:allocator_may_return_null=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=32:allocator_may_return_null=1" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=32:allocator_may_return_null=0:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit +// RUN: %env_scudo_opts="hard_rss_limit_mb=32:allocator_may_return_null=1:can_use_proc_maps_statm=0" not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-hardlimit + +// Tests that the soft and hard RSS limits work as intended. Without limit or +// with a high limit, the test should pass without any malloc returning NULL or +// the program dying. +// If a limit is specified, it should return some NULL or die depending on +// allocator_may_return_null. This should also work without statm. + +#include +#include +#include +#include + +static const size_t kNumAllocs = 64; +static const size_t kAllocSize = 1 << 20; // 1MB. + +static void *allocs[kNumAllocs]; + +int main(int argc, char *argv[]) { + int returned_null = 0; + for (int i = 0; i < kNumAllocs; i++) { + // sleep for 100ms every 8 allocations, to allow the RSS check to catch up. + if (i != 0 && (i & 0x7) == 0) + usleep(100000); + allocs[i] = malloc(kAllocSize); + if (allocs[i]) + memset(allocs[i], 0xff, kAllocSize); // Dirty the pages. + else + returned_null++; + } + for (int i = 0; i < kNumAllocs; i++) + free(allocs[i]); + if (returned_null == 0) + printf("All malloc calls succeeded\n"); + else + printf("%d malloc calls returned NULL\n", returned_null); + return 0; +} + +// CHECK-nolimit: All malloc calls succeeded +// CHECK-softlimit: soft RSS limit exhausted +// CHECK-softlimit-NOT: malloc calls +// CHECK-softlimit-returnnull: soft RSS limit exhausted +// CHECK-softlimit-returnnull: malloc calls returned NULL +// CHECK-hardlimit: hard RSS limit exhausted +// CHECK-hardlimit-NOT: malloc calls diff --git a/ndk-test/sanitize/scudo/src/sized-delete.cpp b/ndk-test/sanitize/scudo/src/sized-delete.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b2945bea9c42536addf280d1a829a5b37ea699a --- /dev/null +++ b/ndk-test/sanitize/scudo/src/sized-delete.cpp @@ -0,0 +1,40 @@ +// RUN: %clangxx_scudo -fsized-deallocation %s -o %t +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 %run %t gooddel 2>&1 +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 not %run %t baddel 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=DeleteSizeMismatch=0 %run %t baddel 2>&1 +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 %run %t gooddelarr 2>&1 +// RUN: %env_scudo_opts=DeleteSizeMismatch=1 not %run %t baddelarr 2>&1 | FileCheck %s +// RUN: %env_scudo_opts=DeleteSizeMismatch=0 %run %t baddelarr 2>&1 + +// Ensures that the sized delete operator errors out when the appropriate +// option is passed and the sizes do not match between allocation and +// deallocation functions. + +#include +#include +#include + +#include + +int main(int argc, char **argv) { + assert(argc == 2); + if (!strcmp(argv[1], "gooddel")) { + long long *p = new long long; + operator delete(p, sizeof(long long)); + } + if (!strcmp(argv[1], "baddel")) { + long long *p = new long long; + operator delete(p, 2); + } + if (!strcmp(argv[1], "gooddelarr")) { + char *p = new char[64]; + operator delete[](p, 64); + } + if (!strcmp(argv[1], "baddelarr")) { + char *p = new char[63]; + operator delete[](p, 64); + } + return 0; +} + +// CHECK: ERROR: invalid sized delete when deallocating address diff --git a/ndk-test/sanitize/scudo/src/sizes.cpp b/ndk-test/sanitize/scudo/src/sizes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f7ccbebedc3005d7eb85add8f8116ab842b20618 --- /dev/null +++ b/ndk-test/sanitize/scudo/src/sizes.cpp @@ -0,0 +1,75 @@ +// RUN: %clangxx_scudo %s -lstdc++ -o %t +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t malloc 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-calloc +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t calloc 2>&1 +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_scudo_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-oom +// RUN: %env_scudo_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_scudo_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 +// RUN: %run %t usable 2>&1 + +// Tests for various edge cases related to sizes, notably the maximum size the +// allocator can allocate. Tests that an integer overflow in the parameters of +// calloc is caught. + +#include +#include +#include +#include + +#include +#include + +#include + +int main(int argc, char **argv) { + assert(argc == 2); + +#if __LP64__ || defined(_WIN64) + static const size_t kMaxAllowedMallocSize = 1ULL << 40; + static const size_t kChunkHeaderSize = 16; +#else + static const size_t kMaxAllowedMallocSize = 2UL << 30; + static const size_t kChunkHeaderSize = 8; +#endif + + if (!strcmp(argv[1], "malloc")) { + void *p = malloc(kMaxAllowedMallocSize); + assert(!p); + p = malloc(kMaxAllowedMallocSize - kChunkHeaderSize); + assert(!p); + } else if (!strcmp(argv[1], "calloc")) { + // Trigger an overflow in calloc. + size_t size = std::numeric_limits::max(); + void *p = calloc((size / 0x1000) + 1, 0x1000); + assert(!p); + } else if (!strcmp(argv[1], "new")) { + void *p = operator new(kMaxAllowedMallocSize); + assert(!p); + } else if (!strcmp(argv[1], "new-nothrow")) { + void *p = operator new(kMaxAllowedMallocSize, std::nothrow); + assert(!p); + } else if (!strcmp(argv[1], "usable")) { + // Playing with the actual usable size of a chunk. + void *p = malloc(1007); + assert(p); + size_t size = __sanitizer_get_allocated_size(p); + assert(size >= 1007); + memset(p, 'A', size); + p = realloc(p, 2014); + assert(p); + size = __sanitizer_get_allocated_size(p); + assert(size >= 2014); + memset(p, 'B', size); + free(p); + } else { + assert(0); + } + + return 0; +} + +// CHECK-max: {{Scudo ERROR: requested allocation size .* exceeds maximum supported size}} +// CHECK-oom: Scudo ERROR: allocator is out of memory +// CHECK-calloc: Scudo ERROR: calloc parameters overflow diff --git a/ndk-test/sanitize/scudo/src/tsd_destruction.c b/ndk-test/sanitize/scudo/src/tsd_destruction.c new file mode 100644 index 0000000000000000000000000000000000000000..2964df99745b32adf46fa3609db99727c4ed274c --- /dev/null +++ b/ndk-test/sanitize/scudo/src/tsd_destruction.c @@ -0,0 +1,43 @@ +// RUN: %clang_scudo %s -o %t +// RUN: %run %t 2>&1 + +#include +#include +#include +#include +#include + +// Some of glibc's own thread local data is destroyed after a user's thread +// local destructors are called, via __libc_thread_freeres. This might involve +// calling free, as is the case for strerror_thread_freeres. +// If there is no prior heap operation in the thread, this free would end up +// initializing some thread specific data that would never be destroyed +// properly, while still being deallocated when the TLS goes away. As a result, +// a program could SEGV, usually in +// __sanitizer::AllocatorGlobalStats::Unregister, where one of the doubly +// linked list links would refer to a now unmapped memory area. + +// This test reproduces those circumstances. Success means executing without +// a segmentation fault. + +const int kNumThreads = 16; +pthread_t tid[kNumThreads]; + +void *thread_func(void *arg) { + uintptr_t i = (uintptr_t)arg; + if ((i & 1) == 0) + free(malloc(16)); + // Calling strerror_l allows for strerror_thread_freeres to be called. + strerror_l(0, LC_GLOBAL_LOCALE); + return 0; +} + +int main(int argc, char **argv) { + for (uintptr_t j = 0; j < 8; j++) { + for (uintptr_t i = 0; i < kNumThreads; i++) + pthread_create(&tid[i], 0, thread_func, (void *)i); + for (uintptr_t i = 0; i < kNumThreads; i++) + pthread_join(tid[i], 0); + } + return 0; +} diff --git a/ndk-test/sanitize/trace-pc-guard/CMakeLists.txt b/ndk-test/sanitize/trace-pc-guard/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f61b3a56bd97d9b6716ad003f2a7624c26ee4130 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.5) + +# project name +project (asan_test) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../exe) + +# Add executable +file(GLOB TESTCASES src/*.cpp) +foreach(srcfile ${TESTCASES}) + get_filename_component(testname ${srcfile} NAME_WE) + add_executable( ${testname} ${srcfile}) +endforeach(add_subdirectory(src/init-order)) + diff --git a/ndk-test/sanitize/trace-pc-guard/build.py b/ndk-test/sanitize/trace-pc-guard/build.py new file mode 100755 index 0000000000000000000000000000000000000000..a4638819078a77b198fb2cd413fca49f318e1ede --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/build.py @@ -0,0 +1,40 @@ +#!/usr/bin/python + +import os +import subprocess +import sys +import shutil + +pre_dir = os.path.abspath(os.path.dirname(os.getcwd())) +config_dir = os.path.join(os.path.dirname(pre_dir),'script') +sys.path.append(config_dir) + +from config_args import linux_args + +# Add asan compile options +asan_flags = [ "-DCMAKE_CXX_FLAGS=-fsanitize-coverage=trace-pc-guard,indirect-calls" ] + +build_args=[] +build_args.extend(linux_args) +build_args.extend(asan_flags) + +def rm_build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + +def build_linux(): + rm_build() + os.mkdir("build") + build_cmd = "cmake " + (" ".join(build_args))+ " .." + print(build_cmd) + res1 = subprocess.Popen(build_cmd,cwd='build',shell=True).wait() + # print(res1) + res2 = subprocess.Popen('make',cwd='build',shell=True).wait() + # print(res2) + rm_build() + +if __name__ == '__main__': + build_linux() diff --git a/ndk-test/sanitize/trace-pc-guard/src/sample.cpp b/ndk-test/sanitize/trace-pc-guard/src/sample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bac9c0d2e443f56906ed053eb8a4e9f4bb5f0e27 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/sample.cpp @@ -0,0 +1,5 @@ +// trace-pc-guard-example.cc +void foo() { } +int main(int argc, char **argv) { + if (argc > 1) foo(); +} diff --git a/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_symbolize.cpp b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_symbolize.cpp new file mode 100644 index 0000000000000000000000000000000000000000..daa994c8116251bd3554dbfce16c9966fd9f879a --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_symbolize.cpp @@ -0,0 +1,28 @@ +// Tests trace pc guard coverage collection. +// +// REQUIRES: x86_64-linux +// XFAIL: tsan +// +// RUN: DIR=%t_workdir +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: rm -rf $DIR + +#include + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + foo(); +} + +// CHECK: main +// CHECK: SanitizerCoverage: ./sanitizer_coverage_symbolize.{{.*}}.sancov: 2 PCs written diff --git a/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard-dso.cpp b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard-dso.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3316c5d72fb0f9eb6c61578b0fe2e7827a5c9bc4 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard-dso.cpp @@ -0,0 +1,74 @@ +// Tests trace pc guard coverage collection. + +// REQUIRES: has_sancovcc,stable-runtime +// UNSUPPORTED: ubsan +// XFAIL: tsan,darwin,powerpc64,s390x,mips +// XFAIL: android && asan + +// RUN: DIR=%t_workdir +// RUN: CLANG_ARGS="-O0 -fsanitize-coverage=trace-pc-guard" +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -DSHARED1 $CLANG_ARGS -shared %s -o %t_1.so -fPIC +// RUN: %clangxx -DSHARED2 $CLANG_ARGS -shared %s -o %t_2.so -fPIC +// RUN: %clangxx -DMAIN $CLANG_ARGS %s -o %t %t_1.so %t_2.so +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov \ +// RUN: %t %t_1.so %t_2.so 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-SANCOV %s +// RUN: rm -rf $DIR + +#include + +extern "C" { + int bar(); + int baz(); +} + +#ifdef MAIN + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + bar(); + baz(); +} + +#endif // MAIN + +extern "C" { + +#ifdef SHARED1 +int bar() { + fprintf(stderr, "bar\n"); + return 1; +} +#endif + +#ifdef SHARED2 +int baz() { + fprintf(stderr, "baz\n"); + return 1; +} +#endif + +} // extern "C" + +// CHECK: main +// CHECK-NEXT: foo +// CHECK-NEXT: bar +// CHECK-NEXT: baz +// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}.sancov: 2 PCs written +// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_2.so.{{.*}}.sancov: 1 PCs written +// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_1.so.{{.*}}.sancov: 1 PCs written +// +// CHECK-SANCOV: Ignoring {{.*}}_1.so and its coverage because __sanitizer_cov* functions were not found. +// CHECK-SANCOV: Ignoring {{.*}}_2.so and its coverage because __sanitizer_cov* functions were not found. +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard-dso.cpp:[[@LINE-42]] foo +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard-dso.cpp:[[@LINE-38]] main diff --git a/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard-init.cpp b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard-init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0b2da9aebac8e27298eca479b91c9484ab8645f0 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard-init.cpp @@ -0,0 +1,72 @@ +// Tests trace pc guard coverage collection. +// +// REQUIRES: has_sancovcc,stable-runtime,x86_64-linux +// +// RUN: DIR=%t_workdir +// RUN: CLANG_ARGS="-O0 -fsanitize-coverage=trace-pc-guard" +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -DSHARED1 $CLANG_ARGS -shared %s -o %t_1.so -fPIC +// RUN: %clangxx -DSTATIC1 $CLANG_ARGS %s -c -o %t_2.o +// RUN: %clangxx -DMAIN $CLANG_ARGS %s -o %t %t_1.so %t_2.o +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: rm -rf $DIR + +#include +#include +#include + +extern "C" { + int bar(); + int baz(); +} + +#ifdef MAIN + +extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, uint32_t *stop) { + fprintf(stderr, "__sanitizer_cov_trace_pc_guard_init\n"); +} + +extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { } + + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + bar(); + baz(); +} + +#endif // MAIN + +extern "C" { + +#ifdef SHARED1 +int bar() { + fprintf(stderr, "bar\n"); + return 1; +} +#endif + +#ifdef STATIC1 +int baz() { + fprintf(stderr, "baz\n"); + return 1; +} +#endif + +} // extern "C" + +// Init is called once per DSO. +// CHECK: __sanitizer_cov_trace_pc_guard_init +// CHECK-NEXT: __sanitizer_cov_trace_pc_guard_init +// CHECK-NEXT: main +// CHECK-NEXT: foo +// CHECK-NEXT: bar +// CHECK-NEXT: baz diff --git a/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard.cpp b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b151fc247dcf970714f738943145f2019787ee24 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/sanitizer_coverage_trace_pc_guard.cpp @@ -0,0 +1,41 @@ +// Tests trace pc guard coverage collection. + +// REQUIRES: has_sancovcc,stable-runtime +// UNSUPPORTED: ubsan,i386-darwin +// XFAIL: tsan,powerpc64,s390x,mips +// XFAIL: android && asan + +// RUN: DIR=%t_workdir +// RUN: rm -rf $DIR +// RUN: mkdir -p $DIR +// RUN: cd $DIR +// RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -o %t +// RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s +// RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK-SANCOV %s +// RUN: %env_tool_opts=coverage=0 %t 2>&1 | FileCheck --check-prefix=CHECK-NOCOV %s +// RUN: rm -rf $DIR +// Make some room to stabilize line numbers + +#include + +int foo() { + fprintf(stderr, "foo\n"); + return 1; +} + +int main() { + fprintf(stderr, "main\n"); + foo(); + foo(); +} + +// CHECK: main +// CHECK-NEXT: foo +// CHECK-NEXT: foo +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard.{{.*}}.sancov: 2 PCs written +// +// CHECK-SANCOV: sanitizer_coverage_trace_pc_guard.cpp:[[@LINE-16]] foo +// CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard.cpp:[[@LINE-12]] main +// +// CHECK-NOCOV-NOT: SanitizerCoverage diff --git a/ndk-test/sanitize/trace-pc-guard/src/trace-pc-guard-cb.cpp b/ndk-test/sanitize/trace-pc-guard/src/trace-pc-guard-cb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52c46e5d90f93835951e9a0dc6ead0832dd412a9 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/trace-pc-guard-cb.cpp @@ -0,0 +1,43 @@ +// trace-pc-guard-cb.cc +#include +#include +#include + +// This callback is inserted by the compiler as a module constructor +// into every DSO. 'start' and 'stop' correspond to the +// beginning and end of the section with the guards for the entire +// binary (executable or DSO). The callback will be called at least +// once per DSO and may be called multiple times with the same parameters. +extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *start, + uint32_t *stop) { + static uint64_t N; // Counter for the guards. + if (start == stop || *start) return; // Initialize only once. + printf("INIT: %p %p\n", start, stop); + for (uint32_t *x = start; x < stop; x++) + *x = ++N; // Guards should start from 1. +} + +// This callback is inserted by the compiler on every edge in the +// control flow (some optimizations apply). +// Typically, the compiler will emit the code like this: +// if(*guard) +// __sanitizer_cov_trace_pc_guard(guard); +// But for large functions it will emit a simple call: +// __sanitizer_cov_trace_pc_guard(guard); +extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) { + if (!*guard) return; // Duplicate the guard check. + // If you set *guard to 0 this code will not be called again for this edge. + // Now you can get the PC and do whatever you want: + // store it somewhere or symbolize it and print right away. + // The values of `*guard` are as you set them in + // __sanitizer_cov_trace_pc_guard_init and so you can make them consecutive + // and use them to dereference an array or a bit vector. + void *PC = __builtin_return_address(0); + char PcDescr[1024]; + // This function is a part of the sanitizer run-time. + // To use it, link with AddressSanitizer or other sanitizer. + __sanitizer_symbolize_pc(PC, "%p %F %L", PcDescr, sizeof(PcDescr)); + printf("guard: %p %x PC %s\n", guard, *guard, PcDescr); +} + +int main(){} \ No newline at end of file diff --git a/ndk-test/sanitize/trace-pc-guard/src/trace-pc-guard-example.cpp b/ndk-test/sanitize/trace-pc-guard/src/trace-pc-guard-example.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2cbf3de2d8b0fa583d120d57318d0628ba124890 --- /dev/null +++ b/ndk-test/sanitize/trace-pc-guard/src/trace-pc-guard-example.cpp @@ -0,0 +1,6 @@ +// trace-pc-guard-example.cc +void foo() { } +int main(int argc, char **argv) { + if (argc > 1) foo(); +} + diff --git a/ndk-test/sanitize/ubsan/CMakeLists.txt b/ndk-test/sanitize/ubsan/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ced58fed777fb2e741e1eb58ed6c78c8a17326d0 --- /dev/null +++ b/ndk-test/sanitize/ubsan/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.5) + +# project name +project (ubsan_test) + +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/../exe) + +# Add executable +file(GLOB TESTCASES src/*.cpp) +foreach(srcfile ${TESTCASES}) + get_filename_component(testname ${srcfile} NAME_WE) + set(CMAKE_CXX_FLAGS "-fsanitize=undefined") + + if (${testname} STREQUAL "add-overflow") + add_executable(add-overflow-32 ${srcfile}) + target_compile_definitions(add-overflow-32 PRIVATE ADD_I32) + add_executable(add-overflow-64 ${srcfile}) + target_compile_definitions(add-overflow-64 PRIVATE ADD_I64) + add_executable(add-overflow-128 ${srcfile}) + target_compile_definitions(add-overflow-128 PRIVATE ADD_I128) + elseif(${testname} STREQUAL "div-zero") + add_executable(div-zero-0 ${srcfile}) + target_compile_definitions(div-zero-0 PRIVATE DIVIDEND=0) + add_executable(div-zero-1U ${srcfile}) + target_compile_definitions(div-zero-1U PRIVATE DIVIDEND=1U) + add_executable(div-zero-123 ${srcfile}) + target_compile_definitions(div-zero-123 PRIVATE "DIVIDEND=intmax(123)") + add_executable(div-zero-1-5 ${srcfile}) + target_compile_options(div-zero-1-5 PRIVATE -fsanitize=float-divide-by-zero) + target_compile_definitions(div-zero-1-5 PRIVATE DIVIDEND=1.5) + elseif(${testname} STREQUAL "shift") + set(UBSAN_SHIFT_FLAG "-fno-sanitize-recover=shift") + set(CMAKE_CXX_FLAGS "") + # LSH_OVERFLOW + # CHECK-LSH_OVERFLOW: shift.cpp:[[@LINE+1]]:5: runtime error: left shift of negative value -2147483648 + add_executable(shift-lsh-overflow ${srcfile}) + target_compile_options(shift-lsh-overflow PRIVATE -fsanitize=shift ${UBSAN_SHIFT_FLAG} "-DLSH_OVERFLOW" "-DOP=<<") + add_executable(shift-lsh-overflow-1 ${srcfile}) + target_compile_options(shift-lsh-overflow-1 PRIVATE -fsanitize=shift-base ${UBSAN_SHIFT_FLAG} "-DLSH_OVERFLOW" "-DOP=<<") + add_executable(shift-lsh-overflow-2 ${srcfile}) + target_compile_options(shift-lsh-overflow-2 PRIVATE -fsanitize=shift-exponent ${UBSAN_SHIFT_FLAG} "-DLSH_OVERFLOW" "-DOP=<<") + # TOO_LOW + # CHECK-TOO_LOW: shift.cpp:[[@LINE+1]]:5: runtime error: shift exponent -3 is negative + add_executable(shift-too-low ${srcfile}) + target_compile_options(shift-too-low PRIVATE ${UBSAN_SHIFT_FLAG} "-DTOO_LOW" "-DOP=<<") + # TOO_HIGH + # CHECK-TOO_HIGH: shift.cpp:[[@LINE+1]]:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + add_executable(shift-high ${srcfile}) + target_compile_options(shift-high PRIVATE ${UBSAN_SHIFT_FLAG} "-DTOO_HIGH" "-DOP=>>") + #end + elseif(${testname} STREQUAL "uadd-overflow") + add_executable(uadd-overflow-32 ${srcfile}) + target_compile_definitions(uadd-overflow-32 PRIVATE ADD_I32) + add_executable(uadd-overflow-64 ${srcfile}) + target_compile_definitions(add-overflow-64 PRIVATE ADD_I64) + add_executable(uadd-overflow-128 ${srcfile}) + target_compile_definitions(uadd-overflow-128 PRIVATE ADD_I128) + elseif(${testname} STREQUAL "cast-overflow") + # ld.lld: error: undefined symbol: __sync_val_compare_and_swap_1 + continue() + elseif(${testname} STREQUAL "unreachable") + # undefined symbol: returns_unexpectedly + continue() + else() + add_executable(${testname} ${srcfile}) + endif() +endforeach() + +file(GLOB CASES_C src/*.c) +foreach(src_c ${CASES_C}) + get_filename_component(name_c ${src_c} NAME_WE) + set(CMAKE_CXX_FLAGS "-fsanitize=undefined") + if(${name_c} STREQUAL "vla") + # did not work as expected + add_executable(vla ${src_c}) + target_compile_options(shift-lsh-overflow-1 PRIVATE -fsanitize=vla-bound -O3) + endif() +endforeach() \ No newline at end of file diff --git a/ndk-test/sanitize/ubsan/build.py b/ndk-test/sanitize/ubsan/build.py new file mode 100755 index 0000000000000000000000000000000000000000..a787c5c01ba33e8bf055eac7fdd698f19dab131e --- /dev/null +++ b/ndk-test/sanitize/ubsan/build.py @@ -0,0 +1,40 @@ +#!/usr/bin/python + +import os +import sys +import subprocess +import shutil + +pre_dir = os.path.abspath(os.path.dirname(os.getcwd())) +config_dir = os.path.join(os.path.dirname(pre_dir),'script') +sys.path.append(config_dir) + +from config_args import linux_args + +# Add ubsan compile options +ubsan_flags = [] + +build_args=[] +build_args.extend(linux_args) +build_args.extend(ubsan_flags) + +def rm_build(): + if os.path.exists("build") and os.path.isdir("build"): + shutil.rmtree("build") + while True: + if not os.path.exists("build"): + break + +def build_linux(): + rm_build() + os.mkdir("build") + build_cmd = "cmake " + (" ".join(build_args))+ " .." + print(build_cmd) + res1 = subprocess.Popen(build_cmd,cwd='build',shell=True).wait() + # print(res1) + res2 = subprocess.Popen('make',cwd='build',shell=True).wait() + # print(res2) + rm_build() + +if __name__ == '__main__': + build_linux() diff --git a/ndk-test/sanitize/ubsan/src/add-overflow.cpp b/ndk-test/sanitize/ubsan/src/add-overflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..301941b852b40c98bc825c7be7b5574841811fb8 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/add-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=signed-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=signed-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=signed-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include +#include + +int main() { + // These promote to 'int'. + (void)(int8_t(0x7f) + int8_t(0x7f)); + (void)(int16_t(0x3fff) + int16_t(0x4000)); + +#ifdef ADD_I32 + int32_t k = 0x12345678; + k += 0x789abcde; + // CHECK-ADD_I32: add-overflow.cpp:[[@LINE-1]]:5: runtime error: signed integer overflow: 305419896 + 2023406814 cannot be represented in type 'int' +#endif + +#ifdef ADD_I64 + (void)(int64_t(8000000000000000000ll) + int64_t(2000000000000000000ll)); + // CHECK-ADD_I64: 8000000000000000000 + 2000000000000000000 cannot be represented in type '{{long( long)?}}' +#endif + +#ifdef ADD_I128 +# if defined(__SIZEOF_INT128__) && !defined(_WIN32) + (void)((__int128_t(1) << 126) + (__int128_t(1) << 126)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x40000000000000000000000000000000 \+ 0x40000000000000000000000000000000 cannot be represented in type '__int128'|__int128 not supported}} +#endif +} diff --git a/ndk-test/sanitize/ubsan/src/alignment-assumption-attribute-align_value-on-lvalue.cpp b/ndk-test/sanitize/ubsan/src/alignment-assumption-attribute-align_value-on-lvalue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97d4b4623c2dad85f1998d1811107d46e4634485 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/alignment-assumption-attribute-align_value-on-lvalue.cpp @@ -0,0 +1,36 @@ +// RUN: %clang -x c -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" +// RUN: %clang -x c -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" +// RUN: %clang -x c -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" +// RUN: %clang -x c -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" + +// RUN: %clang -x c++ -fsanitize=alignment -O0 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" +// RUN: %clang -x c++ -fsanitize=alignment -O1 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" +// RUN: %clang -x c++ -fsanitize=alignment -O2 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" +// RUN: %clang -x c++ -fsanitize=alignment -O3 %s -o %t && %run %t 2>&1 | FileCheck %s --implicit-check-not=" assumption " --implicit-check-not="note:" --implicit-check-not="error:" + +#include + +typedef char *__attribute__((align_value(0x8000))) aligned_char; + +struct ac_struct { + aligned_char a; +}; + +char *load_from_ac_struct(struct ac_struct *x) { + return x->a; +} + +int main(int argc, char* argv[]) { + char *ptr = (char *)malloc(2); + + struct ac_struct x; + x.a = ptr + 1; // FIXME: it is weird that this does not also have an assumption. + load_from_ac_struct(&x); + // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-9]]:13: runtime error: assumption of 32768 byte alignment for pointer of type 'aligned_char' (aka 'char *') failed + // CHECK: {{.*}}alignment-assumption-{{.*}}.cpp:[[@LINE-17]]:30: note: alignment assumption was specified here + // CHECK: 0x{{.*}}: note: address is {{.*}} aligned, misalignment offset is {{.*}} byte + + free(ptr); + + return 0; +} diff --git a/ndk-test/sanitize/ubsan/src/bool.cpp b/ndk-test/sanitize/ubsan/src/bool.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f6dc24e4bc7834c30edbeab241212413cd74b998 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/bool.cpp @@ -0,0 +1,13 @@ +// RUN: %clangxx -fsanitize=bool %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %env_ubsan_opts=print_summary=1:report_error_type=1 not %run %t 2>&1 | FileCheck %s --check-prefix=SUMMARY + +unsigned char NotABool = 123; + +int main(int argc, char **argv) { + bool *p = (bool*)&NotABool; + + // CHECK: bool.cpp:[[@LINE+1]]:10: runtime error: load of value 123, which is not a valid value for type 'bool' + return *p; + // SUMMARY: SUMMARY: {{.*}}Sanitizer: invalid-bool-load {{.*}}bool.cpp:[[@LINE-1]] +} diff --git a/ndk-test/sanitize/ubsan/src/bounds.cpp b/ndk-test/sanitize/ubsan/src/bounds.cpp new file mode 100644 index 0000000000000000000000000000000000000000..03b2a9341f4da16c315a96fd97310ae5faef3f1c --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/bounds.cpp @@ -0,0 +1,31 @@ +// RUN: %clangxx -fsanitize=bounds %s -O3 -o %t +// RUN: %run %t 0 0 0 +// RUN: %run %t 1 2 3 +// RUN: %expect_crash %run %t 2 0 0 2>&1 | FileCheck %s --check-prefix=CHECK-A-2 +// RUN: %run %t 0 3 0 2>&1 | FileCheck %s --check-prefix=CHECK-B-3 +// RUN: %run %t 0 0 4 2>&1 | FileCheck %s --check-prefix=CHECK-C-4 + +int get_int(int *const p __attribute__((pass_object_size(0))), int i) { + // CHECK-A-2: bounds.cpp:[[@LINE+1]]:10: runtime error: index 2 out of bounds for type 'int *' + return p[i]; +} + +int get_double(double *const p __attribute__((pass_object_size(0))), int i) { + // CHECK-A-2: bounds.cpp:[[@LINE+1]]:10: runtime error: index 2 out of bounds for type 'double *' + return p[i]; +} + +int main(int argc, char **argv) { + int bar[2]; + get_int(bar, argv[1][0] - '0'); + + double baz[2]; + get_double(baz, argv[1][0] - '0'); + + int arr[2][3][4] = {}; + + return arr[argv[1][0] - '0'][argv[2][0] - '0'][argv[3][0] - '0']; + // CHECK-A-2: bounds.cpp:[[@LINE-1]]:10: runtime error: index 2 out of bounds for type 'int[2][3][4]' + // CHECK-B-3: bounds.cpp:[[@LINE-2]]:10: runtime error: index 3 out of bounds for type 'int[3][4]' + // CHECK-C-4: bounds.cpp:[[@LINE-3]]:10: runtime error: index 4 out of bounds for type 'int[4]' +} diff --git a/ndk-test/sanitize/ubsan/src/cast-overflow.cpp b/ndk-test/sanitize/ubsan/src/cast-overflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..479c39f28428ade9245e42b837341f911f8ae6c1 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/cast-overflow.cpp @@ -0,0 +1,167 @@ +// RUN: %clangxx -fsanitize=float-cast-overflow %s -o %t +// RUN: %run %t _ +// RUN: %env_ubsan_opts=print_summary=1:report_error_type=1 %run %t 0 2>&1 | FileCheck %s --check-prefix=CHECK-0 +// RUN: %run %t 1 2>&1 | FileCheck %s --check-prefix=CHECK-1 +// RUN: %run %t 2 2>&1 | FileCheck %s --check-prefix=CHECK-2 +// RUN: %run %t 3 2>&1 | FileCheck %s --check-prefix=CHECK-3 +// RUN: %run %t 4 2>&1 | FileCheck %s --check-prefix=CHECK-4 +// RUN: %run %t 5 2>&1 | FileCheck %s --check-prefix=CHECK-5 +// RUN: %run %t 6 2>&1 | FileCheck %s --check-prefix=CHECK-6 +// FIXME: %run %t 7 2>&1 | FileCheck %s --check-prefix=CHECK-7 +// FIXME: not %run %t 8 2>&1 | FileCheck %s --check-prefix=CHECK-8 +// RUN: not %run %t 9 2>&1 | FileCheck %s --check-prefix=CHECK-9 + +// This test assumes float and double are IEEE-754 single- and double-precision. + +#if defined(__APPLE__) +# include +# define BYTE_ORDER __DARWIN_BYTE_ORDER +# define BIG_ENDIAN __DARWIN_BIG_ENDIAN +# define LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#elif defined(__FreeBSD__) || defined(__NetBSD__) +# include +# ifndef BYTE_ORDER +# define BYTE_ORDER _BYTE_ORDER +# endif +# ifndef BIG_ENDIAN +# define BIG_ENDIAN _BIG_ENDIAN +# endif +# ifndef LITTLE_ENDIAN +# define LITTLE_ENDIAN _LITTLE_ENDIAN +# endif +#elif defined(__sun__) && defined(__svr4__) +// Solaris provides _BIG_ENDIAN/_LITTLE_ENDIAN selector in sys/types.h. +# include +# define BIG_ENDIAN 4321 +# define LITTLE_ENDIAN 1234 +# if defined(_BIG_ENDIAN) +# define BYTE_ORDER BIG_ENDIAN +# else +# define BYTE_ORDER LITTLE_ENDIAN +# endif +#elif defined(_WIN32) +# define BYTE_ORDER 0 +# define BIG_ENDIAN 1 +# define LITTLE_ENDIAN 0 +#else +# include +# define BYTE_ORDER __BYTE_ORDER +# define BIG_ENDIAN __BIG_ENDIAN +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif // __APPLE__ +#include +#include +#include + +float Inf; +float NaN; + +int main(int argc, char **argv) { + float MaxFloatRepresentableAsInt = 0x7fffff80; + (int)MaxFloatRepresentableAsInt; // ok + (int)-MaxFloatRepresentableAsInt; // ok + + float MinFloatRepresentableAsInt = -0x7fffffff - 1; + (int)MinFloatRepresentableAsInt; // ok + + float MaxFloatRepresentableAsUInt = 0xffffff00u; + (unsigned int)MaxFloatRepresentableAsUInt; // ok + +#ifdef __SIZEOF_INT128__ + unsigned __int128 FloatMaxAsUInt128 = -((unsigned __int128)1 << 104); + (void)(float)FloatMaxAsUInt128; // ok +#endif + + float NearlyMinusOne = -0.99999; + unsigned Zero = NearlyMinusOne; // ok + + // Build a '+Inf'. +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned char InfVal[] = { 0x00, 0x00, 0x80, 0x7f }; +#else + unsigned char InfVal[] = { 0x7f, 0x80, 0x00, 0x00 }; +#endif + float Inf; + memcpy(&Inf, InfVal, 4); + + // Build a 'NaN'. +#if BYTE_ORDER == LITTLE_ENDIAN + unsigned char NaNVal[] = { 0x01, 0x00, 0x80, 0x7f }; +#else + unsigned char NaNVal[] = { 0x7f, 0x80, 0x00, 0x01 }; +#endif + float NaN; + memcpy(&NaN, NaNVal, 4); + + double DblInf = (double)Inf; // ok + + switch (argv[1][0]) { + // FIXME: Produce a source location for these checks and test for it here. + + // Floating point -> integer overflow. + case '0': { + // Note that values between 0x7ffffe00 and 0x80000000 may or may not + // successfully round-trip, depending on the rounding mode. + // CHECK-0: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: 2.14748{{.*}} is outside the range of representable values of type 'int' + static int test_int = MaxFloatRepresentableAsInt + 0x80; + // CHECK-0: SUMMARY: {{.*}}Sanitizer: float-cast-overflow {{.*}}cast-overflow.cpp:[[@LINE-1]] + return 0; + } + case '1': { + // CHECK-1: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: -2.14748{{.*}} is outside the range of representable values of type 'int' + static int test_int = MinFloatRepresentableAsInt - 0x100; + return 0; + } + case '2': { + // CHECK-2: {{.*}}cast-overflow.cpp:[[@LINE+2]]:37: runtime error: -1 is outside the range of representable values of type 'unsigned int' + volatile float f = -1.0; + volatile unsigned u = (unsigned)f; + return 0; + } + case '3': { + // CHECK-3: {{.*}}cast-overflow.cpp:[[@LINE+1]]:37: runtime error: 4.2949{{.*}} is outside the range of representable values of type 'unsigned int' + static int test_int = (unsigned)(MaxFloatRepresentableAsUInt + 0x100); + return 0; + } + + case '4': { + // CHECK-4: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: {{.*}} is outside the range of representable values of type 'int' + static int test_int = Inf; + return 0; + } + case '5': { + // CHECK-5: {{.*}}cast-overflow.cpp:[[@LINE+1]]:27: runtime error: {{.*}} is outside the range of representable values of type 'int' + static int test_int = NaN; + return 0; + } + + // Integer -> floating point overflow. + case '6': { + // CHECK-6: cast-overflow.cpp:[[@LINE+2]]:{{27: runtime error: 3.40282e\+38 is outside the range of representable values of type 'int'| __int128 not supported}} +#if defined(__SIZEOF_INT128__) && !defined(_WIN32) + static int test_int = (float)(FloatMaxAsUInt128 + 1); + return 0; +#else + // Print the same line as the check above. That way the test is robust to + // line changes around it + printf("%s:%d: __int128 not supported", __FILE__, __LINE__ - 5); + return 0; +#endif + } + // FIXME: The backend cannot lower __fp16 operations on x86 yet. + //case '7': + // (__fp16)65504; // ok + // // CHECK-7: runtime error: 65505 is outside the range of representable values of type '__fp16' + // return (__fp16)65505; + + // Floating point -> floating point overflow. + case '8': + // CHECK-8: {{.*}}cast-overflow.cpp:[[@LINE+1]]:19: runtime error: 1e+39 is outside the range of representable values of type 'float' + return (float)1e39; + case '9': + volatile long double ld = 300.0; + // CHECK-9: {{.*}}cast-overflow.cpp:[[@LINE+1]]:14: runtime error: 300 is outside the range of representable values of type 'char' + char c = ld; + return c; + } +} diff --git a/ndk-test/sanitize/ubsan/src/div-zero.cpp b/ndk-test/sanitize/ubsan/src/div-zero.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0c18db5aa18daff61724d450fc9195f5a30ccb4b --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/div-zero.cpp @@ -0,0 +1,15 @@ +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=0 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND=1U %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=integer-divide-by-zero -DDIVIDEND='intmax(123)' %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=float-divide-by-zero -DDIVIDEND=1.5 %s -o %t && %run %t 2>&1 | FileCheck %s + +#if defined(__SIZEOF_INT128__) && !defined(_WIN32) +typedef __int128 intmax; +#else +typedef long long intmax; +#endif + +int main() { + // CHECK: div-zero.cpp:[[@LINE+1]]:12: runtime error: division by zero + DIVIDEND / 0; +} diff --git a/ndk-test/sanitize/ubsan/src/enum.cpp b/ndk-test/sanitize/ubsan/src/enum.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8e95f8b403a93e33da682b4d25b866a349474f13 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/enum.cpp @@ -0,0 +1,21 @@ +// RUN: %clangxx -fsanitize=enum %s -O3 -o %t && %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-PLAIN +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E" %s -O3 -o %t && %run %t +// RUN: %clangxx -fsanitize=enum -std=c++11 -DE="class E : bool" %s -O3 -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-BOOL + +// FIXME: UBSan fails to add the correct instrumentation code for some reason on +// Windows. +// XFAIL: windows-msvc + +enum E { a = 1 } e; +#undef E + +int main(int argc, char **argv) { + // memset(&e, 0xff, sizeof(e)); + for (unsigned char *p = (unsigned char*)&e; p != (unsigned char*)(&e + 1); ++p) + *p = 0xff; + + // CHECK-PLAIN: error: load of value 4294967295, which is not a valid value for type 'enum E' + // FIXME: Support marshalling and display of enum class values. + // CHECK-BOOL: error: load of value , which is not a valid value for type 'enum E' + return (int)e != -1; +} diff --git a/ndk-test/sanitize/ubsan/src/missing_return.cpp b/ndk-test/sanitize/ubsan/src/missing_return.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e8146d6c44b0173f5671d24dee99aed7194daa6d --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/missing_return.cpp @@ -0,0 +1,14 @@ +// RUN: %clangxx -fsanitize=return %gmlt %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %env_ubsan_opts=print_stacktrace=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-STACKTRACE +// Error message does not exact what expected +// XFAIL: openbsd + +// CHECK: missing_return.cpp:[[@LINE+1]]:5: runtime error: execution reached the end of a value-returning function without returning a value +int f() __attribute__((noinline)) { +// CHECK-STACKTRACE: #0 {{.*}}f{{.*}}missing_return.cpp:[[@LINE-1]] +} + +int main(int, char **argv) { + return f(); +} diff --git a/ndk-test/sanitize/ubsan/src/nonnull-arg.cpp b/ndk-test/sanitize/ubsan/src/nonnull-arg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0332d96c02130101de1c4594e9d60435e95a7ffb --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/nonnull-arg.cpp @@ -0,0 +1,61 @@ +// RUN: %clangxx -fsanitize=nonnull-attribute -fno-sanitize-recover=all %s -O3 -o %t +// RUN: %run %t nc +// RUN: %run %t nm +// RUN: %run %t nf +// RUN: %run %t nv +// RUN: not %run %t 0c 2>&1 | FileCheck %s --check-prefix=CTOR +// RUN: not %run %t 0m 2>&1 | FileCheck %s --check-prefix=METHOD +// RUN: not %run %t 0f 2>&1 | FileCheck %s --check-prefix=FUNC +// RUN: not %run %t 0v 2>&1 | FileCheck %s --check-prefix=VARIADIC +// +// AArch64 lacks variadic instrumentation for MSAN. +// REQUIRES: stable-runtime + +class C { + int *null_; + int *nonnull_; + +public: + C(int *null, __attribute__((nonnull)) int *nonnull) + : null_(null), nonnull_(nonnull) {} + int value() { return *nonnull_; } + int method(int *nonnull, int *null) __attribute__((nonnull(2))) { + return *nonnull_ + *nonnull; + } +}; + +__attribute__((nonnull)) int func(int *nonnull) { return *nonnull; } + +#include +__attribute__((nonnull)) int variadic(int x, ...) { + va_list args; + va_start(args, x); + int *nonnull = va_arg(args, int*); + int res = *nonnull; + va_end(args); + return res; +} + +int main(int argc, char *argv[]) { + int local = 0; + int *arg = (argv[1][0] == '0') ? 0x0 : &local; + switch (argv[1][1]) { + case 'c': + return C(0x0, arg).value(); + // CTOR: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:21: runtime error: null pointer passed as argument 2, which is declared to never be null + // CTOR-NEXT: {{.*}}nonnull-arg.cpp:19:31: note: nonnull attribute specified here + case 'm': + return C(0x0, &local).method(arg, 0x0); + // METHOD: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:36: runtime error: null pointer passed as argument 1, which is declared to never be null + // METHOD-NEXT: {{.*}}nonnull-arg.cpp:22:54: note: nonnull attribute specified here + case 'f': + return func(arg); + // FUNC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:19: runtime error: null pointer passed as argument 1, which is declared to never be null + // FUNC-NEXT: {{.*}}nonnull-arg.cpp:27:16: note: nonnull attribute specified here + case 'v': + return variadic(42, arg); + // VARIADIC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:27: runtime error: null pointer passed as argument 2, which is declared to never be null + // VARIADIC-NEXT: {{.*}}nonnull-arg.cpp:30:16: note: nonnull attribute specified here + } + return 0; +} diff --git a/ndk-test/sanitize/ubsan/src/nonnull.cpp b/ndk-test/sanitize/ubsan/src/nonnull.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c612cd8247156d5f0ceba300f59215cf0ee0dad4 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/nonnull.cpp @@ -0,0 +1,44 @@ +// RUN: %clangxx -fsanitize=returns-nonnull-attribute -w %s -O3 -o %t +// RUN: %run %t foo 2>&1 | FileCheck %s --check-prefix=NOERROR --allow-empty --implicit-check-not='runtime error' +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -fsanitize=returns-nonnull-attribute -fno-sanitize-recover=returns-nonnull-attribute -w %s -O3 -o %t.abort +// RUN: not %run %t.abort &> /dev/null + +__attribute__((returns_nonnull)) char *foo(char *a); + +char *foo(char *a) { + // CHECK: nonnull.cpp:[[@LINE+2]]:3: runtime error: null pointer returned from function declared to never return null + // CHECK-NEXT: nonnull.cpp:[[@LINE-4]]:16: note: returns_nonnull attribute specified here + return a; +} + +__attribute__((returns_nonnull)) char *bar(int x, char *a) { + if (x > 10) { + // CHECK: nonnull.cpp:[[@LINE+2]]:5: runtime error: null pointer returned from function declared to never return null + // CHECK-NEXT: nonnull.cpp:[[@LINE-3]]:16: note: returns_nonnull attribute specified here + return a; + } else { + // CHECK: nonnull.cpp:[[@LINE+2]]:5: runtime error: null pointer returned from function declared to never return null + // CHECK-NEXT: nonnull.cpp:[[@LINE-7]]:16: note: returns_nonnull attribute specified here + return a; + } +} + +int main(int argc, char **argv) { + char *a = argv[1]; + + foo(a); + + bar(20, a); + + // We expect to see a runtime error the first time we cover the "else"... + bar(5, a); + + // ... but not a second time. + // CHECK-NOT: runtime error + bar(5, a); + + return 0; +} + +// NOERROR-NOT: runtime error diff --git a/ndk-test/sanitize/ubsan/src/null.cpp b/ndk-test/sanitize/ubsan/src/null.cpp new file mode 100644 index 0000000000000000000000000000000000000000..636fab82fd935110c066a83b34c74c0104a00444 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/null.cpp @@ -0,0 +1,58 @@ +// RUN: %clangxx -fsanitize=null -fno-sanitize-recover=null %s -O3 -o %t +// RUN: not %run %t l 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD +// RUN: not %run %t s 2>&1 | FileCheck %s --check-prefix=CHECK-STORE +// RUN: not %run %t r 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE +// RUN: not %run %t m 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER +// RUN: not %run %t f 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN +// RUN: not %run %t t 2>&1 | FileCheck %s --check-prefix=CHECK-VCALL +// RUN: not %run %t u 2>&1 | FileCheck %s --check-prefix=CHECK-VCALL2 + +struct S { + int f() { return 0; } + int k; +}; + +struct T { + virtual int v() { return 1; } +}; + +struct U : T { + virtual int v() { return 2; } +}; + +int main(int, char **argv) { + int *p = 0; + S *s = 0; + T *t = 0; + U *u = 0; + + (void)*p; // ok! + (void)*t; // ok! + (void)*u; // ok! + + switch (argv[1][0]) { + case 'l': + // CHECK-LOAD: null.cpp:[[@LINE+1]]:12: runtime error: load of null pointer of type 'int' + return *p; + case 's': + // CHECK-STORE: null.cpp:[[@LINE+1]]:5: runtime error: store to null pointer of type 'int' + *p = 1; + break; + case 'r': + // CHECK-REFERENCE: null.cpp:[[@LINE+1]]:15: runtime error: reference binding to null pointer of type 'int' + {int &r = *p;} + break; + case 'm': + // CHECK-MEMBER: null.cpp:[[@LINE+1]]:15: runtime error: member access within null pointer of type 'S' + return s->k; + case 'f': + // CHECK-MEMFUN: null.cpp:[[@LINE+1]]:15: runtime error: member call on null pointer of type 'S' + return s->f(); + case 't': + // CHECK-VCALL: null.cpp:[[@LINE+1]]:15: runtime error: member call on null pointer of type 'T' + return t->v(); + case 'u': + // CHECK-VCALL2: null.cpp:[[@LINE+1]]:15: runtime error: member call on null pointer of type 'U' + return u->v(); + } +} diff --git a/ndk-test/sanitize/ubsan/src/shift.cpp b/ndk-test/sanitize/ubsan/src/shift.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50db16dac18ecc06689eebe56f11cc8a28d703ac --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/shift.cpp @@ -0,0 +1,45 @@ +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t1 && not %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t2 && not %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-LSH_OVERFLOW +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t3 && not %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t4 && not %run %t4 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='<<=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t5 && not %run %t5 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_LOW -DOP='>>=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t6 && not %run %t6 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_LOW +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t7 && not %run %t7 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t8 && not %run %t8 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='<<=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t9 && not %run %t9 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH +// RUN: %clangxx -DTOO_HIGH -DOP='>>=' -fsanitize=shift -fno-sanitize-recover=shift %s -o %t10 && not %run %t10 2>&1 | FileCheck %s --check-prefix=CHECK-TOO_HIGH + +// RUN: %clangxx -DLSH_OVERFLOW -DOP='<<' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t12 && %run %t12 +// RUN: %clangxx -DLSH_OVERFLOW -DOP='>>' -fsanitize=shift-exponent -fno-sanitize-recover=shift %s -o %t13 && %run %t13 +// RUN: %clangxx -DTOO_LOW -DOP='<<' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t14 && %run %t14 +// RUN: %clangxx -DTOO_LOW -DOP='>>' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t15 && %run %t15 +// RUN: %clangxx -DTOO_HIGH -DOP='<<' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t16 && %run %t16 +// RUN: %clangxx -DTOO_HIGH -DOP='>>' -fsanitize=shift-base -fno-sanitize-recover=shift %s -o %t17 && %run %t17 + +#include + +int main() { + int a = 1; + unsigned b = 1; + + a <<= 31; // ok in C++11, not ok in C99/C11 + b <<= 31; // ok + b <<= 1; // still ok, unsigned + +#ifdef LSH_OVERFLOW + // CHECK-LSH_OVERFLOW: shift.cpp:[[@LINE+1]]:5: runtime error: left shift of negative value -2147483648 + a OP 1; +#endif + +#ifdef TOO_LOW + a = 0; + // CHECK-TOO_LOW: shift.cpp:[[@LINE+1]]:5: runtime error: shift exponent -3 is negative + a OP (-3); +#endif + +#ifdef TOO_HIGH + a = 0; + // CHECK-TOO_HIGH: shift.cpp:[[@LINE+1]]:5: runtime error: shift exponent 32 is too large for 32-bit type 'int' + a OP 32; +#endif +} diff --git a/ndk-test/sanitize/ubsan/src/uadd-overflow.cpp b/ndk-test/sanitize/ubsan/src/uadd-overflow.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ef8983b56e0cb646799813126d7f88603ab539a --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/uadd-overflow.cpp @@ -0,0 +1,32 @@ +// RUN: %clangxx -DADD_I32 -fsanitize=unsigned-integer-overflow %s -o %t1 && %run %t1 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I32 +// RUN: %clangxx -DADD_I64 -fsanitize=unsigned-integer-overflow %s -o %t2 && %run %t2 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I64 +// RUN: %clangxx -DADD_I128 -fsanitize=unsigned-integer-overflow %s -o %t3 && %run %t3 2>&1 | FileCheck %s --check-prefix=CHECK-ADD_I128 + +#include +#include + +int main() { + // These promote to 'int'. + (void)(uint8_t(0xff) + uint8_t(0xff)); + (void)(uint16_t(0xf0fff) + uint16_t(0x0fff)); + +#ifdef ADD_I32 + uint32_t k = 0x87654321; + k += 0xedcba987; + // CHECK-ADD_I32: uadd-overflow.cpp:[[@LINE-1]]:5: runtime error: unsigned integer overflow: 2271560481 + 3989547399 cannot be represented in type 'unsigned int' +#endif + +#ifdef ADD_I64 + (void)(uint64_t(10000000000000000000ull) + uint64_t(9000000000000000000ull)); + // CHECK-ADD_I64: 10000000000000000000 + 9000000000000000000 cannot be represented in type 'unsigned {{long( long)?}}' +#endif + +#ifdef ADD_I128 +# if defined(__SIZEOF_INT128__) && !defined(_WIN32) + (void)((__uint128_t(1) << 127) + (__uint128_t(1) << 127)); +# else + puts("__int128 not supported"); +# endif + // CHECK-ADD_I128: {{0x80000000000000000000000000000000 \+ 0x80000000000000000000000000000000 cannot be represented in type 'unsigned __int128'|__int128 not supported}} +#endif +} diff --git a/ndk-test/sanitize/ubsan/src/unreachable.cpp b/ndk-test/sanitize/ubsan/src/unreachable.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1b096721ce9ca65a75489d6d7277930a9f6480ae --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/unreachable.cpp @@ -0,0 +1,25 @@ +// RUN: %clang %S/Inputs/returns-unexpectedly.c -O3 -c -o %t.ru.o +// RUN: %clangxx -fsanitize=unreachable -O3 -o %t %s %t.ru.o +// RUN: not %run %t builtin 2>&1 | FileCheck %s -check-prefix=BUILTIN +// RUN: not %run %t noreturn-callee-marked 2>&1 | FileCheck %s -check-prefix=NORETURN1 +// RUN: not %run %t noreturn-caller-marked 2>&1 | FileCheck %s -check-prefix=NORETURN2 + +#include + +void __attribute__((noreturn)) callee_marked_noreturn() { + // NORETURN1: unreachable.cpp:[[@LINE+1]]:1: runtime error: execution reached an unreachable program point +} + +extern "C" void __attribute__((noreturn)) returns_unexpectedly(); + +int main(int, char **argv) { + if (strcmp(argv[1], "builtin") == 0) + // BUILTIN: unreachable.cpp:[[@LINE+1]]:5: runtime error: execution reached an unreachable program point + __builtin_unreachable(); + else if (strcmp(argv[1], "noreturn-callee-marked") == 0) + callee_marked_noreturn(); + else if (strcmp(argv[1], "noreturn-caller-marked") == 0) + // NORETURN2: unreachable.cpp:[[@LINE+1]]:5: runtime error: execution reached an unreachable program point + returns_unexpectedly(); + return 0; +} diff --git a/ndk-test/sanitize/ubsan/src/vla.c b/ndk-test/sanitize/ubsan/src/vla.c new file mode 100644 index 0000000000000000000000000000000000000000..1939551f2ddbe01c03a08d104e5e55ddddde2b58 --- /dev/null +++ b/ndk-test/sanitize/ubsan/src/vla.c @@ -0,0 +1,11 @@ +// RUN: %clang -fsanitize=vla-bound %s -O3 -o %t +// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-MINUS-ONE +// RUN: %run %t a 2>&1 | FileCheck %s --check-prefix=CHECK-ZERO +// RUN: %run %t a b + +int main(int argc, char **argv) { + // CHECK-MINUS-ONE: vla.c:[[@LINE+2]]:11: runtime error: variable length array bound evaluates to non-positive value -1 + // CHECK-ZERO: vla.c:[[@LINE+1]]:11: runtime error: variable length array bound evaluates to non-positive value 0 + int arr[argc - 2]; + return 0; +} diff --git a/ndk-test/script/__pycache__/config.cpython-38.pyc b/ndk-test/script/__pycache__/config.cpython-38.pyc deleted file mode 100644 index b7f3d216b91b742d2e1bf4c13d45a59417857426..0000000000000000000000000000000000000000 Binary files a/ndk-test/script/__pycache__/config.cpython-38.pyc and /dev/null differ diff --git a/ndk-test/script/__pycache__/config_args.cpython-38.pyc b/ndk-test/script/__pycache__/config_args.cpython-38.pyc deleted file mode 100644 index c999666181cfa43477fe7a7e6a8e30413bcf9bbd..0000000000000000000000000000000000000000 Binary files a/ndk-test/script/__pycache__/config_args.cpython-38.pyc and /dev/null differ diff --git a/ndk-test/script/build.py b/ndk-test/script/build.py old mode 100644 new mode 100755 index b35d86fa41e0d8becb9e4aff30a2891bbf14da54..7dd58d35ae9aa910a4a7309ddd46eb898d305404 --- a/ndk-test/script/build.py +++ b/ndk-test/script/build.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 + from distutils.command.build import build import os import fnmatch @@ -7,13 +9,14 @@ import datetime compiler_list = [ 'ndk-test', - #'sanitize', + 'sanitize', + 'arm-neon', ] # Check the output directory def check_target_dir(): target_dir = os.path.join(os.getcwd(),os.path.pardir,'target') - print(target_dir) + # print(target_dir) if os.path.exists(target_dir) and os.path.isdir(target_dir): # print('target directory exist') for filename in os.listdir(target_dir): @@ -58,18 +61,24 @@ def copy_target(): for curdir, dirs, files in os.walk(file_path): for dirname in dirs: if dirname == 'output': - copy_tar_out = os.path.join(find_dir,'target',name,os.path.basename(curdir)) - # print(copy_tar_out) - if not os.path.isdir(copy_tar_out): - os.makedirs(copy_tar_out) - tar_out = os.path.join(curdir,dirname) - # print(tar_out) - print(tar_out + '----->' + copy_tar_out) - for filename in os.listdir(tar_out): - src = os.path.join(tar_out, filename) - dst = os.path.join(copy_tar_out, filename) - print(src + '----->' + dst) - shutil.copy(src, dst) + if name == 'sanitize': + src = os.path.join(file_path,dirname) + copy_tar_out = os.path.join(find_dir,'target',name) + shutil.copytree(src,copy_tar_out) + shutil.rmtree(src) + else: + copy_tar_out = os.path.join(find_dir,'target',name,os.path.basename(curdir)) + # print(copy_tar_out) + if not os.path.isdir(copy_tar_out): + os.makedirs(copy_tar_out) + tar_out = os.path.join(curdir,dirname) + # print(tar_out) + # print(tar_out + '----->' + copy_tar_out) + for filename in os.listdir(tar_out): + src = os.path.join(tar_out, filename) + dst = os.path.join(copy_tar_out, filename) + # print(src + '----->' + dst) + shutil.copy(src, dst) copy_end = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print('copy_target_end:' + copy_end) diff --git a/ndk-test/script/config_args.py b/ndk-test/script/config_args.py index ba3989af50473a7521173abf48d37b46a9d7c883..02cdf5290a50f69e8eea6f179140b4373780d9d6 100644 --- a/ndk-test/script/config_args.py +++ b/ndk-test/script/config_args.py @@ -1,6 +1,23 @@ +import shutil +import os + +cmake_path = shutil.which('cmake') + +cmake_file = os.path.join(os.path.dirname( + cmake_path), '../../../build/cmake/ohos.toolchain.cmake') + +find_toolchain_file = os.path.isfile(cmake_file) + +if(find_toolchain_file): + toolchain_file = cmake_file +else: + print(cmake_file+" not found") + # toolchain_file = "~/openharmony/out/sdk/packages/ohos-sdk/linux/native/build/cmake/ohos.toolchain.cmake" + + linux_args = [ - '-DOHOS_STL=c++_shared' , - '-DOHOS_ARCH=armeabi-v7a' , - '-DOHOS_PLATFORM=OHOS' , - '-DCMAKE_TOOLCHAIN_FILE=~/openharmony/out/sdk/packages/ohos-sdk/linux/native/build/cmake/ohos.toolchain.cmake' , -] + '-DOHOS_STL=c++_shared', + '-DOHOS_ARCH=armeabi-v7a', + '-DOHOS_PLATFORM=OHOS', + '-DCMAKE_TOOLCHAIN_FILE='+toolchain_file, +] \ No newline at end of file diff --git a/ndk-test/target/README.md b/ndk-test/target/README.md deleted file mode 100644 index 78460bc8e3d0afd38d8fe8e4d3772943ff94e7cc..0000000000000000000000000000000000000000 --- a/ndk-test/target/README.md +++ /dev/null @@ -1 +0,0 @@ -- Place the compilation to generate the object file \ No newline at end of file