Skip to content

Commit e215996

Browse files
committed
Finish "Adapt -fsanitize=function to SANITIZER_NON_UNIQUE_TYPEINFO"
i.e., recent 5745ecc: * Bump the function_type_mismatch handler version, as its signature has changed. * The function_type_mismatch handler can return successfully now, so SanitizerKind::Function must be AlwaysRecoverable (like for SanitizerKind::Vptr). * But the minimal runtime would still unconditionally treat a call to the function_type_mismatch handler as failure, so disallow -fsanitize=function in combination with -fsanitize-minimal-runtime (like it was already done for -fsanitize=vptr). * Add tests. Differential Revision: https://reviews.llvm.org/D61479 llvm-svn: 366186
1 parent 3e10905 commit e215996

File tree

10 files changed

+113
-26
lines changed

10 files changed

+113
-26
lines changed

clang/docs/UndefinedBehaviorSanitizer.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ Minimal Runtime
205205

206206
There is a minimal UBSan runtime available suitable for use in production
207207
environments. This runtime has a small attack surface. It only provides very
208-
basic issue logging and deduplication, and does not support ``-fsanitize=vptr``
209-
checking.
208+
basic issue logging and deduplication, and does not support
209+
``-fsanitize=function`` and ``-fsanitize=vptr`` checking.
210210

211211
To use the minimal runtime, add ``-fsanitize-minimal-runtime`` to the clang
212212
command line options. For example, if you're used to compiling with

clang/lib/CodeGen/CGExpr.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -2927,7 +2927,7 @@ enum class CheckRecoverableKind {
29272927

29282928
static CheckRecoverableKind getRecoverableKind(SanitizerMask Kind) {
29292929
assert(Kind.countPopulation() == 1);
2930-
if (Kind == SanitizerKind::Vptr)
2930+
if (Kind == SanitizerKind::Function || Kind == SanitizerKind::Vptr)
29312931
return CheckRecoverableKind::AlwaysRecoverable;
29322932
else if (Kind == SanitizerKind::Return || Kind == SanitizerKind::Unreachable)
29332933
return CheckRecoverableKind::Unrecoverable;

clang/lib/CodeGen/CodeGenFunction.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ enum TypeEvaluationKind {
114114
SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0) \
115115
SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \
116116
SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \
117-
SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \
117+
SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 1) \
118118
SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0) \
119119
SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \
120120
SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \

clang/lib/Driver/SanitizerArgs.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ static const SanitizerMask NeedsUbsanRt =
3131
static const SanitizerMask NeedsUbsanCxxRt =
3232
SanitizerKind::Vptr | SanitizerKind::CFI;
3333
static const SanitizerMask NotAllowedWithTrap = SanitizerKind::Vptr;
34-
static const SanitizerMask NotAllowedWithMinimalRuntime = SanitizerKind::Vptr;
34+
static const SanitizerMask NotAllowedWithMinimalRuntime =
35+
SanitizerKind::Function | SanitizerKind::Vptr;
3536
static const SanitizerMask RequiresPIE =
3637
SanitizerKind::DataFlow | SanitizerKind::HWAddress | SanitizerKind::Scudo;
3738
static const SanitizerMask NeedsUnwindTables =

clang/test/CodeGen/ubsan-function.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -fsanitize=function -fno-sanitize-recover=all | FileCheck %s
2+
3+
// CHECK-LABEL: define void @_Z3funv() #0 prologue <{ i32, i32 }> <{ i32 846595819, i32 trunc (i64 sub (i64 ptrtoint (i8** @0 to i64), i64 ptrtoint (void ()* @_Z3funv to i64)) to i32) }> {
4+
void fun() {}
5+
6+
// CHECK-LABEL: define void @_Z6callerPFvvE(void ()* %f)
7+
// CHECK: getelementptr <{ i32, i32 }>, <{ i32, i32 }>* {{.*}}, i32 0, i32 0, !nosanitize
8+
// CHECK: load i32, i32* {{.*}}, align {{.*}}, !nosanitize
9+
// CHECK: icmp eq i32 {{.*}}, 846595819, !nosanitize
10+
// CHECK: br i1 {{.*}}, label %[[LABEL1:.*]], label %[[LABEL4:.*]], !nosanitize
11+
// CHECK: [[LABEL1]]:
12+
// CHECK: getelementptr <{ i32, i32 }>, <{ i32, i32 }>* {{.*}}, i32 0, i32 1, !nosanitize
13+
// CHECK: load i32, i32* {{.*}}, align {{.*}}, !nosanitize
14+
// CHECK: icmp eq i8* {{.*}}, bitcast ({ i8*, i8* }* @_ZTIFvvE to i8*), !nosanitize
15+
// CHECK: br i1 {{.*}}, label %[[LABEL3:.*]], label %[[LABEL2:[^,]*]], {{.*}}!nosanitize
16+
// CHECK: [[LABEL2]]:
17+
// CHECK: call void @__ubsan_handle_function_type_mismatch_v1_abort(i8* {{.*}}, i64 {{.*}}, i64 {{.*}}, i64 {{.*}}) #{{.*}}, !nosanitize
18+
// CHECK-NOT: unreachable
19+
// CHECK: br label %[[LABEL3]], !nosanitize
20+
// CHECK: [[LABEL3]]:
21+
// CHECK: br label %[[LABEL4]], !nosanitize
22+
void caller(void (*f)()) { f(); }

clang/test/Driver/fsanitize.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,12 @@
759759
// CHECK-TSAN-MINIMAL: error: invalid argument '-fsanitize-minimal-runtime' not allowed with '-fsanitize=thread'
760760

