Blame SOURCES/0002-Misleading-bidirectional-detection.patch

fc83c9
From e7ab823a5f3f57e843069d43033a16165809723a Mon Sep 17 00:00:00 2001
fc83c9
From: serge-sans-paille <sguelton@redhat.com>
fc83c9
Date: Thu, 4 Nov 2021 11:11:53 +0100
fc83c9
Subject: [PATCH 2/3] Misleading bidirectional detection
fc83c9
fc83c9
Differential Revision: https://reviews.llvm.org/D112913
fc83c9
---
fc83c9
 clang-tools-extra/clang-tidy/misc/CMakeLists.txt   |   1 +
fc83c9
 .../clang-tidy/misc/MiscTidyModule.cpp             |   3 +
fc83c9
 .../clang-tidy/misc/MisleadingBidirectional.cpp    | 131 +++++++++++++++++++++
fc83c9
 .../clang-tidy/misc/MisleadingBidirectional.h      |  38 ++++++
fc83c9
 clang-tools-extra/docs/ReleaseNotes.rst            |   5 +
fc83c9
 clang-tools-extra/docs/clang-tidy/checks/list.rst  |   3 +-
fc83c9
 .../checks/misc-misleading-bidirectional.rst       |  21 ++++
fc83c9
 .../checkers/misc-misleading-bidirectional.cpp     |  31 +++++
fc83c9
 8 files changed, 232 insertions(+), 1 deletion(-)
fc83c9
 create mode 100644 clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.cpp
fc83c9
 create mode 100644 clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.h
