Home | History | Annotate | Line # | Download | only in Checkers
      1 //== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This checker adds nullability-related assumptions:
     10 //
     11 // 1. Methods annotated with _Nonnull
     12 // which come from system headers actually return a non-null pointer.
     13 //
     14 // 2. NSDictionary key is non-null after the keyword subscript operation
     15 // on read if and only if the resulting expression is non-null.
     16 //
     17 // 3. NSMutableDictionary index is non-null after a write operation.
     18 //
     19 //===----------------------------------------------------------------------===//
     20 
     21 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
     22 #include "clang/Analysis/SelectorExtras.h"
     23 #include "clang/StaticAnalyzer/Core/Checker.h"
     24 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
     25 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
     26 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
     27 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
     28 
     29 using namespace clang;
     30 using namespace ento;
     31 
     32 /// Records implications between symbols.
     33 /// The semantics is:
     34 ///    (antecedent != 0) => (consequent != 0)
     35 /// These implications are then read during the evaluation of the assumption,
     36 /// and the appropriate antecedents are applied.
     37 REGISTER_MAP_WITH_PROGRAMSTATE(NonNullImplicationMap, SymbolRef, SymbolRef)
     38 
     39 /// The semantics is:
     40 ///    (antecedent == 0) => (consequent == 0)
     41 REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap, SymbolRef, SymbolRef)
     42 
     43 namespace {
     44 
     45 class TrustNonnullChecker : public Checker<check::PostCall,
     46                                            check::PostObjCMessage,
     47                                            check::DeadSymbols,
     48                                            eval::Assume> {
     49   // Do not try to iterate over symbols with higher complexity.
     50   static unsigned constexpr ComplexityThreshold = 10;
     51   Selector ObjectForKeyedSubscriptSel;
     52   Selector ObjectForKeySel;
     53   Selector SetObjectForKeyedSubscriptSel;
     54   Selector SetObjectForKeySel;
     55 
     56 public:
     57   TrustNonnullChecker(ASTContext &Ctx)
     58       : ObjectForKeyedSubscriptSel(
     59             getKeywordSelector(Ctx, "objectForKeyedSubscript")),
     60         ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")),
     61         SetObjectForKeyedSubscriptSel(
     62             getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")),
     63         SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {}
     64 
     65   ProgramStateRef evalAssume(ProgramStateRef State,
     66                              SVal Cond,
     67                              bool Assumption) const {
     68     const SymbolRef CondS = Cond.getAsSymbol();
     69     if (!CondS || CondS->computeComplexity() > ComplexityThreshold)
     70       return State;
     71 
     72     for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) {
     73       const SymbolRef Antecedent = *B;
     74       State = addImplication(Antecedent, State, true);
     75       State = addImplication(Antecedent, State, false);
     76     }
     77 
     78     return State;
     79   }
     80 
     81   void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
     82     // Only trust annotations for system headers for non-protocols.
     83     if (!Call.isInSystemHeader())
     84       return;
     85 
     86     ProgramStateRef State = C.getState();
     87 
     88     if (isNonNullPtr(Call, C))
     89       if (auto L = Call.getReturnValue().getAs<Loc>())
     90         State = State->assume(*L, /*assumption=*/true);
     91 
     92     C.addTransition(State);
     93   }
     94 
     95   void checkPostObjCMessage(const ObjCMethodCall &Msg,
     96                             CheckerContext &C) const {
     97     const ObjCInterfaceDecl *ID = Msg.getReceiverInterface();
     98     if (!ID)
     99       return;
    100 
    101     ProgramStateRef State = C.getState();
    102 
    103     // Index to setter for NSMutableDictionary is assumed to be non-null,
    104     // as an exception is thrown otherwise.
    105     if (interfaceHasSuperclass(ID, "NSMutableDictionary") &&
    106         (Msg.getSelector() == SetObjectForKeyedSubscriptSel ||
    107          Msg.getSelector() == SetObjectForKeySel)) {
    108       if (auto L = Msg.getArgSVal(1).getAs<Loc>())
    109         State = State->assume(*L, /*assumption=*/true);
    110     }
    111 
    112     // Record an implication: index is non-null if the output is non-null.
    113     if (interfaceHasSuperclass(ID, "NSDictionary") &&
    114         (Msg.getSelector() == ObjectForKeyedSubscriptSel ||
    115          Msg.getSelector() == ObjectForKeySel)) {
    116       SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol();
    117       SymbolRef RetS = Msg.getReturnValue().getAsSymbol();
    118 
    119       if (ArgS && RetS) {
    120         // Emulate an implication: the argument is non-null if
    121         // the return value is non-null.
    122         State = State->set<NonNullImplicationMap>(RetS, ArgS);
    123 
    124         // Conversely, when the argument is null, the return value
    125         // is definitely null.
    126         State = State->set<NullImplicationMap>(ArgS, RetS);
    127       }
    128     }
    129 
    130     C.addTransition(State);
    131   }
    132 
    133   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const {
    134     ProgramStateRef State = C.getState();
    135 
    136     State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State);
    137     State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State);
    138 
    139     C.addTransition(State);
    140   }
    141 
    142 private:
    143 
    144   /// \returns State with GDM \p MapName where all dead symbols were
    145   // removed.
    146   template <typename MapName>
    147   ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper,
    148                                   ProgramStateRef State) const {
    149     for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>())
    150       if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second))
    151         State = State->remove<MapName>(P.first);
    152     return State;
    153   }
    154 
    155   /// \returns Whether we trust the result of the method call to be
    156   /// a non-null pointer.
    157   bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const {
    158     QualType ExprRetType = Call.getResultType();
    159     if (!ExprRetType->isAnyPointerType())
    160       return false;
    161 
    162     if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull)
    163       return true;
    164 
    165     // The logic for ObjC instance method calls is more complicated,
    166     // as the return value is nil when the receiver is nil.
    167     if (!isa<ObjCMethodCall>(&Call))
    168       return false;
    169 
    170     const auto *MCall = cast<ObjCMethodCall>(&Call);
    171     const ObjCMethodDecl *MD = MCall->getDecl();
    172 
    173     // Distrust protocols.
    174     if (isa<ObjCProtocolDecl>(MD->getDeclContext()))
    175       return false;
    176 
    177     QualType DeclRetType = MD->getReturnType();
    178     if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull)
    179       return false;
    180 
    181     // For class messages it is sufficient for the declaration to be
    182     // annotated _Nonnull.
    183     if (!MCall->isInstanceMessage())
    184       return true;
    185 
    186     // Alternatively, the analyzer could know that the receiver is not null.
    187     SVal Receiver = MCall->getReceiverSVal();
    188     ConditionTruthVal TV = C.getState()->isNonNull(Receiver);
    189     if (TV.isConstrainedTrue())
    190       return true;
    191 
    192     return false;
    193   }
    194 
    195   /// \return Whether \p ID has a superclass by the name \p ClassName.
    196   bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID,
    197                          StringRef ClassName) const {
    198     if (ID->getIdentifier()->getName() == ClassName)
    199       return true;
    200 
    201     if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
    202       return interfaceHasSuperclass(Super, ClassName);
    203 
    204     return false;
    205   }
    206 
    207 
    208   /// \return a state with an optional implication added (if exists)
    209   /// from a map of recorded implications.
    210   /// If \p Negated is true, checks NullImplicationMap, and assumes
    211   /// the negation of \p Antecedent.
    212   /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise.
    213   ProgramStateRef addImplication(SymbolRef Antecedent,
    214                                  ProgramStateRef InputState,
    215                                  bool Negated) const {
    216     if (!InputState)
    217       return nullptr;
    218     SValBuilder &SVB = InputState->getStateManager().getSValBuilder();
    219     const SymbolRef *Consequent =
    220         Negated ? InputState->get<NonNullImplicationMap>(Antecedent)
    221                 : InputState->get<NullImplicationMap>(Antecedent);
    222     if (!Consequent)
    223       return InputState;
    224 
    225     SVal AntecedentV = SVB.makeSymbolVal(Antecedent);
    226     ProgramStateRef State = InputState;
    227 
    228     if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue())
    229         || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) {
    230       SVal ConsequentS = SVB.makeSymbolVal(*Consequent);
    231       State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated);
    232       if (!State)
    233         return nullptr;
    234 
    235       // Drop implications from the map.
    236       if (Negated) {
    237         State = State->remove<NonNullImplicationMap>(Antecedent);
    238         State = State->remove<NullImplicationMap>(*Consequent);
    239       } else {
    240         State = State->remove<NullImplicationMap>(Antecedent);
    241         State = State->remove<NonNullImplicationMap>(*Consequent);
    242       }
    243     }
    244 
    245     return State;
    246   }
    247 };
    248 
    249 } // end empty namespace
    250 
    251 void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {
    252   Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext());
    253 }
    254 
    255 bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) {
    256   return true;
    257 }
    258