svf-tools 1.0.1194 → 1.0.1195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/package.json +1 -1
  2. package/svf/include/AE/Core/AbstractState.h +3 -3
  3. package/svf/include/AE/Core/AddressValue.h +2 -2
  4. package/svf/include/AE/Svfexe/AbsExtAPI.h +12 -32
  5. package/svf/include/AE/Svfexe/AbstractInterpretation.h +13 -3
  6. package/svf/lib/AE/Svfexe/AEDetector.cpp +20 -4
  7. package/svf/lib/AE/Svfexe/AbsExtAPI.cpp +153 -193
  8. package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +189 -56
  9. package/SVF-doxygen/doxygen.config +0 -2548
  10. package/SVF-doxygen/wiki/PAG.png +0 -0
  11. package/SVF-doxygen/wiki/andersen.png +0 -0
  12. package/SVF-doxygen/wiki/callgraph.png +0 -0
  13. package/SVF-doxygen/wiki/consG.png +0 -0
  14. package/SVF-doxygen/wiki/cpu2000-flto +0 -432
  15. package/SVF-doxygen/wiki/cpu2006-flto +0 -417
  16. package/SVF-doxygen/wiki/cpu2017-wllvm.cfg +0 -999
  17. package/SVF-doxygen/wiki/database.png +0 -0
  18. package/SVF-doxygen/wiki/framework.png +0 -0
  19. package/SVF-doxygen/wiki/help.png +0 -0
  20. package/SVF-doxygen/wiki/icfg.png +0 -0
  21. package/SVF-doxygen/wiki/mssa-cha.png +0 -0
  22. package/SVF-doxygen/wiki/pagedge.png +0 -0
  23. package/SVF-doxygen/wiki/pagnode.png +0 -0
  24. package/SVF-doxygen/wiki/pt.png +0 -0
  25. package/SVF-doxygen/wiki/setupcmake.png +0 -0
  26. package/SVF-doxygen/wiki/setupconfiguration.png +0 -0
  27. package/SVF-doxygen/wiki/setupdashboard.png +0 -0
  28. package/SVF-doxygen/wiki/setupdebug.png +0 -0
  29. package/SVF-doxygen/wiki/setupenv.png +0 -0
  30. package/SVF-doxygen/wiki/startup.png +0 -0
  31. package/SVF-doxygen/wiki/svf-stat.pdf +0 -0
  32. package/SVF-doxygen/wiki/svfg-framework.png +0 -0
  33. package/SVF-doxygen/wiki/svfg.png +0 -0
  34. package/SVF-doxygen/wiki/svfg_opt.png +0 -0
  35. package/SVF-doxygen/wiki/svfgedge-cha.png +0 -0
  36. package/SVF-doxygen/wiki/svfgnode-cha.png +0 -0
  37. package/SVF-doxygen/wiki/svfpic/README.md +0 -6
  38. package/SVF-doxygen/wiki/svfpic/ass-1debug1.png +0 -0
  39. package/SVF-doxygen/wiki/svfpic/ass-1debug2.png +0 -0
  40. package/SVF-doxygen/wiki/svfpic/build.jpg +0 -0
  41. package/SVF-doxygen/wiki/svfpic/cmd.png +0 -0
  42. package/SVF-doxygen/wiki/svfpic/connect1.jpg +0 -0
  43. package/SVF-doxygen/wiki/svfpic/connect2.png +0 -0
  44. package/SVF-doxygen/wiki/svfpic/connect3.png +0 -0
  45. package/SVF-doxygen/wiki/svfpic/connect4.jpg +0 -0
  46. package/SVF-doxygen/wiki/svfpic/connect5.jpg +0 -0
  47. package/SVF-doxygen/wiki/svfpic/connect6.png +0 -0
  48. package/SVF-doxygen/wiki/svfpic/connect7.jpg +0 -0
  49. package/SVF-doxygen/wiki/svfpic/continue.png +0 -0
  50. package/SVF-doxygen/wiki/svfpic/debug-new.png +0 -0
  51. package/SVF-doxygen/wiki/svfpic/debug-new2.png +0 -0
  52. package/SVF-doxygen/wiki/svfpic/debug1.jpeg +0 -0
  53. package/SVF-doxygen/wiki/svfpic/debug2.jpeg +0 -0
  54. package/SVF-doxygen/wiki/svfpic/debug3.png +0 -0
  55. package/SVF-doxygen/wiki/svfpic/debug4.png +0 -0
  56. package/SVF-doxygen/wiki/svfpic/debug5.jpeg +0 -0
  57. package/SVF-doxygen/wiki/svfpic/debug6.jpeg +0 -0
  58. package/SVF-doxygen/wiki/svfpic/docker_sys_requirement.png +0 -0
  59. package/SVF-doxygen/wiki/svfpic/docker_sys_requirements.png +0 -0
  60. package/SVF-doxygen/wiki/svfpic/dockerbuild.png +0 -0
  61. package/SVF-doxygen/wiki/svfpic/dockerbuild2.jpg +0 -0
  62. package/SVF-doxygen/wiki/svfpic/dockerbuild3.jpg +0 -0
  63. package/SVF-doxygen/wiki/svfpic/dockerbuild4.png +0 -0
  64. package/SVF-doxygen/wiki/svfpic/dockerbuild5.jpg +0 -0
  65. package/SVF-doxygen/wiki/svfpic/dockerbuildimage.png +0 -0
  66. package/SVF-doxygen/wiki/svfpic/dockercmd.png +0 -0
  67. package/SVF-doxygen/wiki/svfpic/dockercmd2.png +0 -0
  68. package/SVF-doxygen/wiki/svfpic/dockercontainer.png +0 -0
  69. package/SVF-doxygen/wiki/svfpic/dockerdb1.jpg +0 -0
  70. package/SVF-doxygen/wiki/svfpic/dockerdb10.jpeg +0 -0
  71. package/SVF-doxygen/wiki/svfpic/dockerdb2.jpg +0 -0
  72. package/SVF-doxygen/wiki/svfpic/dockerdb3.jpg +0 -0
  73. package/SVF-doxygen/wiki/svfpic/dockerdb4.jpg +0 -0
  74. package/SVF-doxygen/wiki/svfpic/dockerdb5.png +0 -0
  75. package/SVF-doxygen/wiki/svfpic/dockerdb6.jpeg +0 -0
  76. package/SVF-doxygen/wiki/svfpic/dockerdb7.png +0 -0
  77. package/SVF-doxygen/wiki/svfpic/dockerdb8.png +0 -0
  78. package/SVF-doxygen/wiki/svfpic/dockerdb9.jpeg +0 -0
  79. package/SVF-doxygen/wiki/svfpic/dockerfinshbuilt.png +0 -0
  80. package/SVF-doxygen/wiki/svfpic/dockerimage.png +0 -0
  81. package/SVF-doxygen/wiki/svfpic/dockernameImage.png +0 -0
  82. package/SVF-doxygen/wiki/svfpic/dockerpull.png +0 -0
  83. package/SVF-doxygen/wiki/svfpic/dockerpull2.png +0 -0
  84. package/SVF-doxygen/wiki/svfpic/download.jpg +0 -0
  85. package/SVF-doxygen/wiki/svfpic/extension1.jpeg +0 -0
  86. package/SVF-doxygen/wiki/svfpic/extension2.jpeg +0 -0
  87. package/SVF-doxygen/wiki/svfpic/graphviz.png +0 -0
  88. package/SVF-doxygen/wiki/svfpic/hellodb.png +0 -0
  89. package/SVF-doxygen/wiki/svfpic/hellodb2.png +0 -0
  90. package/SVF-doxygen/wiki/svfpic/hviz_0.png +0 -0
  91. package/SVF-doxygen/wiki/svfpic/hviz_1.png +0 -0
  92. package/SVF-doxygen/wiki/svfpic/hviz_2.png +0 -0
  93. package/SVF-doxygen/wiki/svfpic/installC:C++Ext.png +0 -0
  94. package/SVF-doxygen/wiki/svfpic/installCMakeExt.png +0 -0
  95. package/SVF-doxygen/wiki/svfpic/installRCext.png +0 -0
  96. package/SVF-doxygen/wiki/svfpic/installdockerext.png +0 -0
  97. package/SVF-doxygen/wiki/svfpic/launch1.png +0 -0
  98. package/SVF-doxygen/wiki/svfpic/openfile.png +0 -0
  99. package/SVF-doxygen/wiki/svfpic/pathfolder.png +0 -0
  100. package/SVF-doxygen/wiki/svfpic/restart.png +0 -0
  101. package/SVF-doxygen/wiki/svfpic/rundocker.png +0 -0
  102. package/SVF-doxygen/wiki/svfpic/runinCLI.png +0 -0
  103. package/SVF-doxygen/wiki/svfpic/screen.png +0 -0
  104. package/SVF-doxygen/wiki/svfpic/settings1.jpg +0 -0
  105. package/SVF-doxygen/wiki/svfpic/settings2.jpg +0 -0
  106. package/SVF-doxygen/wiki/svfpic/settings3.jpg +0 -0
  107. package/SVF-doxygen/wiki/svfpic/shortlists.png +0 -0
  108. package/SVF-doxygen/wiki/svfpic/start.png +0 -0
  109. package/SVF-doxygen/wiki/svfpic/start1.png +0 -0
  110. package/SVF-doxygen/wiki/svfpic/update0.png +0 -0
  111. package/SVF-doxygen/wiki/svfpic/verify_docker.png +0 -0
  112. package/SVF-doxygen/wiki/svfpic/vs_entry_window.png +0 -0
  113. package/SVF-doxygen/wiki/svfpic/wsl.png +0 -0
  114. package/SVF-doxygen/wiki/svfpic/wsl_1.png +0 -0
  115. package/SVF-doxygen/wiki/svfpic/wsl_2.png +0 -0
  116. package/SVF-doxygen/wiki/svfpic/wsl_3.png +0 -0
  117. package/SVF-doxygen/wiki/tools.png +0 -0
  118. package/SVF-doxygen/wiki/users.png +0 -0
  119. package/SVF-doxygen/wiki/vm1.png +0 -0
  120. package/SVF-doxygen/wiki/vm2.png +0 -0
  121. package/SVF-doxygen/wiki/vm3.png +0 -0
  122. package/SVF-doxygen/wiki/vm4.png +0 -0
  123. package/SVF-doxygen/wiki/vm5.png +0 -0
  124. package/SVF-doxygen/wiki/vscode_build_tasks.png +0 -0
  125. package/SVF-doxygen/wiki/vscode_cpp_extension.png +0 -0
  126. package/SVF-doxygen/wiki/vscode_debug_list.png +0 -0
  127. package/SVF-doxygen/wiki/vscode_dir_structure.png +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svf-tools",
