//== 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 { // 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 (SymbolRef Antecedent : CondS->symbols()) { 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()) 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()) 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(RetS, ArgS); // Conversely, when the argument is null, the return value // is definitely null. State = State->set(ArgS, RetS); } } C.addTransition(State); } void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { ProgramStateRef State = C.getState(); State = dropDeadFromGDM(SymReaper, State); State = dropDeadFromGDM(SymReaper, State); C.addTransition(State); } private: /// \returns State with GDM \p MapName where all dead symbols were // removed. template ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper, ProgramStateRef State) const { for (const std::pair &P : State->get()) if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) State = State->remove(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(&Call)) return false; const auto *MCall = cast(&Call); const ObjCMethodDecl *MD = MCall->getDecl(); // Distrust protocols. if (isa(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(Antecedent) : InputState->get(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(), Negated); if (!State) return nullptr; // Drop implications from the map. if (Negated) { State = State->remove(Antecedent); State = State->remove(*Consequent); } else { State = State->remove(Antecedent); State = State->remove(*Consequent); } } return State; } }; } // end empty namespace void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { Mgr.registerChecker(Mgr.getASTContext()); } bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) { return true; }