Static Value-Flow Analysis
Loading...
Searching...
No Matches
AbstractStateManager.cpp
Go to the documentation of this file.
1//===- AbstractStateManager.cpp -- State management for abstract execution -//
2//
3// SVF: Static Value-Flow Analysis
4//
5// Copyright (C) <2013-> <Yulei Sui>
6//
7
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Affero General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU Affero General Public License for more details.
17
18// You should have received a copy of the GNU Affero General Public License
19// along with this program. If not, see <http://www.gnu.org/licenses/>.
20//
21//===----------------------------------------------------------------------===//
22//
23// Created on: Mar 2026
24// Author: Jiawei Wang
25//
28#include "Graphs/SVFG.h"
29#include "MSSA/SVFGBuilder.h"
30#include "WPA/Andersen.h"
31#include "Util/Options.h"
32
33using namespace SVF;
34
36 : svfir(svfir), svfg(nullptr)
37{
39 {
40 SVFGBuilder memSSA(true);
41 svfg = memSSA.buildFullSVFG(pta);
42 }
43}
44
49
50// ===----------------------------------------------------------------------===//
51// State Access
52// ===----------------------------------------------------------------------===//
53
55{
56 if (abstractTrace.count(node) == 0)
57 {
58 assert(false && "No preAbsTrace for this node");
59 abort();
60 }
61 return abstractTrace[node];
62}
63
65{
67 {
68 // Semi-sparse: only replace ObjVar state. ValVars live at their
69 // def-sites and must not be overwritten by state replacement.
70 abstractTrace[node].updateAddrStateOnly(state);
71 }
72 else
73 {
74 abstractTrace[node] = state;
75 }
76}
77
79{
80 return abstractTrace.count(node) != 0;
81}
82
83// ===----------------------------------------------------------------------===//
84// Abstract Value Access — ValVar
85// ===----------------------------------------------------------------------===//
86
88{
89 u32_t id = var->getId();
91
92 // Constants: store into current node's state and return
94 if (const ConstIntValVar* constInt = SVFUtil::dyn_cast<ConstIntValVar>(var))
95 {
96 as[id] = IntervalValue(constInt->getSExtValue(), constInt->getSExtValue());
97 return as[id];
98 }
99 if (const ConstFPValVar* constFP = SVFUtil::dyn_cast<ConstFPValVar>(var))
100 {
101 as[id] = IntervalValue(constFP->getFPValue(), constFP->getFPValue());
102 return as[id];
103 }
104 if (SVFUtil::isa<ConstNullPtrValVar>(var))
105 {
106 as[id] = AddressValue();
107 return as[id];
108 }
109 if (SVFUtil::isa<ConstDataValVar>(var))
110 {
111 as[id] = IntervalValue::top();
112 return as[id];
113 }
114
115 // Dense mode: try current node's state first.
116 // If absent, fall through to the def-site lookup below — matching
117 // upstream behaviour where getAbstractValue always reads from the
118 // def-site. Returning top or bottom here would be wrong: top loses
119 // concrete values like NULL addresses; bottom misjudges branch
120 // feasibility for uninitialised variables like argc.
121 if (!semiSparse)
122 {
123 if (as.inVarToValTable(id) || as.inVarToAddrsTable(id))
124 return as[id];
125 }
126
127 // Semi-sparse mode: pull from def-site first, then check current state
128 const ICFGNode* defNode = var->getICFGNode();
130 {
132 if (varMap.count(id))
133 return getAbstractState(defNode)[id];
134 }
135
136 // Fallback for call-result ValVars: their getICFGNode() returns
137 // CallICFGNode but the value is written by RetPE at RetICFGNode.
138 if (const CallICFGNode* callNode =
139 defNode ? SVFUtil::dyn_cast<CallICFGNode>(defNode) : nullptr)
140 {
141 const RetICFGNode* retNode = callNode->getRetICFGNode();
143 {
145 if (retMap.count(id))
146 return getAbstractState(retNode)[id];
147 }
148 }
149
150 as[id] = IntervalValue::top();
151 return as[id];
152}
153
154// ===----------------------------------------------------------------------===//
155// Abstract Value Access — ObjVar / SVFVar dispatch
156// ===----------------------------------------------------------------------===//
157
164
166{
167 // Check ObjVar first since ObjVar inherits from ValVar
168 if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
169 return getAbstractValue(objVar, node);
170 if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
171 return getAbstractValue(valVar, node);
172 assert(false && "Unknown SVFVar kind");
173 abort();
174}
175
176// ===----------------------------------------------------------------------===//
177// hasAbstractValue — side-effect-free existence check
178//
179// Mirrors the lookup chain in getAbstractValue but stops short of the
180// top-fallback. Returns false when getAbstractValue would only be able
181// to return top as a default.
182// ===----------------------------------------------------------------------===//
183
185{
186 // Constants are always "present" (their value is intrinsic).
187 if (SVFUtil::isa<ConstIntValVar>(var) || SVFUtil::isa<ConstFPValVar>(var) ||
188 SVFUtil::isa<ConstNullPtrValVar>(var) || SVFUtil::isa<ConstDataValVar>(var))
189 return true;
190
191 u32_t id = var->getId();
193
194 // Dense mode: stored at the current node.
195 if (!semiSparse)
196 {
197 auto it = abstractTrace.find(node);
198 if (it != abstractTrace.end() &&
199 (it->second.inVarToValTable(id) || it->second.inVarToAddrsTable(id)))
200 return true;
201 }
202
203 // Semi-sparse (and dense fall-through): check the def-site.
204 const ICFGNode* defNode = var->getICFGNode();
205 if (defNode)
206 {
207 auto it = abstractTrace.find(defNode);
208 if (it != abstractTrace.end() && it->second.getVarToVal().count(id))
209 return true;
210
211 // Fallback for call-result ValVars: value lives at the RetICFGNode.
212 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(defNode))
213 {
214 const RetICFGNode* retNode = callNode->getRetICFGNode();
215 auto rit = abstractTrace.find(retNode);
216 if (rit != abstractTrace.end() && rit->second.getVarToVal().count(id))
217 return true;
218 }
219 }
220 return false;
221}
222
224{
225 auto it = abstractTrace.find(node);
226 if (it == abstractTrace.end())
227 return false;
228 u32_t objId = var->getId();
229 return it->second.getLocToVal().count(objId) != 0;
230}
231
233{
234 if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
235 return hasAbstractValue(objVar, node);
236 if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
237 return hasAbstractValue(valVar, node);
238 return false;
239}
240
241// ===----------------------------------------------------------------------===//
242// Update Abstract Value
243// ===----------------------------------------------------------------------===//
244
246{
247 // In semi-sparse mode, write to the var's def-site so that
248 // getAbstractValue (which reads from def-site) stays consistent.
249 const ICFGNode* target = node;
251 {
252 const ICFGNode* defNode = var->getICFGNode();
253 if (defNode)
254 target = defNode;
255 }
256 abstractTrace[target][var->getId()] = val;
257}
258
265
267{
268 if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
270 else if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
272 else
273 assert(false && "Unknown SVFVar kind");
274}
275
276// ===----------------------------------------------------------------------===//
277// Filtered State Access
278// ===----------------------------------------------------------------------===//
279
281{
283 for (const ValVar* var : vars)
284 {
285 u32_t id = var->getId();
286 result[id] = as[id];
287 }
288}
289
291{
293 for (const ObjVar* var : vars)
294 {
296 result.store(addr, as.load(addr));
297 }
298}
299
301{
303 for (const SVFVar* var : vars)
304 {
305 if (const ValVar* valVar = SVFUtil::dyn_cast<ValVar>(var))
306 {
307 u32_t id = valVar->getId();
308 result[id] = as[id];
309 }
310 else if (const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(var))
311 {
313 result.store(addr, as.load(addr));
314 }
315 }
316}
317
318// ===----------------------------------------------------------------------===//
319// GEP Helpers
320// ===----------------------------------------------------------------------===//
321
323{
324 const ICFGNode* node = gep->getICFGNode();
325 if (gep->isConstantOffset())
326 return IntervalValue((s64_t)gep->accumulateConstantOffset());
327
328 IntervalValue res(0);
329 for (int i = gep->getOffsetVarAndGepTypePairVec().size() - 1; i >= 0; i--)
330 {
331 const ValVar* var = gep->getOffsetVarAndGepTypePairVec()[i].first;
332 const SVFType* type = gep->getOffsetVarAndGepTypePairVec()[i].second;
333
335 if (const ConstIntValVar* constInt = SVFUtil::dyn_cast<ConstIntValVar>(var))
336 idxLb = idxUb = constInt->getSExtValue();
337 else
338 {
340 if (idxItv.isBottom())
341 idxLb = idxUb = 0;
342 else
343 {
345 idxUb = idxItv.ub().getIntNumeral();
346 }
347 }
348
349 if (SVFUtil::isa<SVFPointerType>(type))
350 {
351 u32_t elemNum = gep->getAccessPath().getElementNum(gep->getAccessPath().gepSrcPointeeType());
352 idxLb = (double)Options::MaxFieldLimit() / elemNum < idxLb ? Options::MaxFieldLimit() : idxLb * elemNum;
353 idxUb = (double)Options::MaxFieldLimit() / elemNum < idxUb ? Options::MaxFieldLimit() : idxUb * elemNum;
354 }
355 else
356 {
358 {
359 const std::vector<u32_t>& so = PAG::getPAG()->getTypeInfo(type)->getFlattenedElemIdxVec();
360 if (so.empty() || idxUb >= (APOffset)so.size() || idxLb < 0)
361 idxLb = idxUb = 0;
362 else
363 {
366 }
367 }
368 else
369 idxLb = idxUb = 0;
370 }
371 res = res + IntervalValue(idxLb, idxUb);
372 }
374 if (res.isBottom())
375 res = IntervalValue(0);
376 return res;
377}
378
380{
381 const ICFGNode* node = gep->getICFGNode();
382 if (gep->isConstantOffset())
383 return IntervalValue((s64_t)gep->accumulateConstantByteOffset());
384
385 IntervalValue res(0);
386 for (int i = gep->getOffsetVarAndGepTypePairVec().size() - 1; i >= 0; i--)
387 {
388 const ValVar* idxOperandVar = gep->getOffsetVarAndGepTypePairVec()[i].first;
389 const SVFType* idxOperandType = gep->getOffsetVarAndGepTypePairVec()[i].second;
390
391 if (SVFUtil::isa<SVFArrayType>(idxOperandType) || SVFUtil::isa<SVFPointerType>(idxOperandType))
392 {
394 if (const SVFArrayType* arrOperandType = SVFUtil::dyn_cast<SVFArrayType>(idxOperandType))
395 elemByteSize = arrOperandType->getTypeOfElement()->getByteSize();
396 else if (SVFUtil::isa<SVFPointerType>(idxOperandType))
397 elemByteSize = gep->getAccessPath().gepSrcPointeeType()->getByteSize();
398 else
399 assert(false && "idxOperandType must be ArrType or PtrType");
400
401 if (const ConstIntValVar* op = SVFUtil::dyn_cast<ConstIntValVar>(idxOperandVar))
402 {
403 s64_t lb = (double)Options::MaxFieldLimit() / elemByteSize >= op->getSExtValue()
404 ? op->getSExtValue() * elemByteSize
406 res = res + IntervalValue(lb, lb);
407 }
408 else
409 {
411 if (idxVal.isBottom())
412 res = res + IntervalValue(0, 0);
413 else
414 {
415 s64_t ub = (idxVal.ub().getIntNumeral() < 0) ? 0
416 : (double)Options::MaxFieldLimit() / elemByteSize >= idxVal.ub().getIntNumeral()
417 ? elemByteSize * idxVal.ub().getIntNumeral()
419 s64_t lb = (idxVal.lb().getIntNumeral() < 0) ? 0
420 : (double)Options::MaxFieldLimit() / elemByteSize >= idxVal.lb().getIntNumeral()
421 ? elemByteSize * idxVal.lb().getIntNumeral()
423 res = res + IntervalValue(lb, ub);
424 }
425 }
426 }
427 else if (const SVFStructType* structOperandType = SVFUtil::dyn_cast<SVFStructType>(idxOperandType))
428 {
429 res = res + IntervalValue(gep->getAccessPath().getStructFieldOffset(idxOperandVar, structOperandType));
430 }
431 else
432 {
433 assert(false && "gep type pair only support arr/ptr/struct");
434 }
435 }
436 return res;
437}
438
440{
441 const ICFGNode* node = pointer->getICFGNode();
444 APOffset lb = offset.lb().getIntNumeral() < Options::MaxFieldLimit() ? offset.lb().getIntNumeral()
446 APOffset ub = offset.ub().getIntNumeral() < Options::MaxFieldLimit() ? offset.ub().getIntNumeral()
448 for (APOffset i = lb; i <= ub; i++)
449 {
450 const AbstractValue& addrs = getAbstractValue(pointer, node);
451 for (const auto& addr : addrs.getAddrs())
452 {
453 s64_t baseObj = as.getIDFromAddr(addr);
454 assert(SVFUtil::isa<ObjVar>(svfir->getSVFVar(baseObj)) && "Fail to get the base object address!");
458 }
459 }
460 return gepAddrs;
461}
462
463// ===----------------------------------------------------------------------===//
464// Load / Store through pointer
465// ===----------------------------------------------------------------------===//
466
468{
469 const AbstractValue& ptrVal = getAbstractValue(pointer, node);
471 AbstractValue res;
472 for (auto addr : ptrVal.getAddrs())
473 res.join_with(as.load(addr));
474 return res;
475}
476
477void AbstractStateManager::storeValue(const ValVar* pointer, const AbstractValue& val, const ICFGNode* node)
478{
479 const AbstractValue& ptrVal = getAbstractValue(pointer, node);
481 for (auto addr : ptrVal.getAddrs())
482 as.store(addr, val);
483}
484
485// ===----------------------------------------------------------------------===//
486// Type / Size Helpers
487// ===----------------------------------------------------------------------===//
488
490{
492 if (!ptrVal.isAddr())
493 return nullptr;
494 for (auto addr : ptrVal.getAddrs())
495 {
497 if (objId == 0)
498 continue;
499 return svfir->getBaseObject(objId)->getType();
500 }
501 return nullptr;
502}
503
505{
506 const ICFGNode* node = addr->getICFGNode();
507 if (const ObjVar* objvar = SVFUtil::dyn_cast<ObjVar>(addr->getRHSVar()))
508 {
510 {
511 return svfir->getBaseObject(objvar->getId())->getByteSizeOfObj();
512 }
513 else
514 {
515 const std::vector<SVFVar*>& sizes = addr->getArrSize();
516 u32_t elementSize = 1;
517 u64_t res = elementSize;
518 for (const SVFVar* value : sizes)
519 {
520 const AbstractValue& sizeVal = getAbstractValue(value, node);
521 IntervalValue itv = sizeVal.getInterval();
522 if (itv.isBottom())
524 res = res * itv.ub().getIntNumeral() > Options::MaxFieldLimit()
525 ? Options::MaxFieldLimit() : res * itv.ub().getIntNumeral();
526 }
527 return (u32_t)res;
528 }
529 }
530 assert(false && "Addr rhs value is not ObjVar");
531 abort();
532}
533
534// ===----------------------------------------------------------------------===//
535// Def/Use site queries
536// ===----------------------------------------------------------------------===//
537
539{
541 {
542 assert(svfg && "SVFG is not built for sparse AE");
543 return svfg->getUseSitesOfObjVar(obj, node);
544 }
546 for (const auto* edge : node->getOutEdges())
547 succs.insert(edge->getDstNode());
548 return succs;
549}
550
552{
554 {
555 assert(svfg && "SVFG is not built for sparse AE");
557 }
559 if (const ICFGNode* node = var->getICFGNode())
560 {
561 for (const auto* edge : node->getOutEdges())
562 succs.insert(edge->getDstNode());
563 }
564 return succs;
565}
566
568{
570 {
571 assert(svfg && "SVFG is not built for sparse AE");
572 return svfg->getDefSiteOfValVar(var);
573 }
574 return var->getICFGNode();
575}
576
578{
580 {
581 assert(svfg && "SVFG is not built for sparse AE");
582 return svfg->getDefSiteOfObjVar(obj, node);
583 }
584 for (const auto* edge : node->getInEdges())
585 return edge->getSrcNode();
586 return nullptr;
587}
588
newitem type
Definition cJSON.cpp:2739
buffer offset
Definition cJSON.cpp:1113
void storeValue(const ValVar *pointer, const AbstractValue &val, const ICFGNode *node)
void updateAbstractValue(const ValVar *var, const AbstractValue &val, const ICFGNode *node)
Write a top-level variable's abstract value into abstractTrace[node].
IntervalValue getGepByteOffset(const GepStmt *gep)
Compute the byte offset for a GepStmt.
AbstractState & getAbstractState(const ICFGNode *node)
Retrieve the abstract state for a given ICFG node. Asserts if absent.
u32_t getAllocaInstByteSize(const AddrStmt *addr)
Get the byte size of a stack allocation.
bool hasAbstractState(const ICFGNode *node)
Check if an abstract state exists for a given ICFG node.
bool hasAbstractValue(const ValVar *var, const ICFGNode *node) const
const AbstractValue & getAbstractValue(const ValVar *var, const ICFGNode *node)
IntervalValue getGepElementIndex(const GepStmt *gep)
Compute the flattened element index for a GepStmt.
const ICFGNode * getDefSiteOfValVar(const ValVar *var) const
Given a ValVar, find its definition-site ICFGNode.
Set< const ICFGNode * > getUseSitesOfObjVar(const ObjVar *obj, const ICFGNode *node) const
Given an ObjVar and its use-site ICFGNode, find all downstream use-site ICFGNodes.
const ICFGNode * getDefSiteOfObjVar(const ObjVar *obj, const ICFGNode *node) const
Given an ObjVar and its use-site ICFGNode, find the definition-site ICFGNode.
AddressValue getGepObjAddrs(const ValVar *pointer, IntervalValue offset)
Compute GEP object addresses for a pointer at a given element offset.
AbstractValue loadValue(const ValVar *pointer, const ICFGNode *node)
const SVFType * getPointeeElement(const ObjVar *var, const ICFGNode *node)
Get the pointee type for a pointer variable.
Map< const ICFGNode *, AbstractState > abstractTrace
AbstractStateManager(SVFIR *svfir, AndersenWaveDiff *pta)
Set< const ICFGNode * > getUseSitesOfValVar(const ValVar *var) const
Given a ValVar, find all use-site ICFGNodes.
void updateAbstractState(const ICFGNode *node, const AbstractState &state)
const VarToAbsValMap & getVarToVal() const
get var2val map
u32_t getIDFromAddr(u32_t addr) const
Return the internal index if addr is an address otherwise return the value of idx.
static u32_t getVirtualMemAddress(u32_t idx)
The physical address starts with 0x7f...... + idx.
void join_with(const AbstractValue &other)
IntervalValue & getInterval()
AddressValue & getAddrs()
bool isConstantByteSize() const
Check if byte size is a const value.
u32_t getByteSizeOfObj() const
Get the byte size of this object.
const SVFType * getType() const
Get obj type.
s64_t getIntNumeral() const
const GEdgeSetTy & getOutEdges() const
const GEdgeSetTy & getInEdges() const
u32_t getFlattenedElemIdx(const SVFType *T, u32_t origId)
Flattened element idx of an array or struct by considering stride.
Definition IRGraph.cpp:144
const StInfo * getTypeInfo(const SVFType *T) const
Get struct info.
Definition IRGraph.cpp:242
void meet_with(const IntervalValue &other)
Return a intersected IntervalValue.
bool isBottom() const
static IntervalValue top()
Create the IntervalValue [-inf, +inf].
const BoundedInt & lb() const
Return the lower bound.
static Option< bool > ModelArrays
Definition Options.h:185
static const OptionMap< u32_t > AESparsity
Definition Options.h:243
static const Option< u32_t > MaxFieldLimit
Maximum number of field derivations for an object.
Definition Options.h:35
SVFG * buildFullSVFG(BVDataPTAImpl *pta)
const ICFGNode * getDefSiteOfValVar(const ValVar *var) const
Definition SVFG.cpp:765
const Set< const ICFGNode * > getUseSitesOfValVar(const ValVar *var) const
Definition SVFG.cpp:795
const ICFGNode * getDefSiteOfObjVar(const ObjVar *obj, const ICFGNode *node) const
Definition SVFG.cpp:772
const Set< const ICFGNode * > getUseSitesOfObjVar(const ObjVar *obj, const ICFGNode *node) const
Definition SVFG.cpp:815
const BaseObjVar * getBaseObject(NodeID id) const
Definition SVFIR.h:496
const SVFVar * getSVFVar(NodeID id) const
ObjVar/GepObjVar/BaseObjVar.
Definition SVFIR.h:133
static SVFIR * getPAG(bool buildFromFile=false)
Singleton design here to make sure we only have one instance during any analysis.
Definition SVFIR.h:118
const GepObjVar * getGepObjVar(NodeID id) const
Definition SVFIR.h:167
u32_t getByteSize() const
Definition SVFType.h:289
NodeID getId() const
Get ID.
Definition SVFValue.h:163
std::vector< u32_t > & getFlattenedElemIdxVec()
Definition SVFType.h:125
const ICFGNode * getICFGNode() const
for isBitcode
Definition BasicTypes.h:70
unsigned long long u64_t
Definition GeneralType.h:49
u32_t NodeID
Definition GeneralType.h:56
s64_t APOffset
Definition GeneralType.h:60
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76
unsigned u32_t
Definition GeneralType.h:47
signed long long s64_t
Definition GeneralType.h:50