1 //== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==//
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
7 //===----------------------------------------------------------------------===//
9 // This checker adds nullability-related assumptions:
11 // 1. Methods annotated with _Nonnull
12 // which come from system headers actually return a non-null pointer.
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.
17 // 3. NSMutableDictionary index is non-null after a write operation.
19 //===----------------------------------------------------------------------===//
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"
29 using namespace clang
;
32 /// Records implications between symbols.
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
)
40 /// (antecedent == 0) => (consequent == 0)
41 REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap
, SymbolRef
, SymbolRef
)
45 class TrustNonnullChecker
: public Checker
<check::PostCall
,
46 check::PostObjCMessage
,
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
;
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")) {}
65 ProgramStateRef
evalAssume(ProgramStateRef State
,
67 bool Assumption
) const {
68 const SymbolRef CondS
= Cond
.getAsSymbol();
69 if (!CondS
|| CondS
->computeComplexity() > ComplexityThreshold
)
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);
81 void checkPostCall(const CallEvent
&Call
, CheckerContext
&C
) const {
82 // Only trust annotations for system headers for non-protocols.
83 if (!Call
.isInSystemHeader())
86 ProgramStateRef State
= C
.getState();
88 if (isNonNullPtr(Call
, C
))
89 if (auto L
= Call
.getReturnValue().getAs
<Loc
>())
90 State
= State
->assume(*L
, /*assumption=*/true);
92 C
.addTransition(State
);
95 void checkPostObjCMessage(const ObjCMethodCall
&Msg
,
96 CheckerContext
&C
) const {
97 const ObjCInterfaceDecl
*ID
= Msg
.getReceiverInterface();
101 ProgramStateRef State
= C
.getState();
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);
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();
120 // Emulate an implication: the argument is non-null if
121 // the return value is non-null.
122 State
= State
->set
<NonNullImplicationMap
>(RetS
, ArgS
);
124 // Conversely, when the argument is null, the return value
125 // is definitely null.
126 State
= State
->set
<NullImplicationMap
>(ArgS
, RetS
);
130 C
.addTransition(State
);
133 void checkDeadSymbols(SymbolReaper
&SymReaper
, CheckerContext
&C
) const {
134 ProgramStateRef State
= C
.getState();
136 State
= dropDeadFromGDM
<NullImplicationMap
>(SymReaper
, State
);
137 State
= dropDeadFromGDM
<NonNullImplicationMap
>(SymReaper
, State
);
139 C
.addTransition(State
);
144 /// \returns State with GDM \p MapName where all dead symbols were
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
);
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())
162 if (getNullabilityAnnotation(ExprRetType
) == Nullability::Nonnull
)
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
))
170 const auto *MCall
= cast
<ObjCMethodCall
>(&Call
);
171 const ObjCMethodDecl
*MD
= MCall
->getDecl();
173 // Distrust protocols.
174 if (isa
<ObjCProtocolDecl
>(MD
->getDeclContext()))
177 QualType DeclRetType
= MD
->getReturnType();
178 if (getNullabilityAnnotation(DeclRetType
) != Nullability::Nonnull
)
181 // For class messages it is sufficient for the declaration to be
182 // annotated _Nonnull.
183 if (!MCall
->isInstanceMessage())
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())
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
)
201 if (const ObjCInterfaceDecl
*Super
= ID
->getSuperClass())
202 return interfaceHasSuperclass(Super
, ClassName
);
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 {
218 SValBuilder
&SVB
= InputState
->getStateManager().getSValBuilder();
219 const SymbolRef
*Consequent
=
220 Negated
? InputState
->get
<NonNullImplicationMap
>(Antecedent
)
221 : InputState
->get
<NullImplicationMap
>(Antecedent
);
225 SVal AntecedentV
= SVB
.makeSymbolVal(Antecedent
);
226 ProgramStateRef State
= InputState
;
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
);
235 // Drop implications from the map.
237 State
= State
->remove
<NonNullImplicationMap
>(Antecedent
);
238 State
= State
->remove
<NullImplicationMap
>(*Consequent
);
240 State
= State
->remove
<NullImplicationMap
>(Antecedent
);
241 State
= State
->remove
<NonNullImplicationMap
>(*Consequent
);
249 } // end empty namespace
251 void ento::registerTrustNonnullChecker(CheckerManager
&Mgr
) {
252 Mgr
.registerChecker
<TrustNonnullChecker
>(Mgr
.getASTContext());
255 bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager
&mgr
) {