summaryrefslogtreecommitdiffstats
path: root/clang
diff options
context:
space:
mode:
Diffstat (limited to 'clang')
-rw-r--r--clang/docs/UndefinedBehaviorSanitizer.rst12
-rw-r--r--clang/include/clang/Basic/Sanitizers.def5
-rw-r--r--clang/lib/CodeGen/CGCall.cpp110
-rw-r--r--clang/lib/CodeGen/CGDecl.cpp38
-rw-r--r--clang/lib/CodeGen/CGExprScalar.cpp6
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.cpp12
-rw-r--r--clang/lib/CodeGen/CodeGenFunction.h17
-rw-r--r--clang/lib/Driver/SanitizerArgs.cpp11
-rw-r--r--clang/lib/Driver/ToolChain.cpp3
-rw-r--r--clang/test/CodeGenObjC/ubsan-nonnull-and-nullability.m31
-rw-r--r--clang/test/CodeGenObjC/ubsan-nullability.m182
11 files changed, 398 insertions, 29 deletions
diff --git a/clang/docs/UndefinedBehaviorSanitizer.rst b/clang/docs/UndefinedBehaviorSanitizer.rst
index 7ff7be93184..f9111ee846d 100644
--- a/clang/docs/UndefinedBehaviorSanitizer.rst
+++ b/clang/docs/UndefinedBehaviorSanitizer.rst
@@ -92,6 +92,12 @@ Available checks are:
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 @@ Available checks are:
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
=====================================
diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def
index c81273ea5fd..c574045e139 100644
--- a/clang/include/clang/Basic/Sanitizers.def
+++ b/clang/include/clang/Basic/Sanitizers.def
@@ -64,6 +64,11 @@ SANITIZER("function", Function)
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)
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index b0dd24b2f54..5f069bfe46b 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -2912,19 +2912,7 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
llvm::Instruction *Ret;
if (RV) {
- if (CurCodeDecl && SanOpts.has(SanitizerKind::ReturnsNonnullAttribute)) {
- if (auto RetNNAttr = CurCodeDecl->getAttr<ReturnsNonNullAttr>()) {
- 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 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
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<ReturnsNonNullAttr>();
+
+ 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<DeclaratorDecl>(CurCodeDecl))
+ if (auto *TSI = DD->getTypeSourceInfo())
+ if (auto FTL = TSI->getTypeLoc().castAs<FunctionTypeLoc>())
+ 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 @@ void CodeGenFunction::EmitNonNullArgCheck(RValue RV, QualType ArgType,
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(
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 4ee8b98e67c..cfadce5c221 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -672,6 +672,28 @@ static void drillIntoBlockVariable(CodeGenFunction &CGF,
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 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
llvm::Value *value = EmitScalarExpr(init);
if (capturedByInit)
drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D));
+ EmitNullabilityCheck(lvalue, value, init->getExprLoc());
EmitStoreThroughLValue(RValue::get(value), lvalue, true);
return;
}
@@ -767,6 +790,8 @@ void CodeGenFunction::EmitScalarInit(const Expr *init, const ValueDecl *D,
if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(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 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
if (D.hasAttr<AnnotateAttr>())
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,
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 2896e3556e5..67f821f2f09 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3132,10 +3132,12 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) {
// 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.
diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index 5f793f8e775..11660b4f419 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -822,6 +822,18 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
}
}
+ // 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<ReturnsNonNullAttr>()))
+ 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").
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index d7b1993d577..2db8a18ceff 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -1375,6 +1375,16 @@ private:
/// information about the layout of the variable.
llvm::DenseMap<const ValueDecl *, BlockByrefInfo> 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 @@ public:
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 @@ public:
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);
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index b0f2caefc9a..b05596a99f6 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -26,18 +26,19 @@ using namespace clang::driver;
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,
};
diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index fbbdb1a19a3..f9dafb54753 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -697,7 +697,8 @@ SanitizerMask ToolChain::getSupportedSanitizers() const {
// 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 ||
diff --git a/clang/test/CodeGenObjC/ubsan-nonnull-and-nullability.m b/clang/test/CodeGenObjC/ubsan-nonnull-and-nullability.m
new file mode 100644
index 00000000000..b927f55cd40
--- /dev/null
+++ b/clang/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);
+}
diff --git a/clang/test/CodeGenObjC/ubsan-nullability.m b/clang/test/CodeGenObjC/ubsan-nullability.m
new file mode 100644
index 00000000000..d72b774ec7e
--- /dev/null
+++ b/clang/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;
+}
OpenPOWER on IntegriCloud