| //== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==// |
| // |
| // 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 adds nullability-related assumptions: |
| // |
| // 1. Methods annotated with _Nonnull |
| // which come from system headers actually return a non-null pointer. |
| // |
| // 2. NSDictionary key is non-null after the keyword subscript operation |
| // on read if and only if the resulting expression is non-null. |
| // |
| // 3. NSMutableDictionary index is non-null after a write operation. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| #include "clang/Analysis/SelectorExtras.h" |
| #include "clang/StaticAnalyzer/Core/Checker.h" |
| #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
| |
| using namespace clang; |
| using namespace ento; |
| |
| /// Records implications between symbols. |
| /// The semantics is: |
| /// (antecedent != 0) => (consequent != 0) |
| /// These implications are then read during the evaluation of the assumption, |
| /// and the appropriate antecedents are applied. |
| REGISTER_MAP_WITH_PROGRAMSTATE(NonNullImplicationMap, SymbolRef, SymbolRef) |
| |
| /// The semantics is: |
| /// (antecedent == 0) => (consequent == 0) |
| REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap, SymbolRef, SymbolRef) |
| |
| namespace { |
| |
| class TrustNonnullChecker : public Checker<check::PostCall, |
| check::PostObjCMessage, |
| check::DeadSymbols, |
| eval::Assume> { |
| // Do not try to iterate over symbols with higher complexity. |
| static unsigned constexpr ComplexityThreshold = 10; |
| Selector ObjectForKeyedSubscriptSel; |
| Selector ObjectForKeySel; |
| Selector SetObjectForKeyedSubscriptSel; |
| Selector SetObjectForKeySel; |
| |
| public: |
| TrustNonnullChecker(ASTContext &Ctx) |
| : ObjectForKeyedSubscriptSel( |
| getKeywordSelector(Ctx, "objectForKeyedSubscript")), |
| ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")), |
| SetObjectForKeyedSubscriptSel( |
| getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")), |
| SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {} |
| |
| ProgramStateRef evalAssume(ProgramStateRef State, |
| SVal Cond, |
| bool Assumption) const { |
| const SymbolRef CondS = Cond.getAsSymbol(); |
| if (!CondS || CondS->computeComplexity() > ComplexityThreshold) |
| return State; |
| |
| for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) { |
| const SymbolRef Antecedent = *B; |
| State = addImplication(Antecedent, State, true); |
| State = addImplication(Antecedent, State, false); |
| } |
| |
| return State; |
| } |
| |
| void checkPostCall(const CallEvent &Call, CheckerContext &C) const { |
| // Only trust annotations for system headers for non-protocols. |
| if (!Call.isInSystemHeader()) |
| return; |
| |
| ProgramStateRef State = C.getState(); |
| |
| if (isNonNullPtr(Call, C)) |
| if (auto L = Call.getReturnValue().getAs<Loc>()) |
| State = State->assume(*L, /*assumption=*/true); |
| |
| C.addTransition(State); |
| } |
| |
| void checkPostObjCMessage(const ObjCMethodCall &Msg, |
| CheckerContext &C) const { |
| const ObjCInterfaceDecl *ID = Msg.getReceiverInterface(); |
| if (!ID) |
| return; |
| |
| ProgramStateRef State = C.getState(); |
| |
| // Index to setter for NSMutableDictionary is assumed to be non-null, |
| // as an exception is thrown otherwise. |
| if (interfaceHasSuperclass(ID, "NSMutableDictionary") && |
| (Msg.getSelector() == SetObjectForKeyedSubscriptSel || |
| Msg.getSelector() == SetObjectForKeySel)) { |
| if (auto L = Msg.getArgSVal(1).getAs<Loc>()) |
| State = State->assume(*L, /*assumption=*/true); |
| } |
| |
| // Record an implication: index is non-null if the output is non-null. |
| if (interfaceHasSuperclass(ID, "NSDictionary") && |
| (Msg.getSelector() == ObjectForKeyedSubscriptSel || |
| Msg.getSelector() == ObjectForKeySel)) { |
| SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol(); |
| SymbolRef RetS = Msg.getReturnValue().getAsSymbol(); |
| |
| if (ArgS && RetS) { |
| // Emulate an implication: the argument is non-null if |
| // the return value is non-null. |
| State = State->set<NonNullImplicationMap>(RetS, ArgS); |
| |
| // Conversely, when the argument is null, the return value |
| // is definitely null. |
| State = State->set<NullImplicationMap>(ArgS, RetS); |
| } |
| } |
| |
| C.addTransition(State); |
| } |
| |
| void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { |
| ProgramStateRef State = C.getState(); |
| |
| State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State); |
| State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State); |
| |
| C.addTransition(State); |
| } |
| |
| private: |
| |
| /// \returns State with GDM \p MapName where all dead symbols were |
| // removed. |
| template <typename MapName> |
| ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper, |
| ProgramStateRef State) const { |
| for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) |
| if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) |
| State = State->remove<MapName>(P.first); |
| return State; |
| } |
| |
| /// \returns Whether we trust the result of the method call to be |
| /// a non-null pointer. |
| bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const { |
| QualType ExprRetType = Call.getResultType(); |
| if (!ExprRetType->isAnyPointerType()) |
| return false; |
| |
| if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull) |
| return true; |
| |
| // The logic for ObjC instance method calls is more complicated, |
| // as the return value is nil when the receiver is nil. |
| if (!isa<ObjCMethodCall>(&Call)) |
| return false; |
| |
| const auto *MCall = cast<ObjCMethodCall>(&Call); |
| const ObjCMethodDecl *MD = MCall->getDecl(); |
| |
| // Distrust protocols. |
| if (isa<ObjCProtocolDecl>(MD->getDeclContext())) |
| return false; |
| |
| QualType DeclRetType = MD->getReturnType(); |
| if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull) |
| return false; |
| |
| // For class messages it is sufficient for the declaration to be |
| // annotated _Nonnull. |
| if (!MCall->isInstanceMessage()) |
| return true; |
| |
| // Alternatively, the analyzer could know that the receiver is not null. |
| SVal Receiver = MCall->getReceiverSVal(); |
| ConditionTruthVal TV = C.getState()->isNonNull(Receiver); |
| if (TV.isConstrainedTrue()) |
| return true; |
| |
| return false; |
| } |
| |
| /// \return Whether \p ID has a superclass by the name \p ClassName. |
| bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID, |
| StringRef ClassName) const { |
| if (ID->getIdentifier()->getName() == ClassName) |
| return true; |
| |
| if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) |
| return interfaceHasSuperclass(Super, ClassName); |
| |
| return false; |
| } |
| |
| |
| /// \return a state with an optional implication added (if exists) |
| /// from a map of recorded implications. |
| /// If \p Negated is true, checks NullImplicationMap, and assumes |
| /// the negation of \p Antecedent. |
| /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise. |
| ProgramStateRef addImplication(SymbolRef Antecedent, |
| ProgramStateRef InputState, |
| bool Negated) const { |
| if (!InputState) |
| return nullptr; |
| SValBuilder &SVB = InputState->getStateManager().getSValBuilder(); |
| const SymbolRef *Consequent = |
| Negated ? InputState->get<NonNullImplicationMap>(Antecedent) |
| : InputState->get<NullImplicationMap>(Antecedent); |
| if (!Consequent) |
| return InputState; |
| |
| SVal AntecedentV = SVB.makeSymbolVal(Antecedent); |
| ProgramStateRef State = InputState; |
| |
| if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue()) |
| || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) { |
| SVal ConsequentS = SVB.makeSymbolVal(*Consequent); |
| State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated); |
| if (!State) |
| return nullptr; |
| |
| // Drop implications from the map. |
| if (Negated) { |
| State = State->remove<NonNullImplicationMap>(Antecedent); |
| State = State->remove<NullImplicationMap>(*Consequent); |
| } else { |
| State = State->remove<NullImplicationMap>(Antecedent); |
| State = State->remove<NonNullImplicationMap>(*Consequent); |
| } |
| } |
| |
| return State; |
| } |
| }; |
| |
| } // end empty namespace |
| |
| void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { |
| Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext()); |
| } |
| |
| bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) { |
| return true; |
| } |