3
- "version": "1.0.1194",
3
+ "version": "1.0.1195",
4
4
  "description": "* <b>[TypeClone](https://github.com/SVF-tools/SVF/wiki/TypeClone) published in our [ECOOP paper](https://yuleisui.github.io/publications/ecoop20.pdf) is now available in SVF </b> * <b>SVF now uses a single script for its build. Just type [`source ./build.sh`](https://github.com/SVF-tools/SVF/blob/master/build.sh) in your terminal, that's it!</b> * <b>SVF now supports LLVM-10.0.0! </b> * <b>We thank [bsauce](https://github.com/bsauce) for writing a user manual of SVF ([link1](https://www.jianshu.com/p/068a08ec749c) and [link2](https://www.jianshu.com/p/777c30d4240e)) in Chinese </b> * <b>SVF now supports LLVM-9.0.0 (Thank [Byoungyoung Lee](https://github.com/SVF-tools/SVF/issues/142) for his help!). </b> * <b>SVF now supports a set of [field-sensitive pointer analyses](https://yuleisui.github.io/publications/sas2019a.pdf). </b> * <b>[Use SVF as an external lib](https://github.com/SVF-tools/SVF/wiki/Using-SVF-as-a-lib-in-your-own-tool) for your own project (Contributed by [Hongxu Chen](https://github.com/HongxuChen)). </b> * <b>SVF now supports LLVM-7.0.0. </b> * <b>SVF now supports Docker. [Try SVF in Docker](https://github.com/SVF-tools/SVF/wiki/Try-SVF-in-Docker)! </b> * <b>SVF now supports [LLVM-6.0.0](https://github.com/svf-tools/SVF/pull/38) (Contributed by [Jack Anthony](https://github.com/jackanth)). </b> * <b>SVF now supports [LLVM-4.0.0](https://github.com/svf-tools/SVF/pull/23) (Contributed by Jared Carlson. Thank [Jared](https://github.com/jcarlson23) and [Will](https://github.com/dtzWill) for their in-depth [discussions](https://github.com/svf-tools/SVF/pull/18) about updating SVF!) </b> * <b>SVF now supports analysis for C++ programs.</b> <br />",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -114,7 +114,7 @@ public:
