Index: cfe/trunk/docs/UndefinedBehaviorSanitizer.rst =================================================================== --- cfe/trunk/docs/UndefinedBehaviorSanitizer.rst +++ cfe/trunk/docs/UndefinedBehaviorSanitizer.rst @@ -92,6 +92,12 @@ parameter which is declared to never be null. - ``-fsanitize=null``: Use of a null pointer or creation of a null reference. + - ``-fsanitize=nullability-arg``: Passing null as a function parameter + which is annotated with ``_Nonnull``. + - ``-fsanitize=nullability-assign``: Assigning null to an lvalue which + is annotated with ``_Nonnull``. + - ``-fsanitize=nullability-return``: Returning null from a function with + a return type annotated with ``_Nonnull``. - ``-fsanitize=object-size``: An attempt to potentially use bytes which the optimizer can determine are not part of the object being accessed. This will also detect some types of undefined behavior that may not @@ -130,11 +136,15 @@ You can also use the following check groups: - ``-fsanitize=undefined``: All of the checks listed above other than - ``unsigned-integer-overflow``. + ``unsigned-integer-overflow`` and the ``nullability-*`` checks. - ``-fsanitize=undefined-trap``: Deprecated alias of ``-fsanitize=undefined``. - ``-fsanitize=integer``: Checks for undefined or suspicious integer behavior (e.g. unsigned integer overflow). + - ``-fsanitize=nullability``: Enables ``nullability-arg``, + ``nullability-assign``, and ``nullability-return``. While violating + nullability does not have undefined behavior, it is often unintentional, + so UBSan offers to catch it. Stack traces and report symbolization ===================================== Index: cfe/trunk/include/clang/Basic/Sanitizers.def =================================================================== --- cfe/trunk/include/clang/Basic/Sanitizers.def +++ cfe/trunk/include/clang/Basic/Sanitizers.def @@ -64,6 +64,11 @@ SANITIZER("integer-divide-by-zero", IntegerDivideByZero) SANITIZER("nonnull-attribute", NonnullAttribute) SANITIZER("null", Null) +SANITIZER("nullability-arg", NullabilityArg) +SANITIZER("nullability-assign", NullabilityAssign) +SANITIZER("nullability-return", NullabilityReturn) +SANITIZER_GROUP("nullability", Nullability, + NullabilityArg | NullabilityAssign | NullabilityReturn) SANITIZER("object-size", ObjectSize) SANITIZER("return", Return) SANITIZER("returns-nonnull-attribute", ReturnsNonnullAttribute) Index: cfe/trunk/lib/CodeGen/CGCall.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCall.cpp +++ cfe/trunk/lib/CodeGen/CGCall.cpp @@ -2912,19 +2912,7 @@ llvm::Instruction *Ret; if (RV) { - if (CurCodeDecl && SanOpts.has(SanitizerKind::ReturnsNonnullAttribute)) { - if (auto RetNNAttr = CurCodeDecl->getAttr()) { - SanitizerScope SanScope(this); - llvm::Value *Cond = Builder.CreateICmpNE( - RV, llvm::Constant::getNullValue(RV->getType())); - llvm::Constant *StaticData[] = { - EmitCheckSourceLocation(EndLoc), - EmitCheckSourceLocation(RetNNAttr->getLocation()), - }; - EmitCheck(std::make_pair(Cond, SanitizerKind::ReturnsNonnullAttribute), - SanitizerHandler::NonnullReturn, StaticData, None); - } - } + EmitReturnValueCheck(RV, EndLoc); Ret = Builder.CreateRet(RV); } else { Ret = Builder.CreateRetVoid(); @@ -2934,6 +2922,62 @@ Ret->setDebugLoc(std::move(RetDbgLoc)); } +void CodeGenFunction::EmitReturnValueCheck(llvm::Value *RV, + SourceLocation EndLoc) { + // A current decl may not be available when emitting vtable thunks. + if (!CurCodeDecl) + return; + + ReturnsNonNullAttr *RetNNAttr = nullptr; + if (SanOpts.has(SanitizerKind::ReturnsNonnullAttribute)) + RetNNAttr = CurCodeDecl->getAttr(); + + if (!RetNNAttr && !requiresReturnValueNullabilityCheck()) + return; + + // Prefer the returns_nonnull attribute if it's present. + SourceLocation AttrLoc; + SanitizerMask CheckKind; + if (RetNNAttr) { + assert(!requiresReturnValueNullabilityCheck() && + "Cannot check nullability and the nonnull attribute"); + AttrLoc = RetNNAttr->getLocation(); + CheckKind = SanitizerKind::ReturnsNonnullAttribute; + } else { + // FIXME: The runtime shouldn't refer to the 'returns_nonnull' attribute. + if (auto *DD = dyn_cast(CurCodeDecl)) + if (auto *TSI = DD->getTypeSourceInfo()) + if (auto FTL = TSI->getTypeLoc().castAs()) + AttrLoc = FTL.getReturnLoc().findNullabilityLoc(); + CheckKind = SanitizerKind::NullabilityReturn; + } + + SanitizerScope SanScope(this); + + llvm::BasicBlock *Check = nullptr; + llvm::BasicBlock *NoCheck = nullptr; + if (requiresReturnValueNullabilityCheck()) { + // Before doing the nullability check, make sure that the preconditions for + // the check are met. + Check = createBasicBlock("nullcheck"); + NoCheck = createBasicBlock("no.nullcheck"); + Builder.CreateCondBr(RetValNullabilityPrecondition, Check, NoCheck); + EmitBlock(Check); + } + + // Now do the null check. If the returns_nonnull attribute is present, this + // is done unconditionally. + llvm::Value *Cond = Builder.CreateIsNotNull(RV); + llvm::Constant *StaticData[] = { + EmitCheckSourceLocation(EndLoc), EmitCheckSourceLocation(AttrLoc), + }; + EmitCheck(std::make_pair(Cond, CheckKind), SanitizerHandler::NonnullReturn, + StaticData, None); + + if (requiresReturnValueNullabilityCheck()) + EmitBlock(NoCheck); +} + static bool isInAllocaArgument(CGCXXABI &ABI, QualType type) { const CXXRecordDecl *RD = type->getAsCXXRecordDecl(); return RD && ABI.getRecordArgABI(RD) == CGCXXABI::RAA_DirectInMemory; @@ -3244,25 +3288,51 @@ SourceLocation ArgLoc, AbstractCallee AC, unsigned ParmNum) { - if (!SanOpts.has(SanitizerKind::NonnullAttribute) || !AC.getDecl()) + if (!AC.getDecl() || !(SanOpts.has(SanitizerKind::NonnullAttribute) || + SanOpts.has(SanitizerKind::NullabilityArg))) return; + + // The param decl may be missing in a variadic function. auto PVD = ParmNum < AC.getNumParams() ? AC.getParamDecl(ParmNum) : nullptr; unsigned ArgNo = PVD ? PVD->getFunctionScopeIndex() : ParmNum; - auto NNAttr = getNonNullAttr(AC.getDecl(), PVD, ArgType, ArgNo); - if (!NNAttr) + + // Prefer the nonnull attribute if it's present. + const NonNullAttr *NNAttr = nullptr; + if (SanOpts.has(SanitizerKind::NonnullAttribute)) + NNAttr = getNonNullAttr(AC.getDecl(), PVD, ArgType, ArgNo); + + bool CanCheckNullability = false; + if (SanOpts.has(SanitizerKind::NullabilityArg) && !NNAttr && PVD) { + auto Nullability = PVD->getType()->getNullability(getContext()); + CanCheckNullability = Nullability && + *Nullability == NullabilityKind::NonNull && + PVD->getTypeSourceInfo(); + } + + if (!NNAttr && !CanCheckNullability) return; + + SourceLocation AttrLoc; + SanitizerMask CheckKind; + if (NNAttr) { + AttrLoc = NNAttr->getLocation(); + CheckKind = SanitizerKind::NonnullAttribute; + } else { + AttrLoc = PVD->getTypeSourceInfo()->getTypeLoc().findNullabilityLoc(); + CheckKind = SanitizerKind::NullabilityArg; + } + SanitizerScope SanScope(this); assert(RV.isScalar()); llvm::Value *V = RV.getScalarVal(); llvm::Value *Cond = Builder.CreateICmpNE(V, llvm::Constant::getNullValue(V->getType())); llvm::Constant *StaticData[] = { - EmitCheckSourceLocation(ArgLoc), - EmitCheckSourceLocation(NNAttr->getLocation()), + EmitCheckSourceLocation(ArgLoc), EmitCheckSourceLocation(AttrLoc), llvm::ConstantInt::get(Int32Ty, ArgNo + 1), }; - EmitCheck(std::make_pair(Cond, SanitizerKind::NonnullAttribute), - SanitizerHandler::NonnullArg, StaticData, None); + EmitCheck(std::make_pair(Cond, CheckKind), SanitizerHandler::NonnullArg, + StaticData, None); } void CodeGenFunction::EmitCallArgs( Index: cfe/trunk/lib/CodeGen/CGDecl.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGDecl.cpp +++ cfe/trunk/lib/CodeGen/CGDecl.cpp @@ -672,6 +672,28 @@ lvalue.setAddress(CGF.emitBlockByrefAddress(lvalue.getAddress(), var)); } +void CodeGenFunction::EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, + SourceLocation Loc) { + if (!SanOpts.has(SanitizerKind::NullabilityAssign)) + return; + + auto Nullability = LHS.getType()->getNullability(getContext()); + if (!Nullability || *Nullability != NullabilityKind::NonNull) + return; + + // Check if the right hand side of the assignment is nonnull, if the left + // hand side must be nonnull. + SanitizerScope SanScope(this); + llvm::Value *IsNotNull = Builder.CreateIsNotNull(RHS); + // FIXME: The runtime shouldn't refer to a 'reference'. + llvm::Constant *StaticData[] = { + EmitCheckSourceLocation(Loc), EmitCheckTypeDescriptor(LHS.getType()), + llvm::ConstantInt::get(Int8Ty, 1), + llvm::ConstantInt::get(Int8Ty, TCK_ReferenceBinding)}; + EmitCheck({{IsNotNull, SanitizerKind::NullabilityAssign}}, + SanitizerHandler::TypeMismatch, StaticData, RHS); +} + void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D, LValue lvalue, bool capturedByInit) { Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime(); @@ -679,6 +701,7 @@ llvm::Value *value = EmitScalarExpr(init); if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast(D)); + EmitNullabilityCheck(lvalue, value, init->getExprLoc()); EmitStoreThroughLValue(RValue::get(value), lvalue, true); return; } @@ -767,6 +790,8 @@ if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast(D)); + EmitNullabilityCheck(lvalue, value, init->getExprLoc()); + // If the variable might have been accessed by its initializer, we // might have to initialize with a barrier. We have to do this for // both __weak and __strong, but __weak got filtered out above. @@ -1880,6 +1905,19 @@ if (D.hasAttr()) EmitVarAnnotations(&D, DeclPtr.getPointer()); + + // We can only check return value nullability if all arguments to the + // function satisfy their nullability preconditions. This makes it necessary + // to emit null checks for args in the function body itself. + if (requiresReturnValueNullabilityCheck()) { + auto Nullability = Ty->getNullability(getContext()); + if (Nullability && *Nullability == NullabilityKind::NonNull) { + SanitizerScope SanScope(this); + RetValNullabilityPrecondition = + Builder.CreateAnd(RetValNullabilityPrecondition, + Builder.CreateIsNotNull(Arg.getAnyValue())); + } + } } void CodeGenModule::EmitOMPDeclareReduction(const OMPDeclareReductionDecl *D, Index: cfe/trunk/lib/CodeGen/CGExprScalar.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGExprScalar.cpp +++ cfe/trunk/lib/CodeGen/CGExprScalar.cpp @@ -3132,10 +3132,12 @@ // because the result is altered by the store, i.e., [C99 6.5.16p1] // 'An assignment expression has the value of the left operand after // the assignment...'. - if (LHS.isBitField()) + if (LHS.isBitField()) { CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, &RHS); - else + } else { + CGF.EmitNullabilityCheck(LHS, RHS, E->getExprLoc()); CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS); + } } // If the result is clearly ignored, return now. Index: cfe/trunk/lib/CodeGen/CodeGenFunction.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.h +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h @@ -1375,6 +1375,16 @@ /// information about the layout of the variable. llvm::DenseMap BlockByrefInfos; + /// Used by -fsanitize=nullability-return to determine whether the return + /// value can be checked. + llvm::Value *RetValNullabilityPrecondition = nullptr; + + /// Check if -fsanitize=nullability-return instrumentation is required for + /// this function. + bool requiresReturnValueNullabilityCheck() const { + return RetValNullabilityPrecondition; + } + llvm::BasicBlock *TerminateLandingPad; llvm::BasicBlock *TerminateHandler; llvm::BasicBlock *TrapBB; @@ -1753,6 +1763,9 @@ void EmitFunctionEpilog(const CGFunctionInfo &FI, bool EmitRetDbgLoc, SourceLocation EndLoc); + /// Emit a test that checks if the return value \p RV is nonnull. + void EmitReturnValueCheck(llvm::Value *RV, SourceLocation EndLoc); + /// EmitStartEHSpec - Emit the start of the exception spec. void EmitStartEHSpec(const Decl *D); @@ -3458,6 +3471,10 @@ void EmitBranchOnBoolExpr(const Expr *Cond, llvm::BasicBlock *TrueBlock, llvm::BasicBlock *FalseBlock, uint64_t TrueCount); + /// Given an assignment `*LHS = RHS`, emit a test that checks if \p RHS is + /// nonnull, if \p LHS is marked _Nonnull. + void EmitNullabilityCheck(LValue LHS, llvm::Value *RHS, SourceLocation Loc); + /// \brief Emit a description of a type in a format suitable for passing to /// a runtime sanitizer handler. llvm::Constant *EmitCheckTypeDescriptor(QualType T); Index: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp +++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp @@ -822,6 +822,18 @@ } } + // If we're checking nullability, we need to know whether we can check the + // return value. Initialize the flag to 'true' and refine it in EmitParmDecl. + if (SanOpts.has(SanitizerKind::NullabilityReturn)) { + auto Nullability = FnRetTy->getNullability(getContext()); + if (Nullability && *Nullability == NullabilityKind::NonNull) { + if (!(SanOpts.has(SanitizerKind::ReturnsNonnullAttribute) && + CurCodeDecl && CurCodeDecl->getAttr())) + RetValNullabilityPrecondition = + llvm::ConstantInt::getTrue(getLLVMContext()); + } + } + // If we're in C++ mode and the function name is "main", it is guaranteed // to be norecurse by the standard (3.6.1.3 "The function main shall not be // used within a program"). Index: cfe/trunk/lib/Driver/SanitizerArgs.cpp =================================================================== --- cfe/trunk/lib/Driver/SanitizerArgs.cpp +++ cfe/trunk/lib/Driver/SanitizerArgs.cpp @@ -26,18 +26,19 @@ using namespace llvm::opt; enum : SanitizerMask { - NeedsUbsanRt = Undefined | Integer | CFI, + NeedsUbsanRt = Undefined | Integer | Nullability | CFI, NeedsUbsanCxxRt = Vptr | CFI, NotAllowedWithTrap = Vptr, RequiresPIE = DataFlow, NeedsUnwindTables = Address | Thread | Memory | DataFlow, - SupportsCoverage = Address | Memory | Leak | Undefined | Integer | DataFlow, - RecoverableByDefault = Undefined | Integer, + SupportsCoverage = + Address | Memory | Leak | Undefined | Integer | Nullability | DataFlow, + RecoverableByDefault = Undefined | Integer | Nullability, Unrecoverable = Unreachable | Return, LegacyFsanitizeRecoverMask = Undefined | Integer, NeedsLTO = CFI, - TrappingSupported = - (Undefined & ~Vptr) | UnsignedIntegerOverflow | LocalBounds | CFI, + TrappingSupported = (Undefined & ~Vptr) | UnsignedIntegerOverflow | + Nullability | LocalBounds | CFI, TrappingDefault = CFI, CFIClasses = CFIVCall | CFINVCall | CFIDerivedCast | CFIUnrelatedCast, }; Index: cfe/trunk/lib/Driver/ToolChain.cpp =================================================================== --- cfe/trunk/lib/Driver/ToolChain.cpp +++ cfe/trunk/lib/Driver/ToolChain.cpp @@ -697,7 +697,8 @@ // platform dependent. using namespace SanitizerKind; SanitizerMask Res = (Undefined & ~Vptr & ~Function) | (CFI & ~CFIICall) | - CFICastStrict | UnsignedIntegerOverflow | LocalBounds; + CFICastStrict | UnsignedIntegerOverflow | Nullability | + LocalBounds; if (getTriple().getArch() == llvm::Triple::x86 || getTriple().getArch() == llvm::Triple::x86_64 || getTriple().getArch() == llvm::Triple::arm || Index: cfe/trunk/test/CodeGenObjC/ubsan-nonnull-and-nullability.m =================================================================== --- cfe/trunk/test/CodeGenObjC/ubsan-nonnull-and-nullability.m +++ cfe/trunk/test/CodeGenObjC/ubsan-nonnull-and-nullability.m @@ -0,0 +1,31 @@ +// REQUIRES: asserts +// RUN: %clang_cc1 -x objective-c -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=nullability-return,returns-nonnull-attribute,nullability-arg,nonnull-attribute %s -o - -w | FileCheck %s + +// If both the annotation and the attribute are present, prefer the attribute, +// since it actually affects IRGen. + +// CHECK-LABEL: define nonnull i32* @f1 +__attribute__((returns_nonnull)) int *_Nonnull f1(int *_Nonnull p) { + // CHECK: entry: + // CHECK-NEXT: [[ADDR:%.*]] = alloca i32* + // CHECK-NEXT: store i32* [[P:%.*]], i32** [[ADDR]] + // CHECK-NEXT: [[ARG:%.*]] = load i32*, i32** [[ADDR]] + // CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32* [[ARG]], null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], label %[[CONT:.+]], label %[[HANDLE:[^,]+]] + // CHECK: [[HANDLE]]: + // CHECK-NEXT: call void @__ubsan_handle_nonnull_return_abort + // CHECK-NEXT: unreachable, !nosanitize + // CHECK: [[CONT]]: + // CHECK-NEXT: ret i32* + return p; +} + +// CHECK-LABEL: define void @f2 +void f2(int *_Nonnull __attribute__((nonnull)) p) {} + +// CHECK-LABEL: define void @call_f2 +void call_f2() { + // CHECK: call void @__ubsan_handle_nonnull_arg_abort + // CHECK-NOT: call void @__ubsan_handle_nonnull_arg_abort + f2((void *)0); +} Index: cfe/trunk/test/CodeGenObjC/ubsan-nullability.m =================================================================== --- cfe/trunk/test/CodeGenObjC/ubsan-nullability.m +++ cfe/trunk/test/CodeGenObjC/ubsan-nullability.m @@ -0,0 +1,182 @@ +// REQUIRES: asserts +// RUN: %clang_cc1 -x objective-c -emit-llvm -triple x86_64-apple-macosx10.10.0 -fsanitize=nullability-arg,nullability-assign,nullability-return -w %s -o - | FileCheck %s + +// CHECK: [[NONNULL_RV_LOC1:@.*]] = private unnamed_addr global {{.*}} i32 109, i32 1 {{.*}} i32 100, i32 6 +// CHECK: [[NONNULL_ARG_LOC:@.*]] = private unnamed_addr global {{.*}} i32 204, i32 15 {{.*}} i32 190, i32 23 +// CHECK: [[NONNULL_ASSIGN1_LOC:@.*]] = private unnamed_addr global {{.*}} i32 305, i32 9 +// CHECK: [[NONNULL_ASSIGN2_LOC:@.*]] = private unnamed_addr global {{.*}} i32 405, i32 10 +// CHECK: [[NONNULL_ASSIGN3_LOC:@.*]] = private unnamed_addr global {{.*}} i32 505, i32 10 +// CHECK: [[NONNULL_INIT1_LOC:@.*]] = private unnamed_addr global {{.*}} i32 604, i32 25 +// CHECK: [[NONNULL_INIT2_LOC1:@.*]] = private unnamed_addr global {{.*}} i32 707, i32 26 +// CHECK: [[NONNULL_INIT2_LOC2:@.*]] = private unnamed_addr global {{.*}} i32 707, i32 29 +// CHECK: [[NONNULL_RV_LOC2:@.*]] = private unnamed_addr global {{.*}} i32 817, i32 1 {{.*}} i32 800, i32 6 + +#define NULL ((void *)0) + +// CHECK-LABEL: define i32* @nonnull_retval1 +#line 100 +int *_Nonnull nonnull_retval1(int *p) { + // CHECK: br i1 true, label %[[NULL:.*]], label %[[NONULL:.*]], !nosanitize + // CHECK: [[NULL]]: + // CHECK: [[ICMP:%.*]] = icmp ne i32* {{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_return{{.*}}[[NONNULL_RV_LOC1]] + return p; + // CHECK: [[NONULL]]: + // CHECK-NEXT: ret i32* +} + +#line 190 +void nonnull_arg(int *_Nonnull p) {} + +// CHECK-LABEL: define void @call_func_with_nonnull_arg +#line 200 +void call_func_with_nonnull_arg(int *_Nonnull p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* {{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_arg{{.*}}[[NONNULL_ARG_LOC]] + nonnull_arg(p); +} + +// CHECK-LABEL: define void @nonnull_assign1 +#line 300 +void nonnull_assign1(int *p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* {{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_type_mismatch{{.*}}[[NONNULL_ASSIGN1_LOC]] + int *_Nonnull local; + local = p; +} + +// CHECK-LABEL: define void @nonnull_assign2 +#line 400 +void nonnull_assign2(int *p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* %{{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_type_mismatch{{.*}}[[NONNULL_ASSIGN2_LOC]] + int *_Nonnull arr[1]; + arr[0] = p; +} + +struct S1 { + int *_Nonnull mptr; +}; + +// CHECK-LABEL: define void @nonnull_assign3 +#line 500 +void nonnull_assign3(int *p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* %{{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_type_mismatch{{.*}}[[NONNULL_ASSIGN3_LOC]] + struct S1 s; + s.mptr = p; +} + +// CHECK-LABEL: define void @nonnull_init1 +#line 600 +void nonnull_init1(int *p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* %{{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_type_mismatch{{.*}}[[NONNULL_INIT1_LOC]] + int *_Nonnull local = p; +} + +// CHECK-LABEL: define void @nonnull_init2 +#line 700 +void nonnull_init2(int *p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* %{{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_type_mismatch{{.*}}[[NONNULL_INIT2_LOC1]] + // CHECK: [[ICMP:%.*]] = icmp ne i32* %{{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_type_mismatch{{.*}}[[NONNULL_INIT2_LOC2]] + int *_Nonnull arr[] = {p, p}; +} + +// CHECK-LABEL: define i32* @nonnull_retval2 +#line 800 +int *_Nonnull nonnull_retval2(int *_Nonnull arg1, //< Test this. + int *_Nonnull arg2, //< Test this. + int *_Nullable arg3, //< Don't test the rest. + int *arg4, + int arg5, ...) { + // CHECK: [[ARG1CMP:%.*]] = icmp ne i32* %arg1, null, !nosanitize + // CHECK-NEXT: [[DO_RV_CHECK_1:%.*]] = and i1 true, [[ARG1CMP]], !nosanitize + // CHECK: [[ARG2CMP:%.*]] = icmp ne i32* %arg2, null, !nosanitize + // CHECK-NEXT: [[DO_RV_CHECK_2:%.*]] = and i1 [[DO_RV_CHECK_1]], [[ARG2CMP]] + // CHECK: br i1 [[DO_RV_CHECK_2]], label %[[NULL:.*]], label %[[NONULL:.*]], !nosanitize + // CHECK: [[NULL]]: + // CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32* {{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_return{{.*}}[[NONNULL_RV_LOC2]] + return arg1; + // CHECK: [[NONULL]]: + // CHECK-NEXT: ret i32* +} + +@interface A ++(int *_Nonnull) objc_clsmethod: (int *_Nonnull) arg1; +-(int *_Nonnull) objc_method: (int *_Nonnull) arg1; +@end + +@implementation A + +// CHECK-LABEL: define internal i32* @"\01+[A objc_clsmethod:]" ++(int *_Nonnull) objc_clsmethod: (int *_Nonnull) arg1 { + // CHECK: [[ARG1CMP:%.*]] = icmp ne i32* %arg1, null, !nosanitize + // CHECK-NEXT: [[DO_RV_CHECK:%.*]] = and i1 true, [[ARG1CMP]] + // CHECK: br i1 [[DO_RV_CHECK]], label %[[NULL:.*]], label %[[NONULL:.*]], !nosanitize + // CHECK: [[NULL]]: + // CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32* {{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_return{{.*}} + return arg1; + // CHECK: [[NONULL]]: + // CHECK-NEXT: ret i32* +} + +// CHECK-LABEL: define internal i32* @"\01-[A objc_method:]" +-(int *_Nonnull) objc_method: (int *_Nonnull) arg1 { + // CHECK: [[ARG1CMP:%.*]] = icmp ne i32* %arg1, null, !nosanitize + // CHECK-NEXT: [[DO_RV_CHECK:%.*]] = and i1 true, [[ARG1CMP]] + // CHECK: br i1 [[DO_RV_CHECK]], label %[[NULL:.*]], label %[[NONULL:.*]], !nosanitize + // CHECK: [[NULL]]: + // CHECK-NEXT: [[ICMP:%.*]] = icmp ne i32* {{.*}}, null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_return{{.*}} + return arg1; + // CHECK: [[NONULL]]: + // CHECK-NEXT: ret i32* +} +@end + +// CHECK-LABEL: define void @call_A +void call_A(A *a, int *p) { + // CHECK: [[ICMP:%.*]] = icmp ne i32* [[P1:%.*]], null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_arg{{.*}} !nosanitize + // CHECK: call i32* {{.*}} @objc_msgSend to i32* {{.*}}({{.*}}, i32* [[P1]]) + [a objc_method: p]; + + // CHECK: [[ICMP:%.*]] = icmp ne i32* [[P2:%.*]], null, !nosanitize + // CHECK-NEXT: br i1 [[ICMP]], {{.*}}, !nosanitize + // CHECK: call void @__ubsan_handle_nonnull_arg{{.*}} !nosanitize + // CHECK: call i32* {{.*}} @objc_msgSend to i32* {{.*}}({{.*}}, i32* [[P2]]) + [A objc_clsmethod: p]; +} + +void dont_crash(int *_Nonnull p, ...) {} + +int main() { + nonnull_retval1(NULL); + nonnull_retval2(NULL, NULL, NULL, NULL, 0, 0, 0, 0); + call_func_with_nonnull_arg(NULL); + nonnull_assign1(NULL); + nonnull_assign2(NULL); + nonnull_assign3(NULL); + nonnull_init1(NULL); + nonnull_init2(NULL); + call_A(NULL, NULL); + dont_crash(NULL, NULL); + return 0; +}