//===-- NullabilityChecker.cpp - Nullability checker ----------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This checker tries to find nullability violations. There are several kinds of // possible violations: // * Null pointer is passed to a pointer which has a _Nonnull type. // * Null pointer is returned from a function which has a _Nonnull return type. // * Nullable pointer is passed to a pointer which has a _Nonnull type. // * Nullable pointer is returned from a function which has a _Nonnull return // type. // * Nullable pointer is dereferenced. // // This checker propagates the nullability information of the pointers and looks // for the patterns that are described above. Explicit casts are trusted and are // considered a way to suppress false positives for this checker. The other way // to suppress warnings would be to add asserts or guarding if statements to the // code. In addition to the nullability propagation this checker also uses some // heuristics to suppress potential false positives. // //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/Analysis/AnyCall.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Path.h" usingnamespaceclang; usingnamespaceento; namespace { /// Returns the most nullable nullability. This is used for message expressions /// like [receiver method], where the nullability of this expression is either /// the nullability of the receiver or the nullability of the return type of the /// method, depending on which is more nullable. Contradicted is considered to /// be the most nullable, to avoid false positive results. Nullability getMostNullable(Nullability Lhs, Nullability Rhs) { … } const char *getNullabilityString(Nullability Nullab) { … } // These enums are used as an index to ErrorMessages array. enum class ErrorKind : int { … }; class NullabilityChecker : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, check::PostCall, check::PostStmt<ExplicitCastExpr>, check::PostObjCMessage, check::DeadSymbols, eval::Assume, check::Location, check::Event<ImplicitNullDerefEvent>, check::BeginFunction> { … }; class NullabilityState { … }; bool operator==(NullabilityState Lhs, NullabilityState Rhs) { … } // For the purpose of tracking historical property accesses, the key for lookup // is an object pointer (could be an instance or a class) paired with the unique // identifier for the property being invoked on that object. ObjectPropPair; // Metadata associated with the return value from a recorded property access. struct ConstrainedPropertyVal { … }; bool operator==(const ConstrainedPropertyVal &Lhs, const ConstrainedPropertyVal &Rhs) { … } } // end anonymous namespace REGISTER_MAP_WITH_PROGRAMSTATE(…) … REGISTER_MAP_WITH_PROGRAMSTATE(…) // We say "the nullability type invariant is violated" when a location with a // non-null type contains NULL or a function with a non-null return type returns // NULL. Violations of the nullability type invariant can be detected either // directly (for example, when NULL is passed as an argument to a nonnull // parameter) or indirectly (for example, when, inside a function, the // programmer defensively checks whether a nonnull parameter contains NULL and // finds that it does). // // As a matter of policy, the nullability checker typically warns on direct // violations of the nullability invariant (although it uses various // heuristics to suppress warnings in some cases) but will not warn if the // invariant has already been violated along the path (either directly or // indirectly). As a practical matter, this prevents the analyzer from // (1) warning on defensive code paths where a nullability precondition is // determined to have been violated, (2) warning additional times after an // initial direct violation has been discovered, and (3) warning after a direct // violation that has been implicitly or explicitly suppressed (for // example, with a cast of NULL to _Nonnull). In essence, once an invariant // violation is detected on a path, this checker will be essentially turned off // for the rest of the analysis // // The analyzer takes this approach (rather than generating a sink node) to // ensure coverage of defensive paths, which may be important for backwards // compatibility in codebases that were developed without nullability in mind. REGISTER_TRAIT_WITH_PROGRAMSTATE(…) … enum class NullConstraint { … }; static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, ProgramStateRef State) { … } static bool isValidPointerType(QualType T) { … } const SymbolicRegion * NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { … } PathDiagnosticPieceRef NullabilityChecker::NullabilityBugVisitor::VisitNode( const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &BR) { … } /// Returns true when the value stored at the given location has been /// constrained to null after being passed through an object of nonnnull type. static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, SVal LV, QualType T) { … } static bool checkParamsForPreconditionViolation(ArrayRef<ParmVarDecl *> Params, ProgramStateRef State, const LocationContext *LocCtxt) { … } static bool checkSelfIvarsForInvariantViolation(ProgramStateRef State, const LocationContext *LocCtxt) { … } static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, CheckerContext &C) { … } void NullabilityChecker::reportBugIfInvariantHolds( StringRef Msg, ErrorKind Error, CheckKind CK, ExplodedNode *N, const MemRegion *Region, CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { … } /// Cleaning up the program state. void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { … } /// This callback triggers when a pointer is dereferenced and the analyzer does /// not know anything about the value of that pointer. When that pointer is /// nullable, this code emits a warning. void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { … } void NullabilityChecker::checkBeginFunction(CheckerContext &C) const { … } // Whenever we see a load from a typed memory region that's been annotated as // 'nonnull', we want to trust the user on that and assume that it is is indeed // non-null. // // We do so even if the value is known to have been assigned to null. // The user should be warned on assigning the null value to a non-null pointer // as opposed to warning on the later dereference of this pointer. // // \code // int * _Nonnull var = 0; // we want to warn the user here... // // . . . // *var = 42; // ...and not here // \endcode void NullabilityChecker::checkLocation(SVal Location, bool IsLoad, const Stmt *S, CheckerContext &Context) const { … } /// Find the outermost subexpression of E that is not an implicit cast. /// This looks through the implicit casts to _Nonnull that ARC adds to /// return expressions of ObjC types when the return type of the function or /// method is non-null but the express is not. static const Expr *lookThroughImplicitCasts(const Expr *E) { … } /// This method check when nullable pointer or null value is returned from a /// function that has nonnull return type. void NullabilityChecker::checkPreStmt(const ReturnStmt *S, CheckerContext &C) const { … } /// This callback warns when a nullable pointer or a null value is passed to a /// function that expects its argument to be nonnull. void NullabilityChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { … } /// Suppress the nullability warnings for some functions. void NullabilityChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { … } static Nullability getReceiverNullability(const ObjCMethodCall &M, ProgramStateRef State) { … } // The return value of a property access is typically a temporary value which // will not be tracked in a persistent manner by the analyzer. We use // evalAssume() in order to immediately record constraints on those temporaries // at the time they are imposed (e.g. by a nil-check conditional). ProgramStateRef NullabilityChecker::evalAssume(ProgramStateRef State, SVal Cond, bool Assumption) const { … } /// Calculate the nullability of the result of a message expr based on the /// nullability of the receiver, the nullability of the return value, and the /// constraints. void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const { … } /// Explicit casts are trusted. If there is a disagreement in the nullability /// annotations in the destination and the source or '0' is casted to nonnull /// track the value as having contraditory nullability. This will allow users to /// suppress warnings. void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const { … } /// For a given statement performing a bind, attempt to syntactically /// match the expression resulting in the bound value. static const Expr * matchValueExprForBind(const Stmt *S) { … } /// Returns true if \param S is a DeclStmt for a local variable that /// ObjC automated reference counting initialized with zero. static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S) { … } /// Propagate the nullability information through binds and warn when nullable /// pointer or null symbol is assigned to a pointer with a nonnull type. void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const { … } void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep) const { … } void ento::registerNullabilityBase(CheckerManager &mgr) { … } bool ento::shouldRegisterNullabilityBase(const CheckerManager &mgr) { … } #define REGISTER_CHECKER(name, trackingRequired) … // The checks are likely to be turned on by default and it is possible to do // them without tracking any nullability related information. As an optimization // no nullability information will be tracked when only these two checks are // enables. REGISTER_CHECKER(NullPassedToNonnull, false) REGISTER_CHECKER(NullReturnedFromNonnull, false) REGISTER_CHECKER(NullableDereferenced, true) REGISTER_CHECKER(NullablePassedToNonnull, true) REGISTER_CHECKER(NullableReturnedFromNonnull, true)