114
114
  /// Return the internal index if addr is an address otherwise return the value of idx
115
115
  inline u32_t getIDFromAddr(u32_t addr)
116
116
  {
117
- return _freedAddrs.count(addr) ? AddressValue::getInternalID(InvalidMemAddr) : AddressValue::getInternalID(addr);
117
+ return _freedAddrs.count(addr) ? AddressValue::getInternalID(BlackHoleObjAddr) : AddressValue::getInternalID(addr);
118
118
  }
119
119
 
120
120
  AbstractState&operator=(const AbstractState&rhs)
@@ -187,9 +187,9 @@ public:
187
187
  return addr == NullMemAddr;
188
188
  }
189
189
 
190
- static inline bool isInvalidMem(u32_t addr)
190
+ static inline bool isBlackHoleObjAddr(u32_t addr)
191
191
  {
192
- return addr == InvalidMemAddr;
192
+ return addr == BlackHoleObjAddr;
193
193
  }
194
194
 
195
195
 
@@ -32,8 +32,8 @@
32
32
 
33
33
  #define AddressMask 0x7f000000
34
34
  #define FlippedAddressMask (AddressMask^0xffffffff)
35
- // the address of InvalidMem(the black hole), getVirtualMemAddress(2);
36
- #define InvalidMemAddr 0x7f000000 + 2
35
+ // the address of BlackHole object, getVirtualMemAddress(2);
36
+ #define BlackHoleObjAddr 0x7f000000 + 2
37
37
  // the address of NullMem, getVirtualMemAddress(0);
38
38
  #define NullMemAddr 0x7f000000
39
39
 
@@ -74,43 +74,23 @@ public:
74
74
  */
75
75
  void handleExtAPI(const CallICFGNode *call);
76
76
 
77
- /**
78
- * @brief Handles the strcpy API call.
79
- * @param call Pointer to the call ICFG node.
80
- */
81
- void handleStrcpy(const CallICFGNode *call);
77
+ // --- Shared primitives used by string/memory handlers ---
82
78
 
83
- /**
84
- * @brief Calculates the length of a string.
85
- * @param as Reference to the abstract state.
86
- * @param strValue Pointer to the SVF variable representing the string.
87
- * @return The interval value representing the string length.
88
- */
79
+ /// Get the byte size of each element for a pointer/array variable.
80
+ u32_t getElementSize(AbstractState& as, const SVFVar* var);
81
+
82
+ /// Check if an interval length is usable (not bottom, not unbounded).
83
+ static bool isValidLength(const IntervalValue& len);
84
+
85
+ /// Calculate the length of a null-terminated string in abstract state.
89
86
  IntervalValue getStrlen(AbstractState& as, const SVF::SVFVar *strValue);
90
87
 
91
- /**
92
- * @brief Handles the strcat API call.
93
- * @param call Pointer to the call ICFG node.
94
- */
95
- void handleStrcat(const SVF::CallICFGNode *call);
88
+ // --- String/memory operation handlers ---
96
89
 
