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