/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 41 | getKeywordSelector(Ctx, "objectForKeyedSubscript")), |
60 | 41 | ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")), |
61 | | SetObjectForKeyedSubscriptSel( |
62 | 41 | getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")), |
63 | 41 | SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {} |
64 | | |
65 | | ProgramStateRef evalAssume(ProgramStateRef State, |
66 | | SVal Cond, |
67 | 6.20k | bool Assumption) const { |
68 | 6.20k | const SymbolRef CondS = Cond.getAsSymbol(); |
69 | 6.20k | if (!CondS || CondS->computeComplexity() > ComplexityThreshold1.98k ) |
70 | 4.22k | return State; |
71 | | |
72 | 4.43k | for (SymbolRef Antecedent : CondS->symbols())1.98k { |
73 | 4.43k | State = addImplication(Antecedent, State, true); |
74 | 4.43k | State = addImplication(Antecedent, State, false); |
75 | 4.43k | } |
76 | | |
77 | 1.98k | return State; |
78 | 6.20k | } |
79 | | |
80 | 206 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const { |
81 | | // Only trust annotations for system headers for non-protocols. |
82 | 206 | if (!Call.isInSystemHeader()) |
83 | 202 | return; |
84 | | |
85 | 4 | ProgramStateRef State = C.getState(); |
86 | | |
87 | 4 | if (isNonNullPtr(Call, C)) |
88 | 0 | if (auto L = Call.getReturnValue().getAs<Loc>()) |
89 | 0 | State = State->assume(*L, /*assumption=*/true); |
90 | | |
91 | 4 | C.addTransition(State); |
92 | 4 | } |
93 | | |
94 | | void checkPostObjCMessage(const ObjCMethodCall &Msg, |
95 | 0 | CheckerContext &C) const { |
96 | 0 | const ObjCInterfaceDecl *ID = Msg.getReceiverInterface(); |
97 | 0 | if (!ID) |
98 | 0 | return; |
99 | | |
100 | 0 | ProgramStateRef State = C.getState(); |
101 | | |
102 | | // Index to setter for NSMutableDictionary is assumed to be non-null, |
103 | | // as an exception is thrown otherwise. |
104 | 0 | if (interfaceHasSuperclass(ID, "NSMutableDictionary") && |
105 | 0 | (Msg.getSelector() == SetObjectForKeyedSubscriptSel || |
106 | 0 | Msg.getSelector() == SetObjectForKeySel)) { |
107 | 0 | if (auto L = Msg.getArgSVal(1).getAs<Loc>()) |
108 | 0 | State = State->assume(*L, /*assumption=*/true); |
109 | 0 | } |
110 | | |
111 | | // Record an implication: index is non-null if the output is non-null. |
112 | 0 | if (interfaceHasSuperclass(ID, "NSDictionary") && |
113 | 0 | (Msg.getSelector() == ObjectForKeyedSubscriptSel || |
114 | 0 | Msg.getSelector() == ObjectForKeySel)) { |
115 | 0 | SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol(); |
116 | 0 | SymbolRef RetS = Msg.getReturnValue().getAsSymbol(); |
117 | |
|
118 | 0 | if (ArgS && RetS) { |
119 | | // Emulate an implication: the argument is non-null if |
120 | | // the return value is non-null. |
121 | 0 | State = State->set<NonNullImplicationMap>(RetS, ArgS); |
122 | | |
123 | | // Conversely, when the argument is null, the return value |
124 | | // is definitely null. |
125 | 0 | State = State->set<NullImplicationMap>(ArgS, RetS); |
126 | 0 | } |
127 | 0 | } |
128 | |
|
129 | 0 | C.addTransition(State); |
130 | 0 | } |
131 | | |
132 | 2.76k | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { |
133 | 2.76k | ProgramStateRef State = C.getState(); |
134 | | |
135 | 2.76k | State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State); |
136 | 2.76k | State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State); |
137 | | |
138 | 2.76k | C.addTransition(State); |
139 | 2.76k | } |
140 | | |
141 | | private: |
142 | | |
143 | | /// \returns State with GDM \p MapName where all dead symbols were |
144 | | // removed. |
145 | | template <typename MapName> |
146 | | ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper, |
147 | 5.53k | ProgramStateRef State) const { |
148 | 5.53k | for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) |
149 | 0 | if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) |
150 | 0 | State = State->remove<MapName>(P.first); |
151 | 5.53k | return State; |
152 | 5.53k | } TrustNonnullChecker.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::TrustNonnullChecker::dropDeadFromGDM<(anonymous namespace)::NullImplicationMap>(clang::ento::SymbolReaper&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>) const Line | Count | Source | 147 | 2.76k | ProgramStateRef State) const { | 148 | 2.76k | for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) | 149 | 0 | if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) | 150 | 0 | State = State->remove<MapName>(P.first); | 151 | 2.76k | return State; | 152 | 2.76k | } |
TrustNonnullChecker.cpp:llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const> (anonymous namespace)::TrustNonnullChecker::dropDeadFromGDM<(anonymous namespace)::NonNullImplicationMap>(clang::ento::SymbolReaper&, llvm::IntrusiveRefCntPtr<clang::ento::ProgramState const>) const Line | Count | Source | 147 | 2.76k | ProgramStateRef State) const { | 148 | 2.76k | for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) | 149 | 0 | if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) | 150 | 0 | State = State->remove<MapName>(P.first); | 151 | 2.76k | return State; | 152 | 2.76k | } |
|
153 | | |
154 | | /// \returns Whether we trust the result of the method call to be |
155 | | /// a non-null pointer. |
156 | 4 | bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const { |
157 | 4 | QualType ExprRetType = Call.getResultType(); |
158 | 4 | if (!ExprRetType->isAnyPointerType()) |
159 | 4 | return false; |
160 | | |
161 | 0 | if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull) |
162 | 0 | return true; |
163 | | |
164 | | // The logic for ObjC instance method calls is more complicated, |
165 | | // as the return value is nil when the receiver is nil. |
166 | 0 | if (!isa<ObjCMethodCall>(&Call)) |
167 | 0 | return false; |
168 | | |
169 | 0 | const auto *MCall = cast<ObjCMethodCall>(&Call); |
170 | 0 | const ObjCMethodDecl *MD = MCall->getDecl(); |
171 | | |
172 | | // Distrust protocols. |
173 | 0 | if (isa<ObjCProtocolDecl>(MD->getDeclContext())) |
174 | 0 | return false; |
175 | | |
176 | 0 | QualType DeclRetType = MD->getReturnType(); |
177 | 0 | if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull) |
178 | 0 | return false; |
179 | | |
180 | | // For class messages it is sufficient for the declaration to be |
181 | | // annotated _Nonnull. |
182 | 0 | if (!MCall->isInstanceMessage()) |
183 | 0 | return true; |
184 | | |
185 | | // Alternatively, the analyzer could know that the receiver is not null. |
186 | 0 | SVal Receiver = MCall->getReceiverSVal(); |
187 | 0 | ConditionTruthVal TV = C.getState()->isNonNull(Receiver); |
188 | 0 | if (TV.isConstrainedTrue()) |
189 | 0 | return true; |
190 | | |
191 | 0 | return false; |
192 | 0 | } |
193 | | |
194 | | /// \return Whether \p ID has a superclass by the name \p ClassName. |
195 | | bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID, |
196 | 0 | StringRef ClassName) const { |
197 | 0 | if (ID->getIdentifier()->getName() == ClassName) |
198 | 0 | return true; |
199 | | |
200 | 0 | if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) |
201 | 0 | return interfaceHasSuperclass(Super, ClassName); |
202 | | |
203 | 0 | return false; |
204 | 0 | } |
205 | | |
206 | | |
207 | | /// \return a state with an optional implication added (if exists) |
208 | | /// from a map of recorded implications. |
209 | | /// If \p Negated is true, checks NullImplicationMap, and assumes |
210 | | /// the negation of \p Antecedent. |
211 | | /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise. |
212 | | ProgramStateRef addImplication(SymbolRef Antecedent, |
213 | | ProgramStateRef InputState, |
214 | 8.86k | bool Negated) const { |
215 | 8.86k | if (!InputState) |
216 | 0 | return nullptr; |
217 | 8.86k | SValBuilder &SVB = InputState->getStateManager().getSValBuilder(); |
218 | 8.86k | const SymbolRef *Consequent = |
219 | 8.86k | Negated ? InputState->get<NonNullImplicationMap>(Antecedent)4.43k |
220 | 8.86k | : InputState->get<NullImplicationMap>(Antecedent)4.43k ; |
221 | 8.86k | if (!Consequent) |
222 | 8.86k | return InputState; |
223 | | |
224 | 0 | SVal AntecedentV = SVB.makeSymbolVal(Antecedent); |
225 | 0 | ProgramStateRef State = InputState; |
226 | |
|
227 | 0 | if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue()) |
228 | 0 | || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) { |
229 | 0 | SVal ConsequentS = SVB.makeSymbolVal(*Consequent); |
230 | 0 | State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated); |
231 | 0 | if (!State) |
232 | 0 | return nullptr; |
233 | | |
234 | | // Drop implications from the map. |
235 | 0 | if (Negated) { |
236 | 0 | State = State->remove<NonNullImplicationMap>(Antecedent); |
237 | 0 | State = State->remove<NullImplicationMap>(*Consequent); |
238 | 0 | } else { |
239 | 0 | State = State->remove<NullImplicationMap>(Antecedent); |
240 | 0 | State = State->remove<NonNullImplicationMap>(*Consequent); |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | 0 | return State; |
245 | 0 | } |
246 | | }; |
247 | | |
248 | | } // end empty namespace |
249 | | |
250 | 41 | void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { |
251 | 41 | Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext()); |
252 | 41 | } |
253 | | |
254 | 86 | bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) { |
255 | 86 | return true; |
256 | 86 | } |