fc83c9
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/misc-misleading-bidirectional.rst
fc83c9
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/misc-misleading-bidirectional.cpp
fc83c9
fc83c9
diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
fc83c9
index 7cafe54..e6abac8 100644
fc83c9
--- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
fc83c9
+++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt
fc83c9
@@ -16,6 +16,7 @@ add_clang_library(clangTidyMiscModule
fc83c9
   DefinitionsInHeadersCheck.cpp
fc83c9
   Homoglyph.cpp
fc83c9
   MiscTidyModule.cpp
fc83c9
+  MisleadingBidirectional.cpp
fc83c9
   MisplacedConstCheck.cpp
fc83c9
   NewDeleteOverloadsCheck.cpp
fc83c9
   NoRecursionCheck.cpp
fc83c9
diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
fc83c9
index 5c7bd0c..bb5fde2 100644
fc83c9
--- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
fc83c9
+++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
fc83c9
@@ -11,6 +11,7 @@
fc83c9
 #include "../ClangTidyModuleRegistry.h"
fc83c9
 #include "DefinitionsInHeadersCheck.h"
fc83c9
 #include "Homoglyph.h"
fc83c9
+#include "MisleadingBidirectional.h"
fc83c9
 #include "MisplacedConstCheck.h"
fc83c9
 #include "NewDeleteOverloadsCheck.h"
fc83c9
 #include "NoRecursionCheck.h"
fc83c9
@@ -35,6 +36,8 @@ public:
fc83c9
     CheckFactories.registerCheck<DefinitionsInHeadersCheck>(
fc83c9
         "misc-definitions-in-headers");
fc83c9
     CheckFactories.registerCheck<Homoglyph>("misc-homoglyph");
fc83c9
+    CheckFactories.registerCheck<MisleadingBidirectionalCheck>(
fc83c9
+        "misc-misleading-bidirectional");
fc83c9
     CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const");
fc83c9
     CheckFactories.registerCheck<NewDeleteOverloadsCheck>(
fc83c9
         "misc-new-delete-overloads");
fc83c9
diff --git a/clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.cpp b/clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.cpp
fc83c9
new file mode 100644
fc83c9
index 0000000..7a2f06b
fc83c9
--- /dev/null
fc83c9
+++ b/clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.cpp
fc83c9
@@ -0,0 +1,139 @@
fc83c9
+//===--- MisleadingBidirectional.cpp - clang-tidy -------------------------===//
fc83c9
+//
fc83c9
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
fc83c9
+// See https://llvm.org/LICENSE.txt for license information.
fc83c9
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
fc83c9
+//
fc83c9
+//===----------------------------------------------------------------------===//
fc83c9
+
fc83c9
+#include "MisleadingBidirectional.h"
fc83c9
+
fc83c9
+#include "clang/Frontend/CompilerInstance.h"
fc83c9
+#include "clang/Lex/Preprocessor.h"
fc83c9
+#include "llvm/Support/ConvertUTF.h"
fc83c9
+
fc83c9
+using namespace clang;
fc83c9
+using namespace clang::tidy::misc;
fc83c9
+
fc83c9
+static bool containsMisleadingBidi(StringRef Buffer,
fc83c9
+                                   bool HonorLineBreaks = true) {
fc83c9
+  const char *CurPtr = Buffer.begin();
fc83c9
+
fc83c9
+  enum BidiChar {
fc83c9
+    PS = 0x2029,
fc83c9
+    RLO = 0x202E,
fc83c9
+    RLE = 0x202B,
fc83c9
+    LRO = 0x202D,
fc83c9
+    LRE = 0x202A,
fc83c9
+    PDF = 0x202C,
fc83c9
+    RLI = 0x2067,
fc83c9
+    LRI = 0x2066,
fc83c9
+    FSI = 0x2068,
fc83c9
+    PDI = 0x2069
fc83c9
+  };
fc83c9
+
fc83c9
+  SmallVector<BidiChar> BidiContexts;
fc83c9
+
fc83c9
+  // Scan each character while maintaining a stack of opened bidi context.
fc83c9
+  // RLO/RLE/LRO/LRE all are closed by PDF while RLI LRI and FSI are closed by
fc83c9
+  // PDI. New lines reset the context count. Extra PDF / PDI are ignored.
fc83c9
+  //
fc83c9
+  // Warn if we end up with an unclosed context.
fc83c9
+  while (CurPtr < Buffer.end()) {
fc83c9
+    unsigned char C = *CurPtr;
fc83c9
+    if (isASCII(C)) {
fc83c9
+      ++CurPtr;
fc83c9
+      bool IsParagrapSep =
fc83c9
+          (C == 0xA || C == 0xD || (0x1C <= C && C <= 0x1E) || C == 0x85);
fc83c9
+      bool IsSegmentSep = (C == 0x9 || C == 0xB || C == 0x1F);
fc83c9
+      if (IsParagrapSep || IsSegmentSep)
fc83c9
+        BidiContexts.clear();
fc83c9
+      continue;
fc83c9
+    }
fc83c9
+    llvm::UTF32 CodePoint;
fc83c9
+    llvm::ConversionResult Result = llvm::convertUTF8Sequence(
fc83c9
+        (const llvm::UTF8 **)&CurPtr, (const llvm::UTF8 *)Buffer.end(),
fc83c9
+        &CodePoint, llvm::strictConversion);
fc83c9
+
fc83c9
+    // If conversion fails, utf-8 is designed so that we can just try next char.
fc83c9
+    if (Result != llvm::conversionOK) {
fc83c9
+      ++CurPtr;
fc83c9
+      continue;
fc83c9
+    }
fc83c9
+
fc83c9
+    // Open a PDF context.
fc83c9
+    if (CodePoint == RLO || CodePoint == RLE || CodePoint == LRO ||
fc83c9
+        CodePoint == LRE)
fc83c9
+      BidiContexts.push_back(PDF);
fc83c9
+    // Close PDF Context.
fc83c9
+    else if (CodePoint == PDF) {
fc83c9
+      if (!BidiContexts.empty() && BidiContexts.back() == PDF)
fc83c9
+        BidiContexts.pop_back();
fc83c9
+    }
fc83c9
+    // Open a PDI Context.
fc83c9
+    else if (CodePoint == RLI || CodePoint == LRI || CodePoint == FSI)
fc83c9
+      BidiContexts.push_back(PDI);
fc83c9
+    // Close a PDI Context.
fc83c9
+    else if (CodePoint == PDI) {
fc83c9
+      auto R = std::find(BidiContexts.rbegin(), BidiContexts.rend(), PDI);
fc83c9
+      if (R != BidiContexts.rend())
fc83c9
+        BidiContexts.resize(BidiContexts.rend() - R - 1);
fc83c9
+    }
fc83c9
+    // Line break or equivalent
fc83c9
+    else if (CodePoint == PS)
fc83c9
+      BidiContexts.clear();
fc83c9
+  }
fc83c9
+  return !BidiContexts.empty();
fc83c9
+}
fc83c9
+
fc83c9
+class MisleadingBidirectionalCheck::MisleadingBidirectionalHandler
fc83c9
+    : public CommentHandler {
fc83c9
+public:
fc83c9
+  MisleadingBidirectionalHandler(MisleadingBidirectionalCheck &Check,
fc83c9
+                                 llvm::Optional<std::string> User)
fc83c9
+      : Check(Check) {}
fc83c9
+
fc83c9
+  bool HandleComment(Preprocessor &PP, SourceRange Range) override {
fc83c9
+    // FIXME: check that we are in a /* */ comment
fc83c9
+    StringRef Text =
fc83c9
+        Lexer::getSourceText(CharSourceRange::getCharRange(Range),
fc83c9
+                             PP.getSourceManager(), PP.getLangOpts());
fc83c9
+
fc83c9
+    if (containsMisleadingBidi(Text, true))
fc83c9
+      Check.diag(
fc83c9
+          Range.getBegin(),
fc83c9
+          "comment contains misleading bidirectional Unicode characters");
fc83c9
+    return false;
fc83c9
+  }
fc83c9
+
fc83c9
+private:
fc83c9
+  MisleadingBidirectionalCheck &Check;
fc83c9
+};
fc83c9
+
fc83c9
+MisleadingBidirectionalCheck::MisleadingBidirectionalCheck(
fc83c9
+    StringRef Name, ClangTidyContext *Context)
fc83c9
+    : ClangTidyCheck(Name, Context),
fc83c9
+      Handler(std::make_unique<MisleadingBidirectionalHandler>(
fc83c9
+          *this, Context->getOptions().User)) {}
fc83c9
+
fc83c9
+MisleadingBidirectionalCheck::~MisleadingBidirectionalCheck() = default;
fc83c9
+
fc83c9
+void MisleadingBidirectionalCheck::registerPPCallbacks(
fc83c9
+    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
fc83c9
+  PP->addCommentHandler(Handler.get());
fc83c9
+}
fc83c9
+
fc83c9
+void MisleadingBidirectionalCheck::check(
fc83c9
+    const ast_matchers::MatchFinder::MatchResult &Result) {
fc83c9
+  if (const auto *SL = Result.Nodes.getNodeAs<StringLiteral>("strlit")) {
fc83c9
+    StringRef Literal = SL->getBytes();
fc83c9
+    if (containsMisleadingBidi(Literal, false))
fc83c9
+      diag(SL->getBeginLoc(), "string literal contains misleading "
fc83c9
+                              "bidirectional Unicode characters");
fc83c9
+  }
fc83c9
+}
fc83c9
+
fc83c9
+void MisleadingBidirectionalCheck::registerMatchers(
fc83c9
+    ast_matchers::MatchFinder *Finder) {
fc83c9
+  Finder->addMatcher(ast_matchers::stringLiteral().bind("strlit"), this);
fc83c9
+}
fc83c9
diff --git a/clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.h b/clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.h
fc83c9
new file mode 100644
fc83c9
index 0000000..18e7060
fc83c9
--- /dev/null
fc83c9
+++ b/clang-tools-extra/clang-tidy/misc/MisleadingBidirectional.h
fc83c9
@@ -0,0 +1,38 @@
fc83c9
+//===--- MisleadingBidirectionalCheck.h - clang-tidy ------------*- C++ -*-===//
fc83c9
+//
fc83c9
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
fc83c9
+// See https://llvm.org/LICENSE.txt for license information.
fc83c9
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
fc83c9
+//
fc83c9
+//===----------------------------------------------------------------------===//
fc83c9
+
fc83c9
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISLEADINGBIDIRECTIONALCHECK_H
fc83c9
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISLEADINGBIDIRECTIONALCHECK_H
fc83c9
+
fc83c9
+#include "../ClangTidyCheck.h"
fc83c9
+
fc83c9
+namespace clang {
fc83c9
+namespace tidy {
fc83c9
+namespace misc {
fc83c9
+
fc83c9
+class MisleadingBidirectionalCheck : public ClangTidyCheck {
fc83c9
+public:
fc83c9
+  MisleadingBidirectionalCheck(StringRef Name, ClangTidyContext *Context);
fc83c9
+  ~MisleadingBidirectionalCheck();
fc83c9
+
fc83c9
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
fc83c9
+                           Preprocessor *ModuleExpanderPP) override;
fc83c9
+
fc83c9
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
fc83c9
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
fc83c9
+
fc83c9
+private:
fc83c9
+  class MisleadingBidirectionalHandler;
fc83c9
+  std::unique_ptr<MisleadingBidirectionalHandler> Handler;
fc83c9
+};
fc83c9
+
fc83c9
+} // namespace misc
fc83c9
+} // namespace tidy
fc83c9
+} // namespace clang
fc83c9
+
fc83c9
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISLEADINGBIDIRECTIONALCHECK_H
fc83c9
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
fc83c9
index 37a717e..1eec7db 100644
fc83c9
--- a/clang-tools-extra/docs/ReleaseNotes.rst
fc83c9
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
fc83c9
@@ -218,6 +218,11 @@ New checks
fc83c9
 
