Yoorkin updated this revision to Diff 454722.
Yoorkin added a comment.

Now it can find usage of both `sprintf` and `vsprintf`, and then change a small 
piece of code. Here is an option `PreferSafe`, when value is true, the check 
prefers `snprintf_s` `vsnprintf_s` over `snprintf` `vsnprintf`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D132294/new/

https://reviews.llvm.org/D132294

Files:
  clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
  clang-tools-extra/clang-tidy/bugprone/PrintfWithFixedSizeBufferCheck.cpp
  clang-tools-extra/clang-tidy/bugprone/PrintfWithFixedSizeBufferCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/bugprone/printf-with-fixed-size-buffer.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/checkers/bugprone/printf-with-fixed-size-buffer.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/printf-with-fixed-size-buffer.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone/printf-with-fixed-size-buffer.cpp
@@ -0,0 +1,33 @@
+// RUN: %check_clang_tidy %s bugprone-printf-with-fixed-size-buffer %t
+
+#include <stdio.h>
+
+void f(){
+  char buff[80];
+  sprintf(buff, "Hello, %s!\n", "world");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: string to be written may exceeds the size of buffer; use snprintf instead [bugprone-printf-with-fixed-size-buffer]
+  // CHECK-FIXES: snprintf(buff, sizeof(buff), "Hello, %s!\n", "world");
+}
+
+char sbuff[80];
+
+void f2(){
+  sprintf(sbuff, "Hello, %s!\n", "world");
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: string to be written may exceeds the size of buffer; use snprintf instead [bugprone-printf-with-fixed-size-buffer]
+  // CHECK-FIXES: snprintf(sbuff, sizeof(sbuff), "Hello, %s!\n", "world");
+}
+
+void f3(){
+    char *dynamic_sized_buff = nullptr;
+    sprintf(dynamic_sized_buff, "Hello, %s!\n", "world");
+}
+
+void f4(const char * format, ... ){
+  char buff[100];
+  va_list args;
+  va_start(args, format);
+  vsprintf(buff, format, args);
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: string to be written may exceeds the size of buffer; use vsnprintf instead [bugprone-printf-with-fixed-size-buffer]
+  // CHECK-FIXES: vsnprintf(buff, sizeof(buff), format, args);
+  va_end(args);
+}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -107,6 +107,7 @@
    `bugprone-not-null-terminated-result <bugprone/not-null-terminated-result.html>`_, "Yes"
    `bugprone-parent-virtual-call <bugprone/parent-virtual-call.html>`_, "Yes"
    `bugprone-posix-return <bugprone/posix-return.html>`_, "Yes"
+   `bugprone-printf-with-fixed-size-buffer <bugprone/printf-with-fixed-size-buffer.html>`_, "Yes"
    `bugprone-redundant-branch-condition <bugprone/redundant-branch-condition.html>`_, "Yes"
    `bugprone-reserved-identifier <bugprone/reserved-identifier.html>`_, "Yes"
    `bugprone-shared-ptr-array-mismatch <bugprone/shared-ptr-array-mismatch.html>`_, "Yes"
Index: clang-tools-extra/docs/clang-tidy/checks/bugprone/printf-with-fixed-size-buffer.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/bugprone/printf-with-fixed-size-buffer.rst
@@ -0,0 +1,36 @@
+.. title:: clang-tidy - bugprone-printf-with-fixed-size-buffer
+
+
+bugprone-printf-with-fixed-size-buffer
+=======================================
+
+Finds usage of ``sprintf`` or ``vsprintf``, which write output string into a fixed-size array. 
+It will suggest the counted versions instead.
+
+It's a common idiom to have a fixed-size buffer of characters allocated on 
+the stack and then to printf into the buffer. It may be leading to errors if the 
+string to be written exceeds the size of the array pointed to by buffer.
+
+Example:
+.. code-block:: c++
+
+    void f(){
+      char buff[80];
+      sprintf(buff, "Hello, %s!\n", "world");
+    }
+
+Becomes:
+.. code-block:: c++
+
+    void f(){
+      char buff[80];
+      snprintf(buff, sizeof(buff), "Hello, %s!\n", "world");
+    }
+
+Options
+-------
+
+.. option:: PreferSafe
+
+  When `true`, prefer to use ``snprintf_s`` ``vsnprintf_s`` over ``snprintf`` ``vsnprintf``.
+  Default is `false`.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -99,6 +99,11 @@
 New checks
 ^^^^^^^^^^
 
+- New :doc:`bugprone-printf-with-fixed-size-buffer
+  <clang-tidy/checks/bugprone/printf-with-fixed-size-buffer>` check.
+
+  Finds usage of ``sprintf`` or ``vsprintf``, which write output string into a fixed-size array.
+
 - New :doc:`cppcoreguidelines-avoid-const-or-ref-data-members
   <clang-tidy/checks/cppcoreguidelines/avoid-const-or-ref-data-members>` check.
 
