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