Jaysonyan updated this revision to Diff 381103.
Jaysonyan retitled this revision from "Add %n format specifier warning" to "Add 
%n format specifier warning to clang-tidy".
Jaysonyan added a comment.
Herald added a subscriber: mgorny.
Herald added a project: clang-tools-extra.

Move check for `%n` from clang to clang-tidy as a checker under 
`bugprone-percent-n-format-specifier`.


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

https://reviews.llvm.org/D110436

Files:
  clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
  clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
  clang-tools-extra/clang-tidy/bugprone/PercentNFormatSpecifierCheck.cpp
  clang-tools-extra/clang-tidy/bugprone/PercentNFormatSpecifierCheck.h
  
clang-tools-extra/docs/clang-tidy/checks/bugprone-percent-n-format-specifier.rst
  
clang-tools-extra/test/clang-tidy/checkers/bugprone-percent-n-format-specifier.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-percent-n-format-specifier.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-percent-n-format-specifier.cpp
@@ -0,0 +1,57 @@
+// RUN: %check_clang_tidy %s bugprone-percent-n-format-specifier %t
+
+#include <stdio.h>
+
+void testVariableArgumentList(int *I, ...) {
+  char Buffer[100];
+  FILE *pFile;
+  pFile = fopen("myFile.txt", "w+");
+  va_list args;
+  va_start(args, I);
+
+  // CHECK-MESSAGES: [[@LINE+1]]:21: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vfprintf(pFile, "%n", args);
+  // CHECK-MESSAGES: [[@LINE+1]]:13: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vprintf("%n", args);
+  // CHECK-MESSAGES: [[@LINE+1]]:28: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vsnprintf(Buffer, 100, "%n", args);
+  // CHECK-MESSAGES: [[@LINE+1]]:22: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vsprintf(Buffer, "%n", args);
+
+  // CHECK-MESSAGES: [[@LINE+1]]:20: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vfscanf(pFile, "%n", args);
+  // CHECK-MESSAGES: [[@LINE+1]]:12: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vscanf("%n", args);
+  // CHECK-MESSAGES: [[@LINE+1]]:21: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  vsscanf(Buffer, "%n", args);
+
+  va_end(args);
+}
+
+void testPrintf() {
+  int *I = nullptr;
+  char Buffer[100];
+  FILE *pFile;
+  pFile = fopen("myFile.txt", "w+");
+  // CHECK-MESSAGES: [[@LINE+1]]:12: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  printf("%n", I);
+  // CHECK-MESSAGES: [[@LINE+1]]:22: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  fprintf(pFile, "I %n", I);
+  // CHECK-MESSAGES: [[@LINE+1]]:27: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  snprintf(Buffer, 100, "%n", I);
+  // CHECK-MESSAGES: [[@LINE+1]]:21: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  sprintf(Buffer, "%n", I);
+}
+
+void testScanf() {
+  int *I = nullptr;
+  char Buffer[100];
+  FILE *pFile;
+  pFile = fopen("myFile.txt", "w+");
+  // CHECK-MESSAGES: [[@LINE+1]]:19: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  fscanf(pFile, "%n", I);
+  // CHECK-MESSAGES: [[@LINE+1]]:11: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  scanf("%n", I);
+  // CHECK-MESSAGES: [[@LINE+1]]:20: warning: usage of %n can lead to unsafe writing to memory [bugprone-percent-n-format-specifier]
+  sscanf(Buffer, "%n", I);
+}
Index: clang-tools-extra/docs/clang-tidy/checks/bugprone-percent-n-format-specifier.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/bugprone-percent-n-format-specifier.rst
@@ -0,0 +1,37 @@
+.. title:: clang-tidy - bugprone-percent-n-format-specifier
+
+bugprone-percent-n-format-specifier
+===================================
+
+Finds any usage of the %n format specifier inside the format string
+of a call to printf, scanf, or any of their derivatives. The %n format
+specifier can lead to unsafe writing to memory.
+
+.. code-block:: c++
+   void f(int * i, ...) {
+     char buffer [100];
+     FILE * pFile;
+     pFile = fopen("myFile.txt", "w+");
+     va_list args;
+     va_start(args, i);
+
+     // Will match any of these printf derivatives
+     printf("%n", i);
+     fprintf(pFile, "%n", i);
+     snprintf(buffer, 100, "%n", i);
+     sprintf(buffer, "%n", i);
+     vprintf("%n", args);
+     vfprintf(pFile, "%n", args);
+     vsnprintf(buffer, 100, "%n", args);
+     vsprintf(buffer, "%n", args);
+
+     // Will match any of these scanf derivatives
+     scanf("%n", i);
+     fscanf(pFile, "%n", i);
+     sscanf(buffer, "%n", i);
+     vscanf("%n", args);
+     vfscanf(pFile, "%n", args);
+     vsscanf(buffer, "%n", args);
+
+     va_end(args);
+   }
Index: clang-tools-extra/clang-tidy/bugprone/PercentNFormatSpecifierCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/PercentNFormatSpecifierCheck.h
@@ -0,0 +1,36 @@
+//===--- PercentNFormatSpecifierCheck.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_PERCENTNFORMATSPECIFIERCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PERCENTNFORMATSPECIFIERCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Finds any usage of the %n inside the format string of a call to printf,
+/// scanf or any of their derivatives.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-percent-n-format-specifier.html
+class PercentNFormatSpecifierCheck : public ClangTidyCheck {
+public:
+  PercentNFormatSpecifierCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context){};
+
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_PERCENTNFORMATSPECIFIERCHECK_H
Index: clang-tools-extra/clang-tidy/bugprone/PercentNFormatSpecifierCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/PercentNFormatSpecifierCheck.cpp
@@ -0,0 +1,95 @@
+//===--- PercentNFormatSpecifierCheck.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 "PercentNFormatSpecifierCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/FormatString.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+class Handler : public analyze_format_string::FormatStringHandler {
+  const char *Beg; // Beginning of string literal
+
+  bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
+                             const char *startSpecifier,
+                             unsigned specifierLen) override {
+    const analyze_printf::PrintfConversionSpecifier &CS =
+        FS.getConversionSpecifier();
+    if (CS.getKind() == analyze_format_string::ConversionSpecifier::nArg) {
+      FormatSpecifierLocations.emplace_back(CS.getStart() - Beg);
+      return true;
+    }
+    return false;
+  }
+
+public:
+  std::vector<unsigned int> FormatSpecifierLocations;
+  Handler(const char *Beg) : Beg(Beg) {}
+};
+
+void PercentNFormatSpecifierCheck::registerMatchers(MatchFinder *Finder) {
+  auto PrintfDecl = functionDecl(hasName("::printf"));
+  auto FprintfDecl = functionDecl(hasName("::fprintf"));
+  auto VfprintfDecl = functionDecl(hasName("::vfprintf"));
+  auto SprintfDecl = functionDecl(hasName("::sprintf"));
+  auto SnprintfDecl = functionDecl(hasName("::snprintf"));
+  auto VprintfDecl = functionDecl(hasName("::vprintf"));
+  auto VsprintfDecl = functionDecl(hasName("::vsprintf"));
+  auto VsnprintfDecl = functionDecl(hasName("::vsnprintf"));
+
+  auto ScanfDecl = functionDecl(hasName("::scanf"));
+  auto SscanfDecl = functionDecl(hasName("::sscanf"));
+  auto FscanfDecl = functionDecl(hasName("::fscanf"));
+  auto VfscanfDecl = functionDecl(hasName("::vfscanf"));
+  auto VscanfDecl = functionDecl(hasName("::vscanf"));
+  auto VsscanfDecl = functionDecl(hasName("::vsscanf"));
+
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(
+                   anyOf(PrintfDecl, VprintfDecl, ScanfDecl, VscanfDecl))),
+               hasArgument(0, stringLiteral().bind("StringLiteral"))),
+      this);
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(anyOf(FprintfDecl, SprintfDecl, VfprintfDecl,
+                                         VsprintfDecl, SscanfDecl, FscanfDecl,
+                                         VfscanfDecl, VsscanfDecl))),
+               hasArgument(1, stringLiteral().bind("StringLiteral"))),
+      this);
+  Finder->addMatcher(
+      callExpr(callee(functionDecl(anyOf(SnprintfDecl, VsnprintfDecl))),
+               hasArgument(2, stringLiteral().bind("StringLiteral"))),
+      this);
+}
+
+void PercentNFormatSpecifierCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const StringLiteral *FormatString =
+      Result.Nodes.getNodeAs<StringLiteral>("StringLiteral");
+  StringRef FormatStringRef = FormatString->getString();
+  Handler H(FormatStringRef.begin());
+  analyze_format_string::ParsePrintfString(
+      H, FormatStringRef.begin(), FormatStringRef.end(), getLangOpts(),
+      Result.Context->getTargetInfo(), false);
+  for (const auto byteNo : H.FormatSpecifierLocations) {
+    const auto loc = FormatString->getLocationOfByte(
+        byteNo, Result.Context->getSourceManager(), getLangOpts(),
+        Result.Context->getTargetInfo());
+    diag(loc, "usage of %%n can lead to unsafe writing to memory");
+  }
+}
+
+} // 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
@@ -34,6 +34,7 @@
   NoEscapeCheck.cpp
   NotNullTerminatedResultCheck.cpp
   ParentVirtualCallCheck.cpp
+  PercentNFormatSpecifierCheck.cpp
   PosixReturnCheck.cpp
   RedundantBranchConditionCheck.cpp
   ReservedIdentifierCheck.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
@@ -39,6 +39,7 @@
 #include "NoEscapeCheck.h"
 #include "NotNullTerminatedResultCheck.h"
 #include "ParentVirtualCallCheck.h"
+#include "PercentNFormatSpecifierCheck.h"
 #include "PosixReturnCheck.h"
 #include "RedundantBranchConditionCheck.h"
 #include "ReservedIdentifierCheck.h"
@@ -138,6 +139,8 @@
         "bugprone-not-null-terminated-result");
     CheckFactories.registerCheck<ParentVirtualCallCheck>(
         "bugprone-parent-virtual-call");
+    CheckFactories.registerCheck<PercentNFormatSpecifierCheck>(
+        "bugprone-percent-n-format-specifier");
     CheckFactories.registerCheck<PosixReturnCheck>(
         "bugprone-posix-return");
     CheckFactories.registerCheck<ReservedIdentifierCheck>(
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to