From 2b1b4cab9605a33a6c90079b44bdddf048104e08 Mon Sep 17 00:00:00 2001 From: Artem Dergachev Date: Fri, 6 Sep 2019 20:55:24 +0000 Subject: [PATCH] [analyzer] pr43179: Make CallDescription defensive against C variadic functions. Most functions that our checkers react upon are not C-style variadic functions, and therefore they have as many actual arguments as they have formal parameters. However, it's not impossible to define a variadic function with the same name. This will crash any checker that relies on CallDescription to check the number of arguments but silently assumes that the number of parameters is the same. Change CallDescription to check both the number of arguments and the number of parameters by default. If we're intentionally trying to match variadic functions, allow specifying arguments and parameters separately (possibly omitting any of them). For now we only have one CallDescription which would make use of those, namely __builtin_va_start itself. Differential Revision: https://reviews.llvm.org/D67019 llvm-svn: 371256 --- .../StaticAnalyzer/Core/PathSensitive/CallEvent.h | 20 +++++++++++++++++--- clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp | 4 +++- clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 6 ++++-- clang/test/Analysis/cast-value-weird.cpp | 9 +++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 clang/test/Analysis/cast-value-weird.cpp diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index b2826a8..8d1a672 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -1064,8 +1064,19 @@ class CallDescription { // e.g. "{a, b}" represent the qualified names, like "a::b". std::vector QualifiedName; Optional RequiredArgs; + Optional RequiredParams; int Flags; + // A constructor helper. + static Optional readRequiredParams(Optional RequiredArgs, + Optional RequiredParams) { + if (RequiredParams) + return RequiredParams; + if (RequiredArgs) + return static_cast(*RequiredArgs); + return None; + } + public: /// Constructs a CallDescription object. /// @@ -1078,14 +1089,17 @@ public: /// call. Omit this parameter to match every occurrence of call with a given /// name regardless the number of arguments. CallDescription(int Flags, ArrayRef QualifiedName, - Optional RequiredArgs = None) + Optional RequiredArgs = None, + Optional RequiredParams = None) : QualifiedName(QualifiedName), RequiredArgs(RequiredArgs), + RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)), Flags(Flags) {} /// Construct a CallDescription with default flags. CallDescription(ArrayRef QualifiedName, - Optional RequiredArgs = None) - : CallDescription(0, QualifiedName, RequiredArgs) {} + Optional RequiredArgs = None, + Optional RequiredParams = None) + : CallDescription(0, QualifiedName, RequiredArgs, RequiredParams) {} /// Get the name of the function that this object matches. StringRef getFunctionName() const { return QualifiedName.back(); } diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 7e3ce68..9f3b16a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -116,7 +116,9 @@ const SmallVector // vswprintf is the wide version of vsnprintf, // vsprintf has no wide version {{"vswscanf", 3}, 2}}; -const CallDescription ValistChecker::VaStart("__builtin_va_start", 2), + +const CallDescription + ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1), ValistChecker::VaCopy("__builtin_va_copy", 2), ValistChecker::VaEnd("__builtin_va_end", 1); } // end anonymous namespace diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 7226ccb..b5d8599 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -368,7 +368,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const { if (CD.Flags & CDF_MaybeBuiltin) { return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && - (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()); + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); } if (!CD.IsLookupDone) { @@ -407,7 +408,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const { return false; } - return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()); + return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams == parameters().size()); } SVal CallEvent::getArgSVal(unsigned Index) const { diff --git a/clang/test/Analysis/cast-value-weird.cpp b/clang/test/Analysis/cast-value-weird.cpp new file mode 100644 index 0000000..f15cc19 --- /dev/null +++ b/clang/test/Analysis/cast-value-weird.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,apiModeling -verify %s + +// expected-no-diagnostics + +namespace llvm { +template +void cast(...); +void a() { cast(int()); } // no-crash +} // namespace llvm -- 2.7.4