Index: clang-tools-extra/clang-tidy/bugprone/PrintfWithFixedSizeBufferCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/PrintfWithFixedSizeBufferCheck.h
@@ -0,0 +1,47 @@
+//===--- PrintfWithFixedSizeBufferCheck.h - clang-tidy ----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PRINTFWITHFIXEDSIZEBUFFERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PRINTFWITHFIXEDSIZEBUFFERCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Check if `sprintf` is called with a fixed size buffer. Replaced with counted
+/// version `snprintf`.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/sprintf-with-fixed-size-buffer.html
+class PrintfWithFixedSizeBufferCheck : public ClangTidyCheck {
+public:
+  PrintfWithFixedSizeBufferCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context),
+        PreferSafe(Options.get<bool>("PreferSafe", false)) {}
+
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus;
+  }
+  
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+  /// Registering for sprintf and vsprintf calls.
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  bool PreferSafe;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PRINTFWITHFIXEDSIZEBUFFERCHECK_H
Index: clang-tools-extra/clang-tidy/bugprone/PrintfWithFixedSizeBufferCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/PrintfWithFixedSizeBufferCheck.cpp
@@ -0,0 +1,92 @@
+//===--- PrintfWithFixedSizeBufferCheck.cpp - clang-tidy ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "PrintfWithFixedSizeBufferCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringRef.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+void PrintfWithFixedSizeBufferCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store<bool>(Opts, "PreferSafe", PreferSafe);
+}
+
+void PrintfWithFixedSizeBufferCheck::registerMatchers(MatchFinder *Finder) {
+  auto HasConstantArrayType = hasType(constantArrayType());
+  auto HasFixedSizeBufferMatcher =
+      hasArgument(0, anyOf(memberExpr(HasConstantArrayType).bind("MemberExpr"),
+                           declRefExpr(HasConstantArrayType).bind("DeclRef")));
+
+  Finder->addMatcher(callExpr(allOf(HasFixedSizeBufferMatcher,
+                                    callee(functionDecl(hasName("::sprintf")))))
+                         .bind("sprintf"),
+                     this);
+
+  Finder->addMatcher(
+      callExpr(allOf(HasFixedSizeBufferMatcher,
+                     callee(functionDecl(hasName("::vsprintf")))))
+          .bind("vsprintf"),
+      this);
+}
+
+void PrintfWithFixedSizeBufferCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const Expr *CA;
+  const auto *ME = Result.Nodes.getNodeAs<MemberExpr>("MemberExpr");
+  const auto *DR = Result.Nodes.getNodeAs<DeclRefExpr>("DeclRef");
+
+  if (ME != nullptr)
+    CA = ME;
+  else
+    CA = DR;
+
+  const CallExpr *Call = nullptr;
+  const Expr *SecondArgument = nullptr;
+
+  std::string Recommendation;
+  if (Call = Result.Nodes.getNodeAs<CallExpr>("sprintf"))
+    Recommendation = "snprintf";
+  else if (Call = Result.Nodes.getNodeAs<CallExpr>("vsprintf"))
+    Recommendation = "vsnprintf";
+
+  assert(Call && "Unhandled binding in the Matcher");
+
+  SecondArgument = Call->getArg(1);
+
+  if (PreferSafe) {
+    Recommendation += "_s";
+  }
+
+  // Insert the size of buffer after first argument.
+  std::string SizeofText = "sizeof(";
+  SizeofText +=
+      Lexer::getSourceText(CharSourceRange::getTokenRange(CA->getSourceRange()),
+                           *Result.SourceManager, getLangOpts());
+  SizeofText += "), ";
+
+  FixItHint ReplacementHint = FixItHint::CreateReplacement(
+      Call->getCallee()->getSourceRange(), Recommendation);
+  FixItHint SizeofHint =
+      FixItHint::CreateInsertion(SecondArgument->getBeginLoc(), SizeofText);
+  auto D =
+      diag(Call->getBeginLoc(), "string to be written may exceeds the size of "
+                                "buffer; use %0 instead")
+      << Recommendation << Call->getCallee()->getSourceRange()
+      << SecondArgument->getSourceRange() << ReplacementHint << SizeofHint;
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -36,6 +36,7 @@
   NotNullTerminatedResultCheck.cpp
   ParentVirtualCallCheck.cpp
   PosixReturnCheck.cpp
+  PrintfWithFixedSizeBufferCheck.cpp
   RedundantBranchConditionCheck.cpp
   ReservedIdentifierCheck.cpp
   SharedPtrArrayMismatchCheck.cpp
Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -41,6 +41,7 @@
 #include "NotNullTerminatedResultCheck.h"
 #include "ParentVirtualCallCheck.h"
 #include "PosixReturnCheck.h"
+#include "PrintfWithFixedSizeBufferCheck.h"
 #include "RedundantBranchConditionCheck.h"
 #include "ReservedIdentifierCheck.h"
 #include "SharedPtrArrayMismatchCheck.h"
@@ -132,6 +133,8 @@
         "bugprone-move-forwarding-reference");
     CheckFactories.registerCheck<MultipleStatementMacroCheck>(
         "bugprone-multiple-statement-macro");
+    CheckFactories.registerCheck<PrintfWithFixedSizeBufferCheck>(
+        "bugprone-printf-with-fixed-size-buffer");
     CheckFactories.registerCheck<RedundantBranchConditionCheck>(
         "bugprone-redundant-branch-condition");
     CheckFactories.registerCheck<cppcoreguidelines::NarrowingConversionsCheck>(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to