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