summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2012-12-13 07:11:50 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2012-12-13 07:11:50 +0000
commit463b48ba7eab6d7c96d23b59caea7f25de548293 (patch)
tree6b55f8068dd1c79805b0ecfcaadec1b26c8c0946
parentcdc2e82c317163693c99a8cbef76685e86420395 (diff)
ubsan: Add -fsanitize=bool and -fsanitize=enum, which check for loads of
bit-patterns which are not valid values for enumerated or boolean types. These checks are the ubsan analogue of !range metadata. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@170108 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--docs/UsersManual.rst5
-rw-r--r--include/clang/Basic/Sanitizers.def9
-rw-r--r--lib/CodeGen/CGExpr.cpp49
-rw-r--r--test/CodeGen/catch-undef-behavior.c10
-rw-r--r--test/CodeGenCXX/catch-undef-behavior.cpp35
-rw-r--r--test/Driver/fsanitize.c6
6 files changed, 94 insertions, 20 deletions
diff --git a/docs/UsersManual.rst b/docs/UsersManual.rst
index 6284255f87..22a42dc056 100644
--- a/docs/UsersManual.rst
+++ b/docs/UsersManual.rst
@@ -834,8 +834,13 @@ are listed below.
- ``-fsanitize=alignment``: Use of a misaligned pointer or creation
of a misaligned reference.
+ - ``-fsanitize=bool``: Load of a ``bool`` value which is neither
+ ``true`` nor ``false``.
- ``-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
+ is not in the range of representable values for that enumerated
+ type.
- ``-fsanitize=float-cast-overflow``: Conversion to, from, or
between floating-point types which would overflow the
destination.
diff --git a/include/clang/Basic/Sanitizers.def b/include/clang/Basic/Sanitizers.def
index 57ca6eb185..3e02b3ac14 100644
--- a/include/clang/Basic/Sanitizers.def
+++ b/include/clang/Basic/Sanitizers.def
@@ -56,7 +56,9 @@ SANITIZER("thread", Thread)
// UndefinedBehaviorSanitizer
SANITIZER("alignment", Alignment)
+SANITIZER("bool", Bool)
SANITIZER("bounds", Bounds)
+SANITIZER("enum", Enum)
SANITIZER("float-cast-overflow", FloatCastOverflow)
SANITIZER("float-divide-by-zero", FloatDivideByZero)
SANITIZER("integer-divide-by-zero", IntegerDivideByZero)
@@ -76,9 +78,10 @@ SANITIZER("unsigned-integer-overflow", UnsignedIntegerOverflow)
// include all the sanitizers which have low overhead, no ABI or address space
// layout implications, and only catch undefined behavior.
SANITIZER_GROUP("undefined", Undefined,
- Alignment | Bounds | FloatCastOverflow | FloatDivideByZero |
- IntegerDivideByZero | Null | ObjectSize | Return | Shift |
- SignedIntegerOverflow | Unreachable | VLABound | Vptr)
+ Alignment | Bool | Bounds | Enum | FloatCastOverflow |
+ FloatDivideByZero | IntegerDivideByZero | Null | ObjectSize |
+ Return | Shift | SignedIntegerOverflow | Unreachable |
+ VLABound | Vptr)
SANITIZER_GROUP("integer", Integer,
SignedIntegerOverflow | UnsignedIntegerOverflow | Shift |
diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp
index 0f81c0b878..d74ae88990 100644
--- a/lib/CodeGen/CGExpr.cpp
+++ b/lib/CodeGen/CGExpr.cpp
@@ -924,23 +924,22 @@ static bool hasBooleanRepresentation(QualType Ty) {
return false;
}
-llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
+static bool getRangeForType(CodeGenFunction &CGF, QualType Ty,
+ llvm::APInt &Min, llvm::APInt &End,
+ bool StrictEnums) {
const EnumType *ET = Ty->getAs<EnumType>();
- bool IsRegularCPlusPlusEnum = (getLangOpts().CPlusPlus && ET &&
- CGM.getCodeGenOpts().StrictEnums &&
- !ET->getDecl()->isFixed());
+ bool IsRegularCPlusPlusEnum = CGF.getLangOpts().CPlusPlus && StrictEnums &&
+ ET && !ET->getDecl()->isFixed();
bool IsBool = hasBooleanRepresentation(Ty);
if (!IsBool && !IsRegularCPlusPlusEnum)
- return NULL;
+ return false;
- llvm::APInt Min;
- llvm::APInt End;
if (IsBool) {
- Min = llvm::APInt(getContext().getTypeSize(Ty), 0);
- End = llvm::APInt(getContext().getTypeSize(Ty), 2);
+ Min = llvm::APInt(CGF.getContext().getTypeSize(Ty), 0);
+ End = llvm::APInt(CGF.getContext().getTypeSize(Ty), 2);
} else {
const EnumDecl *ED = ET->getDecl();
- llvm::Type *LTy = ConvertTypeForMem(ED->getIntegerType());
+ llvm::Type *LTy = CGF.ConvertTypeForMem(ED->getIntegerType());
unsigned Bitwidth = LTy->getScalarSizeInBits();
unsigned NumNegativeBits = ED->getNumNegativeBits();
unsigned NumPositiveBits = ED->getNumPositiveBits();
@@ -956,6 +955,14 @@ llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
Min = llvm::APInt(Bitwidth, 0);
}
}
+ return true;
+}
+
+llvm::MDNode *CodeGenFunction::getRangeForLoadFromType(QualType Ty) {
+ llvm::APInt Min, End;
+ if (!getRangeForType(*this, Ty, Min, End,
+ CGM.getCodeGenOpts().StrictEnums))
+ return 0;
llvm::MDBuilder MDHelper(getLLVMContext());
return MDHelper.createRange(Min, End);
@@ -1014,7 +1021,27 @@ llvm::Value *CodeGenFunction::EmitLoadOfScalar(llvm::Value *Addr, bool Volatile,
if (Ty->isAtomicType())
Load->setAtomic(llvm::SequentiallyConsistent);
- if (CGM.getCodeGenOpts().OptimizationLevel > 0)
+ if ((getLangOpts().SanitizeBool && hasBooleanRepresentation(Ty)) ||
+ (getLangOpts().SanitizeEnum && Ty->getAs<EnumType>())) {
+ llvm::APInt Min, End;
+ if (getRangeForType(*this, Ty, Min, End, true)) {
+ --End;
+ llvm::Value *Check;
+ if (!Min)
+ Check = Builder.CreateICmpULE(
+ Load, llvm::ConstantInt::get(getLLVMContext(), End));
+ else {
+ llvm::Value *Upper = Builder.CreateICmpSLE(
+ Load, llvm::ConstantInt::get(getLLVMContext(), End));
+ llvm::Value *Lower = Builder.CreateICmpSGE(
+ Load, llvm::ConstantInt::get(getLLVMContext(), Min));
+ Check = Builder.CreateAnd(Upper, Lower);
+ }
+ // FIXME: Provide a SourceLocation.
+ EmitCheck(Check, "load_invalid_value", EmitCheckTypeDescriptor(Ty),
+ EmitCheckValue(Load), CRK_Recoverable);
+ }
+ } else if (CGM.getCodeGenOpts().OptimizationLevel > 0)
if (llvm::MDNode *RangeInfo = getRangeForLoadFromType(Ty))
Load->setMetadata(llvm::LLVMContext::MD_range, RangeInfo);
diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c
index 5af957c989..088e3a9e0f 100644
--- a/test/CodeGen/catch-undef-behavior.c
+++ b/test/CodeGen/catch-undef-behavior.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
// RUN: %clang_cc1 -fsanitize=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL
// RUN: %clang_cc1 -fsanitize=signed-integer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-OVERFLOW
@@ -246,3 +246,11 @@ int int_divide_overflow(int a, int b) {
// CHECK: }
// CHECK-OVERFLOW: }
}
+
+// CHECK: @sour_bool
+_Bool sour_bool(_Bool *p) {
+ // CHECK: %[[OK:.*]] = icmp ule i8 {{.*}}, 1
+ // CHECK: br i1 %[[OK]]
+ // CHECK: call void @__ubsan_handle_load_invalid_value_abort(i8* bitcast ({{.*}}), i64 {{.*}})
+ return *p;
+}
diff --git a/test/CodeGenCXX/catch-undef-behavior.cpp b/test/CodeGenCXX/catch-undef-behavior.cpp
index 86a306daf5..a4e13bfa26 100644
--- a/test/CodeGenCXX/catch-undef-behavior.cpp
+++ b/test/CodeGenCXX/catch-undef-behavior.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
+// RUN: %clang_cc1 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
// CHECK: @_Z17reference_binding
void reference_binding(int *p) {
@@ -136,3 +136,36 @@ int no_return() {
// CHECK: call void @__ubsan_handle_missing_return(i8* bitcast ({{.*}}* @{{.*}} to i8*)) noreturn nounwind
// CHECK-NEXT: unreachable
}
+
+// CHECK: @_Z9sour_bool
+bool sour_bool(bool *p) {
+ // CHECK: %[[OK:.*]] = icmp ule i8 {{.*}}, 1
+ // CHECK: br i1 %[[OK]]
+ // CHECK: call void @__ubsan_handle_load_invalid_value_abort(i8* bitcast ({{.*}}), i64 {{.*}})
+ return *p;
+}
+
+enum E1 { e1a = 0, e1b = 127 } e1;
+enum E2 { e2a = -1, e2b = 64 } e2;
+enum E3 { e3a = (1u << 31) - 1 } e3;
+
+// CHECK: @_Z14bad_enum_value
+int bad_enum_value() {
+ // CHECK: %[[E1:.*]] = icmp ule i32 {{.*}}, 127
+ // CHECK: br i1 %[[E1]]
+ // CHECK: call void @__ubsan_handle_load_invalid_value_abort(
+ int a = e1;
+
+ // CHECK: %[[E2HI:.*]] = icmp sle i32 {{.*}}, 127
+ // CHECK: %[[E2LO:.*]] = icmp sge i32 {{.*}}, -128
+ // CHECK: %[[E2:.*]] = and i1 %[[E2HI]], %[[E2LO]]
+ // CHECK: br i1 %[[E2]]
+ // CHECK: call void @__ubsan_handle_load_invalid_value_abort(
+ int b = e2;
+
+ // CHECK: %[[E3:.*]] = icmp ule i32 {{.*}}, 2147483647
+ // CHECK: br i1 %[[E3]]
+ // CHECK: call void @__ubsan_handle_load_invalid_value_abort(
+ int c = e3;
+ return a + b + c;
+}
diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c
index 80c1768afd..06a688f940 100644
--- a/test/Driver/fsanitize.c
+++ b/test/Driver/fsanitize.c
@@ -1,13 +1,11 @@
// RUN: %clang -target x86_64-linux-gnu -fcatch-undefined-behavior %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
// 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|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds|enum|bool),?){15}"}}
-// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
-// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds),?){13}"}}
-//
// RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER
// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift),?){4}"}}
-// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr,bool,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|shift|unreachable|return|vla-bound|alignment|null|object-size|bounds),?){11}"}}
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address-full %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-FULL