| // Cross-DSO diagnostics. |
| // The rules are: |
| // * If the library needs diagnostics, the main executable must request at |
| // least some diagnostics as well (to link the diagnostic runtime). |
| // * -fsanitize-trap on the caller side overrides everything. |
| // * otherwise, the callee decides between trap/recover/norecover. |
| |
| // Full-recover. |
| // RUN: %clangxx_cfi_dso_diag -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so |
| // RUN: %clangxx_cfi_dso_diag -g %s -o %t %t-so.so |
| |
| // RUN: %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \ |
| // RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER |
| |
| // RUN: %t i_v 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER |
| |
| // RUN: %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \ |
| // RUN: --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER |
| |
| // RUN: %t ic_ 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ALL-RECOVER |
| |
| // Trap on icall, no-recover on cast. |
| // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ |
| // RUN: -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so |
| // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ |
| // RUN: -g %s -o %t %t-so.so |
| |
| // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL |
| |
| // RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL |
| |
| // RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-DIAG |
| |
| // Callee: trap on icall, no-recover on cast. |
| // Caller: recover on everything. |
| // The same as in the previous case, behaviour is decided by the callee. |
| // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \ |
| // RUN: -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so |
| // RUN: %clangxx_cfi_dso_diag \ |
| // RUN: -g %s -o %t %t-so.so |
| |
| // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL |
| |
| // RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL |
| |
| // RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-DIAG |
| |
| // Caller in trapping mode, callee with full diagnostic+recover. |
| // Caller wins. |
| // cfi-nvcall is non-trapping in the main executable to link the diagnostic runtime library. |
| // RUN: %clangxx_cfi_dso_diag \ |
| // RUN: -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so |
| // RUN: %clangxx_cfi_dso -fno-sanitize-trap=cfi-nvcall \ |
| // RUN: -g %s -o %t %t-so.so |
| |
| // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL |
| |
| // RUN: %expect_crash %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL |
| |
| // RUN: %expect_crash %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \ |
| // RUN: --check-prefix=VCALL-NODIAG --check-prefix=VCALL-FATAL |
| |
| // REQUIRES: cxxabi |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| struct A { |
| virtual void f(); |
| }; |
| |
| void *create_B(); |
| |
| #ifdef SHARED_LIB |
| |
| #include "../../utils.h" |
| struct B { |
| virtual void f(); |
| }; |
| void B::f() {} |
| |
| void *create_B() { |
| create_derivers<B>(); |
| return (void *)(new B()); |
| } |
| |
| #else |
| |
| void A::f() {} |
| |
| int main(int argc, char *argv[]) { |
| assert(argc == 2); |
| assert(strlen(argv[1]) == 3); |
| |
| // ICALL-FATAL: =0= |
| // CAST-FATAL: =0= |
| // VCALL-FATAL: =0= |
| // ALL-RECOVER: =0= |
| fprintf(stderr, "=0=\n"); |
| |
| void *p; |
| if (argv[1][0] == 'i') { |
| // ICALL-DIAG: runtime error: control flow integrity check for type 'void *(int)' failed during indirect function call |
| // ICALL-DIAG-NEXT: note: create_B() defined here |
| // ICALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during indirect function call |
| p = ((void *(*)(int))create_B)(42); |
| } else { |
| p = create_B(); |
| } |
| |
| // ICALL-FATAL-NOT: =1= |
| // CAST-FATAL: =1= |
| // VCALL-FATAL: =1= |
| // ALL-RECOVER: =1= |
| fprintf(stderr, "=1=\n"); |
| |
| A *a; |
| if (argv[1][1] == 'c') { |
| // CAST-DIAG: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type |
| // CAST-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B' |
| // CAST-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during cast to unrelated type |
| a = (A*)p; |
| } else { |
| // Invisible to CFI. |
| memcpy(&a, &p, sizeof(a)); |
| } |
| |
| // ICALL-FATAL-NOT: =2= |
| // CAST-FATAL-NOT: =2= |
| // VCALL-FATAL: =2= |
| // ALL-RECOVER: =2= |
| fprintf(stderr, "=2=\n"); |
| |
| // VCALL-DIAG: runtime error: control flow integrity check for type 'A' failed during virtual call |
| // VCALL-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B' |
| // VCALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during virtual call |
| if (argv[1][2] == 'v') { |
| a->f(); // UB here |
| } |
| |
| // ICALL-FATAL-NOT: =3= |
| // CAST-FATAL-NOT: =3= |
| // VCALL-FATAL-NOT: =3= |
| // ALL-RECOVER: =3= |
| fprintf(stderr, "=3=\n"); |
| |
| } |
| #endif |