fc83c9
   Detects confusable unicode identifiers.
fc83c9
 
fc83c9
+- New :doc:`misc-misleading-bidirectional <clang-tidy/checks/misc-misleading-bidirectional>` check.
fc83c9
+
fc83c9
+  Inspect string literal and comments for unterminated bidirectional Unicode
fc83c9
+  characters.
fc83c9
+
fc83c9
 New check aliases
fc83c9
 ^^^^^^^^^^^^^^^^^
fc83c9
 
fc83c9
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
fc83c9
index df9a95c..b118639 100644
fc83c9
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
fc83c9
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
fc83c9
@@ -207,7 +207,8 @@ Clang-Tidy Checks
fc83c9
    `llvmlibc-implementation-in-namespace <llvmlibc-implementation-in-namespace.html>`_,
fc83c9
    `llvmlibc-restrict-system-libc-headers <llvmlibc-restrict-system-libc-headers.html>`_, "Yes"
fc83c9
    `misc-definitions-in-headers <misc-definitions-in-headers.html>`_, "Yes"
fc83c9
-   `misc-homoglyph <misc-homoglyph.html>`_, "Yes"
fc83c9
+   `misc-homoglyph <misc-homoglyph.html>`_,
fc83c9
+   `misc-misleading-bidirectional <misc-misleading-bidirectional.html>`_,
fc83c9
    `misc-misplaced-const <misc-misplaced-const.html>`_,
