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