97
- /**
98
- * @brief Handles the memcpy API call.
99
- * @param as Reference to the abstract state.
100
- * @param dst Pointer to the destination SVF variable.
101
- * @param src Pointer to the source SVF variable.
102
- * @param len The interval value representing the length to copy.
103
- * @param start_idx The starting index for copying.
104
- */
90
+ void handleStrcpy(const CallICFGNode *call);
91
+ void handleStrcat(const CallICFGNode *call);
92
+ void handleStrncat(const CallICFGNode *call);
105
93
  void handleMemcpy(AbstractState& as, const SVF::SVFVar *dst, const SVF::SVFVar *src, IntervalValue len, u32_t start_idx);
106
-
107
- /**
108
- * @brief Handles the memset API call.
109
- * @param as Reference to the abstract state.
110
- * @param dst Pointer to the destination SVF variable.
111
- * @param elem The interval value representing the element to set.
112
- * @param len The interval value representing the length to set.
113
- */
114
94
  void handleMemset(AbstractState& as, const SVFVar* dst, IntervalValue elem, IntervalValue len);
115
95
 
116
96
  /**
@@ -36,6 +36,8 @@
36
36
  #include "Util/SVFBugReport.h"
37
37
  #include "Util/SVFStat.h"
38
38
  #include "Graphs/SCC.h"
39
+ #include "Graphs/CallGraph.h"
40
+ #include <deque>
39
41
 
40
42
  namespace SVF
41
43
  {
@@ -144,6 +146,13 @@ public:
144
146
  /// Program entry
145
147
  void analyse();
146
148
 
149
+ /// Analyze all entry points (functions without callers)
150
+ void analyzeFromAllProgEntries();
151
+
152
+ /// Get all entry point functions (functions without callers)
153
+ std::deque<const FunObjVar*> collectProgEntryFuns();
154
+
155
+
147
156
  static AbstractInterpretation& getAEInstance()
148
157
  {
149
158
  static AbstractInterpretation instance;
@@ -218,14 +227,14 @@ private:
218
227
  *
219
228
  * @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles
220
229
  */
221
- virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle);
230
+ virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle, const CallICFGNode* caller = nullptr);
222
231
 
223
232
  /**
224
233
  * Handle a function using worklist algorithm
225
234
  *
226
235
  * @param funEntry The entry node of the function to handle
227
236
  */
228
- void handleFunction(const ICFGNode* funEntry);
237
+ void handleFunction(const ICFGNode* funEntry, const CallICFGNode* caller = nullptr);
229
238
 
