Hi all, Recently I thought it would be useful to have a sanitizer for detecting overflows in pointer expressions. Such overflows are undefined behavior and are pretty much always bugs. While it's true that if such an overflowed pointer is dereferenced a tool such as ASan will catch the error, detection of these bugs when the occur helps fix them without requiring an input that triggers a crash.
Two examples of this in the wild: * binutils undefined behavior bug that leads to segfault when built with clang[1] * ASTVector bug I just submitted patch for, discovered using this sanitizer[2] Attached are patches for clang and compiler-rt that implement this sanitizer and seem to work well in my testing so far. There is some work to do yet: * Adding lit tests to clang/compiler-rt * Finalizing what constructs are useful/worth checking (iterative process, I imagine) * More testing/benchmarking Before tackling the above, I was hoping to get some early feedback: * Is this something the community is interested in/would find useful? * Code review (the current implementation should be complete in terms of the checking code itself) Thank you for your time, here's to finding even more bugs! :) ~Will [1] http://wdtz.org/undefined-behavior-in-binutils-causes-segfault.html [2] http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20131028/091878.html
From bf46609c78ccdd27253dcaeee39ffcac7a156456 Mon Sep 17 00:00:00 2001 From: Will Dietz <[email protected]> Date: Sun, 27 Oct 2013 20:19:55 -0500 Subject: [PATCH] Add handler for pointer overflow sanitizer. --- lib/ubsan/ubsan_handlers.cc | 19 +++++++++++++++++++ lib/ubsan/ubsan_handlers.h | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/lib/ubsan/ubsan_handlers.cc b/lib/ubsan/ubsan_handlers.cc index d556431..fbaa8c2 100644 --- a/lib/ubsan/ubsan_handlers.cc +++ b/lib/ubsan/ubsan_handlers.cc @@ -279,3 +279,22 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort( __ubsan_handle_function_type_mismatch(Data, Function); Die(); } + +void __ubsan::__ubsan_handle_pointer_overflow(PointerOverflowData *Data, + ValueHandle Base, + ValueHandle Result) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, + "pointer index expression with base %0 overflowed to %1") + << (void *)Base << (void*)Result; +} + +void __ubsan::__ubsan_handle_pointer_overflow_abort(PointerOverflowData *Data, + ValueHandle Base, + ValueHandle Result) { + __ubsan_handle_pointer_overflow(Data, Base, Result); + Die(); +} diff --git a/lib/ubsan/ubsan_handlers.h b/lib/ubsan/ubsan_handlers.h index 14e6f04..d56bfa7 100644 --- a/lib/ubsan/ubsan_handlers.h +++ b/lib/ubsan/ubsan_handlers.h @@ -121,6 +121,12 @@ RECOVERABLE(function_type_mismatch, FunctionTypeMismatchData *Data, ValueHandle Val) +struct PointerOverflowData { + SourceLocation Loc; +}; + +RECOVERABLE(pointer_overflow, PointerOverflowData *Data, ValueHandle Base, + ValueHandle Result) } #endif // UBSAN_HANDLERS_H -- 1.8.4.1
From 2cd6f1c679864ffc88a8b258d23ef1c5836cbce1 Mon Sep 17 00:00:00 2001 From: Will Dietz <[email protected]> Date: Sun, 27 Oct 2013 14:53:17 -0500 Subject: [PATCH] Add 'pointer-overflow' sanitizer for undefined overflow in GEP's. Handles things like: "char *p = NULL; --p;". --- include/clang/Basic/Sanitizers.def | 9 ++-- lib/CodeGen/CGExpr.cpp | 93 +++++++++++++++++++++++++++++++++++- lib/CodeGen/CGExprAgg.cpp | 7 +++ lib/CodeGen/CGExprCXX.cpp | 3 + lib/CodeGen/CGExprScalar.cpp | 21 ++++++-- lib/CodeGen/CodeGenFunction.h | 3 + test/Driver/fsanitize.c | 6 +- 7 files changed, 128 insertions(+), 14 deletions(-) diff --git a/include/clang/Basic/Sanitizers.def b/include/clang/Basic/Sanitizers.def index c9b31a3..188e62f 100644 --- a/include/clang/Basic/Sanitizers.def +++ b/include/clang/Basic/Sanitizers.def @@ -68,6 +68,7 @@ SANITIZER("function", Function) SANITIZER("integer-divide-by-zero", IntegerDivideByZero) SANITIZER("null", Null) SANITIZER("object-size", ObjectSize) +SANITIZER("pointer-overflow", PointerOverflow) SANITIZER("return", Return) SANITIZER("shift", Shift) SANITIZER("signed-integer-overflow", SignedIntegerOverflow) @@ -86,8 +87,8 @@ SANITIZER("dataflow", DataFlow) SANITIZER_GROUP("undefined", Undefined, Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow | FloatDivideByZero | Function | IntegerDivideByZero | Null | - ObjectSize | Return | Shift | SignedIntegerOverflow | - Unreachable | VLABound | Vptr) + ObjectSize | PointerOverflow | Return | Shift | + SignedIntegerOverflow | Unreachable | VLABound | Vptr) // -fsanitize=undefined-trap (and its alias -fcatch-undefined-behavior) includes // all sanitizers included by -fsanitize=undefined, except those that require @@ -96,8 +97,8 @@ SANITIZER_GROUP("undefined", Undefined, SANITIZER_GROUP("undefined-trap", UndefinedTrap, Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow | FloatDivideByZero | IntegerDivideByZero | Null | ObjectSize | - Return | Shift | SignedIntegerOverflow | Unreachable | - VLABound) + PointerOverflow | Return | Shift | SignedIntegerOverflow | + Unreachable | VLABound) SANITIZER_GROUP("integer", Integer, SignedIntegerOverflow | UnsignedIntegerOverflow | Shift | diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 221d132..dcf1d61 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -28,6 +28,7 @@ #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/GetElementPtrTypeIterator.h" using namespace clang; using namespace CodeGen; @@ -663,6 +664,89 @@ void CodeGenFunction::EmitBoundsCheck(const Expr *E, const Expr *Base, EmitCheck(Check, "out_of_bounds", StaticData, Index, CRK_Recoverable); } +void CodeGenFunction::EmitInBoundsGEPOverflowCheck(llvm::Value *GEPExpr, + SourceLocation Loc) { + llvm::GEPOperator *GEP = cast<llvm::GEPOperator>(GEPExpr); + if (!SanOpts->PointerOverflow) + return; + + assert(GEP->isInBounds()); + + const llvm::DataLayout &DL = CGM.getDataLayout(); + llvm::Type *IntPtrTy = DL.getIntPtrType(GEP->getPointerOperandType()); + + llvm::Function *SAddIntrinsic = + CGM.getIntrinsic(llvm::Intrinsic::sadd_with_overflow, IntPtrTy); + llvm::Function *SMulIntrinsic = + CGM.getIntrinsic(llvm::Intrinsic::smul_with_overflow, IntPtrTy); + + llvm::Value *TotalOffset = NULL; + llvm::Value *Valid = Builder.getTrue(); + + // Emit check operations for each GEP operand, building up total signed offset + llvm::gep_type_iterator GTI = llvm::gep_type_begin(GEP); + for (llvm::GetElementPtrInst::op_iterator OI = llvm::next(GEP->op_begin()), + OE = GEP->op_end(); + OI != OE; ++OI) { + llvm::Value *Index = *OI; + llvm::Value *LocalOffset; + if (llvm::StructType *STy = dyn_cast<llvm::StructType>(*GTI++)) { + unsigned FieldNo = cast<llvm::ConstantInt>(Index)->getZExtValue(); + LocalOffset = llvm::ConstantInt::get( + IntPtrTy, DL.getStructLayout(STy)->getElementOffset(FieldNo)); + } else { + // Otherwise emit a multiply to compute to offset + llvm::Constant *ElementSize = + llvm::ConstantInt::get(IntPtrTy, DL.getTypeAllocSize(*GTI)); + llvm::Value *IndexS = Builder.CreateIntCast(Index, IntPtrTy, true); + llvm::Value *ResultAndOverflow = + Builder.CreateCall2(SMulIntrinsic, ElementSize, IndexS); + LocalOffset = Builder.CreateExtractValue(ResultAndOverflow, 0); + Valid = Builder.CreateAnd( + Valid, + Builder.CreateNot(Builder.CreateExtractValue(ResultAndOverflow, 1))); + } + + // If this is first offset, use it as total offset. + if (!TotalOffset) { + TotalOffset = LocalOffset; + continue; + } + + // Otherwise add LocalOffset to running total in TotalOffset + llvm::Value *TotalAndOverflow = + Builder.CreateCall2(SAddIntrinsic, TotalOffset, LocalOffset); + TotalOffset = Builder.CreateExtractValue(TotalAndOverflow, 0); + Valid = Builder.CreateAnd( + Valid, + Builder.CreateNot(Builder.CreateExtractValue(TotalAndOverflow, 1))); + } + + // Once computed the (signed) offset as specified by all + // the index operands, add it to the unsigned base pointer. + llvm::Value *Positive = Builder.CreateICmpSGE( + TotalOffset, llvm::ConstantInt::getNullValue(IntPtrTy)); + llvm::Value *PtrInt = + Builder.CreatePtrToInt(GEP->getPointerOperand(), IntPtrTy); + + // Compute result, with wrapping semantics + llvm::Value *Result = Builder.CreateAdd(PtrInt, TotalOffset); + llvm::Value *PosValid = Builder.CreateICmpUGE(Result, PtrInt); + llvm::Value *NegValid = Builder.CreateICmpULT(Result, PtrInt); + + Valid = Builder.CreateAnd(Valid, + Builder.CreateSelect(Positive, PosValid, NegValid)); + + llvm::Constant *StaticArgs[] = { + EmitCheckSourceLocation(Loc) + }; + llvm::Value *DynamicArgs[] = { + EmitCheckValue(GEP->getPointerOperand()), + EmitCheckValue(GEP) + }; + EmitCheck(Valid, "pointer_overflow", StaticArgs, + DynamicArgs, CRK_Recoverable); +} CodeGenFunction::ComplexPairTy CodeGenFunction:: EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, @@ -2276,6 +2360,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, } else { Idx = Builder.CreateNSWMul(Idx, numElements); Address = Builder.CreateInBoundsGEP(Address, Idx, "arrayidx"); + EmitInBoundsGEPOverflowCheck(Address, E->getExprLoc()); } } else if (const ObjCObjectType *OIT = E->getType()->getAs<ObjCObjectType>()){ // Indexing over an interface, as in "NSString *P; P[4];" @@ -2313,15 +2398,19 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E, if (getLangOpts().isSignedOverflowDefined()) Address = Builder.CreateGEP(ArrayPtr, Args, "arrayidx"); - else + else { Address = Builder.CreateInBoundsGEP(ArrayPtr, Args, "arrayidx"); + EmitInBoundsGEPOverflowCheck(Address, E->getExprLoc()); + } } else { // The base must be a pointer, which is not an aggregate. Emit it. llvm::Value *Base = EmitScalarExpr(E->getBase()); if (getLangOpts().isSignedOverflowDefined()) Address = Builder.CreateGEP(Base, Idx, "arrayidx"); - else + else { Address = Builder.CreateInBoundsGEP(Base, Idx, "arrayidx"); + EmitInBoundsGEPOverflowCheck(Address, E->getExprLoc()); + } } QualType T = E->getBase()->getType()->getPointeeType(); diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index 9d0f3a9..ebb605e 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -343,6 +343,7 @@ AggExprEmitter::VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) { llvm::Value *IdxStart[] = { Zero, Zero }; llvm::Value *ArrayStart = Builder.CreateInBoundsGEP(ArrayPtr, IdxStart, "arraystart"); + CGF.EmitInBoundsGEPOverflowCheck(ArrayStart, E->getExprLoc()); CGF.EmitStoreThroughLValue(RValue::get(ArrayStart), Start); ++Field; @@ -360,6 +361,7 @@ AggExprEmitter::VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) { llvm::Value *IdxEnd[] = { Zero, Size }; llvm::Value *ArrayEnd = Builder.CreateInBoundsGEP(ArrayPtr, IdxEnd, "arrayend"); + CGF.EmitInBoundsGEPOverflowCheck(ArrayEnd, E->getExprLoc()); CGF.EmitStoreThroughLValue(RValue::get(ArrayEnd), EndOrLength); } else if (Ctx.hasSameType(Field->getType(), Ctx.getSizeType())) { // Length. @@ -384,6 +386,7 @@ void AggExprEmitter::EmitArrayInit(llvm::Value *DestPtr, llvm::ArrayType *AType, llvm::Value *indices[] = { zero, zero }; llvm::Value *begin = Builder.CreateInBoundsGEP(DestPtr, indices, "arrayinit.begin"); + CGF.EmitInBoundsGEPOverflowCheck(begin, E->getExprLoc()); // Exception safety requires us to destroy all the // already-constructed members if an initializer throws. @@ -423,6 +426,7 @@ void AggExprEmitter::EmitArrayInit(llvm::Value *DestPtr, llvm::ArrayType *AType, // Advance to the next element. if (i > 0) { element = Builder.CreateInBoundsGEP(element, one, "arrayinit.element"); + CGF.EmitInBoundsGEPOverflowCheck(element, E->getExprLoc()); // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially @@ -457,6 +461,7 @@ void AggExprEmitter::EmitArrayInit(llvm::Value *DestPtr, llvm::ArrayType *AType, // Advance to the start of the rest of the array. if (NumInitElements) { element = Builder.CreateInBoundsGEP(element, one, "arrayinit.start"); + CGF.EmitInBoundsGEPOverflowCheck(element, E->getExprLoc()); if (endOfInit) Builder.CreateStore(element, endOfInit); } @@ -464,6 +469,7 @@ void AggExprEmitter::EmitArrayInit(llvm::Value *DestPtr, llvm::ArrayType *AType, llvm::Value *end = Builder.CreateInBoundsGEP(begin, llvm::ConstantInt::get(CGF.SizeTy, NumArrayElements), "arrayinit.end"); + CGF.EmitInBoundsGEPOverflowCheck(end, E->getExprLoc()); llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); llvm::BasicBlock *bodyBB = CGF.createBasicBlock("arrayinit.body"); @@ -484,6 +490,7 @@ void AggExprEmitter::EmitArrayInit(llvm::Value *DestPtr, llvm::ArrayType *AType, // Move on to the next element. llvm::Value *nextElement = Builder.CreateInBoundsGEP(currentElement, one, "arrayinit.next"); + CGF.EmitInBoundsGEPOverflowCheck(nextElement, E->getExprLoc()); // Tell the EH cleanup that we finished with the last element. if (endOfInit) Builder.CreateStore(nextElement, endOfInit); diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 5674442..465ac78 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -757,6 +757,7 @@ CodeGenFunction::EmitNewArrayInitializer(const CXXNewExpr *E, // Find the end of the array, hoisted out of the loop. llvm::Value *endPtr = Builder.CreateInBoundsGEP(beginPtr, numElements, "array.end"); + EmitInBoundsGEPOverflowCheck(endPtr, E->getExprLoc()); unsigned initializerElements = 0; @@ -1474,6 +1475,7 @@ static void EmitArrayDelete(CodeGenFunction &CGF, llvm::Value *arrayEnd = CGF.Builder.CreateInBoundsGEP(deletedPtr, numElements, "delete.end"); + CGF.EmitInBoundsGEPOverflowCheck(arrayEnd, E->getExprLoc()); // Note that it is legal to allocate a zero-length array, and we // can never fold the check away because the length should always @@ -1522,6 +1524,7 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { } Ptr = Builder.CreateInBoundsGEP(Ptr, GEP, "del.first"); + EmitInBoundsGEPOverflowCheck(Ptr, E->getExprLoc()); } assert(ConvertTypeForMem(DeleteTy) == diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index f3fd604..7e75681 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -1639,8 +1639,10 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, if (!isInc) numElts = Builder.CreateNSWNeg(numElts, "vla.negsize"); if (CGF.getLangOpts().isSignedOverflowDefined()) value = Builder.CreateGEP(value, numElts, "vla.inc"); - else + else { value = Builder.CreateInBoundsGEP(value, numElts, "vla.inc"); + CGF.EmitInBoundsGEPOverflowCheck(value, E->getExprLoc()); + } // Arithmetic on function pointers (!) is just +-1. } else if (type->isFunctionType()) { @@ -1649,8 +1651,10 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, value = CGF.EmitCastToVoidPtr(value); if (CGF.getLangOpts().isSignedOverflowDefined()) value = Builder.CreateGEP(value, amt, "incdec.funcptr"); - else + else { value = Builder.CreateInBoundsGEP(value, amt, "incdec.funcptr"); + CGF.EmitInBoundsGEPOverflowCheck(value, E->getExprLoc()); + } value = Builder.CreateBitCast(value, input->getType()); // For everything else, we can just do a simple increment. @@ -1658,8 +1662,10 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, llvm::Value *amt = Builder.getInt32(amount); if (CGF.getLangOpts().isSignedOverflowDefined()) value = Builder.CreateGEP(value, amt, "incdec.ptr"); - else + else { value = Builder.CreateInBoundsGEP(value, amt, "incdec.ptr"); + CGF.EmitInBoundsGEPOverflowCheck(value, E->getExprLoc()); + } } // Vector increment/decrement. @@ -1719,8 +1725,10 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, if (CGF.getLangOpts().isSignedOverflowDefined()) value = Builder.CreateGEP(value, sizeValue, "incdec.objptr"); - else + else { value = Builder.CreateInBoundsGEP(value, sizeValue, "incdec.objptr"); + CGF.EmitInBoundsGEPOverflowCheck(value, E->getExprLoc()); + } value = Builder.CreateBitCast(value, input->getType()); } @@ -2350,6 +2358,7 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, } else { index = CGF.Builder.CreateNSWMul(index, numElements, "vla.index"); pointer = CGF.Builder.CreateInBoundsGEP(pointer, index, "add.ptr"); + CGF.EmitInBoundsGEPOverflowCheck(pointer, expr->getExprLoc()); } return pointer; } @@ -2366,7 +2375,9 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF, if (CGF.getLangOpts().isSignedOverflowDefined()) return CGF.Builder.CreateGEP(pointer, index, "add.ptr"); - return CGF.Builder.CreateInBoundsGEP(pointer, index, "add.ptr"); + Value *GEP = CGF.Builder.CreateInBoundsGEP(pointer, index, "add.ptr"); + CGF.EmitInBoundsGEPOverflowCheck(GEP, expr->getExprLoc()); + return GEP; } // Construct an fmuladd intrinsic to represent a fused mul-add of MulOp and diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index b07e903..9d292db 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1662,6 +1662,9 @@ public: void EmitBoundsCheck(const Expr *E, const Expr *Base, llvm::Value *Index, QualType IndexType, bool Accessed); + /// \brief Emit overflow checks for inbounds GEP's if enabled. + void EmitInBoundsGEPOverflowCheck(llvm::Value *GEP, SourceLocation Loc); + llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre); ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, diff --git a/test/Driver/fsanitize.c b/test/Driver/fsanitize.c index 6dbde97..d47fae0 100644 --- a/test/Driver/fsanitize.c +++ b/test/Driver/fsanitize.c @@ -1,16 +1,16 @@ // RUN: %clang -target x86_64-linux-gnu -fcatch-undefined-behavior %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|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool),?){14}"}} +// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|pointer-overflow),?){15}"}} // CHECK-UNDEFINED-TRAP: "-fsanitize-undefined-trap-on-error" // 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|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool),?){16}"}} +// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|pointer-overflow),?){17}"}} // 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,bool,enum %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,pointer-overflow %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|unreachable|return|vla-bound|alignment|null|object-size|array-bounds),?){12}"}} // RUN: %clang -target x86_64-linux-gnu -fsanitize=address-full %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-FULL -- 1.7.1
_______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
