forked from OSchip/llvm-project
[ubsan] Diagnose invalid uses of builtins (clang)
On some targets, passing zero to the clz() or ctz() builtins has undefined behavior. I ran into this issue while debugging UB in __hash_table from libcxx: the bug I was seeing manifested itself differently under -O0 vs -Os, due to a UB call to clz() (see: libcxx/r304617). This patch introduces a check which can detect UB calls to builtins. llvm.org/PR26979 Differential Revision: https://reviews.llvm.org/D34590 llvm-svn: 309459
This commit is contained in:
parent
4335c3992a
commit
10c3102071
|
@ -75,6 +75,7 @@ Available checks are:
|
|||
of a misaligned reference.
|
||||
- ``-fsanitize=bool``: Load of a ``bool`` value which is neither
|
||||
``true`` nor ``false``.
|
||||
- ``-fsanitize=builtin``: Passing invalid values to compiler builtins.
|
||||
- ``-fsanitize=bounds``: Out of bounds array indexing, in cases
|
||||
where the array bound can be statically determined.
|
||||
- ``-fsanitize=enum``: Load of a value of an enumerated type which
|
||||
|
|
|
@ -60,6 +60,7 @@ SANITIZER("leak", Leak)
|
|||
SANITIZER("alignment", Alignment)
|
||||
SANITIZER("array-bounds", ArrayBounds)
|
||||
SANITIZER("bool", Bool)
|
||||
SANITIZER("builtin", Builtin)
|
||||
SANITIZER("enum", Enum)
|
||||
SANITIZER("float-cast-overflow", FloatCastOverflow)
|
||||
SANITIZER("float-divide-by-zero", FloatDivideByZero)
|
||||
|
@ -107,11 +108,12 @@ SANITIZER("safe-stack", SafeStack)
|
|||
// -fsanitize=undefined includes all the sanitizers which have low overhead, no
|
||||
// ABI or address space layout implications, and only catch undefined behavior.
|
||||
SANITIZER_GROUP("undefined", Undefined,
|
||||
Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
|
||||
FloatDivideByZero | IntegerDivideByZero | NonnullAttribute |
|
||||
Null | ObjectSize | PointerOverflow | Return |
|
||||
ReturnsNonnullAttribute | Shift | SignedIntegerOverflow |
|
||||
Unreachable | VLABound | Function | Vptr)
|
||||
Alignment | Bool | Builtin | ArrayBounds | Enum |
|
||||
FloatCastOverflow | FloatDivideByZero |
|
||||
IntegerDivideByZero | NonnullAttribute | Null | ObjectSize |
|
||||
PointerOverflow | Return | ReturnsNonnullAttribute | Shift |
|
||||
SignedIntegerOverflow | Unreachable | VLABound | Function |
|
||||
Vptr)
|
||||
|
||||
// -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
|
||||
SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
|
||||
|
|
|
@ -641,6 +641,26 @@ struct CallObjCArcUse final : EHScopeStack::Cleanup {
|
|||
};
|
||||
}
|
||||
|
||||
Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
|
||||
BuiltinCheckKind Kind) {
|
||||
assert(Kind == BCK_CLZPassedZero ||
|
||||
Kind == BCK_CTZPassedZero && "Unsupported builtin check kind");
|
||||
|
||||
Value *ArgValue = EmitScalarExpr(E);
|
||||
if (!SanOpts.has(SanitizerKind::Builtin) || !getTarget().isCLZForZeroUndef())
|
||||
return ArgValue;
|
||||
|
||||
SanitizerScope SanScope(this);
|
||||
Value *Cond = Builder.CreateICmpNE(
|
||||
ArgValue, llvm::Constant::getNullValue(ArgValue->getType()));
|
||||
EmitCheck(std::make_pair(Cond, SanitizerKind::Builtin),
|
||||
SanitizerHandler::InvalidBuiltin,
|
||||
{EmitCheckSourceLocation(E->getExprLoc()),
|
||||
llvm::ConstantInt::get(Builder.getInt8Ty(), Kind)},
|
||||
None);
|
||||
return ArgValue;
|
||||
}
|
||||
|
||||
RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
||||
unsigned BuiltinID, const CallExpr *E,
|
||||
ReturnValueSlot ReturnValue) {
|
||||
|
@ -792,7 +812,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
|||
case Builtin::BI__builtin_ctz:
|
||||
case Builtin::BI__builtin_ctzl:
|
||||
case Builtin::BI__builtin_ctzll: {
|
||||
Value *ArgValue = EmitScalarExpr(E->getArg(0));
|
||||
Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CTZPassedZero);
|
||||
|
||||
llvm::Type *ArgType = ArgValue->getType();
|
||||
Value *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType);
|
||||
|
@ -809,7 +829,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
|
|||
case Builtin::BI__builtin_clz:
|
||||
case Builtin::BI__builtin_clzl:
|
||||
case Builtin::BI__builtin_clzll: {
|
||||
Value *ArgValue = EmitScalarExpr(E->getArg(0));
|
||||
Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);
|
||||
|
||||
llvm::Type *ArgType = ArgValue->getType();
|
||||
Value *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
|
||||
|
|
|
@ -111,6 +111,7 @@ enum TypeEvaluationKind {
|
|||
SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \
|
||||
SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \
|
||||
SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \
|
||||
SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \
|
||||
SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \
|
||||
SANITIZER_CHECK(MissingReturn, missing_return, 0) \
|
||||
SANITIZER_CHECK(MulOverflow, mul_overflow, 0) \
|
||||
|
@ -3613,6 +3614,17 @@ public:
|
|||
SourceLocation Loc,
|
||||
const Twine &Name = "");
|
||||
|
||||
/// Specifies which type of sanitizer check to apply when handling a
|
||||
/// particular builtin.
|
||||
enum BuiltinCheckKind {
|
||||
BCK_CTZPassedZero,
|
||||
BCK_CLZPassedZero,
|
||||
};
|
||||
|
||||
/// Emits an argument for a call to a builtin. If the builtin sanitizer is
|
||||
/// enabled, a runtime check specified by \p Kind is also emitted.
|
||||
llvm::Value *EmitCheckedArgForBuiltin(const Expr *E, BuiltinCheckKind Kind);
|
||||
|
||||
/// \brief Emit a description of a type in a format suitable for passing to
|
||||
/// a runtime sanitizer handler.
|
||||
llvm::Constant *EmitCheckTypeDescriptor(QualType T);
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -w -emit-llvm -o - %s -fsanitize=builtin | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple arm64-none-linux-gnu -w -emit-llvm -o - %s -fsanitize=builtin | FileCheck %s --check-prefix=NOT-UB
|
||||
|
||||
// NOT-UB-NOT: __ubsan_handle_invalid_builtin
|
||||
|
||||
// CHECK: define void @check_ctz
|
||||
void check_ctz(int n) {
|
||||
// CHECK: [[NOT_ZERO:%.*]] = icmp ne i32 [[N:%.*]], 0, !nosanitize
|
||||
// CHECK-NEXT: br i1 [[NOT_ZERO]]
|
||||
//
|
||||
// Handler block:
|
||||
// CHECK: call void @__ubsan_handle_invalid_builtin
|
||||
// CHECK-NEXT: unreachable
|
||||
//
|
||||
// Continuation block:
|
||||
// CHECK: call i32 @llvm.cttz.i32(i32 [[N]], i1 true)
|
||||
__builtin_ctz(n);
|
||||
|
||||
// CHECK: call void @__ubsan_handle_invalid_builtin
|
||||
__builtin_ctzl(n);
|
||||
|
||||
// CHECK: call void @__ubsan_handle_invalid_builtin
|
||||
__builtin_ctzll(n);
|
||||
}
|
||||
|
||||
// CHECK: define void @check_clz
|
||||
void check_clz(int n) {
|
||||
// CHECK: [[NOT_ZERO:%.*]] = icmp ne i32 [[N:%.*]], 0, !nosanitize
|
||||
// CHECK-NEXT: br i1 [[NOT_ZERO]]
|
||||
//
|
||||
// Handler block:
|
||||
// CHECK: call void @__ubsan_handle_invalid_builtin
|
||||
// CHECK-NEXT: unreachable
|
||||
//
|
||||
// Continuation block:
|
||||
// CHECK: call i32 @llvm.ctlz.i32(i32 [[N]], i1 true)
|
||||
__builtin_clz(n);
|
||||
|
||||
// CHECK: call void @__ubsan_handle_invalid_builtin
|
||||
__builtin_clzl(n);
|
||||
|
||||
// CHECK: call void @__ubsan_handle_invalid_builtin
|
||||
__builtin_clzll(n);
|
||||
}
|
|
@ -3,18 +3,18 @@
|
|||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize-undefined-trap-on-error -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute|function),?){18}"}}
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
|
||||
// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-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),?){19}"}}
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
|
||||
// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
|
||||
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
|
||||
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){20}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN
|
||||
// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
|
||||
// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-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),?){18}"}}
|
||||
|
||||
// RUN: %clang -target i386-unknown-openbsd -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-OPENBSD
|
||||
// CHECK-UNDEFINED-OPENBSD: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
|
||||
// CHECK-UNDEFINED-OPENBSD: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-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),?){18}"}}
|
||||
|
||||
// RUN: %clang -target i386-pc-win32 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32
|
||||
// RUN: %clang -target i386-pc-win32 -fsanitize=undefined -x c++ %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-WIN --check-prefix=CHECK-UNDEFINED-WIN32 --check-prefix=CHECK-UNDEFINED-WIN-CXX
|
||||
|
@ -23,7 +23,7 @@
|
|||
// CHECK-UNDEFINED-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone-i386.lib"
|
||||
// CHECK-UNDEFINED-WIN64: "--dependent-lib={{[^"]*}}ubsan_standalone-x86_64.lib"
|
||||
// CHECK-UNDEFINED-WIN-CXX: "--dependent-lib={{[^"]*}}ubsan_standalone_cxx{{[^"]*}}.lib"
|
||||
// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
|
||||
// CHECK-UNDEFINED-WIN-SAME: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-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),?){18}"}}
|
||||
|
||||
// RUN: %clang -target i386-pc-win32 -fsanitize-coverage=bb %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-COVERAGE-WIN32
|
||||
// CHECK-COVERAGE-WIN32: "--dependent-lib={{[^"]*}}ubsan_standalone-i386.lib"
|
||||
|
@ -42,7 +42,7 @@
|
|||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address,undefined -fno-sanitize=all -fsanitize=thread %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FNO-SANITIZE-ALL
|
||||
// CHECK-FNO-SANITIZE-ALL: "-fsanitize=thread"
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,builtin,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
|
||||
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|pointer-overflow|array-bounds|returns-nonnull-attribute|nonnull-attribute),?){15}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=shift -fno-sanitize=shift-base %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-FSANITIZE-SHIFT-PARTIAL
|
||||
|
@ -221,7 +221,7 @@
|
|||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN
|
||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=thread -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN
|
||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fsanitize-recover=all -fno-sanitize-recover=undefined -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-RECOVER-UBSAN
|
||||
// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){17}"}}
|
||||
// CHECK-RECOVER-UBSAN: "-fsanitize-recover={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift-base|shift-exponent|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){18}"}}
|
||||
// CHECK-NO-RECOVER-UBSAN-NOT: sanitize-recover
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu %s -fsanitize=undefined -fno-sanitize-recover=all -fsanitize-recover=object-size,shift-base -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-RECOVER
|
||||
|
|
Loading…
Reference in New Issue