29 #include "llvm/Support/Path.h"
36 using namespace clang;
63 std::min(static_cast<char>(Lhs), static_cast<char>(Rhs)));
66 const char *getNullabilityString(
Nullability Nullab) {
68 case Nullability::Contradicted:
69 return "contradicted";
74 case Nullability::Nonnull:
77 llvm_unreachable(
"Unexpected enumeration.");
86 NullableAssignedToNonnull,
87 NullableReturnedToNonnull,
89 NullablePassedToNonnull
92 const char *
const ErrorMessages[] = {
93 "Null is assigned to a pointer which is expected to have non-null value",
94 "Null passed to a callee that requires a non-null argument",
95 "Null is returned from a function that is expected to return a non-null "
97 "Nullable pointer is assigned to a pointer which is expected to have "
99 "Nullable pointer is returned from a function that is expected to return a "
101 "Nullable pointer is dereferenced",
102 "Nullable pointer is passed to a callee that requires a non-null argument"};
104 class NullabilityChecker
105 :
public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
106 check::PostCall, check::PostStmt<ExplicitCastExpr>,
107 check::PostObjCMessage, check::DeadSymbols,
108 check::Event<ImplicitNullDerefEvent>> {
109 mutable std::unique_ptr<BugType> BT;
122 const char *Sep)
const override;
124 struct NullabilityChecksFilter {
132 CheckName CheckNameNullReturnedFromNonnull;
134 CheckName CheckNameNullablePassedToNonnull;
135 CheckName CheckNameNullableReturnedFromNonnull;
138 NullabilityChecksFilter Filter;
146 class NullabilityBugVisitor
149 NullabilityBugVisitor(
const MemRegion *M) : Region(M) {}
151 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
154 ID.AddPointer(Region);
174 const Stmt *ValueExpr =
nullptr,
175 bool SuppressPath =
false)
const;
180 BT.reset(
new BugType(
this,
"Nullability",
"Memory error"));
181 const char *Msg = ErrorMessages[
static_cast<int>(Error)];
182 std::unique_ptr<BugReport> R(
new BugReport(*BT, Msg, N));
184 R->markInteresting(Region);
185 R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region));
188 R->addRange(ValueExpr->getSourceRange());
189 if (Error == ErrorKind::NilAssignedToNonnull ||
190 Error == ErrorKind::NilPassedToNonnull ||
191 Error == ErrorKind::NilReturnedToNonnull)
200 bool CheckSuperRegion =
false)
const;
203 class NullabilityState {
206 : Nullab(Nullab), Source(Source) {}
208 const Stmt *getNullabilitySource()
const {
return Source; }
212 void Profile(llvm::FoldingSetNodeID &ID)
const {
213 ID.AddInteger(static_cast<char>(Nullab));
214 ID.AddPointer(Source);
217 void print(raw_ostream &Out)
const {
218 Out << getNullabilityString(Nullab) <<
"\n";
230 bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
231 return Lhs.getValue() == Rhs.getValue() &&
232 Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
247 enum class NullConstraint { IsNull, IsNotNull,
Unknown };
253 return NullConstraint::IsNotNull;
255 return NullConstraint::IsNull;
260 NullabilityChecker::getTrackRegion(
SVal Val,
bool CheckSuperRegion)
const {
268 const MemRegion *Region = RegionSVal->getRegion();
270 if (CheckSuperRegion) {
274 return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
286 const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region);
287 const NullabilityState *TrackedNullabPrev =
288 StatePrev->get<NullabilityMap>(Region);
292 if (TrackedNullabPrev &&
293 TrackedNullabPrev->getValue() == TrackedNullab->getValue())
297 const Stmt *
S = TrackedNullab->getNullabilitySource();
308 std::string InfoText =
309 (llvm::Twine(
"Nullability '") +
310 getNullabilityString(TrackedNullab->getValue()) +
"' is infered")
326 return Nullability::Nonnull;
330 template <
typename ParamVarDeclRange>
335 for (
const auto *ParamDecl : Params) {
336 if (ParamDecl->isParameterPack())
342 auto RegVal = State->getLValue(ParamDecl, LocCtxt)
343 .template getAs<loc::MemRegionVal>();
347 auto ParamValue = State->getSVal(RegVal->getRegion())
348 .
template getAs<DefinedOrUnknownSVal>();
361 if (State->get<PreconditionViolated>())
369 if (
const auto *BlockD = dyn_cast<BlockDecl>(D)) {
379 if (
const auto *FuncDecl = dyn_cast<FunctionDecl>(D)) {
391 void NullabilityChecker::reportBugIfPreconditionHolds(
399 OriginalState = OriginalState->set<PreconditionViolated>(
true);
407 void NullabilityChecker::checkDeadSymbols(
SymbolReaper &SR,
413 NullabilityMapTy Nullabilities = State->get<NullabilityMap>();
415 E = Nullabilities.end();
418 assert(Region &&
"Non-symbolic region is tracked.");
419 if (SR.
isDead(Region->getSymbol())) {
420 State = State->remove<NullabilityMap>(
I->first);
440 getTrackRegion(Event.
Location,
true);
445 const NullabilityState *TrackedNullability =
446 State->get<NullabilityMap>(Region);
448 if (!TrackedNullability)
451 if (Filter.CheckNullableDereferenced &&
457 reportBug(ErrorKind::NullableDereferenced, Event.
SinkNode, Region, BR);
459 reportBug(ErrorKind::NullablePassedToNonnull, Event.
SinkNode, Region, BR);
469 void NullabilityChecker::checkPreStmt(
const ReturnStmt *S,
475 if (!RetExpr->getType()->isAnyPointerType())
479 if (State->get<PreconditionViolated>())
506 if (Filter.CheckNullReturnedFromNonnull &&
507 Nullness == NullConstraint::IsNull &&
508 RetExprTypeLevelNullability != Nullability::Nonnull &&
509 RequiredNullability == Nullability::Nonnull) {
514 reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N,
nullptr, C,
519 const MemRegion *Region = getTrackRegion(*RetSVal);
523 const NullabilityState *TrackedNullability =
524 State->get<NullabilityMap>(Region);
525 if (TrackedNullability) {
526 Nullability TrackedNullabValue = TrackedNullability->getValue();
527 if (Filter.CheckNullableReturnedFromNonnull &&
528 Nullness != NullConstraint::IsNotNull &&
530 RequiredNullability == Nullability::Nonnull) {
533 reportBugIfPreconditionHolds(ErrorKind::NullableReturnedToNonnull, N,
539 State = State->set<NullabilityMap>(Region,
540 NullabilityState(RequiredNullability,
548 void NullabilityChecker::checkPreCall(
const CallEvent &Call,
554 if (State->get<PreconditionViolated>())
561 if (Param->isParameterPack())
564 const Expr *ArgExpr =
nullptr;
571 if (!Param->getType()->isAnyPointerType() &&
572 !Param->getType()->isReferenceType())
582 if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull &&
583 ArgExprTypeLevelNullability != Nullability::Nonnull &&
584 RequiredNullability == Nullability::Nonnull) {
588 reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N,
nullptr, C,
593 const MemRegion *Region = getTrackRegion(*ArgSVal);
597 const NullabilityState *TrackedNullability =
598 State->get<NullabilityMap>(Region);
600 if (TrackedNullability) {
601 if (Nullness == NullConstraint::IsNotNull ||
605 if (Filter.CheckNullablePassedToNonnull &&
606 RequiredNullability == Nullability::Nonnull) {
608 reportBugIfPreconditionHolds(ErrorKind::NullablePassedToNonnull, N,
609 Region, C, ArgExpr,
true);
612 if (Filter.CheckNullableDereferenced &&
613 Param->getType()->isReferenceType()) {
615 reportBugIfPreconditionHolds(ErrorKind::NullableDereferenced, N, Region,
624 State = State->set<NullabilityMap>(
625 Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr));
627 if (State != OrigState)
632 void NullabilityChecker::checkPostCall(
const CallEvent &Call,
647 if (State->get<PreconditionViolated>())
658 if (llvm::sys::path::filename(FilePath).startswith(
"CG")) {
659 State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
664 const NullabilityState *TrackedNullability =
665 State->get<NullabilityMap>(Region);
667 if (!TrackedNullability &&
679 return Nullability::Nonnull;
687 if (Nullness == NullConstraint::IsNotNull)
688 return Nullability::Nonnull;
691 if (ValueRegionSVal) {
692 const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
695 const NullabilityState *TrackedSelfNullability =
696 State->get<NullabilityMap>(SelfRegion);
697 if (TrackedSelfNullability)
698 return TrackedSelfNullability->getValue();
706 void NullabilityChecker::checkPostObjCMessage(
const ObjCMethodCall &M,
716 if (State->get<PreconditionViolated>())
719 const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue());
723 auto Interface =
Decl->getClassInterface();
728 if (
Name.startswith(
"NS")) {
739 State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
745 if (
Name.find(
"Array") != StringRef::npos &&
746 (FirstSelectorSlot ==
"firstObject" ||
747 FirstSelectorSlot ==
"lastObject")) {
749 State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
758 if (
Name.find(
"String") != StringRef::npos) {
760 if (Param->getName() ==
"encoding") {
761 State = State->set<NullabilityMap>(ReturnRegion,
762 Nullability::Contradicted);
773 const NullabilityState *NullabilityOfReturn =
774 State->get<NullabilityMap>(ReturnRegion);
776 if (NullabilityOfReturn) {
780 Nullability RetValTracked = NullabilityOfReturn->getValue();
782 getMostNullable(RetValTracked, SelfNullability);
783 if (ComputedNullab != RetValTracked &&
785 const Stmt *NullabilitySource =
786 ComputedNullab == RetValTracked
787 ? NullabilityOfReturn->getNullabilitySource()
789 State = State->set<NullabilityMap>(
790 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
804 RetNullability = Nullability::Nonnull;
806 Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
808 const Stmt *NullabilitySource = ComputedNullab == RetNullability
811 State = State->set<NullabilityMap>(
812 ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
831 if (State->get<PreconditionViolated>())
843 const MemRegion *Region = getTrackRegion(*RegionSVal);
848 if (DestNullability == Nullability::Nonnull) {
850 if (Nullness == NullConstraint::IsNull) {
851 State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
857 const NullabilityState *TrackedNullability =
858 State->get<NullabilityMap>(Region);
860 if (!TrackedNullability) {
863 State = State->set<NullabilityMap>(Region,
864 NullabilityState(DestNullability, CE));
869 if (TrackedNullability->getValue() != DestNullability &&
870 TrackedNullability->getValue() != Nullability::Contradicted) {
871 State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
880 if (
auto *BinOp = dyn_cast<BinaryOperator>(S)) {
882 return BinOp->getRHS();
886 if (
auto *DS = dyn_cast<DeclStmt>(S)) {
887 if (DS->isSingleDecl()) {
888 auto *VD = dyn_cast<
VarDecl>(DS->getSingleDecl());
892 if (
const Expr *Init = VD->getInit())
921 if (!DS || !DS->isSingleDecl())
924 auto *VD = dyn_cast<
VarDecl>(DS->getSingleDecl());
929 if(!VD->getType().getQualifiers().hasObjCLifetime())
932 const Expr *Init = VD->getInit();
933 assert(Init &&
"ObjC local under ARC without initializer");
936 if (!isa<ImplicitValueInitExpr>(Init))
944 void NullabilityChecker::checkBind(
SVal L,
SVal V,
const Stmt *S,
947 dyn_cast_or_null<TypedValueRegion>(L.
getAsRegion());
956 if (State->get<PreconditionViolated>())
960 if (!ValDefOrUnknown)
966 if (
SymbolRef Sym = ValDefOrUnknown->getAsSymbol())
970 if (Filter.CheckNullPassedToNonnull &&
971 RhsNullness == NullConstraint::IsNull &&
972 ValNullability != Nullability::Nonnull &&
973 LocNullability == Nullability::Nonnull &&
984 reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N,
nullptr, C,
991 const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown);
995 const NullabilityState *TrackedNullability =
996 State->get<NullabilityMap>(ValueRegion);
998 if (TrackedNullability) {
999 if (RhsNullness == NullConstraint::IsNotNull ||
1002 if (Filter.CheckNullablePassedToNonnull &&
1003 LocNullability == Nullability::Nonnull) {
1006 reportBugIfPreconditionHolds(ErrorKind::NullableAssignedToNonnull, N,
1017 const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() :
S;
1018 State = State->set<NullabilityMap>(
1019 ValueRegion, NullabilityState(ValNullability, NullabilitySource));
1025 const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() :
S;
1026 State = State->set<NullabilityMap>(
1027 ValueRegion, NullabilityState(LocNullability, NullabilitySource));
1032 void NullabilityChecker::printState(raw_ostream &Out,
ProgramStateRef State,
1033 const char *NL,
const char *Sep)
const {
1035 NullabilityMapTy B = State->get<NullabilityMap>();
1043 Out <<
I->first <<
" : ";
1044 I->second.print(Out);
1049 #define REGISTER_CHECKER(name, trackingRequired) \
1050 void ento::register##name##Checker(CheckerManager &mgr) { \
1051 NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \
1052 checker->Filter.Check##name = true; \
1053 checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
1054 checker->NeedTracking = checker->NeedTracking || trackingRequired; \
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
TypedValueRegion - An abstract class representing regions having a typed value.
bool isConstrainedFalse() const
Return true if the constraint is perfectly constrained to 'false'.
A (possibly-)qualified type.
MemRegion - The root abstract class for all memory regions.
ExplodedNode * generateErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generate a transition to a node that will be used to report an error.
bool hasDeadSymbols() const
bool isInstanceMessage() const
bool operator==(CanQual< T > x, CanQual< U > y)
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
FunctionType - C99 6.7.5.3 - Function Declarators.
A helper class which wraps a boolean value set to false by default.
ProgramPoint getLocation() const
getLocation - Returns the edge associated with the given node.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
virtual QualType getValueType() const =0
Decl - This represents one declaration (or definition), e.g.
const RegionTy * getAs() const
The base class of the type hierarchy.
static Nullability getReceiverNullability(const ObjCMethodCall &M, ProgramStateRef State)
VarDecl - An instance of this class is created to represent a variable declaration or definition...
bool isReceiverSelfOrSuper() const
Checks if the receiver refers to 'self' or 'super'.
ExplodedNode * getPredecessor()
Returns the previous node in the exploded graph, which includes the state of the program before the c...
ParmVarDecl - Represents a parameter to a function.
const bool wasInlined
If we are post visiting a call, this flag will be set if the call was inlined.
static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, ProgramStateRef State)
class LLVM_ALIGNAS(8) DependentTemplateSpecializationType const IdentifierInfo * Name
Represents a template specialization type whose template cannot be resolved, e.g. ...
AnalysisDeclContext contains the context data for the function or method under analysis.
bool isAnyPointerType() const
This class provides a convenience implementation for clone() using the Curiously-Recurring Template P...
AnalysisDeclContext * getAnalysisDeclContext() const
BugReporter & getBugReporter()
Values of this type can be null.
Represents any expression that calls an Objective-C method.
virtual Kind getKind() const =0
Returns the kind of call this is.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
const LangOptions & getLangOpts() const
QualType getReturnType() const
Whether values of this type can be null is (explicitly) unspecified.
A builtin binary operation expression such as "x + y" or "x <= y".
const Decl * getDecl() const
detail::InMemoryDirectory::const_iterator I
We dereferenced a location that may be null.
const LocationContext * getLocationContext() const
ArrayRef< ParmVarDecl * > parameters() const override
StringRef getFilename(SourceLocation SpellingLoc) const
Return the filename of the file containing a SourceLocation.
ID
Defines the set of possible language-specific address spaces.
SymbolicRegion - A special, "non-concrete" region.
bool isDead(SymbolRef sym) const
Returns whether or not a symbol has been confirmed dead.
virtual const Expr * getArgExpr(unsigned Index) const
Returns the expression associated with a given argument.
Expr - This represents one expression.
const ProgramStateRef & getState() const
StringRef getName() const
Return the actual identifier string.
virtual ArrayRef< ParmVarDecl * > parameters() const =0
Return call's formal parameters.
const ProgramStateRef & getState() const
Optional< T > getAs() const
Convert to the specified SVal type, returning None if this SVal is not of the desired type...
static SVal getValue(SVal val, SValBuilder &svalBuilder)
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
An expression that sends a message to the given Objective-C object or class.
#define REGISTER_CHECKER(name, trackingRequired)
REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, NullabilityState) enum class NullConstraint
BugReporter is a utility class for generating PathDiagnostics for analysis.
SourceLocation getLocStart() const LLVM_READONLY
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a typedef named NameTy...
const TemplateArgument * iterator
static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S)
Returns true if.
void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
DeclStmt - Adaptor class for mixing declarations with statements and expressions. ...
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Selector getSelector() const
void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx)
const Decl * getDecl() const
A class responsible for cleaning up unused symbols.
static const Expr * matchValueExprForBind(const Stmt *S)
For a given statement performing a bind, attempt to syntactically match the expression resulting in t...
const ObjCMethodDecl * getDecl() const override
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
ASTContext & getASTContext()
const FunctionType * getFunctionType(bool BlocksToo=true) const
Looks through the Decl's underlying type to extract a FunctionType when possible. ...
detail::InMemoryDirectory::const_iterator E
const MemRegion * getAsRegion() const
const Expr * getRetValue() const
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
Represents an abstract call to a function or method along a particular path.
ExplicitCastExpr - An explicit cast written in the source code.
Optional< T > getAs() const
Convert to the specified ProgramPoint type, returning None if this ProgramPoint is not of the desired...
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
const T * getAs() const
Member-template getAs<specific type>'.
static bool checkParamsForPreconditionViolation(const ParamVarDeclRange &Params, ProgramStateRef State, const LocationContext *LocCtxt)
static Nullability getNullabilityAnnotation(QualType Type)
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
An attributed type is a type to which a type attribute has been applied.
static bool checkPreconditionViolation(ProgramStateRef State, ExplodedNode *N, CheckerContext &C)
bool trackNullOrUndefValue(const ExplodedNode *N, const Stmt *S, BugReport &R, bool IsArg=false, bool EnableNullFPSuppression=true)
Attempts to add visitors to trace a null or undefined value back to its point of origin, whether it is a symbol constrained to null or an explicit assignment.
SourceManager & getSourceManager()
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
ElementRegin is used to represent both array elements and casts.
Tag that can use a checker name as a message provider (see SimpleProgramPointTag).
This class provides an interface through which checkers can create individual bug reports...
virtual const ObjCMessageExpr * getOriginExpr() const
SVal getReturnValue() const
Returns the return value of the call.
This class handles loading and caching of source files into memory.
SourceManager & getSourceManager()
const LocationContext * getLocationContext() const