summaryrefslogtreecommitdiff
path: root/test/cfi/cross-dso/icall/diag.cpp
blob: 579ee835604a8c01fe3be399bb7561ec1ea972f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// 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 %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag -g %s -o %t %ld_flags_rpath_exe

// 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 %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag -fsanitize-trap=cfi-icall -fno-sanitize-recover=cfi-unrelated-cast \
// RUN:     -g %s -o %t %ld_flags_rpath_exe

// 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 %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso_diag \
// RUN:     -g %s -o %t %ld_flags_rpath_exe

// 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 %dynamiclib %ld_flags_rpath_so
// RUN: %clangxx_cfi_dso -fno-sanitize-trap=cfi-nvcall \
// RUN:     -g %s -o %t %ld_flags_rpath_exe

// 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