1 // RUN: %clangxx -DDETERMINE_UNIQUE %s -o %t-unique
2 // RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -DSHARED_LIB -fPIC -shared -o %t-so.so
3 // RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -o %t %t-so.so
4 // RUN: %run %t 2>&1 | FileCheck %s --check-prefix=CHECK $(%run %t-unique UNIQUE)
5 // Verify that we can disable symbolization if needed:
6 // RUN: %env_ubsan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM $(%run %t-unique NOSYM-UNIQUE)
7 // XFAIL: windows-msvc
8 // Unsupported function flag
9 // UNSUPPORTED: openbsd
10 
11 #ifdef DETERMINE_UNIQUE
12 
13 #include <iostream>
14 
15 #include "../../../../../lib/sanitizer_common/sanitizer_platform.h"
16 
main(int,char ** argv)17 int main(int, char **argv) {
18   if (!SANITIZER_NON_UNIQUE_TYPEINFO)
19     std::cout << "--check-prefix=" << argv[1];
20 }
21 
22 #else
23 
24 struct Shared {};
25 using FnShared = void (*)(Shared *);
26 FnShared getShared();
27 
28 struct __attribute__((visibility("hidden"))) Hidden {};
29 using FnHidden = void (*)(Hidden *);
30 FnHidden getHidden();
31 
32 namespace {
33 struct Private {};
34 } // namespace
35 using FnPrivate = void (*)(void *);
36 FnPrivate getPrivate();
37 
38 #ifdef SHARED_LIB
39 
fnShared(Shared *)40 void fnShared(Shared *) {}
getShared()41 FnShared getShared() { return fnShared; }
42 
fnHidden(Hidden *)43 void fnHidden(Hidden *) {}
getHidden()44 FnHidden getHidden() { return fnHidden; }
45 
fnPrivate(Private *)46 void fnPrivate(Private *) {}
getPrivate()47 FnPrivate getPrivate() { return reinterpret_cast<FnPrivate>(fnPrivate); }
48 
49 #else
50 
51 #include <stdint.h>
52 
f()53 void f() {}
54 
g(int x)55 void g(int x) {}
56 
make_valid_call()57 void make_valid_call() {
58   // CHECK-NOT: runtime error: call to function g
59   reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(g))(42);
60 }
61 
make_invalid_call()62 void make_invalid_call() {
63   // CHECK: function.cpp:[[@LINE+4]]:3: runtime error: call to function f() through pointer to incorrect function type 'void (*)(int)'
64   // CHECK-NEXT: function.cpp:[[@LINE-11]]: note: f() defined here
65   // NOSYM: function.cpp:[[@LINE+2]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
66   // NOSYM-NEXT: ({{.*}}+0x{{.*}}): note: (unknown) defined here
67   reinterpret_cast<void (*)(int)>(reinterpret_cast<uintptr_t>(f))(42);
68 }
69 
f1(int)70 void f1(int) {}
f2(unsigned int)71 void f2(unsigned int) {}
f3(int)72 void f3(int) noexcept {}
f4(unsigned int)73 void f4(unsigned int) noexcept {}
74 
check_noexcept_calls()75 void check_noexcept_calls() {
76   void (*p1)(int);
77   p1 = &f1;
78   p1(0);
79   p1 = reinterpret_cast<void (*)(int)>(&f2);
80   // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f2(unsigned int) through pointer to incorrect function type 'void (*)(int)'
81   // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
82   p1(0);
83   p1 = &f3;
84   p1(0);
85   p1 = reinterpret_cast<void (*)(int)>(&f4);
86   // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f4(unsigned int) through pointer to incorrect function type 'void (*)(int)'
87   // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int)'
88   p1(0);
89 
90   void (*p2)(int) noexcept;
91   p2 = reinterpret_cast<void (*)(int) noexcept>(&f1);
92   // TODO: Unclear whether calling a non-noexcept function through a pointer to
93   // nexcept function should cause an error.
94   // CHECK-NOT: function.cpp:[[@LINE+2]]:3: runtime error: call to function f1(int) through pointer to incorrect function type 'void (*)(int) noexcept'
95   // NOSYM-NOT: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept'
96   p2(0);
97   p2 = reinterpret_cast<void (*)(int) noexcept>(&f2);
98   // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f2(unsigned int) through pointer to incorrect function type 'void (*)(int) noexcept'
99   // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept'
100   p2(0);
101   p2 = &f3;
102   p2(0);
103   p2 = reinterpret_cast<void (*)(int) noexcept>(&f4);
104   // CHECK: function.cpp:[[@LINE+2]]:3: runtime error: call to function f4(unsigned int) through pointer to incorrect function type 'void (*)(int) noexcept'
105   // NOSYM: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(int) noexcept'
106   p2(0);
107 }
108 
check_cross_dso()109 void check_cross_dso() {
110   getShared()(nullptr);
111 
112   // UNIQUE: function.cpp:[[@LINE+2]]:3: runtime error: call to function fnHidden(Hidden*) through pointer to incorrect function type 'void (*)(Hidden *)'
113   // NOSYM-UNIQUE: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)(Hidden *)'
114   getHidden()(nullptr);
115 
116   // TODO: Unlike GCC, Clang fails to prefix the typeinfo name for the function
117   // type with "*", so this erroneously only fails for "*UNIQUE":
118   // 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 *)'
119   // NOSYM-UNIQUE: function.cpp:[[@LINE+1]]:3: runtime error: call to function (unknown) through pointer to incorrect function type 'void (*)((anonymous namespace)::Private *)'
120   reinterpret_cast<void (*)(Private *)>(getPrivate())(nullptr);
121 }
122 
main(void)123 int main(void) {
124   make_valid_call();
125   make_invalid_call();
126   check_noexcept_calls();
127   check_cross_dso();
128   // Check that no more errors will be printed.
129   // CHECK-NOT: runtime error: call to function
130   // NOSYM-NOT: runtime error: call to function
131   make_invalid_call();
132 }
133 
134 #endif
135 
136 #endif
137