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
| // RUN: %clangxx -DDETERMINE_UNIQUE %s -o %t-unique
// RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -DSHARED_LIB -fPIC -shared -o %t-so.so
// RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -o %t %t-so.so
// RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK $(%run %t-unique UNIQUE)
// Verify that we can disable symbolization if needed:
// RUN: %env_ubsan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM $(%run %t-unique NOSYM-UNIQUE)
// XFAIL: windows-msvc
// Unsupported function flag
// UNSUPPORTED: openbsd
#ifdef DETERMINE_UNIQUE
#include <iostream>
#include "../../../../../lib/sanitizer_common/sanitizer_platform.h"
int main(int, char **argv) {
if (!SANITIZER_NON_UNIQUE_TYPEINFO)
std::cout << "--check-prefix=" << argv[1];
}
#else
struct Shared {};
using FnShared = void (*)(Shared *);
FnShared getShared();
struct __attribute__((visibility("hidden"))) Hidden {};
using FnHidden = void (*)(Hidden *);
FnHidden getHidden();
namespace {
struct Private {};
} // namespace
using FnPrivate = void (*)(void *);
FnPrivate getPrivate();
#ifdef SHARED_LIB
void fnShared(Shared *) {}
FnShared getShared() { return fnShared; }
void fnHidden(Hidden *) {}
FnHidden getHidden() { return fnHidden; }
void fnPrivate(Private *) {}
FnPrivate getPrivate() { return reinterpret_cast<FnPrivate>(fnPrivate); }
#else
#include <stdint.h>
void f() {}
void g(int x) {}
void make_valid_call() {
// CHECK-NOT: runtime error: call to function g
reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42);
}
void make_invalid_call() {
// CHECK: function.cpp:[[@LINE+4]]:3: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)'
// CHECK-NEXT: function.cpp:[[@LINE-11]]: note: f() defined here
// NOSYM: function.cpp:[[@LINE+2]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
// NOSYM-NEXT: ({{.*}}+0x{{.*}}): note: (unknown) defined here
reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42);
}
void f1(int) {}
void f2(unsigned int) {}
void f3(int) noexcept {}
void f4(unsigned int) noexcept {}
void check_noexcept_calls() {
void (*p1)(int);
p1 = &f1;
p1(0);
p1 = reinterpret_cast<void (*)(int)>(&f2);
// CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f2(unsigned int) through pointer to incorrect function type 'void (*)(int)'
// NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
p1(0);
p1 = &f3;
p1(0);
p1 = reinterpret_cast<void (*)(int)>(&f4);
// CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f4(unsigned int) through pointer to incorrect function type 'void (*)(int)'
// NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
p1(0);
void (*p2)(int) noexcept;
p2 = reinterpret_cast<void (*)(int) noexcept>(&f1);
// TODO: Unclear whether calling a non-noexcept function through a pointer to
// nexcept function should cause an error.
// CHECK-NOT: function.cpp:[[@LINE+2]]:3: runtime error: call to function f1(int) through pointer to incorrect function type 'void (*)(int) noexcept'
// NOSYM-NOT: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept'
p2(0);
p2 = reinterpret_cast<void (*)(int) noexcept>(&f2);
// CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f2(unsigned int) through pointer to incorrect function type 'void (*)(int) noexcept'
// NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept'
p2(0);
p2 = &f3;
p2(0);
p2 = reinterpret_cast<void (*)(int) noexcept>(&f4);
// CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f4(unsigned int) through pointer to incorrect function type 'void (*)(int) noexcept'
// NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept'
p2(0);
}
void check_cross_dso() {
getShared()(nullptr);
// UNIQUE: function.cpp:[[@LINE+2]]:3: runtime error: call to function fnHidden(Hidden*) through pointer to incorrect function type 'void (*)(Hidden *)'
// NOSYM-UNIQUE: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(Hidden *)'
getHidden()(nullptr);
// TODO: Unlike GCC, Clang fails to prefix the typeinfo name for the function
// type with "*", so this erroneously only fails for "*UNIQUE":
// UNIQUE: function.cpp:[[@LINE+2]]:3: runtime error: call to function fnPrivate((anonymous namespace)::Private*) through pointer to incorrect function type 'void (*)((anonymous namespace)::Private *)'
// NOSYM-UNIQUE: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)((anonymous namespace)::Private *)'
reinterpret_cast<void (*)(Private *)>(getPrivate())(nullptr);
}
int main(void) {
make_valid_call();
make_invalid_call();
check_noexcept_calls();
check_cross_dso();
// Check that no more errors will be printed.
// CHECK-NOT: runtime error: call to function
// NOSYM-NOT: runtime error: call to function
make_invalid_call();
}
#endif
#endif
|