From 463b48ba7eab6d7c96d23b59caea7f25de548293 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 13 Dec 2012 07:11:50 +0000 Subject: 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 --- docs/UsersManual.rst | 5 ++++ include/clang/Basic/Sanitizers.def | 9 ++++-- lib/CodeGen/CGExpr.cpp | 49 +++++++++++++++++++++++++------- test/CodeGen/catch-undef-behavior.c | 10 ++++++- test/CodeGenCXX/catch-undef-behavior.cpp | 35 ++++++++++++++++++++++- test/Driver/fsanitize.c | 6 ++-- 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(); - 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())) { + 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 -- cgit v1.2.3