clang 23.0.0git
SymbolManager.cpp
Go to the documentation of this file.
1//===- SymbolManager.h - Management of Symbolic Values --------------------===//
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 file defines SymbolManager, a class that manages symbolic values
10// created for use by ExprEngine and related classes.
11//
12//===----------------------------------------------------------------------===//
13
16#include "clang/AST/Expr.h"
19#include "clang/Basic/LLVM.h"
24#include "llvm/Support/Compiler.h"
25#include "llvm/Support/ErrorHandling.h"
26#include "llvm/Support/raw_ostream.h"
27#include <cassert>
28
29using namespace clang;
30using namespace ento;
31
32void SymExpr::anchor() {}
33
34StringRef SymbolConjured::getKindStr() const { return "conj_$"; }
35StringRef SymbolDerived::getKindStr() const { return "derived_$"; }
36StringRef SymbolExtent::getKindStr() const { return "extent_$"; }
37StringRef SymbolMetadata::getKindStr() const { return "meta_$"; }
38StringRef SymbolRegionValue::getKindStr() const { return "reg_$"; }
39
40LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }
41
42void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
43 OS << '(';
44 Sym->dumpToStream(OS);
45 OS << ')';
46}
47
49 const llvm::APSInt &Value) {
50 if (Value.isUnsigned())
51 OS << Value.getZExtValue();
52 else
53 OS << Value.getSExtValue();
54 if (Value.isUnsigned())
55 OS << 'U';
56}
57
60 OS << ' ' << BinaryOperator::getOpcodeStr(Op) << ' ';
61}
62
63void SymbolCast::dumpToStream(raw_ostream &os) const {
64 os << '(' << ToTy << ") (";
65 Operand->dumpToStream(os);
66 os << ')';
67}
68
69void UnarySymExpr::dumpToStream(raw_ostream &os) const {
71 bool Binary = isa<BinarySymExpr>(Operand);
72 if (Binary)
73 os << '(';
74 Operand->dumpToStream(os);
75 if (Binary)
76 os << ')';
77}
78
80 // Sometimes the CFG element is invalid, avoid dereferencing it.
81 if (Elem.getParent() == nullptr ||
82 Elem.getIndexInBlock() >= Elem.getParent()->size())
83 return nullptr;
84 switch (Elem->getKind()) {
86 if (const auto *Init = Elem->castAs<CFGInitializer>().getInitializer()) {
87 return Init->getInit();
88 }
89 return nullptr;
91 return Elem->castAs<CFGScopeBegin>().getTriggerStmt();
93 return Elem->castAs<CFGScopeEnd>().getTriggerStmt();
95 return Elem->castAs<CFGNewAllocator>().getAllocatorExpr();
97 return Elem->castAs<CFGLifetimeEnds>().getTriggerStmt();
99 return Elem->castAs<CFGLoopExit>().getLoopStmt();
101 return Elem->castAs<CFGStmt>().getStmt();
103 return Elem->castAs<CFGConstructor>().getStmt();
105 return Elem->castAs<CFGCXXRecordTypedCall>().getStmt();
107 return Elem->castAs<CFGAutomaticObjDtor>().getTriggerStmt();
109 return Elem->castAs<CFGDeleteDtor>().getDeleteExpr();
111 return nullptr;
113 return nullptr;
115 return Elem->castAs<CFGTemporaryDtor>().getBindTemporaryExpr();
117 return nullptr;
119 return nullptr;
120 }
121 return nullptr;
122}
123
124void SymbolConjured::dumpToStream(raw_ostream &os) const {
125 os << getKindStr() << getSymbolID() << '{' << T << ", LC" << LCtx->getID();
126 if (auto *S = getStmt())
127 os << ", S" << S->getID(LCtx->getDecl()->getASTContext());
128 else
129 os << ", no stmt";
130 os << ", #" << Count << '}';
131}
132
133void SymbolDerived::dumpToStream(raw_ostream &os) const {
134 os << getKindStr() << getSymbolID() << '{' << getParentSymbol() << ','
135 << getRegion() << '}';
136}
137
138void SymbolExtent::dumpToStream(raw_ostream &os) const {
139 os << getKindStr() << getSymbolID() << '{' << getRegion() << '}';
140}
141
142void SymbolMetadata::dumpToStream(raw_ostream &os) const {
143 os << getKindStr() << getSymbolID() << '{' << getRegion() << ',' << T << '}';
144}
145
146void SymbolData::anchor() {}
147
148void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
149 os << getKindStr() << getSymbolID() << '<' << getType() << ' ' << R << '>';
150}
151
153 return itr == X.itr;
154}
155
157 return itr != X.itr;
158}
159
161 itr.push_back(SE);
162}
163
165 assert(!itr.empty() && "attempting to iterate on an 'end' iterator");
166 expand();
167 return *this;
168}
169
171 assert(!itr.empty() && "attempting to dereference an 'end' iterator");
172 return itr.back();
173}
174
175void SymExpr::symbol_iterator::expand() {
176 const SymExpr *SE = itr.pop_back_val();
177
178 switch (SE->getKind()) {
179 case SymExpr::SymbolRegionValueKind:
180 case SymExpr::SymbolConjuredKind:
181 case SymExpr::SymbolDerivedKind:
182 case SymExpr::SymbolExtentKind:
183 case SymExpr::SymbolMetadataKind:
184 return;
185 case SymExpr::SymbolCastKind:
186 itr.push_back(cast<SymbolCast>(SE)->getOperand());
187 return;
188 case SymExpr::UnarySymExprKind:
189 itr.push_back(cast<UnarySymExpr>(SE)->getOperand());
190 return;
191 case SymExpr::SymIntExprKind:
192 itr.push_back(cast<SymIntExpr>(SE)->getLHS());
193 return;
194 case SymExpr::IntSymExprKind:
195 itr.push_back(cast<IntSymExpr>(SE)->getRHS());
196 return;
197 case SymExpr::SymSymExprKind: {
198 const auto *x = cast<SymSymExpr>(SE);
199 itr.push_back(x->getLHS());
200 itr.push_back(x->getRHS());
201 return;
202 }
203 }
204 llvm_unreachable("unhandled expansion case");
205}
206
208 return T;
209}
210
212 return R->getValueType();
213}
214
216 ASTContext &Ctx = R->getMemRegionManager().getContext();
217 return Ctx.getSizeType();
218}
219
221 return T;
222}
223
225 return R->getValueType();
226}
227
229 T = T.getCanonicalType();
230
231 if (Loc::isLocType(T))
232 return true;
233
234 if (T->isIntegralOrEnumerationType())
235 return true;
236
237 if (T->isRecordType() && !T->isUnionType())
238 return true;
239
240 return false;
241}
242
244 const SymbolRef Dependent) {
245 auto &dependencies = SymbolDependencies[Primary];
246 if (!dependencies) {
247 dependencies = std::make_unique<SymbolRefSmallVectorTy>();
248 }
249 dependencies->push_back(Dependent);
250}
251
253 const SymbolRef Primary) {
254 SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary);
255 if (I == SymbolDependencies.end())
256 return nullptr;
257 return I->second.get();
258}
259
260void SymbolReaper::markDependentsLive(SymbolRef sym) {
261 // Do not mark dependents more then once.
262 SymbolMapTy::iterator LI = TheLiving.find(sym);
263 assert(LI != TheLiving.end() && "The primary symbol is not live.");
264 if (LI->second == HaveMarkedDependents)
265 return;
266 LI->second = HaveMarkedDependents;
267
268 if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) {
269 for (const auto I : *Deps) {
270 if (TheLiving.contains(I))
271 continue;
272 markLive(I);
273 }
274 }
275}
276
278 TheLiving[sym] = NotProcessed;
279 markDependentsLive(sym);
280}
281
283 LiveRegionRoots.insert(region->getBaseRegion());
285}
286
288 LazilyCopiedRegionRoots.insert(region->getBaseRegion());
289}
290
292 for (auto SR = dyn_cast<SubRegion>(region); SR;
293 SR = dyn_cast<SubRegion>(SR->getSuperRegion())) {
294 if (const auto ER = dyn_cast<ElementRegion>(SR)) {
295 SVal Idx = ER->getIndex();
296 for (SymbolRef Sym : Idx.symbols())
297 markLive(Sym);
298 }
299 }
300}
301
303 if (isa<SymbolMetadata>(sym))
304 MetadataInUse.insert(sym);
305}
306
308 // TODO: For now, liveness of a memory region is equivalent to liveness of its
309 // base region. In fact we can do a bit better: say, if a particular FieldDecl
310 // is not used later in the path, we can diagnose a leak of a value within
311 // that field earlier than, say, the variable that contains the field dies.
312 MR = MR->getBaseRegion();
313 if (LiveRegionRoots.count(MR))
314 return true;
315
316 if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
317 return isLive(SR->getSymbol());
318
319 if (const auto *VR = dyn_cast<VarRegion>(MR))
320 return isLive(VR, true);
321
322 // FIXME: This is a gross over-approximation. What we really need is a way to
323 // tell if anything still refers to this region. Unlike SymbolicRegions,
324 // AllocaRegions don't have associated symbols, though, so we don't actually
325 // have a way to track their liveness.
327}
328
329bool SymbolReaper::isLazilyCopiedRegion(const MemRegion *MR) const {
330 // TODO: See comment in isLiveRegion.
331 return LazilyCopiedRegionRoots.count(MR->getBaseRegion());
332}
333
334bool SymbolReaper::isReadableRegion(const MemRegion *MR) {
335 return isLiveRegion(MR) || isLazilyCopiedRegion(MR);
336}
337
339 if (TheLiving.count(sym)) {
340 markDependentsLive(sym);
341 return true;
342 }
343
344 bool KnownLive;
345
346 switch (sym->getKind()) {
347 case SymExpr::SymbolRegionValueKind:
348 KnownLive = isReadableRegion(cast<SymbolRegionValue>(sym)->getRegion());
349 break;
350 case SymExpr::SymbolConjuredKind:
351 KnownLive = false;
352 break;
353 case SymExpr::SymbolDerivedKind:
354 KnownLive = isLive(cast<SymbolDerived>(sym)->getParentSymbol());
355 break;
356 case SymExpr::SymbolExtentKind:
357 KnownLive = isLiveRegion(cast<SymbolExtent>(sym)->getRegion());
358 break;
359 case SymExpr::SymbolMetadataKind:
360 KnownLive = MetadataInUse.count(sym) &&
362 if (KnownLive)
363 MetadataInUse.erase(sym);
364 break;
365 case SymExpr::SymIntExprKind:
366 KnownLive = isLive(cast<SymIntExpr>(sym)->getLHS());
367 break;
368 case SymExpr::IntSymExprKind:
369 KnownLive = isLive(cast<IntSymExpr>(sym)->getRHS());
370 break;
371 case SymExpr::SymSymExprKind:
372 KnownLive = isLive(cast<SymSymExpr>(sym)->getLHS()) &&
373 isLive(cast<SymSymExpr>(sym)->getRHS());
374 break;
375 case SymExpr::SymbolCastKind:
376 KnownLive = isLive(cast<SymbolCast>(sym)->getOperand());
377 break;
378 case SymExpr::UnarySymExprKind:
379 KnownLive = isLive(cast<UnarySymExpr>(sym)->getOperand());
380 break;
381 }
382
383 if (KnownLive)
384 markLive(sym);
385
386 return KnownLive;
387}
388
389bool
390SymbolReaper::isLive(const Expr *ExprVal, const LocationContext *ELCtx) const {
391 if (LCtx == nullptr)
392 return false;
393
394 if (LCtx != ELCtx) {
395 // If the reaper's location context is a parent of the expression's
396 // location context, then the expression value is now "out of scope".
397 if (LCtx->isParentOf(ELCtx))
398 return false;
399 return true;
400 }
401
402 // If no statement is provided, everything in this and parent contexts is
403 // live.
404 if (!Loc)
405 return true;
406
407 return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
408}
409
410bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
411 const StackFrameContext *VarContext = VR->getStackFrame();
412
413 if (!VarContext)
414 return true;
415
416 if (!LCtx)
417 return false;
418 const StackFrameContext *CurrentContext = LCtx->getStackFrame();
419
420 if (VarContext == CurrentContext) {
421 // If no statement is provided, everything is live.
422 if (!Loc)
423 return true;
424
425 // Anonymous parameters of an inheriting constructor are live for the entire
426 // duration of the constructor.
428 return true;
429
430 if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
431 return true;
432
433 if (!includeStoreBindings)
434 return false;
435
436 unsigned &cachedQuery =
437 const_cast<SymbolReaper *>(this)->includedRegionCache[VR];
438
439 if (cachedQuery) {
440 return cachedQuery == 1;
441 }
442
443 // Query the store to see if the region occurs in any live bindings.
444 if (Store store = reapedStore.getStore()) {
445 bool hasRegion =
446 reapedStore.getStoreManager().includedInBindings(store, VR);
447 cachedQuery = hasRegion ? 1 : 2;
448 return hasRegion;
449 }
450
451 return false;
452 }
453
454 return VarContext->isParentOf(CurrentContext);
455}
Defines the clang::ASTContext interface.
This file defines AnalysisDeclContext, a class that manages the analysis context data for context sen...
static const MemRegion * getRegion(const CallEvent &Call, const MutexDescriptor &Descriptor, bool IsLock)
#define X(type, name)
Definition Value.h:97
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:226
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
StringRef getOpcodeStr() const
Definition Expr.h:4107
BinaryOperatorKind Opcode
Definition Expr.h:4046
Represents C++ object destructor implicitly generated for automatic object or temporary bound to cons...
Definition CFG.h:445
Represents a function call that returns a C++ object by value.
Definition CFG.h:187
Represents C++ constructor call.
Definition CFG.h:158
Represents C++ object destructor generated from a call to delete.
Definition CFG.h:470
@ CleanupFunction
Definition CFG.h:80
@ CXXRecordTypedCall
Definition CFG.h:69
@ FullExprCleanup
Definition CFG.h:65
@ AutomaticObjectDtor
Definition CFG.h:73
Represents C++ base or member initializer from constructor's initialization list.
Definition CFG.h:229
const CXXCtorInitializer * getInitializer() const
Definition CFG.h:234
Represents the point where the lifetime of an automatic object ends.
Definition CFG.h:294
Represents the point where a loop ends.
Definition CFG.h:275
Represents C++ allocator call.
Definition CFG.h:249
Represents beginning of a scope implicitly generated by the compiler on encountering a CompoundStmt.
Definition CFG.h:345
Represents end of a scope implicitly generated by the compiler after the last Stmt in a CompoundStmt'...
Definition CFG.h:371
Represents C++ object destructor implicitly generated at the end of full expression for temporary obj...
Definition CFG.h:538
This represents one expression.
Definition Expr.h:112
bool isLive(const CFGBlock *B, const VarDecl *D)
Return true if a variable is live at the end of a specified block.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
bool isParentOf(const LocationContext *LC) const
const StackFrameContext * getStackFrame() const
A (possibly-)qualified type.
Definition TypeBase.h:937
It represents a stack frame of the call stack (based on CallEvent).
Stmt - This represents one statement.
Definition Stmt.h:86
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition Expr.cpp:1406
static void dumpToStreamImpl(raw_ostream &os, const SymExpr *Value)
static bool isLocType(QualType T)
Definition SVals.h:262
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:98
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:56
llvm::iterator_range< SymExpr::symbol_iterator > symbols() const
Definition SVals.h:156
Iterator over symbols that the current symbol depends on.
Definition SymExpr.h:91
bool operator!=(const symbol_iterator &X) const
bool operator==(const symbol_iterator &X) const
virtual void dumpToStream(raw_ostream &os) const
Definition SymExpr.h:81
SymExpr(Kind k, SymbolID Sym)
Definition SymExpr.h:56
Kind getKind() const
Definition SymExpr.h:69
virtual void dump() const
SymbolID getSymbolID() const
Get a unique identifier for this symbol.
Definition SymExpr.h:77
void dumpToStream(raw_ostream &os) const override
const Stmt * getStmt() const
StringRef getKindStr() const override
Get a string representation of the kind of the region.
void dumpToStream(raw_ostream &os) const override
QualType getType() const override
LLVM_ATTRIBUTE_RETURNS_NONNULL SymbolRef getParentSymbol() const
StringRef getKindStr() const override
Get a string representation of the kind of the region.
void dumpToStream(raw_ostream &os) const override
QualType getType() const override
LLVM_ATTRIBUTE_RETURNS_NONNULL const TypedValueRegion * getRegion() const
LLVM_ATTRIBUTE_RETURNS_NONNULL const SubRegion * getRegion() const
void dumpToStream(raw_ostream &os) const override
QualType getType() const override
StringRef getKindStr() const override
Get a string representation of the kind of the region.
void addSymbolDependency(const SymbolRef Primary, const SymbolRef Dependent)
Add artificial symbol dependency.
const SymbolRefSmallVectorTy * getDependentSymbols(const SymbolRef Primary)
static bool canSymbolicate(QualType T)
void dumpToStream(raw_ostream &os) const override
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getRegion() const
StringRef getKindStr() const override
Get a string representation of the kind of the region.
QualType getType() const override
void markLive(SymbolRef sym)
Unconditionally marks a symbol as live.
void markElementIndicesLive(const MemRegion *region)
SymbolReaper(const StackFrameContext *Ctx, const Stmt *s, SymbolManager &symmgr, StoreManager &storeMgr)
Construct a reaper object, which removes everything which is not live before we execute statement s i...
void markInUse(SymbolRef sym)
Marks a symbol as important to a checker.
bool isLiveRegion(const MemRegion *region)
void markLazilyCopied(const MemRegion *region)
bool isLive(SymbolRef sym)
void dumpToStream(raw_ostream &os) const override
QualType getType() const override
StringRef getKindStr() const override
Get a string representation of the kind of the region.
void dumpToStream(raw_ostream &os) const override
const VarDecl * getDecl() const override=0
const StackFrameContext * getStackFrame() const
It might return null.
SmallVector< SymbolRef, 2 > SymbolRefSmallVectorTy
Definition SymExpr.h:134
const SymExpr * SymbolRef
Definition SymExpr.h:133
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
Definition StoreRef.h:27
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
@ Dependent
Parse the block as a dependent block, which may be used in some template instantiations but not other...
Definition Parser.h:142
U cast(CodeGen::Address addr)
Definition Address.h:327