fc83c9
    `misc-new-delete-overloads <misc-new-delete-overloads.html>`_,
fc83c9
    `misc-no-recursion <misc-no-recursion.html>`_,
fc83c9
diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc-misleading-bidirectional.rst b/clang-tools-extra/docs/clang-tidy/checks/misc-misleading-bidirectional.rst
fc83c9
new file mode 100644
fc83c9
index 0000000..16ffc97
fc83c9
--- /dev/null
fc83c9
+++ b/clang-tools-extra/docs/clang-tidy/checks/misc-misleading-bidirectional.rst
fc83c9
@@ -0,0 +1,21 @@
fc83c9
+.. title:: clang-tidy - misc-misleading-bidirectional
fc83c9
+
fc83c9
+misc-misleading-bidirectional
fc83c9
+=============================
fc83c9
+
fc83c9
+Warn about unterminated bidirectional unicode sequence, detecting potential attack
fc83c9
+as described in the `Trojan Source <https://www.trojansource.codes>`_ attack.
fc83c9
+
fc83c9
+Example:
fc83c9
+
fc83c9
+.. code-block:: c++
fc83c9
+
fc83c9
+    #include <iostream>
fc83c9
+
fc83c9
+    int main() {
fc83c9
+        bool isAdmin = false;
fc83c9
+        /*‮ } ⁦if (isAdmin)⁩ ⁦ begin admins only */
fc83c9
+            std::cout << "You are an admin.\n";
fc83c9
+        /* end admins only ‮ { ⁦*/
fc83c9
+        return 0;
fc83c9
+    }
fc83c9
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc-misleading-bidirectional.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc-misleading-bidirectional.cpp
fc83c9
new file mode 100644
fc83c9
index 0000000..7a1746d
fc83c9
--- /dev/null
fc83c9
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc-misleading-bidirectional.cpp
fc83c9
@@ -0,0 +1,31 @@
fc83c9
+// RUN: %check_clang_tidy %s misc-misleading-bidirectional %t
fc83c9
+
fc83c9
+void func(void) {
fc83c9
+  int admin = 0;
fc83c9
+  /*‮ }⁦if(admin)⁩ ⁦ begin*/
fc83c9
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comment contains misleading bidirectional Unicode characters [misc-misleading-bidirectional]
fc83c9
+  const char msg[] = "‮⁦if(admin)⁩ ⁦tes";
fc83c9
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: string literal contains misleading bidirectional Unicode characters [misc-misleading-bidirectional]
fc83c9
+}
fc83c9
+
fc83c9
+void all_fine(void) {
fc83c9
+  char valid[] = "some‮valid‬sequence";
fc83c9
+  /* EOL ends bidi‮ sequence
fc83c9
+   * end it's fine to do so.
fc83c9
+   * EOL ends ⁧isolate too
fc83c9
+   */
fc83c9
+}
fc83c9
+
fc83c9
+int invalid_utf_8(void) {
fc83c9
+  bool isAdmin = false;
fc83c9
+
fc83c9
+  // the comment below contains an invalid utf8 character, but should still be
fc83c9
+  // processed.
fc83c9
+
fc83c9
+  // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: comment contains misleading bidirectional Unicode characters [misc-misleading-bidirectional]
fc83c9
+  /*€‮ } ⁦if (isAdmin)⁩ ⁦ begin admins only */
fc83c9
+  return 1;
fc83c9
+  /* end admins only ‮ { ⁦*/
fc83c9
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: comment contains misleading bidirectional Unicode characters [misc-misleading-bidirectional]
fc83c9
+  return 0;
fc83c9
+}
fc83c9
-- 
fc83c9
1.8.3.1
fc83c9