Author: Endre Fülöp
Date: 2026-02-04T15:14:28Z
New Revision: 5cc22a9772c6b7da201530a74f6cd8b821003f90

URL: 
https://github.com/llvm/llvm-project/commit/5cc22a9772c6b7da201530a74f6cd8b821003f90
DIFF: 
https://github.com/llvm/llvm-project/commit/5cc22a9772c6b7da201530a74f6cd8b821003f90.diff

LOG: [clang][analyzer] Add OpaqueSTLFunctionsModeling (#178910)

This modeling checker forces conservative evaluation for `std::sort`,
`std::stable_sort`, and `std::inplace_merge` to prevent false positives
caused by complex STL internals that cannot be adequately modeled by the
engine.

Using `evalCall` to avoid emitting false positives is more efficient than
suppressing them after analysis in `BugReporterVisitor`s.

Related to #177804

Added: 
    clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
    clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp

Modified: 
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
    clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td 
b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 3af694ceda1e3..58e785d5ca36f 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -347,6 +347,10 @@ def ErrnoModeling : Checker<"Errno">,
   HelpText<"Make the special value 'errno' available to other checkers.">,
   Documentation<NotDocumented>;
 
+def OpaqueSTLFunctionsModeling : Checker<"OpaqueSTLFunctionsModeling">,
+  HelpText<"Model opaque, conservative evaluation of some STL functions">,
+  Documentation<NotDocumented>;
+
 def TrustNonnullChecker : Checker<"TrustNonnull">,
   HelpText<"Trust that returns from framework methods annotated with _Nonnull "
            "are not null">,

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt 
b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 2df36d8e672ae..3edbd7ebdc1bf 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -86,6 +86,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   ObjCSelfInitChecker.cpp
   ObjCSuperDeallocChecker.cpp
   ObjCUnusedIVarsChecker.cpp
+  OpaqueSTLFunctionsModeling.cpp
   OSObjectCStyleCast.cpp
   PaddingChecker.cpp
   PointerArithChecker.cpp

diff  --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp 
b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
new file mode 100644
index 0000000000000..cf94773d5d199
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
@@ -0,0 +1,54 @@
+//===--- OpaqueSTLFunctionsModeling.cpp 
-----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Models STL functions whose best accurate model is to invalidate their
+// arguments. Only functions where this simple approach is sufficient and won't
+// interfere with the modeling of other checkers should be put here.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class OpaqueSTLFunctionsModeling : public Checker<eval::Call> {
+public:
+  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+  const CallDescriptionSet ModeledFunctions{
+      {CDM::SimpleFunc, {"std", "sort"}},
+      {CDM::SimpleFunc, {"std", "stable_sort"}},
+      {CDM::SimpleFunc, {"std", "inplace_merge"}}};
+};
+} // namespace
+
+bool OpaqueSTLFunctionsModeling::evalCall(const CallEvent &Call,
+                                          CheckerContext &C) const {
+  if (!ModeledFunctions.contains(Call))
+    return false;
+
+  ProgramStateRef InvalidatedRegionsState =
+      Call.invalidateRegions(C.blockCount(), C.getState());
+  C.addTransition(InvalidatedRegionsState);
+  return true;
+}
+
+void ento::registerOpaqueSTLFunctionsModeling(CheckerManager &Mgr) {
+  Mgr.registerChecker<OpaqueSTLFunctionsModeling>();
+}
+
+bool ento::shouldRegisterOpaqueSTLFunctionsModeling(const CheckerManager &Mgr) 
{
+  return Mgr.getLangOpts().CPlusPlus;
+}

diff  --git 
a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h 
b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
index dc53af269c9c2..3b3b316469094 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
@@ -10,6 +10,12 @@ typedef unsigned char uint8_t;
 typedef __typeof__(sizeof(int)) size_t;
 void *memmove(void *s1, const void *s2, size_t n);
 
+#define TRIGGER_DIV_BY_ZERO \
+do {         \
+  int z = 0; \
+  z = 5/z;   \
+} while (0)
+
 namespace std {
 
   template <class _Tp>
@@ -41,8 +47,7 @@ namespace std {
       // Fake use-after-free.
       // No warning is expected as we are suppressing warning coming
       // out of std::list.
-      int z = 0;
-      z = 5/z;
+      TRIGGER_DIV_BY_ZERO;
     }
     bool empty() const;
   };
@@ -69,8 +74,7 @@ namespace std {
       // Fake error trigger.
       // No warning is expected as we are suppressing warning coming
       // out of std::basic_string.
-      int z = 0;
-      z = 5/z;
+      TRIGGER_DIV_BY_ZERO;
     }
 
     _CharT *getBuffer() {
@@ -108,8 +112,7 @@ __independent_bits_engine<_Engine, _UIntType>
   // Fake error trigger.
   // No warning is expected as we are suppressing warning coming
   // out of std::__independent_bits_engine.
-  int z = 0;
-  z = 5/z;
+  TRIGGER_DIV_BY_ZERO;
 }
 
 #if __has_feature(cxx_decltype)
@@ -130,8 +133,7 @@ class shared_ptr
     // Fake error trigger.
     // No warning is expected as we are suppressing warning coming
     // out of std::shared_ptr.
-    int z = 0;
-    z = 5/z;
+    TRIGGER_DIV_BY_ZERO;
   }
 };
 
@@ -142,5 +144,28 @@ shared_ptr<_Tp>::shared_ptr(nullptr_t) {
 }
 
 #endif // __has_feature(cxx_decltype)
+
+template<typename _RandomAccessIterator>
+void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
+  // Fake error trigger
+  // std::sort is expected to be evaluated conservatively.
+  TRIGGER_DIV_BY_ZERO;
+}
+
+template<typename _RandomAccessIterator>
+void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
+  // Fake error trigger
+  // std::stable_sort is expected to be evaluated conservatively.
+  TRIGGER_DIV_BY_ZERO;
+}
+
+template<typename _BidirectionalIterator>
+void inplace_merge(_BidirectionalIterator __first,
+                   _BidirectionalIterator __middle,
+                   _BidirectionalIterator __last) {
+  // Fake error trigger
+  // std::inplace_merge is expected to be evaluated conservatively.
+  TRIGGER_DIV_BY_ZERO;
 }
+} // namespace std
 

diff  --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp 
b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp
new file mode 100644
index 0000000000000..283cf27c4f993
--- /dev/null
+++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_analyze_cc1 -verify %s        \
+// RUN:   -analyzer-checker=core,apiModeling \
+// RUN:   -analyzer-dump-egraph=%t.dot       \
+// RUN:   -analyze-function="test_opaque_handling()"
+// RUN: grep 'apiModeling.OpaqueSTLFunctionsModeling' %t.dot | count 3
+
+// expected-no-diagnostics
+
+#include "../Inputs/system-header-simulator-cxx-std-suppression.h"
+
+void test_opaque_handling() {
+  int arr[5] = {};
+  std::sort(arr, arr + 5); // no-warning
+  std::stable_sort(arr, arr + 5); // no-warning
+  std::inplace_merge(arr, arr + 2, arr + 5); // no-warning
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to