761761
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-MINIMAL
762-
// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
762+
// CHECK-UBSAN-MINIMAL: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
763763
// CHECK-UBSAN-MINIMAL: "-fsanitize-minimal-runtime"
764764

765+
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=function -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-FUNCTION-MINIMAL
766+
// CHECK-UBSAN-FUNCTION-MINIMAL: error: invalid argument '-fsanitize=function' not allowed with '-fsanitize-minimal-runtime'
767+
765768
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize=vptr -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-VPTR-MINIMAL
766769
// CHECK-UBSAN-VPTR-MINIMAL: error: invalid argument '-fsanitize=vptr' not allowed with '-fsanitize-minimal-runtime'
767770

compiler-rt/lib/ubsan/ubsan_handlers_cxx.cc

+7-8
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,17 @@ static bool handleFunctionTypeMismatch(FunctionTypeMismatchData *Data,
185185
return true;
186186
}
187187

188-
void __ubsan_handle_function_type_mismatch(FunctionTypeMismatchData *Data,
189-
ValueHandle Function,
190-
ValueHandle calleeRTTI,
191-
ValueHandle fnRTTI) {
188+
void __ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data,
189+
ValueHandle Function,
190+
ValueHandle calleeRTTI,
191+
ValueHandle fnRTTI) {
192192
GET_REPORT_OPTIONS(false);
193193
handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts);
194194
}
195195

196-
void __ubsan_handle_function_type_mismatch_abort(FunctionTypeMismatchData *Data,
197-
ValueHandle Function,
198-
ValueHandle calleeRTTI,
199-
ValueHandle fnRTTI) {
196+
void __ubsan_handle_function_type_mismatch_v1_abort(
197+
FunctionTypeMismatchData *Data, ValueHandle Function,
198+
ValueHandle calleeRTTI, ValueHandle fnRTTI) {
200199
GET_REPORT_OPTIONS(true);
201200
if (handleFunctionTypeMismatch(Data, Function, calleeRTTI, fnRTTI, Opts))
202201
Die();

compiler-rt/lib/ubsan/ubsan_handlers_cxx.h

+8-7
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@ struct FunctionTypeMismatchData {
4040
};
4141

4242
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
43-
__ubsan_handle_function_type_mismatch(FunctionTypeMismatchData *Data,
44-
ValueHandle Val, ValueHandle calleeRTTI,
45-
ValueHandle fnRTTI);
43+
__ubsan_handle_function_type_mismatch_v1(FunctionTypeMismatchData *Data,
44+
ValueHandle Val,
45+
ValueHandle calleeRTTI,
46+
ValueHandle fnRTTI);
4647
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
47-
__ubsan_handle_function_type_mismatch_abort(FunctionTypeMismatchData *Data,
48-
ValueHandle Val,
49-
ValueHandle calleeRTTI,
50-
ValueHandle fnRTTI);
48+
__ubsan_handle_function_type_mismatch_v1_abort(FunctionTypeMismatchData *Data,
49+
ValueHandle Val,
50+
ValueHandle calleeRTTI,
51+
ValueHandle fnRTTI);
5152
}
5253

5354
#endif // UBSAN_HANDLERS_H

compiler-rt/lib/ubsan/ubsan_interface.inc

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss)
2121
INTERFACE_FUNCTION(__ubsan_handle_dynamic_type_cache_miss_abort)
2222
INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow)
2323
INTERFACE_FUNCTION(__ubsan_handle_float_cast_overflow_abort)
24-
INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch)
25-
INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_abort)
24+
INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1)
25+
INTERFACE_FUNCTION(__ubsan_handle_function_type_mismatch_v1_abort)
2626
INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion)
2727
INTERFACE_FUNCTION(__ubsan_handle_implicit_conversion_abort)
2828
INTERFACE_FUNCTION(__ubsan_handle_invalid_builtin)

compiler-rt/test/ubsan/TestCases/TypeCheck/Function/function.cpp

+64-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,53 @@
1-
// RUN: %clangxx -std=c++17 -fsanitize=function %s -O3 -g -o %t
2-
// RUN: %run %t 2>&1 | FileCheck %s
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)
35
// Verify that we can disable symbolization if needed:
4-
// RUN: %env_ubsan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM
6+
// RUN: %env_ubsan_opts=symbolize=0 %run %t 2>&1 | FileCheck %s --check-prefix=NOSYM $(%run %t-unique NOSYM-UNIQUE)
57
// XFAIL: windows-msvc
68
// Unsupported function flag
79
// UNSUPPORTED: openbsd
810

11+
#ifdef DETERMINE_UNIQUE
12+
13+
#include <iostream>
14+
15+
#include "../../../../../lib/sanitizer_common/sanitizer_platform.h"
16+
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+
40+
void fnShared(Shared *) {}
41+
FnShared getShared() { return fnShared; }
42+
43+
void fnHidden(Hidden *) {}
44+
FnHidden getHidden() { return fnHidden; }
45+
46+
void fnPrivate(Private *) {}
47+
FnPrivate getPrivate() { return reinterpret_cast<FnPrivate>(fnPrivate); }
48+
49+
#else
50+
951
#include <stdint.h>
1052

1153
void f() {}
@@ -64,12 +106,31 @@ void check_noexcept_calls() {
64106
p2(0);
65107
}
66108

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+
67123
int main(void) {
68124
make_valid_call();
69125
make_invalid_call();
70126
check_noexcept_calls();
127+
check_cross_dso();
71128
// Check that no more errors will be printed.
72129
// CHECK-NOT: runtime error: call to function
73130
// NOSYM-NOT: runtime error: call to function
74131
make_invalid_call();
75132
}
133+
134+
#endif
135+
136+
#endif

0 commit comments

Comments
 (0)