Index: projects/compiler-rt/lib/ubsan/ubsan_handlers.h =================================================================== --- projects/compiler-rt/lib/ubsan/ubsan_handlers.h +++ projects/compiler-rt/lib/ubsan/ubsan_handlers.h @@ -130,6 +130,14 @@ /// \brief Handle returning null from function with returns_nonnull attribute. RECOVERABLE(nonnull_return, NonNullReturnData *Data) +struct NonNullArgData { + SourceLocation Loc; + SourceLocation DeclLoc; +}; + +/// \brief Handle passing null pointer to function with nonnull attribute. +RECOVERABLE(nonnull_arg, NonNullArgData *Data, ValueHandle ArgIndex); + } #endif // UBSAN_HANDLERS_H Index: projects/compiler-rt/lib/ubsan/ubsan_handlers.cc =================================================================== --- projects/compiler-rt/lib/ubsan/ubsan_handlers.cc +++ projects/compiler-rt/lib/ubsan/ubsan_handlers.cc @@ -351,3 +351,29 @@ GET_REPORT_OPTIONS(true); handleNonnullReturn(Data, Opts); } + +static void handleNonnullArg(NonNullArgData *Data, ValueHandle ArgIndex, + ReportOptions Opts) { + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + ScopedReport R(Opts); + + Diag(Loc, DL_Error, "null pointer passed as argument %0, which is declared to " + "never be null") << ArgIndex; + if (!Data->DeclLoc.isInvalid()) + Diag(Data->DeclLoc, DL_Note, "function declared here"); +} + +void __ubsan::__ubsan_handle_nonnull_arg(NonNullArgData *Data, + ValueHandle ArgIndex) { + GET_REPORT_OPTIONS(false); + handleNonnullArg(Data, ArgIndex, Opts); +} + +void __ubsan::__ubsan_handle_nonnull_arg_abort(NonNullArgData *Data, + ValueHandle ArgIndex) { + GET_REPORT_OPTIONS(true); + handleNonnullArg(Data, ArgIndex, Opts); +} Index: projects/compiler-rt/test/ubsan/TestCases/Misc/nonnull-arg.cpp =================================================================== --- /dev/null +++ projects/compiler-rt/test/ubsan/TestCases/Misc/nonnull-arg.cpp @@ -0,0 +1,42 @@ +// RUN: %clangxx -fsanitize=nonnull-attribute -fno-sanitize-recover %s -O3 -o %t +// RUN: %run %t nc +// RUN: %run %t nm +// RUN: %run %t nf +// RUN: not %run %t 0c 2>&1 | FileCheck %s --check-prefix=CTOR +// RUN: not %run %t 0m 2>&1 | FileCheck %s --check-prefix=METHOD +// RUN: not %run %t 0f 2>&1 | FileCheck %s --check-prefix=FUNC + +class C { + int *null_; + int *nonnull_; + +public: + C(int *null, __attribute__((nonnull)) int *nonnull) + : null_(null), nonnull_(nonnull) {} + int value() { return *nonnull_; } + int method(int *nonnull, int *null) __attribute__((nonnull(2))) { + return *nonnull_ + *nonnull; + } +}; + +__attribute__((nonnull)) int func(int *nonnull) { return *nonnull; } + +int main(int argc, char *argv[]) { + int local = 0; + int *arg = (argv[1][0] == '0') ? 0x0 : &local; + switch (argv[1][1]) { + case 'c': + return C(0x0, arg).value(); + // CTOR: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:21: runtime error: null pointer passed as argument 2, which is declared to never be null + // CTOR-NEXT: {{.*}}nonnull-arg.cpp:14:3: note: function declared here + case 'm': + return C(0x0, &local).method(arg, 0x0); + // METHOD: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:36: runtime error: null pointer passed as argument 1, which is declared to never be null + // METHOD-NEXT: {{.*}}nonnull-arg.cpp:17:7: note: function declared here + case 'f': + return func(arg); + // FUNC: {{.*}}nonnull-arg.cpp:[[@LINE-1]]:19: runtime error: null pointer passed as argument 1, which is declared to never be null + // FUNC-NEXT: {{.*}}nonnull-arg.cpp:22:30: note: function declared here + } + return 0; +} Index: tools/clang/include/clang/Basic/Sanitizers.def =================================================================== --- tools/clang/include/clang/Basic/Sanitizers.def +++ tools/clang/include/clang/Basic/Sanitizers.def @@ -59,6 +59,7 @@ SANITIZER("float-divide-by-zero", FloatDivideByZero) SANITIZER("function", Function) SANITIZER("integer-divide-by-zero", IntegerDivideByZero) +SANITIZER("nonnull-attribute", NonnullAttribute) SANITIZER("null", Null) SANITIZER("object-size", ObjectSize) SANITIZER("return", Return) @@ -79,9 +80,10 @@ // ABI or address space layout implications, and only catch undefined behavior. SANITIZER_GROUP("undefined", Undefined, Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow | - FloatDivideByZero | Function | IntegerDivideByZero | Null | - ObjectSize | Return | ReturnsNonnullAttribute | Shift | - SignedIntegerOverflow | Unreachable | VLABound | Vptr) + FloatDivideByZero | Function | IntegerDivideByZero | + NonnullAttribute | Null | ObjectSize | Return | + ReturnsNonnullAttribute | Shift | SignedIntegerOverflow | + Unreachable | VLABound | Vptr) // -fsanitize=undefined-trap includes // all sanitizers included by -fsanitize=undefined, except those that require @@ -89,9 +91,9 @@ // -fsanitize-undefined-trap-on-error flag. SANITIZER_GROUP("undefined-trap", UndefinedTrap, Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow | - FloatDivideByZero | IntegerDivideByZero | Null | - ObjectSize | Return | ReturnsNonnullAttribute | Shift | - SignedIntegerOverflow | Unreachable | VLABound) + FloatDivideByZero | IntegerDivideByZero | NonnullAttribute | + Null | ObjectSize | Return | ReturnsNonnullAttribute | + Shift | SignedIntegerOverflow | Unreachable | VLABound) SANITIZER_GROUP("integer", Integer, SignedIntegerOverflow | UnsignedIntegerOverflow | Shift | Index: tools/clang/lib/CodeGen/CGCall.cpp =================================================================== --- tools/clang/lib/CodeGen/CGCall.cpp +++ tools/clang/lib/CodeGen/CGCall.cpp @@ -2408,10 +2408,37 @@ } } +static void emitNonNullArgCheck(CodeGenFunction &CGF, RValue RV, + SourceLocation ArgLoc, const FunctionDecl *FD, + unsigned ParmNum) { + if (!CGF.SanOpts->NonnullAttribute || !FD || ParmNum >= FD->getNumParams()) + return; + const NonNullAttr *NNAtt = FD->getAttr(); + auto PVD = FD->getParamDecl(ParmNum); + if (!(NNAtt && NNAtt->isNonNull(PVD->getFunctionScopeIndex())) && + !PVD->hasAttr()) + return; + CodeGenFunction::SanitizerScope SanScope(&CGF); + assert(RV.isScalar()); + llvm::Value *V = RV.getScalarVal(); + llvm::Value *Cond = + CGF.Builder.CreateICmpNE(V, llvm::Constant::getNullValue(V->getType())); + llvm::Constant *StaticData[] = { + CGF.EmitCheckSourceLocation(ArgLoc), + CGF.EmitCheckSourceLocation(FD->getLocation())}; + llvm::Value *DynamicData[] = { + llvm::ConstantInt::get(CGF.Int32Ty, PVD->getFunctionScopeIndex() + 1), + }; + CGF.EmitCheck(Cond, "nonnull_arg", StaticData, DynamicData, + CodeGenFunction::CRK_Recoverable); +} + void CodeGenFunction::EmitCallArgs(CallArgList &Args, ArrayRef ArgTypes, CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd, + const FunctionDecl *CalleeDecl, + unsigned ParamsToSkip, bool ForceColumnInfo) { CGDebugInfo *DI = getDebugInfo(); SourceLocation CallLoc; @@ -2435,6 +2462,8 @@ for (int I = ArgTypes.size() - 1; I >= 0; --I) { CallExpr::const_arg_iterator Arg = ArgBeg + I; EmitCallArg(Args, *Arg, ArgTypes[I]); + emitNonNullArgCheck(*this, Args.back().RV, Arg->getExprLoc(), CalleeDecl, + ParamsToSkip + I); // Restore the debug location. if (DI) DI->EmitLocation(Builder, CallLoc, ForceColumnInfo); } @@ -2449,6 +2478,8 @@ CallExpr::const_arg_iterator Arg = ArgBeg + I; assert(Arg != ArgEnd); EmitCallArg(Args, *Arg, ArgTypes[I]); + emitNonNullArgCheck(*this, Args.back().RV, Arg->getExprLoc(), CalleeDecl, + ParamsToSkip + I); // Restore the debug location. if (DI) DI->EmitLocation(Builder, CallLoc, ForceColumnInfo); } Index: tools/clang/lib/CodeGen/CGClass.cpp =================================================================== --- tools/clang/lib/CodeGen/CGClass.cpp +++ tools/clang/lib/CodeGen/CGClass.cpp @@ -1674,7 +1674,7 @@ // Add the rest of the user-supplied arguments. const FunctionProtoType *FPT = D->getType()->castAs(); - EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end()); + EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end(), E->getConstructor()); // Insert any ABI-specific implicit constructor arguments. unsigned ExtraArgs = CGM.getCXXABI().addImplicitConstructorArgs( @@ -1716,8 +1716,8 @@ Args.add(RValue::get(Src), QT); // Skip over first argument (Src). - EmitCallArgs(Args, FPT->isVariadic(), FPT->param_type_begin() + 1, - FPT->param_type_end(), E->arg_begin() + 1, E->arg_end()); + EmitCallArgs(Args, FPT, E->arg_begin() + 1, E->arg_end(), E->getConstructor(), + /*ParamsToSkip*/ 1); EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, RequiredArgs::All), Callee, ReturnValueSlot(), Args, D); Index: tools/clang/lib/CodeGen/CGExpr.cpp =================================================================== --- tools/clang/lib/CodeGen/CGExpr.cpp +++ tools/clang/lib/CodeGen/CGExpr.cpp @@ -3320,7 +3320,8 @@ CallArgList Args; EmitCallArgs(Args, dyn_cast(FnType), E->arg_begin(), - E->arg_end(), ForceColumnInfo); + E->arg_end(), E->getDirectCallee(), /*ParamsToSkip*/ 0, + ForceColumnInfo); const CGFunctionInfo &FnInfo = CGM.getTypes().arrangeFreeFunctionCall(Args, FnType); Index: tools/clang/lib/CodeGen/CGExprCXX.cpp =================================================================== --- tools/clang/lib/CodeGen/CGExprCXX.cpp +++ tools/clang/lib/CodeGen/CGExprCXX.cpp @@ -55,20 +55,18 @@ const FunctionProtoType *FPT = MD->getType()->castAs(); RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, Args.size()); - + // And the rest of the call args. - CallExpr::const_arg_iterator ArgBeg, ArgEnd; - if (CE == nullptr) { - ArgBeg = ArgEnd = nullptr; - } else if (auto OCE = dyn_cast(CE)) { + if (CE) { // Special case: skip first argument of CXXOperatorCall (it is "this"). - ArgBeg = OCE->arg_begin() + 1; - ArgEnd = OCE->arg_end(); + unsigned ArgsToSkip = isa(CE) ? 1 : 0; + EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(), + CE->getDirectCallee()); } else { - ArgBeg = CE->arg_begin(); - ArgEnd = CE->arg_end(); + assert( + FPT->getNumParams() == 0 && + "No CallExpr specified for function with non-zero number of arguments"); } - EmitCallArgs(Args, FPT, ArgBeg, ArgEnd); return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee, ReturnValue, Args, MD); @@ -284,7 +282,7 @@ RequiredArgs required = RequiredArgs::forPrototypePlus(FPT, 1); // And the rest of the call args - EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end()); + EmitCallArgs(Args, FPT, E->arg_begin(), E->arg_end(), E->getDirectCallee()); return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required), Callee, ReturnValue, Args); } @@ -1237,10 +1235,9 @@ // We start at 1 here because the first argument (the allocation size) // has already been emitted. - EmitCallArgs(allocatorArgs, allocatorType->isVariadic(), - allocatorType->param_type_begin() + 1, - allocatorType->param_type_end(), E->placement_arg_begin(), - E->placement_arg_end()); + EmitCallArgs(allocatorArgs, allocatorType, E->placement_arg_begin(), + E->placement_arg_end(), /*CalleeDecl*/ nullptr, + /*ParamsToSkip*/ 1); // Emit the allocation call. If the allocator is a global placement // operator, just "inline" it directly. Index: tools/clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- tools/clang/lib/CodeGen/CodeGenFunction.h +++ tools/clang/lib/CodeGen/CodeGenFunction.h @@ -2617,74 +2617,61 @@ void EmitCallArgs(CallArgList &Args, const T *CallArgTypeInfo, CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd, - bool ForceColumnInfo = false) { - if (CallArgTypeInfo) { - EmitCallArgs(Args, CallArgTypeInfo->isVariadic(), - CallArgTypeInfo->param_type_begin(), - CallArgTypeInfo->param_type_end(), ArgBeg, ArgEnd, - ForceColumnInfo); - } else { - // T::param_type_iterator might not have a default ctor. - const QualType *NoIter = nullptr; - EmitCallArgs(Args, /*AllowExtraArguments=*/true, NoIter, NoIter, ArgBeg, - ArgEnd, ForceColumnInfo); - } - } - - template - void EmitCallArgs(CallArgList& Args, - bool AllowExtraArguments, - ArgTypeIterator ArgTypeBeg, - ArgTypeIterator ArgTypeEnd, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd, - bool ForceColumnInfo = false) { + const FunctionDecl *CalleeDecl = nullptr, + unsigned ParamsToSkip = 0, bool ForceColumnInfo = false) { SmallVector ArgTypes; CallExpr::const_arg_iterator Arg = ArgBeg; - - // First, use the argument types that the type info knows about - for (ArgTypeIterator I = ArgTypeBeg, E = ArgTypeEnd; I != E; ++I, ++Arg) { - assert(Arg != ArgEnd && "Running over edge of argument list!"); + assert((ParamsToSkip == 0 || CallArgTypeInfo) && + "Can't skip parameters if type info is not provided"); + if (CallArgTypeInfo) { + // First, use the argument types that the type info knows about + for (auto I = CallArgTypeInfo->param_type_begin() + ParamsToSkip, + E = CallArgTypeInfo->param_type_end(); + I != E; ++I, ++Arg) { + assert(Arg != ArgEnd && "Running over edge of argument list!"); #ifndef NDEBUG - QualType ArgType = *I; - QualType ActualArgType = Arg->getType(); - if (ArgType->isPointerType() && ActualArgType->isPointerType()) { - QualType ActualBaseType = - ActualArgType->getAs()->getPointeeType(); - QualType ArgBaseType = - ArgType->getAs()->getPointeeType(); - if (ArgBaseType->isVariableArrayType()) { - if (const VariableArrayType *VAT = - getContext().getAsVariableArrayType(ActualBaseType)) { - if (!VAT->getSizeExpr()) - ActualArgType = ArgType; + QualType ArgType = *I; + QualType ActualArgType = Arg->getType(); + if (ArgType->isPointerType() && ActualArgType->isPointerType()) { + QualType ActualBaseType = + ActualArgType->getAs()->getPointeeType(); + QualType ArgBaseType = + ArgType->getAs()->getPointeeType(); + if (ArgBaseType->isVariableArrayType()) { + if (const VariableArrayType *VAT = + getContext().getAsVariableArrayType(ActualBaseType)) { + if (!VAT->getSizeExpr()) + ActualArgType = ArgType; + } } } - } - assert(getContext().getCanonicalType(ArgType.getNonReferenceType()). - getTypePtr() == - getContext().getCanonicalType(ActualArgType).getTypePtr() && - "type mismatch in call argument!"); + assert(getContext().getCanonicalType(ArgType.getNonReferenceType()). + getTypePtr() == + getContext().getCanonicalType(ActualArgType).getTypePtr() && + "type mismatch in call argument!"); #endif - ArgTypes.push_back(*I); + ArgTypes.push_back(*I); + } } - // Either we've emitted all the call args, or we have a call to variadic - // function or some other call that allows extra arguments. - assert((Arg == ArgEnd || AllowExtraArguments) && + // function. + assert((Arg == ArgEnd || CallArgTypeInfo == nullptr || + CallArgTypeInfo->isVariadic()) && "Extra arguments in non-variadic function!"); // If we still have any arguments, emit them using the type of the argument. for (; Arg != ArgEnd; ++Arg) ArgTypes.push_back(Arg->getType()); - EmitCallArgs(Args, ArgTypes, ArgBeg, ArgEnd, ForceColumnInfo); + EmitCallArgs(Args, ArgTypes, ArgBeg, ArgEnd, CalleeDecl, ParamsToSkip, + ForceColumnInfo); } void EmitCallArgs(CallArgList &Args, ArrayRef ArgTypes, CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd, - bool ForceColumnInfo = false); + const FunctionDecl *CalleeDecl = nullptr, + unsigned ParamsToSkip = 0, bool ForceColumnInfo = false); private: const TargetCodeGenInfo &getTargetHooks() const { Index: tools/clang/test/CodeGen/catch-undef-behavior.c =================================================================== --- tools/clang/test/CodeGen/catch-undef-behavior.c +++ tools/clang/test/CodeGen/catch-undef-behavior.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s -// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-TRAP +// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s +// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute,nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-TRAP // 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 @@ -446,6 +446,20 @@ return a; } +// CHECK-LABEL: @call_decl_nonnull +__attribute__((nonnull)) void decl_nonnull(int *a); +void call_decl_nonnull(int *a) { + // CHECK: [[OK:%.*]] = icmp ne i32* {{.*}}, null + // CHECK: br i1 [[OK]] + // CHECK: call void @__ubsan_handle_nonnull_arg + + // CHECK: [[OK:%.*]] = icmp ne i32* {{.*}}, null + // CHECK-TRAP: br i1 [[OK]] + // CHECK-TRAP: call void @llvm.trap() [[NR_NUW]] + // CHECK-TRAP: unreachable + decl_nonnull(a); +} + // CHECK: ![[WEIGHT_MD]] = metadata !{metadata !"branch_weights", i32 1048575, i32 1} // CHECK-TRAP: attributes [[NR_NUW]] = { noreturn nounwind } Index: tools/clang/test/Driver/fsanitize.c =================================================================== --- tools/clang/test/Driver/fsanitize.c +++ tools/clang/test/Driver/fsanitize.c @@ -1,13 +1,13 @@ // 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|returns-nonnull-attribute),?){15}"}} +// 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|returns-nonnull-attribute|nonnull-attribute),?){16}"}} // 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|returns-nonnull-attribute),?){17}"}} +// 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|returns-nonnull-attribute|nonnull-attribute),?){18}"}} // 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|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){16}"}} +// CHECK-UNDEFINED-DARWIN: "-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|array-bounds|enum|bool|returns-nonnull-attribute|nonnull-attribute),?){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}"}} @@ -16,7 +16,7 @@ // CHECK-BOUNDS: "-fsanitize={{((array-bounds|local-bounds),?){2}"}} // 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 -// 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|returns-nonnull-attribute),?){13}"}} +// 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|returns-nonnull-attribute|nonnull-attribute),?){14}"}} // RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP-ON-ERROR-UNDEF // CHECK-UNDEFINED-TRAP-ON-ERROR-UNDEF: '-fsanitize=undefined' not allowed with '-fsanitize-undefined-trap-on-error'