Home | History | Annotate | Download | only in icall
      1 // Cross-DSO diagnostics.
      2 // The rules are:
      3 // * If the library needs diagnostics, the main executable must request at
      4 //   least some diagnostics as well (to link the diagnostic runtime).
      5 // * -fsanitize-trap on the caller side overrides everything.
      6 // * otherwise, the callee decides between trap/recover/norecover.
      7 
      8 // Full-recover.
      9 // RUN: %clangxx_cfi_dso_diag -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so
     10 // RUN: %clangxx_cfi_dso_diag -g %s -o %t %t-so.so
     11 
     12 // RUN: %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \
     13 // RUN:                            --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER
     14 
     15 // RUN: %t i_v 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-NODIAG \
     16 // RUN:                            --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER
     17 
     18 // RUN: %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \
     19 // RUN:                            --check-prefix=VCALL-DIAG --check-prefix=ALL-RECOVER
     20 
     21 // RUN: %t ic_ 2>&1 | FileCheck %s --check-prefix=ICALL-DIAG --check-prefix=CAST-DIAG \
     22 // RUN:                            --check-prefix=VCALL-NODIAG --check-prefix=ALL-RECOVER
     23 
     24 // Trap on icall, no-recover on cast.
     25 // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
     26 // RUN:     -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so
     27 // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
     28 // RUN:     -g %s -o %t %t-so.so
     29 
     30 // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     31 // RUN:                                          --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL
     32 
     33 // RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \
     34 // RUN:                                --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL
     35 
     36 // RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     37 // RUN:                            --check-prefix=VCALL-DIAG
     38 
     39 // Callee: trap on icall, no-recover on cast.
     40 // Caller: recover on everything.
     41 // The same as in the previous case, behaviour is decided by the callee.
     42 // RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
     43 // RUN:     -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so
     44 // RUN: %clangxx_cfi_dso_diag \
     45 // RUN:     -g %s -o %t %t-so.so
     46 
     47 // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     48 // RUN:                                          --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL
     49 
     50 // RUN: not %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-DIAG \
     51 // RUN:                                --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL
     52 
     53 // RUN: %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     54 // RUN:                            --check-prefix=VCALL-DIAG
     55 
     56 // Caller in trapping mode, callee with full diagnostic+recover.
     57 // Caller wins.
     58 // cfi-nvcall is non-trapping in the main executable to link the diagnostic runtime library.
     59 // RUN: %clangxx_cfi_dso_diag \
     60 // RUN:     -g -DSHARED_LIB %s -fPIC -shared -o %t-so.so
     61 // RUN: %clangxx_cfi_dso -fno-sanitize-trap=cfi-nvcall \
     62 // RUN:     -g %s -o %t %t-so.so
     63 
     64 // RUN: %expect_crash %t icv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     65 // RUN:                                          --check-prefix=VCALL-NODIAG --check-prefix=ICALL-FATAL
     66 
     67 // RUN: %expect_crash %t _cv 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     68 // RUN:                                          --check-prefix=VCALL-NODIAG --check-prefix=CAST-FATAL
     69 
     70 // RUN: %expect_crash %t __v 2>&1 | FileCheck %s --check-prefix=ICALL-NODIAG --check-prefix=CAST-NODIAG \
     71 // RUN:                                          --check-prefix=VCALL-NODIAG --check-prefix=VCALL-FATAL
     72 
     73 // REQUIRES: cxxabi
     74 
     75 #include <assert.h>
     76 #include <stdio.h>
     77 #include <string.h>
     78 
     79 struct A {
     80   virtual void f();
     81 };
     82 
     83 void *create_B();
     84 
     85 #ifdef SHARED_LIB
     86 
     87 #include "../../utils.h"
     88 struct B {
     89   virtual void f();
     90 };
     91 void B::f() {}
     92 
     93 void *create_B() {
     94   create_derivers<B>();
     95   return (void *)(new B());
     96 }
     97 
     98 #else
     99 
    100 void A::f() {}
    101 
    102 int main(int argc, char *argv[]) {
    103   assert(argc == 2);
    104   assert(strlen(argv[1]) == 3);
    105 
    106   // ICALL-FATAL: =0=
    107   // CAST-FATAL:  =0=
    108   // VCALL-FATAL: =0=
    109   // ALL-RECOVER: =0=
    110   fprintf(stderr, "=0=\n");
    111 
    112   void *p;
    113   if (argv[1][0] == 'i') {
    114     // ICALL-DIAG: runtime error: control flow integrity check for type 'void *(int)' failed during indirect function call
    115     // ICALL-DIAG-NEXT: note: create_B() defined here
    116     // ICALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during indirect function call
    117     p = ((void *(*)(int))create_B)(42);
    118   } else {
    119     p = create_B();
    120   }
    121 
    122   // ICALL-FATAL-NOT: =1=
    123   // CAST-FATAL:      =1=
    124   // VCALL-FATAL:     =1=
    125   // ALL-RECOVER:     =1=
    126   fprintf(stderr, "=1=\n");
    127 
    128   A *a;
    129   if (argv[1][1] == 'c') {
    130     // CAST-DIAG: runtime error: control flow integrity check for type 'A' failed during cast to unrelated type
    131     // CAST-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B'
    132     // CAST-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during cast to unrelated type
    133     a = (A*)p;
    134   } else {
    135     // Invisible to CFI.
    136     memcpy(&a, &p, sizeof(a));
    137   }
    138 
    139   // ICALL-FATAL-NOT: =2=
    140   // CAST-FATAL-NOT:  =2=
    141   // VCALL-FATAL:     =2=
    142   // ALL-RECOVER:     =2=
    143   fprintf(stderr, "=2=\n");
    144 
    145   // VCALL-DIAG: runtime error: control flow integrity check for type 'A' failed during virtual call
    146   // VCALL-DIAG-NEXT: note: vtable is of type '{{(struct )?}}B'
    147   // VCALL-NODIAG-NOT: runtime error: control flow integrity check {{.*}} during virtual call
    148   if (argv[1][2] == 'v') {
    149     a->f(); // UB here
    150   }
    151 
    152   // ICALL-FATAL-NOT: =3=
    153   // CAST-FATAL-NOT:  =3=
    154   // VCALL-FATAL-NOT: =3=
    155   // ALL-RECOVER: =3=
    156   fprintf(stderr, "=3=\n");
    157 
    158 }
    159 #endif
    160