Static Value-Flow Analysis
Loading...
Searching...
No Matches
ObjTypeInference.cpp
Go to the documentation of this file.
1//===- ObjTypeInference.cpp -- Type inference----------------------------//
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/*
24 * ObjTypeInference.cpp
25 *
26 * Created by Xiao Cheng on 10/01/24.
27 *
28 */
29
31#include "SVF-LLVM/BasicTypes.h"
32#include "SVF-LLVM/LLVMModule.h"
33#include "SVF-LLVM/LLVMUtil.h"
34#include "SVF-LLVM/CppUtil.h"
35#include "Util/Casting.h"
36
37#define TYPE_DEBUG 0 /* Turn this on if you're debugging type inference */
38#define ERR_MSG(msg) \
39 do \
40 { \
41 SVFUtil::errs() << SVFUtil::errMsg("Error ") << __FILE__ << ':' \
42 << __LINE__ << ": " << (msg) << '\n'; \
43 } while (0)
44#define ABORT_MSG(msg) \
45 do \
46 { \
47 ERR_MSG(msg); \
48 abort(); \
49 } while (0)
50#define ABORT_IFNOT(condition, msg) \
51 do \
52 { \
53 if (!(condition)) \
54 ABORT_MSG(msg); \
55 } while (0)
56
57#if TYPE_DEBUG
58#define WARN_MSG(msg) \
59 do \
60 { \
61 SVFUtil::outs() << SVFUtil::wrnMsg("Warning ") << __FILE__ << ':' \
62 << __LINE__ << ": " << msg << '\n'; \
63 } while (0)
64#define WARN_IFNOT(condition, msg) \
65 do \
66 { \
67 if (!(condition)) \
68 WARN_MSG(msg); \
69 } while (0)
70#else
71#define WARN_MSG(msg)
72#define WARN_IFNOT(condition, msg)
73#endif
74
75using namespace SVF;
76using namespace SVFUtil;
77using namespace LLVMUtil;
78using namespace cppUtil;
79
80// llvm::Value::hasUseList() was added in LLVM 21 alongside the change that
81// stopped instances of ConstantData (e.g. plain integer/float constants)
82// from carrying a use-list at all (see llvm/llvm-project@87f312a, "IR:
83// Remove uselist for constantdata"). On LLVM < 21 the method doesn't exist,
84// but the exact same condition is available through the public
85// isa<ConstantData> check that hasUseList() itself wraps internally.
86#if LLVM_VERSION_MAJOR >= 21
87static inline bool hasUseList(const Value* v)
88{
89 return v->hasUseList();
90}
91#else
92static inline bool hasUseList(const Value* v)
93{
94 return !SVFUtil::isa<ConstantData>(v);
95}
96#endif
97
98
99const std::string TYPEMALLOC = "TYPE_MALLOC";
100
104{
105 assert(val && "value cannot be empty");
106 if (SVFUtil::isa<LoadInst, StoreInst>(val))
107 {
108 return llvm::getLoadStoreType(const_cast<Value *>(val));
109 }
110 else if (const auto *gepInst = SVFUtil::dyn_cast<GetElementPtrInst>(val))
111 {
112 return gepInst->getSourceElementType();
113 }
114 else if (const auto *call = SVFUtil::dyn_cast<CallBase>(val))
115 {
116 return call->getFunctionType();
117 }
118 else if (const auto *allocaInst = SVFUtil::dyn_cast<AllocaInst>(val))
119 {
120 return allocaInst->getAllocatedType();
121 }
122 else if (const auto *globalValue = SVFUtil::dyn_cast<GlobalValue>(val))
123 {
124 return globalValue->getValueType();
125 }
126 else
127 {
128 ABORT_MSG("unknown value:" + dumpValueAndDbgInfo(val));
129 }
130}
131
133{
134 ABORT_IFNOT(val, "val cannot be null");
135 // heap has a default type of 8-bit integer type
136 if (SVFUtil::isa<Instruction>(val) && LLVMUtil::isHeapAllocExtCallViaRet(
137 SVFUtil::cast<Instruction>(val)))
138 return int8Type();
139 // otherwise we return a pointer type in the default address space
140 return ptrType();
141}
142
147
155{
156 const Type* res = inferPointsToType(var);
157 // infer type by leveraging the type alignment of src and dst in memcpy
158 // for example,
159 //
160 // %tmp = alloca %struct.outer
161 // %inner_v = alloca %struct.inner
162 // %ptr = getelementptr inbounds %struct.outer, ptr %tmp, i32 0, i32 1, !dbg !38
163 // %0 = load ptr, ptr %ptr, align 8, !dbg !38
164 // call void @llvm.memcpy.p0.p0.i64(ptr %inner_v, ptr %0, i64 24, i1 false)
165 //
166 // It is difficult to infer the type of %0 without deep alias analysis,
167 // but we can infer the obj type of %0 based on that of %inner_v.
168 if (res == defaultType(var))
169 {
170 // hasUseList() was removed from Value's public API in LLVM 18-20; use
171 // the version-portable compat shim defined above instead.
172 if (!hasUseList(var)) return res;
173 for (const auto& use: var->users())
174 {
175 if (const CallBase* cs = SVFUtil::dyn_cast<CallBase>(use))
176 {
177 if (const Function* calledFun = cs->getCalledFunction())
179 {
180 assert(cs->getNumOperands() > 1 && "arguments should be greater than 1");
181 const Value* dst = cs->getArgOperand(0);
182 const Value* src = cs->getArgOperand(1);
183 if(calledFun->getName().find("iconv") != std::string::npos)
184 {
185 if(var == cs->getArgOperand(0))
186 return res;
187 dst = cs->getArgOperand(3), src = cs->getArgOperand(1);
188 }
189
190 if (var == dst) return inferPointsToType(src);
191 else if (var == src) return inferPointsToType(dst);
192 else ABORT_MSG("invalid memcpy call");
193 }
194 }
195 }
196 }
197 return res;
198}
199
201{
202 if (isAlloc(var)) return fwInferObjType(var);
205 if (sources.empty())
206 {
207 // cannot find allocation, try to fw infer starting from var
208 types.insert(fwInferObjType(var));
209 }
210 else
211 {
212 for (const auto &source: sources)
213 {
214 types.insert(fwInferObjType(source));
215 }
216 }
218 ABORT_IFNOT(largestTy, "return type cannot be null");
219 return largestTy;
220}
221
227{
228 if (const AllocaInst *allocaInst = SVFUtil::dyn_cast<AllocaInst>(var))
229 {
230 // stack object
232 }
233 else if (const GlobalValue *global = SVFUtil::dyn_cast<GlobalValue>(var))
234 {
235 // global object
236 return infersiteToType(global);
237 }
238 else
239 {
240 // for heap or static object, we forward infer its type
241
242 // consult cache
243 auto tIt = _valueToType.find(var);
244 if (tIt != _valueToType.end())
245 {
246 return tIt->second ? tIt->second : defaultType(var);
247 }
248
249 // simulate the call stack, the second element indicates whether we should update valueTypes for current value
251 Set<ValueBoolPair> visited;
252 workList.push({var, false});
253
254 while (!workList.empty())
255 {
256 auto curPair = workList.pop();
257 if (visited.count(curPair))
258 continue;
259 visited.insert(curPair);
260 const Value* curValue = curPair.first;
261 bool canUpdate = curPair.second;
263
265 &canUpdate](const Value* infersite)
266 {
267 if (canUpdate)
268 infersites.insert(infersite);
269 };
271 [this, &infersites, &workList, &canUpdate](const auto& pUser)
272 {
273 auto vIt = _valueToInferSites.find(pUser);
274 if (canUpdate)
275 {
276 if (vIt != _valueToInferSites.end())
277 {
278 infersites.insert(vIt->second.begin(),
279 vIt->second.end());
280 }
281 }
282 else
283 {
284 if (vIt == _valueToInferSites.end())
285 workList.push({pUser, false});
286 }
287 };
288 if (!canUpdate && !_valueToInferSites.count(curValue))
289 {
290 workList.push({curValue, true});
291 }
292 if (const auto* gepInst =
293 SVFUtil::dyn_cast<GetElementPtrInst>(curValue))
295 // hasUseList() was removed from Value's public API in LLVM 18-20; use
296 // the version-portable compat shim defined above instead.
297 if (!hasUseList(curValue)) continue;
298 for (const auto it : curValue->users())
299 {
300 if (const auto* loadInst = SVFUtil::dyn_cast<LoadInst>(it))
301 {
302 /*
303 * infer based on load, e.g.,
304 %call = call i8* malloc()
305 %1 = bitcast i8* %call to %struct.MyStruct*
306 %q = load %struct.MyStruct, %struct.MyStruct* %1
307 */
309 }
310 else if (const auto* storeInst =
311 SVFUtil::dyn_cast<StoreInst>(it))
312 {
313 if (storeInst->getPointerOperand() == curValue)
314 {
315 /*
316 * infer based on store (pointer operand), e.g.,
317 %call = call i8* malloc()
318 %1 = bitcast i8* %call to %struct.MyStruct*
319 store %struct.MyStruct .., %struct.MyStruct* %1
320 */
322 }
323 else
324 {
325 for (const auto nit :
326 storeInst->getPointerOperand()->users())
327 {
328 /*
329 * propagate across store (value operand) and load
330 %call = call i8* malloc()
331 store i8* %call, i8** %p
332 %q = load i8*, i8** %p
333 ..infer based on %q..
334 */
335 if (SVFUtil::isa<LoadInst>(nit))
337 }
338 /*
339 * infer based on store (value operand) <- gep (result element)
340 */
341 if (const auto* gepInst =
342 SVFUtil::dyn_cast<GetElementPtrInst>(
343 storeInst->getPointerOperand()))
344 {
345 /*
346 %call1 = call i8* @TYPE_MALLOC(i32 noundef 16, i32
347 noundef 2), !dbg !39 %2 = bitcast i8* %call1 to
348 %struct.MyStruct*, !dbg !41 %3 = load
349 %struct.MyStruct*, %struct.MyStruct** %p, align 8,
350 !dbg !42 %next = getelementptr inbounds
351 %struct.MyStruct, %struct.MyStruct* %3, i32 0, i32
352 1, !dbg !43 store %struct.MyStruct* %2,
353 %struct.MyStruct** %next, align 8, !dbg !44 %5 =
354 load %struct.MyStruct*, %struct.MyStruct** %p,
355 align 8, !dbg !48 %next3 = getelementptr inbounds
356 %struct.MyStruct, %struct.MyStruct* %5, i32 0, i32
357 1, !dbg !49 %6 = load %struct.MyStruct*,
358 %struct.MyStruct** %next3, align 8, !dbg !49 infer
359 site -> %f1 = getelementptr inbounds
360 %struct.MyStruct, %struct.MyStruct* %6, i32 0, i32
361 0, !dbg !50
362 */
363 const Value* gepBase = gepInst->getPointerOperand();
364 if (const auto* load =
365 SVFUtil::dyn_cast<LoadInst>(gepBase))
366 {
367 for (const auto loadUse :
368 load->getPointerOperand()->users())
369 {
370 if (loadUse == load ||
371 !SVFUtil::isa<LoadInst>(loadUse))
372 continue;
373 for (const auto gepUse : loadUse->users())
374 {
375 if (!SVFUtil::isa<GetElementPtrInst>(
376 gepUse))
377 continue;
378 for (const auto loadUse2 :
379 gepUse->users())
380 {
381 if (SVFUtil::isa<LoadInst>(
382 loadUse2))
383 {
385 loadUse2);
386 }
387 }
388 }
389 }
390 }
391 else if (const auto* alloc =
392 SVFUtil::dyn_cast<AllocaInst>(gepBase))
393 {
394 /*
395 %2 = alloca %struct.ll, align 8
396 store i32 0, ptr %1, align 4
397 %3 = call noalias noundef nonnull ptr
398 @_Znwm(i64 noundef 16) #2 %4 = getelementptr
399 inbounds %struct.ll, ptr %2, i32 0, i32 1
400 store ptr %3, ptr %4, align 8
401 %5 = getelementptr inbounds %struct.ll, ptr
402 %2, i32 0, i32 1 %6 = load ptr, ptr %5, align
403 8 %7 = getelementptr inbounds %struct.ll, ptr
404 %6, i32 0, i32 0
405 */
406 for (const auto gepUse : alloc->users())
407 {
408 if (!SVFUtil::isa<GetElementPtrInst>(
409 gepUse))
410 continue;
411 for (const auto loadUse2 : gepUse->users())
412 {
413 if (SVFUtil::isa<LoadInst>(loadUse2))
414 {
416 loadUse2);
417 }
418 }
419 }
420 }
421 }
422 }
423 }
424 else if (const auto* gepInst =
425 SVFUtil::dyn_cast<GetElementPtrInst>(it))
426 {
427 /*
428 * infer based on gep (pointer operand)
429 %call = call i8* malloc()
430 %1 = bitcast i8* %call to %struct.MyStruct*
431 %next = getelementptr inbounds %struct.MyStruct,
432 %struct.MyStruct* %1, i32 0..
433 */
434 if (gepInst->getPointerOperand() == curValue)
436 }
437 else if (const auto* bitcast =
438 SVFUtil::dyn_cast<BitCastInst>(it))
439 {
440 // continue on bitcast
442 }
443 else if (const auto* phiNode = SVFUtil::dyn_cast<PHINode>(it))
444 {
445 // continue on bitcast
447 }
448 else if (const auto* retInst =
449 SVFUtil::dyn_cast<ReturnInst>(it))
450 {
451 /*
452 * propagate from return to caller
453 Function Attrs: noinline nounwind optnone uwtable
454 define dso_local i8* @malloc_wrapper() #0 !dbg !22 {
455 entry:
456 %call = call i8* @malloc(i32 noundef 16), !dbg !25
457 ret i8* %call, !dbg !26
458 }
459 %call = call i8* @malloc_wrapper()
460 ..infer based on %call..
461 */
462 for (const auto callsite : retInst->getFunction()->users())
463 {
464 if (const auto* callBase =
465 SVFUtil::dyn_cast<CallBase>(callsite))
466 {
467 // skip function as parameter
468 // e.g., call void @foo(%struct.ssl_ctx_st* %9, i32 (i8*, i32, i32, i8*)* @passwd_callback)
469 if (callBase->getCalledFunction() !=
470 retInst->getFunction())
471 continue;
473 }
474 }
475 }
476 else if (const auto* callBase = SVFUtil::dyn_cast<CallBase>(it))
477 {
478 /*
479 * propagate from callsite to callee
480 %call = call i8* @malloc(i32 noundef 16)
481 %0 = bitcast i8* %call to %struct.Node*, !dbg !43
482 call void @foo(%struct.Node* noundef %0), !dbg !45
483
484 define dso_local void @foo(%struct.Node* noundef %param)
485 #0 !dbg !22 {...}
486 ..infer based on the formal param %param..
487 */
488 // skip global function value -> callsite
489 // e.g., def @foo() -> call @foo()
490 // we don't skip function as parameter, e.g., def @foo() -> call @bar(..., @foo)
491 if (SVFUtil::isa<Function>(curValue) &&
492 curValue == callBase->getCalledFunction())
493 continue;
494 // skip indirect call
495 // e.g., %0 = ... -> call %0(...)
496 if (!callBase->hasArgument(curValue))
497 continue;
498 if (Function* calleeFunc = callBase->getCalledFunction())
499 {
501 // for varargs function, we cannot directly get the value-flow between actual and formal args e.g., consider the following vararg function @callee 1: call void @callee(%arg) 2: define dso_local i32 @callee(...) #0 !dbg !17 { 3: ....... 4: %5 = load i32, ptr %vaarg.addr, align 4, !dbg !55 5: .......
502 // 6: }
503 // it is challenging to precisely identify the forward value-flow of %arg (Line 2) because the function definition of callee (Line 2) does not have any formal args related to the actual arg %arg therefore we track all possible instructions like ``load i32, ptr %vaarg.addr''
504 if (calleeFunc->isVarArg())
505 {
506 // conservatively track all var args
507 for (auto& I : instructions(calleeFunc))
508 {
509 if (auto* load =
510 llvm::dyn_cast<llvm::LoadInst>(&I))
511 {
512 llvm::Value* loadPointer =
513 load->getPointerOperand();
514 if (loadPointer->getName().compare(
515 "vaarg.addr") == 0)
516 {
518 }
519 }
520 }
521 }
522 else if (!calleeFunc->isDeclaration())
523 {
525 calleeFunc->getArg(pos));
526 }
527 }
528 }
529 }
530 if (canUpdate)
531 {
533 std::transform(infersites.begin(), infersites.end(),
534 std::inserter(types, types.begin()),
538 }
539 }
540 const Type* type = _valueToType[var];
541 if (type == nullptr)
542 {
544 WARN_MSG("Using default type, trace ID is " +
545 std::to_string(traceId) + ":" + dumpValueAndDbgInfo(var));
546 }
547 ABORT_IFNOT(type, "type cannot be a null ptr");
548 return type;
549 }
550}
551
558{
559
560 // consult cache
561 auto tIt = _valueToAllocs.find(var);
562 if (tIt != _valueToAllocs.end())
563 {
564 return tIt->second;
565 }
566
567 // simulate the call stack, the second element indicates whether we should update sources for current value
569 Set<ValueBoolPair> visited;
570 workList.push({var, false});
571 while (!workList.empty())
572 {
573 auto curPair = workList.pop();
574 if (visited.count(curPair)) continue;
575 visited.insert(curPair);
576 const Value *curValue = curPair.first;
577 bool canUpdate = curPair.second;
578
579 Set<const Value *> sources;
580 auto insertAllocs = [&sources, &canUpdate](const Value *source)
581 {
582 if (canUpdate) sources.insert(source);
583 };
584 auto insertAllocsOrPushWorklist = [this, &sources, &workList, &canUpdate](const auto &pUser)
585 {
586 auto vIt = _valueToAllocs.find(pUser);
587 if (canUpdate)
588 {
589 if (vIt != _valueToAllocs.end())
590 {
591 sources.insert(vIt->second.begin(), vIt->second.end());
592 }
593 }
594 else
595 {
596 if (vIt == _valueToAllocs.end()) workList.push({pUser, false});
597 }
598 };
599
600 if (!canUpdate && !_valueToAllocs.count(curValue))
601 {
602 workList.push({curValue, true});
603 }
604
605 if (isAlloc(curValue))
606 {
608 }
609 else if (const auto *bitCastInst = SVFUtil::dyn_cast<BitCastInst>(curValue))
610 {
611 Value *prevVal = bitCastInst->getOperand(0);
613 }
614 else if (const auto *phiNode = SVFUtil::dyn_cast<PHINode>(curValue))
615 {
616 for (u32_t i = 0; i < phiNode->getNumOperands(); ++i)
617 {
619 }
620 }
621 else if (const auto *loadInst = SVFUtil::dyn_cast<LoadInst>(curValue))
622 {
623 for (const auto use: loadInst->getPointerOperand()->users())
624 {
625 if (const StoreInst *storeInst = SVFUtil::dyn_cast<StoreInst>(use))
626 {
627 if (storeInst->getPointerOperand() == loadInst->getPointerOperand())
628 {
629 insertAllocsOrPushWorklist(storeInst->getValueOperand());
630 }
631 }
632 }
633 }
634 else if (const auto *argument = SVFUtil::dyn_cast<Argument>(curValue))
635 {
636 for (const auto use: argument->getParent()->users())
637 {
638 if (const CallBase *callBase = SVFUtil::dyn_cast<CallBase>(use))
639 {
640 // skip function as parameter
641 // e.g., call void @foo(%struct.ssl_ctx_st* %9, i32 (i8*, i32, i32, i8*)* @passwd_callback)
642 if (callBase->getCalledFunction() != argument->getParent()) continue;
643 u32_t pos = argument->getParent()->isVarArg() ? 0 : argument->getArgNo();
644 insertAllocsOrPushWorklist(callBase->getArgOperand(pos));
645 }
646 }
647 }
648 else if (const auto *callBase = SVFUtil::dyn_cast<CallBase>(curValue))
649 {
650 ABORT_IFNOT(!callBase->doesNotReturn(), "callbase does not return:" + dumpValueAndDbgInfo(callBase));
651 if (Function *callee = callBase->getCalledFunction())
652 {
653 if (!callee->isDeclaration())
654 {
655
657 const BasicBlock* exitBB = llvmmodule->getFunExitBB(callee);
658 assert (exitBB && "exit bb is not a basic block?");
659 const Value *pValue = &exitBB->back();
660 const auto *retInst = SVFUtil::dyn_cast<ReturnInst>(pValue);
661 ABORT_IFNOT(retInst && retInst->getReturnValue(), "not return inst?");
662 insertAllocsOrPushWorklist(retInst->getReturnValue());
663 }
664 }
665 }
666 if (canUpdate)
667 {
669 }
670 }
671 Set<const Value *> &srcs = _valueToAllocs[var];
672 if (srcs.empty())
673 {
674 WARN_MSG("Cannot find allocation: " + dumpValueAndDbgInfo(var));
675 }
676 return srcs;
677}
678
683
689{
690 if (const Function *func = cs->getCalledFunction())
691 {
692 if (func->getName().find(TYPEMALLOC) != std::string::npos)
693 {
694 const Type *objType = fwInferObjType(cs);
695 const auto *pInt =
696 SVFUtil::dyn_cast<llvm::ConstantInt>(cs->getOperand(1));
697 assert(pInt && "the second argument is a integer");
700 SVFUtil::outs() << SVFUtil::sucMsg("\t SUCCESS :") << dumpValueAndDbgInfo(cs)
701 << SVFUtil::pasMsg(" TYPE: ")
702 << dumpType(objType) << "\n";
703 else
704 {
705 SVFUtil::errs() << SVFUtil::errMsg("\t FAILURE :") << ":" << dumpValueAndDbgInfo(cs) << " TYPE: "
706 << dumpType(objType) << "\n";
707 abort();
708 }
709 }
710 }
711}
712
714{
715#if TYPE_DEBUG
719 {
720 ERR_MSG("original type is:" + dumpType(oTy));
721 ERR_MSG("infered type is:" + dumpType(iTy));
722 ABORT_MSG("wrong type, trace ID is " + std::to_string(traceId) + ":" + dumpValueAndDbgInfo(val));
723 }
724#endif
725}
726
728{
729 assert(callBase->hasArgument(arg) && "callInst does not have argument arg?");
730 auto it = std::find(callBase->arg_begin(), callBase->arg_end(), arg);
731 assert(it != callBase->arg_end() && "Didn't find argument?");
732 return std::distance(callBase->arg_begin(), it);
733}
734
735
737{
738 if (objTys.empty()) return nullptr;
739 // map type size to types from with key in descending order
741 for (const Type *ty: objTys)
742 {
744 }
745 assert(!typeSzToTypes.empty() && "typeSzToTypes cannot be empty");
747 std::tie(std::ignore, largestTypes) = *typeSzToTypes.begin();
748 assert(!largestTypes.empty() && "largest element cannot be empty");
749 return *largestTypes.begin();
750}
751
753{
755 if (SVFUtil::isa<ArrayType>(objTy))
757 else if (const auto *st = SVFUtil::dyn_cast<StructType>(objTy))
758 {
761 if (!classTyHasVTable(st))
763 }
764 return num;
765}
766
767
778{
779 auto it = _thisPtrClassNames.find(thisPtr);
780 if (it != _thisPtrClassNames.end()) return it->second;
781
783
784 // Lambda for checking a function is a valid name source & extracting a class name from it
785 auto addNamesFromFunc = [&names](const Function *func) -> void
786 {
787 ABORT_IFNOT(isClsNameSource(func), "Func is invalid class name source: " + dumpValueAndDbgInfo(func));
788 for (const auto &name : extractClsNamesFromFunc(func)) names.insert(name);
789 };
790
791 // Lambda for getting callee & extracting class name for calls to constructors/destructors/template funcs
792 auto addNamesFromCall = [&names, &addNamesFromFunc](const CallBase *call) -> void
793 {
794 ABORT_IFNOT(isClsNameSource(call), "Call is invalid class name source: " + dumpValueAndDbgInfo(call));
795
796 const auto *func = call->getCalledFunction();
797 if (isDynCast(func)) names.insert(extractClsNameFromDynCast(call));
799 };
800
801 // Walk backwards to find all valid source sites for the pointer (e.g. stack/global/heap variables)
802 for (const auto &val: bwFindAllocOrClsNameSources(thisPtr))
803 {
804 // A source site is either a constructor/destructor/template function from which the class name can be
805 // extracted; a call to a C++ constructor/destructor/template function from which the class name can be
806 // extracted; or an allocation site of an object (i.e. a stack/global/heap variable), from which a
807 // forward walk can be performed to find calls to C++ constructor/destructor/template functions from
808 // which the class' name can then be extracted; skip starting pointer
809 if (val == thisPtr) continue;
810
811 if (const auto *func = SVFUtil::dyn_cast<Function>(val))
812 {
813 // Constructor/destructor/template func; extract name from func directly
815 }
816 else if (isClsNameSource(val))
817 {
818 // Call to constructor/destructor/template func; get callee; extract name from callee
819 ABORT_IFNOT(SVFUtil::isa<CallBase>(val), "Call source site is not a callbase: " + dumpValueAndDbgInfo(val));
820 addNamesFromCall(SVFUtil::cast<CallBase>(val));
821 }
822 else if (isAlloc(val))
823 {
824 // Stack/global/heap allocation site; walk forward; find constructor/destructor/template calls
825 ABORT_IFNOT((SVFUtil::isa<AllocaInst, CallBase, GlobalVariable>(val)),
826 "Alloc site source is not a stack/heap/global variable: " + dumpValueAndDbgInfo(val));
827 for (const auto *src : fwFindClsNameSources(val))
828 {
829 if (const auto *func = SVFUtil::dyn_cast<Function>(src)) addNamesFromFunc(func);
830 else if (const auto *call = SVFUtil::dyn_cast<CallBase>(src)) addNamesFromCall(call);
831 else ABORT_MSG("Source site from forward walk is invalid: " + dumpValueAndDbgInfo(src));
832 }
833 }
834 else
835 {
836 ERR_MSG("Unsupported source type found:" + dumpValueAndDbgInfo(val));
837 }
838 }
839
841}
842
851{
852
853 // consult cache
856 {
857 return tIt->second;
858 }
859
860 // simulate the call stack, the second element indicates whether we should update sources for current value
862 Set<ValueBoolPair> visited;
863 workList.push({startValue, false});
864 while (!workList.empty())
865 {
866 auto curPair = workList.pop();
867 if (visited.count(curPair)) continue;
868 visited.insert(curPair);
869 const Value *curValue = curPair.first;
870 bool canUpdate = curPair.second;
871
872 Set<const Value *> sources;
873 auto insertSource = [&sources, &canUpdate](const Value *source)
874 {
875 if (canUpdate) sources.insert(source);
876 };
877 auto insertSourcesOrPushWorklist = [this, &sources, &workList, &canUpdate](const auto &pUser)
878 {
880 if (canUpdate)
881 {
882 if (vIt != _valueToAllocOrClsNameSources.end() && !vIt->second.empty())
883 {
884 sources.insert(vIt->second.begin(), vIt->second.end());
885 }
886 }
887 else
888 {
889 if (vIt == _valueToAllocOrClsNameSources.end()) workList.push({pUser, false});
890 }
891 };
892
894 {
895 workList.push({curValue, true});
896 }
897
898 // If current value is an instruction inside a constructor/destructor/template, use it as a source
899 if (const auto *inst = SVFUtil::dyn_cast<Instruction>(curValue))
900 {
901 if (const auto *parent = inst->getFunction())
902 {
904 }
905 }
906
907 // If the current value is an object (global, heap, stack, etc) or name source (constructor/destructor,
908 // a C++ dynamic cast, or a template function), use it as a source
910 {
912 }
913
914 // Explore the current value further depending on the type of the value; use cached values if possible
915 if (const auto *getElementPtrInst = SVFUtil::dyn_cast<GetElementPtrInst>(curValue))
916 {
918 }
919 else if (const auto *bitCastInst = SVFUtil::dyn_cast<BitCastInst>(curValue))
920 {
922 }
923 else if (const auto *phiNode = SVFUtil::dyn_cast<PHINode>(curValue))
924 {
925 for (const auto *op : phiNode->operand_values())
926 {
928 }
929 }
930 else if (const auto *loadInst = SVFUtil::dyn_cast<LoadInst>(curValue))
931 {
932 for (const auto *user : loadInst->getPointerOperand()->users())
933 {
934 if (const auto *storeInst = SVFUtil::dyn_cast<StoreInst>(user))
935 {
936 if (storeInst->getPointerOperand() == loadInst->getPointerOperand())
937 {
938 insertSourcesOrPushWorklist(storeInst->getValueOperand());
939 }
940 }
941 }
942 }
943 else if (const auto *argument = SVFUtil::dyn_cast<Argument>(curValue))
944 {
945 for (const auto *user: argument->getParent()->users())
946 {
947 if (const auto *callBase = SVFUtil::dyn_cast<CallBase>(user))
948 {
949 // skip function as parameter
950 // e.g., call void @foo(%struct.ssl_ctx_st* %9, i32 (i8*, i32, i32, i8*)* @passwd_callback)
951 if (callBase->getCalledFunction() != argument->getParent()) continue;
952 u32_t pos = argument->getParent()->isVarArg() ? 0 : argument->getArgNo();
954 }
955 }
956 }
957 else if (const auto *callBase = SVFUtil::dyn_cast<CallBase>(curValue))
958 {
959 ABORT_IFNOT(!callBase->doesNotReturn(), "callbase does not return:" + dumpValueAndDbgInfo(callBase));
960 if (const auto *callee = callBase->getCalledFunction())
961 {
962 if (!callee->isDeclaration())
963 {
965 const BasicBlock* exitBB = llvmmodule->getFunExitBB(callee);
966 assert (exitBB && "exit bb is not a basic block?");
967 const Value *pValue = &exitBB->back();
968 const auto *retInst = SVFUtil::dyn_cast<ReturnInst>(pValue);
969 ABORT_IFNOT(retInst && retInst->getReturnValue(), "not return inst?");
970 insertSourcesOrPushWorklist(retInst->getReturnValue());
971 }
972 }
973 }
974
975 // If updating is allowed; store the gathered sources as sources for the current value in the cache
976 if (canUpdate)
977 {
979 }
980 }
981
982 return _valueToAllocOrClsNameSources[startValue];
983}
984
986{
987 assert(startValue && "startValue was null?");
988
989 // consult cache
991 if (tIt != _objToClsNameSources.end())
992 {
993 return tIt->second;
994 }
995
996 Set<const CallBase *> sources;
997
998 // Lambda for adding a callee to the sources iff it is a constructor/destructor/template/dyncast
999 auto inferViaCppCall = [&sources](const CallBase *caller)
1000 {
1001 if (!caller) return;
1002 if (isClsNameSource(caller)) sources.insert(caller);
1003 };
1004
1005 // Find all calls of starting val (or through cast); add as potential source iff applicable
1006 for (const auto *user : startValue->users())
1007 {
1008 if (const auto *caller = SVFUtil::dyn_cast<CallBase>(user))
1009 {
1011 }
1012 else if (const auto *bitcast = SVFUtil::dyn_cast<BitCastInst>(user))
1013 {
1014 for (const auto *cast_user : bitcast->users())
1015 {
1016 if (const auto *caller = SVFUtil::dyn_cast<CallBase>(cast_user))
1017 {
1019 }
1020 }
1021 }
1022 }
1023
1024 // Store sources in cache for starting value & return the found sources
1026}
#define ABORT_MSG(msg)
const std::string TYPEMALLOC
const Type * infersiteToType(const Value *val)
#define ABORT_IFNOT(condition, msg)
#define ERR_MSG(msg)
#define WARN_MSG(msg)
static bool hasUseList(const Value *v)
newitem type
Definition cJSON.cpp:2739
const char *const name
Definition cJSON.h:264
static LLVMModuleSet * getLLVMModuleSet()
Definition LLVMModule.h:133
LLVMContext & getContext() const
Definition LLVMModule.h:384
ValueToInferSites _valueToInferSites
ValueToSources _valueToAllocs
const Type * selectLargestSizedType(Set< const Type * > &objTys)
select the largest (conservative) type from all types
const Type * inferPointsToType(const Value *var)
Set< const Value * > & bwfindAllocOfVar(const Value *var)
backward collect all possible allocation sites (stack, static, heap) of var
u32_t objTyToNumFields(const Type *objTy)
bool isAlloc(const SVF::Value *val)
is allocation (stack, static, heap)
u32_t getArgPosInCall(const CallBase *callBase, const Value *arg)
Set< const Value * > & bwFindAllocOrClsNameSources(const Value *startValue)
ValueToClassNames _thisPtrClassNames
Set< std::string > & inferThisPtrClsName(const Value *thisPtr)
get or infer the class names of thisptr
void typeSizeDiffTest(const PointerType *oPTy, const Type *iTy, const Value *val)
const Type * fwInferObjType(const Value *var)
forward infer the type of the object pointed by var
const IntegerType * int8Type()
int8 type
const Type * ptrType()
pointer type
const Type * inferObjType(const Value *var)
get or infer the type of the object pointed by the value
const Type * defaultType(const Value *val)
default type
ObjToClsNameSources _objToClsNameSources
Set< const CallBase * > & fwFindClsNameSources(const Value *startValue)
forward find class name sources starting from an allocation
void validateTypeCheck(const CallBase *cs)
validate type inference
ValueToSources _valueToAllocOrClsNameSources
static const Option< u32_t > MaxFieldLimit
Maximum number of field derivations for an object.
Definition Options.h:34
bool isHeapAllocExtCallViaRet(const Instruction *inst)
Definition LLVMUtil.cpp:639
bool isMemcpyExtFun(const Function *fun)
Definition LLVMUtil.cpp:390
std::string dumpType(const Type *type)
Definition LLVMUtil.cpp:617
std::pair< s64_t, u64_t > getIntegerValue(const ConstantInt *intValue)
Definition LLVMUtil.h:85
std::string dumpValueAndDbgInfo(const Value *val)
Definition LLVMUtil.cpp:628
u32_t getNumOfElements(const Type *ety)
Return size of this object based on LLVM value.
Definition LLVMUtil.cpp:297
bool isObject(const Value *ref)
Return true if this value refers to a object.
Definition LLVMUtil.cpp:61
static Type * getPtrElementType(const PointerType *pty)
Definition LLVMUtil.h:134
std::string sucMsg(const std::string &msg)
Returns successful message by converting a string into green string output.
Definition SVFUtil.cpp:59
std::string pasMsg(const std::string &msg)
Print each pass/phase message by converting a string into blue string output.
Definition SVFUtil.cpp:105
std::string errMsg(const std::string &msg)
Print error message by converting a string into red string output.
Definition SVFUtil.cpp:82
std::ostream & errs()
Overwrite llvm::errs()
Definition SVFUtil.h:58
std::ostream & outs()
Overwrite llvm::outs()
Definition SVFUtil.h:52
constexpr std::remove_reference< T >::type && move(T &&t) noexcept
Definition SVFUtil.h:420
std::string extractClsNameFromDynCast(const CallBase *callBase)
extract class name from cpp dyncast function
Definition CppUtil.cpp:993
bool classTyHasVTable(const StructType *ty)
Definition CppUtil.cpp:644
bool isClsNameSource(const Value *val)
Definition CppUtil.cpp:935
Set< std::string > extractClsNamesFromFunc(const Function *foo)
extract class name from the c++ function name, e.g., constructor/destructors
Definition CppUtil.cpp:781
bool isDynCast(const Function *foo)
whether foo is a cpp dyncast function
Definition CppUtil.cpp:983
for isBitcode
Definition BasicTypes.h:70
llvm::Type Type
Definition BasicTypes.h:87
llvm::CallBase CallBase
Definition BasicTypes.h:153
llvm::BasicBlock BasicBlock
Definition BasicTypes.h:90
llvm::AllocaInst AllocaInst
Definition BasicTypes.h:157
llvm::Function Function
Definition BasicTypes.h:89
llvm::GlobalValue GlobalValue
Definition BasicTypes.h:92
llvm::Value Value
LLVM Basic classes.
Definition BasicTypes.h:86
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76
llvm::PointerType PointerType
Definition BasicTypes.h:100
llvm::StoreInst StoreInst
Definition BasicTypes.h:155
unsigned u32_t
Definition GeneralType.h:67
llvm::LLVMContext LLVMContext
Definition BasicTypes.h:72