clang 20.0.0git
TrustNonnullChecker.cpp
Go to the documentation of this file.
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
28
29using namespace clang;
30using 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.
38
39/// The semantics is:
40/// (antecedent == 0) => (consequent == 0)
42
43namespace {
44
45class 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
56public:
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")) {}
64
65 ProgramStateRef evalAssume(ProgramStateRef State,
66 SVal Cond,
67 bool Assumption) const {
68 const SymbolRef CondS = Cond.getAsSymbol();
69 if (!CondS || CondS->computeComplexity() > ComplexityThreshold)
70 return State;
71
72 for (SymbolRef Antecedent : CondS->symbols()) {
73 State = addImplication(Antecedent, State, true);
74 State = addImplication(Antecedent, State, false);
75 }
76
77 return State;
78 }
79
80 void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
81 // Only trust annotations for system headers for non-protocols.
82 if (!Call.isInSystemHeader())
83 return;
84
85 ProgramStateRef State = C.getState();
86
87 if (isNonNullPtr(Call, C))
88 if (auto L = Call.getReturnValue().getAs<Loc>())
89 State = State->assume(*L, /*assumption=*/true);
90
91 C.addTransition(State);
92 }
93
94 void checkPostObjCMessage(const ObjCMethodCall &Msg,
95 CheckerContext &C) const {
97 if (!ID)
98 return;
99
100 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 if (interfaceHasSuperclass(ID, "NSMutableDictionary") &&
105 (Msg.getSelector() == SetObjectForKeyedSubscriptSel ||
106 Msg.getSelector() == SetObjectForKeySel)) {
107 if (auto L = Msg.getArgSVal(1).getAs<Loc>())
108 State = State->assume(*L, /*assumption=*/true);
109 }
110
111 // Record an implication: index is non-null if the output is non-null.
112 if (interfaceHasSuperclass(ID, "NSDictionary") &&
113 (Msg.getSelector() == ObjectForKeyedSubscriptSel ||
114 Msg.getSelector() == ObjectForKeySel)) {
115 SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol();
116 SymbolRef RetS = Msg.getReturnValue().getAsSymbol();
117
118 if (ArgS && RetS) {
119 // Emulate an implication: the argument is non-null if
120 // the return value is non-null.
121 State = State->set<NonNullImplicationMap>(RetS, ArgS);
122
123 // Conversely, when the argument is null, the return value
124 // is definitely null.
125 State = State->set<NullImplicationMap>(ArgS, RetS);
126 }
127 }
128
129 C.addTransition(State);
130 }
131
132 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const {
133 ProgramStateRef State = C.getState();
134
135 State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State);
136 State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State);
137
138 C.addTransition(State);
139 }
140
141private:
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 ProgramStateRef State) const {
148 for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>())
149 if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second))
150 State = State->remove<MapName>(P.first);
151 return State;
152 }
153
154 /// \returns Whether we trust the result of the method call to be
155 /// a non-null pointer.
156 bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const {
157 QualType ExprRetType = Call.getResultType();
158 if (!ExprRetType->isAnyPointerType())
159 return false;
160
161 if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull)
162 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 if (!isa<ObjCMethodCall>(&Call))
167 return false;
168
169 const auto *MCall = cast<ObjCMethodCall>(&Call);
170 const ObjCMethodDecl *MD = MCall->getDecl();
171
172 // Distrust protocols.
173 if (isa<ObjCProtocolDecl>(MD->getDeclContext()))
174 return false;
175
176 QualType DeclRetType = MD->getReturnType();
177 if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull)
178 return false;
179
180 // For class messages it is sufficient for the declaration to be
181 // annotated _Nonnull.
182 if (!MCall->isInstanceMessage())
183 return true;
184
185 // Alternatively, the analyzer could know that the receiver is not null.
186 SVal Receiver = MCall->getReceiverSVal();
187 ConditionTruthVal TV = C.getState()->isNonNull(Receiver);
188 if (TV.isConstrainedTrue())
189 return true;
190
191 return false;
192 }
193
194 /// \return Whether \p ID has a superclass by the name \p ClassName.
195 bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID,
196 StringRef ClassName) const {
197 if (ID->getIdentifier()->getName() == ClassName)
198 return true;
199
200 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
201 return interfaceHasSuperclass(Super, ClassName);
202
203 return false;
204 }
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 bool Negated) const {
215 if (!InputState)
216 return nullptr;
217 SValBuilder &SVB = InputState->getStateManager().getSValBuilder();
218 const SymbolRef *Consequent =
219 Negated ? InputState->get<NonNullImplicationMap>(Antecedent)
220 : InputState->get<NullImplicationMap>(Antecedent);
221 if (!Consequent)
222 return InputState;
223
224 SVal AntecedentV = SVB.makeSymbolVal(Antecedent);
225 ProgramStateRef State = InputState;
226
227 if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue())
228 || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) {
229 SVal ConsequentS = SVB.makeSymbolVal(*Consequent);
230 State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated);
231 if (!State)
232 return nullptr;
233
234 // Drop implications from the map.
235 if (Negated) {
236 State = State->remove<NonNullImplicationMap>(Antecedent);
237 State = State->remove<NullImplicationMap>(*Consequent);
238 } else {
239 State = State->remove<NullImplicationMap>(Antecedent);
240 State = State->remove<NonNullImplicationMap>(*Consequent);
241 }
242 }
243
244 return State;
245 }
246};
247
248} // end empty namespace
249
250void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {
251 Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext());
252}
253
254bool ento::shouldRegisterTrustNonnullChecker(const CheckerManager &mgr) {
255 return true;
256}
StringRef P
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:187
DeclContext * getDeclContext()
Definition: DeclBase.h:455
Represents an ObjC class declaration.
Definition: DeclObjC.h:1153
ObjCMethodDecl - Represents an instance or class method declaration.
Definition: DeclObjC.h:140
QualType getReturnType() const
Definition: DeclObjC.h:329
A (possibly-)qualified type.
Definition: Type.h:941
Smart pointer class that efficiently represents Objective-C method names.
bool isAnyPointerType() const
Definition: Type.h:8011
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
Definition: CallEvent.cpp:308
SVal getReturnValue() const
Returns the return value of the call.
Definition: CallEvent.cpp:322
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ASTContext & getASTContext() const
bool isConstrainedTrue() const
Return true if the constraint is perfectly constrained to 'true'.
Represents any expression that calls an Objective-C method.
Definition: CallEvent.h:1243
const ObjCInterfaceDecl * getReceiverInterface() const
Get the interface for the receiver.
Definition: CallEvent.h:1302
Selector getSelector() const
Definition: CallEvent.h:1291
DefinedSVal makeSymbolVal(SymbolRef Sym)
Make an SVal that represents the given symbol.
Definition: SValBuilder.h:400
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:104
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:86
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:82
Symbolic value.
Definition: SymExpr.h:30
llvm::iterator_range< symbol_iterator > symbols() const
Definition: SymExpr.h:87
virtual unsigned computeComplexity() const =0
A class responsible for cleaning up unused symbols.
bool isLive(SymbolRef sym)
Nullability getNullabilityAnnotation(QualType Type)
Get nullability annotation for a given type.
The JSON file list parser is used to communicate input to InstallAPI.
static Selector getKeywordSelector(ASTContext &Ctx, const IdentifierInfos *...IIs)