230
239
  /**
231
240
  * Handle an ICFG node by merging states and processing statements
@@ -322,9 +331,9 @@ private:
322
331
  AEAPI* api{nullptr};
323
332
 
324
333
  ICFG* icfg;
334
+ CallGraph* callGraph;
325
335
  AEStat* stat;
326
336
 
327
- std::vector<const CallICFGNode*> callSiteStack;
328
337
  Map<const FunObjVar*, const ICFGWTO*> funcToWTO;
329
338
  Set<std::pair<const CallICFGNode*, NodeID>> nonRecursiveCallSites;
330
339
  Set<const FunObjVar*> recursiveFuns;
@@ -358,6 +367,7 @@ private:
358
367
  Map<std::string, std::function<void(const CallICFGNode*)>> func_map;
359
368
 
360
369
  Map<const ICFGNode*, AbstractState> abstractTrace; // abstract states immediately after nodes
370
+ Set<const ICFGNode*> allAnalyzedNodes; // All nodes ever analyzed (across all entry points)
361
371
  std::string moduleName;
362
372
 
363
373
  std::vector<std::unique_ptr<AEDetector>> detectors;
@@ -367,7 +367,7 @@ void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::Add
367
367
  }
368
368
  else
369
369
  {
370
- assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
370
+ assert(AbstractState::isBlackHoleObjAddr(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
371
371
  }
372
372
  }
373
373
  }
@@ -398,7 +398,7 @@ void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::Add
398
398
  }
399
399
  else
400
400
  {
401
- assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
401
+ assert(AbstractState::isBlackHoleObjAddr(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address");
402
402
  }
403
403
  }
404
404
  }
@@ -479,7 +479,23 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
479
479
  SVFIR* svfir = PAG::getPAG();
480
480
  NodeID value_id = value->getId();
481
481
 
482
- assert(as[value_id].isAddr());
482
+ // Lazy initialization for uninitialized pointer parameters in multi-entry analysis.
483
+ // When analyzing a function as an entry point (e.g., not called from main),
484
+ // pointer parameters may not have been initialized via AddrStmt.
485
+ //
486
+ // Example:
487
+ // void process_buffer(char* buf, int len) {
488
+ // buf[0] = 'a'; // accessing buf
489
+ // }
490
+ // When analyzing process_buffer as an entry point, 'buf' is a function parameter
491
+ // with no AddrStmt, so it has no address information in the abstract state.
492
+ // We lazily initialize it to point to the black hole object (BlkPtr), representing
493
+ // an unknown but valid memory location. This allows the analysis to continue
494
+ // while being conservatively sound.
495
+ if (!as[value_id].isAddr())
496
+ {
497
+ as[value_id] = AddressValue(BlackHoleObjAddr);
498
+ }
483
499
  for (const auto& addr : as[value_id].getAddrs())
484
500
  {
485
501
  NodeID objId = as.getIDFromAddr(addr);
@@ -687,7 +703,7 @@ bool NullptrDerefDetector::canSafelyDerefPtr(AbstractState& as, const SVFVar* va
687
703
  for (const auto &addr: AbsVal.getAddrs())
688
704
  {
689
705
  // if the addr itself is invalid mem, report unsafe
690
- if (AbstractState::isInvalidMem(addr))
706
+ if (AbstractState::isBlackHoleObjAddr(addr))
691
707
  return false;
692
708
  // if nullptr is detected, return unsafe
693
709
  else if (AbstractState::isNullMem(addr))
@@ -272,60 +272,19 @@ void AbsExtAPI::initExtFunMap()
272
272
 
273
273
  auto sse_strlen = [&](const CallICFGNode *callNode)
274
274
  {
275
- // check the arg size
276
275
  if (callNode->arg_size() < 1) return;
277
- const SVFVar* strValue = callNode->getArgument(0);
278
276
  AbstractState& as = getAbsStateFromTrace(callNode);
279
- NodeID value_id = strValue->getId();
280
277
  u32_t lhsId = callNode->getRetICFGNode()->getActualRet()->getId();
281
- u32_t dst_size = 0;
282
- for (const auto& addr : as[value_id].getAddrs())
283
- {
284
- NodeID objId = as.getIDFromAddr(addr);
285
- if (svfir->getBaseObject(objId)->isConstantByteSize())
286
- {
287
- dst_size = svfir->getBaseObject(objId)->getByteSizeOfObj();
288
- }
289
- else
290
- {
291
- const ICFGNode* addrNode = svfir->getBaseObject(objId)->getICFGNode();
292
- for (const SVFStmt* stmt2: addrNode->getSVFStmts())
293
- {
294
- if (const AddrStmt* addrStmt = SVFUtil::dyn_cast<AddrStmt>(stmt2))
295
- {
296
- dst_size = as.getAllocaInstByteSize(addrStmt);
297
- }
298
- }
299
- }
300
- }
301
- u32_t len = 0;
302
- NodeID dstid = strValue->getId();
303
- if (as.inVarToAddrsTable(dstid))
304
- {
305
- for (u32_t index = 0; index < dst_size; index++)
306
- {
307
- AbstractValue expr0 =
308
- as.getGepObjAddrs(dstid, IntervalValue(index));
309
- AbstractValue val;
310
- for (const auto &addr: expr0.getAddrs())
311
- {
312
- val.join_with(as.load(addr));
313
- }
314
- if (val.getInterval().is_numeral() && (char) val.getInterval().getIntNumeral() == '\0')
315
- {
316
- break;
317
- }
318
- ++len;
319
- }
320
- }
321
- if (len == 0)
322
- {
323
- as[lhsId] = IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit());
324
- }
278
+ // strlen/wcslen return the number of characters (not bytes).
279
+ // getStrlen returns byte-scaled length (len * elemSize) for use
280
+ // by memcpy/strcpy. Here we need the raw character count, so
281
+ // divide back by elemSize.
282
+ IntervalValue byteLen = getStrlen(as, callNode->getArgument(0));
283
+ u32_t elemSize = getElementSize(as, callNode->getArgument(0));
284
+ if (byteLen.is_numeral() && elemSize > 1)
285
+ as[lhsId] = IntervalValue(byteLen.getIntNumeral() / (s64_t)elemSize);
325
286
  else
326
- {
327
- as[lhsId] = IntervalValue(len);
328
- }
287
+ as[lhsId] = byteLen;
329
288
  };
330
289
  func_map["strlen"] = sse_strlen;
331
290
  func_map["wcslen"] = sse_strlen;
@@ -350,7 +309,7 @@ void AbsExtAPI::initExtFunMap()
350
309
  const u32_t freePtr = callNode->getArgument(0)->getId();
351
310
  for (auto addr: as[freePtr].getAddrs())
352
311
  {
353
- if (AbstractState::isInvalidMem(addr))
312
+ if (AbstractState::isBlackHoleObjAddr(addr))
354
313
  {
355
314
  // Detected a double free — the address has already been freed.
356
315
  // No action is taken at this point.
@@ -480,7 +439,13 @@ void AbsExtAPI::handleExtAPI(const CallICFGNode *call)
480
439
  }
481
440
  else if (extType == STRCAT)
482
441
  {
483
- handleStrcat(call);
442
+ // Both strcat and strncat are annotated as STRCAT.
443
+ // Distinguish by name: strncat/wcsncat contain "ncat".
444
+ const std::string& name = fun->getName();
445
+ if (name.find("ncat") != std::string::npos)
446
+ handleStrncat(call);
447
+ else
448
+ handleStrcat(call);
484
449
  }
485
450
  else
486
451
  {
@@ -489,21 +454,50 @@ void AbsExtAPI::handleExtAPI(const CallICFGNode *call)
489
454
  return;
490
455
  }
491
456
 
492
- void AbsExtAPI::handleStrcpy(const CallICFGNode *call)
457
+ // ===----------------------------------------------------------------------===//
458
+ // Shared primitives for string/memory handlers
459
+ // ===----------------------------------------------------------------------===//
460
+
461
+ /// Get the byte size of each element for a pointer/array variable.
462
+ /// Shared by handleMemcpy, handleMemset, and getStrlen to avoid duplication.
463
+ u32_t AbsExtAPI::getElementSize(AbstractState& as, const SVFVar* var)
493
464
  {
494
- // strcpy, __strcpy_chk, stpcpy , wcscpy, __wcscpy_chk
495
- // get the dst and src
496
- AbstractState& as = getAbsStateFromTrace(call);
497
- const SVFVar* arg0Val = call->getArgument(0);
498
- const SVFVar* arg1Val = call->getArgument(1);
499
- IntervalValue strLen = getStrlen(as, arg1Val);
500
- // no need to -1, since it has \0 as the last byte
501
- handleMemcpy(as, arg0Val, arg1Val, strLen, strLen.lb().getIntNumeral());
465
+ if (var->getType()->isArrayTy())
466
+ {
467
+ return SVFUtil::dyn_cast<SVFArrayType>(var->getType())
468
+ ->getTypeOfElement()->getByteSize();
469
+ }
470
+ if (var->getType()->isPointerTy())
471
+ {
472
+ if (const SVFType* elemType = as.getPointeeElement(var->getId()))
473
+ {
474
+ if (elemType->isArrayTy())
475
+ return SVFUtil::dyn_cast<SVFArrayType>(elemType)
476
+ ->getTypeOfElement()->getByteSize();
477
+ return elemType->getByteSize();
478
+ }
479
+ return 1;
480
+ }
481
+ assert(false && "unsupported type for element size");
482
+ return 1;
483
+ }
484
+
485
+ /// Check if an interval length is usable for memory operations.
486
+ /// Returns false for bottom (no information) or unbounded lower bound
487
+ /// (cannot determine a concrete start for iteration).
488
+ bool AbsExtAPI::isValidLength(const IntervalValue& len)
489
+ {
490
+ return !len.isBottom() && !len.lb().is_minus_infinity();
502
491
  }
503
492
 
493
+ /// Calculate the length of a null-terminated string in abstract state.
494
+ /// Scans memory from the base of strValue looking for a '\0' byte.
495
+ /// Returns an IntervalValue: exact length if '\0' found, otherwise [0, MaxFieldLimit].
504
496
  IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValue)
505
497
  {
506
498
  NodeID value_id = strValue->getId();
499
+
500
+ // Step 1: determine the buffer size (in bytes) backing this pointer
507
501
  u32_t dst_size = 0;
508
502
  for (const auto& addr : as[value_id].getAddrs())
509
503
  {
@@ -524,8 +518,9 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu
524
518
  }
525
519
  }
526
520
  }
521
+
522
+ // Step 2: scan for '\0' terminator
527
523
  u32_t len = 0;
528
- u32_t elemSize = 1;
529
524
  if (as.inVarToAddrsTable(value_id))
530
525
  {
531
526
  for (u32_t index = 0; index < dst_size; index++)
@@ -537,188 +532,153 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu
537
532
  {
538
533
  val.join_with(as.load(addr));
539
534
  }
540
- if (val.getInterval().is_numeral() && (char) val.getInterval().getIntNumeral() == '\0')
535
+ if (val.getInterval().is_numeral() &&
536
+ (char) val.getInterval().getIntNumeral() == '\0')
541
537
  {
542
538
  break;
543
539
  }
544
540
  ++len;
545
541
  }
546
- if (strValue->getType()->isArrayTy())
547
- {
548
- elemSize = SVFUtil::dyn_cast<SVFArrayType>(strValue->getType())->getTypeOfElement()->getByteSize();
549
- }
550
- else if (strValue->getType()->isPointerTy())
551
- {
552
- if (const SVFType* elemType = as.getPointeeElement(value_id))
553
- {
554
- if (elemType->isArrayTy())
555
- elemSize = SVFUtil::dyn_cast<SVFArrayType>(elemType)->getTypeOfElement()->getByteSize();
556
- else
557
- elemSize = elemType->getByteSize();
558
- }
559
- else
560
- {
561
- elemSize = 1;
562
- }
563
- }
564
- else
565
- {
566
- assert(false && "we cannot support this type");
567
- }
568
542
  }
543
+
544
+ // Step 3: scale by element size and return
545
+ u32_t elemSize = getElementSize(as, strValue);
569
546
  if (len == 0)
570
- {
571
547
  return IntervalValue((s64_t)0, (s64_t)Options::MaxFieldLimit());
572
- }
573
- else
574
- {
575
- return IntervalValue(len * elemSize);
576
- }
548
+ return IntervalValue(len * elemSize);
549
+ }
550
+
551
+ // ===----------------------------------------------------------------------===//
552
+ // String/memory operation handlers
553
+ // ===----------------------------------------------------------------------===//
554
+
555
+ /// strcpy(dst, src): copy all of src (including '\0') into dst.
556
+ /// Covers: strcpy, __strcpy_chk, stpcpy, wcscpy, __wcscpy_chk
557
+ void AbsExtAPI::handleStrcpy(const CallICFGNode *call)
558
+ {
559
+ AbstractState& as = getAbsStateFromTrace(call);
560
+ const SVFVar* dst = call->getArgument(0);
561
+ const SVFVar* src = call->getArgument(1);
562
+ IntervalValue srcLen = getStrlen(as, src);
563
+ // no need to -1, since srcLen includes up to (but not past) '\0'
564
+ if (!isValidLength(srcLen)) return;
565
+ handleMemcpy(as, dst, src, srcLen, 0);
577
566
  }
578
567
 
568
+ /// strcat(dst, src): append all of src after the end of dst.
569
+ /// Covers: strcat, __strcat_chk, wcscat, __wcscat_chk
570
+ void AbsExtAPI::handleStrcat(const CallICFGNode *call)
571
+ {
572
+ AbstractState& as = getAbsStateFromTrace(call);
573
+ const SVFVar* dst = call->getArgument(0);
574
+ const SVFVar* src = call->getArgument(1);
575
+ IntervalValue dstLen = getStrlen(as, dst);
576
+ IntervalValue srcLen = getStrlen(as, src);
577
+ if (!isValidLength(dstLen)) return;
578
+ handleMemcpy(as, dst, src, srcLen, dstLen.lb().getIntNumeral());
579
+ }
579
580
 
580
- void AbsExtAPI::handleStrcat(const SVF::CallICFGNode *call)
581
+ /// strncat(dst, src, n): append at most n bytes of src after the end of dst.
582
+ /// Covers: strncat, __strncat_chk, wcsncat, __wcsncat_chk
583
+ void AbsExtAPI::handleStrncat(const CallICFGNode *call)
581
584
  {
582
- // __strcat_chk, strcat, __wcscat_chk, wcscat, __strncat_chk, strncat, __wcsncat_chk, wcsncat
583
- // to check it is strcat group or strncat group
584
585
  AbstractState& as = getAbsStateFromTrace(call);
585
- const FunObjVar *fun = call->getCalledFunction();
586
- const std::vector<std::string> strcatGroup = {"__strcat_chk", "strcat", "__wcscat_chk", "wcscat"};
587
- const std::vector<std::string> strncatGroup = {"__strncat_chk", "strncat", "__wcsncat_chk", "wcsncat"};
588
- if (std::find(strcatGroup.begin(), strcatGroup.end(), fun->getName()) != strcatGroup.end())
589
- {
590
- const SVFVar* arg0Val = call->getArgument(0);
591
- const SVFVar* arg1Val = call->getArgument(1);
592
- IntervalValue strLen0 = getStrlen(as, arg0Val);
593
- IntervalValue strLen1 = getStrlen(as, arg1Val);
594
- IntervalValue totalLen = strLen0 + strLen1;
595
- handleMemcpy(as, arg0Val, arg1Val, strLen1, strLen0.lb().getIntNumeral());
596
- // do memcpy
597
- }
598
- else if (std::find(strncatGroup.begin(), strncatGroup.end(), fun->getName()) != strncatGroup.end())
599
- {
600
- const SVFVar* arg0Val = call->getArgument(0);
601
- const SVFVar* arg1Val = call->getArgument(1);
602
- const SVFVar* arg2Val = call->getArgument(2);
603
- IntervalValue arg2Num = as[arg2Val->getId()].getInterval();
604
- IntervalValue strLen0 = getStrlen(as, arg0Val);
605
- IntervalValue totalLen = strLen0 + arg2Num;
606
- handleMemcpy(as, arg0Val, arg1Val, arg2Num, strLen0.lb().getIntNumeral());
607
- // do memcpy
608
- }
609
- else
610
- {
611
- assert(false && "unknown strcat function, please add it to strcatGroup or strncatGroup");
612
- }
586
+ const SVFVar* dst = call->getArgument(0);
587
+ const SVFVar* src = call->getArgument(1);
588
+ IntervalValue n = as[call->getArgument(2)->getId()].getInterval();
589
+ IntervalValue dstLen = getStrlen(as, dst);
590
+ if (!isValidLength(dstLen)) return;
591
+ handleMemcpy(as, dst, src, n, dstLen.lb().getIntNumeral());
613
592
  }
614
593
 
615
- void AbsExtAPI::handleMemcpy(AbstractState& as, const SVF::SVFVar *dst, const SVF::SVFVar *src, IntervalValue len, u32_t start_idx)
594
+ /// Core memcpy: copy `len` bytes from src to dst starting at dst[start_idx].
595
+ void AbsExtAPI::handleMemcpy(AbstractState& as, const SVF::SVFVar *dst,
596
+ const SVF::SVFVar *src, IntervalValue len,
597
+ u32_t start_idx)
616
598
  {
617
- u32_t dstId = dst->getId(); // pts(dstId) = {objid} objbar objtypeinfo->getType().
599
+ if (!isValidLength(len)) return;
600
+
601
+ u32_t dstId = dst->getId();
618
602
  u32_t srcId = src->getId();
619
- u32_t elemSize = 1;
620
- if (dst->getType()->isArrayTy())
621
- {
622
- elemSize = SVFUtil::dyn_cast<SVFArrayType>(dst->getType())->getTypeOfElement()->getByteSize();
623
- }
624
- // memcpy(i32*, i32*, 40)
625
- else if (dst->getType()->isPointerTy())
626
- {
627
- if (const SVFType* elemType = as.getPointeeElement(dstId))
628
- {
629
- if (elemType->isArrayTy())
630
- elemSize = SVFUtil::dyn_cast<SVFArrayType>(elemType)->getTypeOfElement()->getByteSize();
631
- else
632
- elemSize = elemType->getByteSize();
633
- }
634
- else
635
- {
636
- elemSize = 1;
637
- }
638
- }
639
- else
640
- {
641
- assert(false && "we cannot support this type");
642
- }
643
- u32_t size = std::min((u32_t)Options::MaxFieldLimit(), (u32_t) len.lb().getIntNumeral());
603
+ u32_t elemSize = getElementSize(as, dst);
604
+ u32_t size = std::min((u32_t)Options::MaxFieldLimit(),
605
+ (u32_t)len.lb().getIntNumeral());
644
606
  u32_t range_val = size / elemSize;
645
- if (as.inVarToAddrsTable(srcId) && as.inVarToAddrsTable(dstId))
607
+
608
+ if (!as.inVarToAddrsTable(srcId) || !as.inVarToAddrsTable(dstId))
609
+ return;
610
+
611
+ for (u32_t index = 0; index < range_val; index++)
646
612
  {
647
- for (u32_t index = 0; index < range_val; index++)
613
+ AbstractValue expr_src =
614
+ as.getGepObjAddrs(srcId, IntervalValue(index));
615
+ AbstractValue expr_dst =
616
+ as.getGepObjAddrs(dstId, IntervalValue(index + start_idx));
617
+ for (const auto &dstAddr: expr_dst.getAddrs())
648
618
  {
649
- // dead loop for string and break if there's a \0. If no \0, it will throw err.
650
- AbstractValue expr_src =
651
- as.getGepObjAddrs(srcId, IntervalValue(index));
652
- AbstractValue expr_dst =
653
- as.getGepObjAddrs(dstId, IntervalValue(index + start_idx));
654
- for (const auto &dst: expr_dst.getAddrs())
619
+ for (const auto &srcAddr: expr_src.getAddrs())
655
620
  {
656
- for (const auto &src: expr_src.getAddrs())
621
+ u32_t objId = as.getIDFromAddr(srcAddr);
622
+ if (as.inAddrToValTable(objId) || as.inAddrToAddrsTable(objId))
657
623
  {
658
- u32_t objId = as.getIDFromAddr(src);
659
- if (as.inAddrToValTable(objId))
660
- {
661
- as.store(dst, as.load(src));
662
- }
663
- else if (as.inAddrToAddrsTable(objId))
664
- {
665
- as.store(dst, as.load(src));
666
- }
624
+ as.store(dstAddr, as.load(srcAddr));
667
625
  }
668
626
  }
669
627
  }
670
628
  }
671
629
  }
672
630
 
673
- void AbsExtAPI::handleMemset(AbstractState& as, const SVF::SVFVar *dst, IntervalValue elem, IntervalValue len)
631
+ /// Core memset: fill dst with `elem` for `len` bytes.
632
+ /// Note: elemSize here uses the pointee type's full size (not array element size)
633
+ /// to match how LLVM memset/wmemset intrinsics measure `len`. For a pointer to
634
+ /// wchar_t[100], elemSize = sizeof(wchar_t[100]), so range_val reflects the
635
+ /// number of top-level GEP fields, not individual array elements.
636
+ void AbsExtAPI::handleMemset(AbstractState& as, const SVF::SVFVar *dst,
637
+ IntervalValue elem, IntervalValue len)
674
638
  {
639
+ if (!isValidLength(len)) return;
640
+
675
641
  u32_t dstId = dst->getId();
676
- u32_t size = std::min((u32_t)Options::MaxFieldLimit(), (u32_t) len.lb().getIntNumeral());
677
642
  u32_t elemSize = 1;
678
643
  if (dst->getType()->isArrayTy())
679
644
  {
680
- elemSize = SVFUtil::dyn_cast<SVFArrayType>(dst->getType())->getTypeOfElement()->getByteSize();
645
+ elemSize = SVFUtil::dyn_cast<SVFArrayType>(dst->getType())
646
+ ->getTypeOfElement()->getByteSize();
681
647
  }
682
648
  else if (dst->getType()->isPointerTy())
683
649
  {
684
650
  if (const SVFType* elemType = as.getPointeeElement(dstId))
685
- {
686
651
  elemSize = elemType->getByteSize();
687
- }
688
652
  else
689
- {
690
653
  elemSize = 1;
691
- }
692
654
  }
693
655
  else
694
656
  {
695
- assert(false && "we cannot support this type");
657
+ assert(false && "unsupported type for element size");
696
658
  }
697
-
659
+ u32_t size = std::min((u32_t)Options::MaxFieldLimit(),
660
+ (u32_t)len.lb().getIntNumeral());
698
661
  u32_t range_val = size / elemSize;
662
+
699
663
  for (u32_t index = 0; index < range_val; index++)
700
664
  {
701
- // dead loop for string and break if there's a \0. If no \0, it will throw err.
702
- if (as.inVarToAddrsTable(dstId))
665
+ if (!as.inVarToAddrsTable(dstId))
666
+ break;
667
+ AbstractValue lhs_gep = as.getGepObjAddrs(dstId, IntervalValue(index));
668
+ for (const auto &addr: lhs_gep.getAddrs())
703
669
  {
704
- AbstractValue lhs_gep = as.getGepObjAddrs(dstId, IntervalValue(index));
705
- for (const auto &addr: lhs_gep.getAddrs())
670
+ u32_t objId = as.getIDFromAddr(addr);
671
+ if (as.inAddrToValTable(objId))
706
672
  {
707
- u32_t objId = as.getIDFromAddr(addr);
708
- if (as.inAddrToValTable(objId))
709
- {
710
- AbstractValue tmp = as.load(addr);
711
- tmp.join_with(elem);
712
- as.store(addr, tmp);
713
- }
714
- else
715
- {
716
- as.store(addr, elem);
717
- }
673
+ AbstractValue tmp = as.load(addr);
674
+ tmp.join_with(elem);
675
+ as.store(addr, tmp);
676
+ }
677
+ else
678
+ {
679
+ as.store(addr, elem);
718
680
  }
719
681
  }
720
- else
721
- break;
722
682
  }
723
683
  }
724
684