NoQ created this revision.
NoQ added reviewers: aaron.ballman, gribozavr2, xazax.hun, jkorous, t-rasmud, 
ziqingluo-90, malavikasamak.
Herald added subscribers: steakhal, martong, rnkovacs.
Herald added a project: All.
NoQ requested review of this revision.

This is the initial commit for `-Wunsafe-buffer-usage`, a warning that helps 
codebases (especially modern C++ codebases) transition away from buffer 
accesses. It's a minimal commit that barely implements anything, mostly adds 
skeleton for future work; we have a long road ahead of us.

Backstory in https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734, more 
documentation for the proposed programming model in D136811 
<https://reviews.llvm.org/D136811>.

I'm putting the actual implementation into `libAnalysis` as it's going to be a 
non-trivial analysis - mostly the fixit part where we try to figure out if we 
understand a variable's use pattern well enough to suggest a safe 
container/view replacement. Some parts of it may eventually prove useful for 
any similar fixit machine that tries to change types of variables. More on that 
in the next patch.

The interface for the analysis is currently very primitive, the analysis emits 
operations on raw buffers it thinks are unsafe. The plan is that it'll also 
emit fixit objects, but then the consuming class will figure out how to present 
them.

Warning text is currently somewhat lame, it going to improve a lot once we 
specialize it for different operations, and once we start emitting fixits we'll 
have to rethink it anyway because fixits can't be attached to a specific 
operation (but to an entire variable or even group of variables).

The warning is disabled by default.


Repository:
  rC Clang

https://reviews.llvm.org/D137346

Files:
  clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Analysis/CMakeLists.txt
  clang/lib/Analysis/UnsafeBufferUsage.cpp
  clang/lib/Sema/AnalysisBasedWarnings.cpp
  clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp

Index: clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/warn-unsafe-buffer-usage.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -std=c++20 -Wunsafe-buffer-usage -verify %s
+
+void testIncrement(char *p) {
+  ++p; // expected-warning{{unchecked operation on raw buffer in expression}}
+  p++; // expected-warning{{unchecked operation on raw buffer in expression}}
+  --p; // expected-warning{{unchecked operation on raw buffer in expression}}
+  p--; // expected-warning{{unchecked operation on raw buffer in expression}}
+}
Index: clang/lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -29,6 +29,7 @@
 #include "clang/Analysis/Analyses/ReachableCode.h"
 #include "clang/Analysis/Analyses/ThreadSafety.h"
 #include "clang/Analysis/Analyses/UninitializedValues.h"
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
 #include "clang/Analysis/CFGStmtMap.h"
@@ -2138,6 +2139,22 @@
 } // namespace consumed
 } // namespace clang
 
+//===----------------------------------------------------------------------===//
+// Unsafe buffer usage analysis.
+//===----------------------------------------------------------------------===//
+
+class UnsafeBufferUsageReporter : public UnsafeBufferUsageHandler {
+  Sema &S;
+
+public:
+  UnsafeBufferUsageReporter(Sema &S) : S(S) {}
+
+  void handleUnsafeOperation(const Stmt *Operation) override {
+    S.Diag(Operation->getBeginLoc(), diag::warn_unsafe_buffer_usage);
+  }
+};
+
+
 //===----------------------------------------------------------------------===//
 // AnalysisBasedWarnings - Worker object used by Sema to execute analysis-based
 //  warnings on a function, method, or block.
@@ -2430,6 +2447,12 @@
       if (S.getLangOpts().CPlusPlus && isNoexcept(FD))
         checkThrowInNonThrowingFunc(S, FD, AC);
 
+  // Emit unsafe buffer usage warnings and fixits.
+  if (!Diags.isIgnored(diag::warn_unsafe_buffer_usage, D->getBeginLoc())) {
+    UnsafeBufferUsageReporter R(S);
+    checkUnsafeBufferUsage(D, R);
+  }
+
   // If none of the previous checks caused a CFG build, trigger one here
   // for the logical error handler.
   if (LogicalErrorHandler::hasActiveDiagnostics(Diags, D->getBeginLoc())) {
Index: clang/lib/Analysis/UnsafeBufferUsage.cpp
===================================================================
--- /dev/null
+++ clang/lib/Analysis/UnsafeBufferUsage.cpp
@@ -0,0 +1,79 @@
+//===- UnsafeBufferUsage.cpp - Replace pointers with modern 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace ast_matchers;
+
+namespace {
+// TODO: Better abstractions over gadgets.
+using GadgetList = std::vector<const Stmt *>;
+}
+
+// Scan the function and return a list of gadgets found with provided kits.
+static GadgetList findGadgets(const Decl *D) {
+
+  class GadgetFinderCallback : public MatchFinder::MatchCallback {
+    GadgetList &Output;
+
+  public:
+    GadgetFinderCallback(GadgetList &Output) : Output(Output) {}
+
+    void run(const MatchFinder::MatchResult &Result) override {
+      Output.push_back(Result.Nodes.getNodeAs<Stmt>("root_node"));
+    }
+  };
+
+  GadgetList G;
+  MatchFinder M;
+
+  auto IncrementMatcher = unaryOperator(
+    hasOperatorName("++"),
+    hasUnaryOperand(hasType(pointerType()))
+  );
+  auto DecrementMatcher = unaryOperator(
+    hasOperatorName("--"),
+    hasUnaryOperand(hasType(pointerType()))
+  );
+
+  GadgetFinderCallback CB(G);
+
+  M.addMatcher(
+      stmt(forEachDescendant(
+        stmt(
+          anyOf(
+            IncrementMatcher,
+            DecrementMatcher
+            /* Fill me in! */
+          )
+          // FIXME: Idiomatically there should be a forCallable(equalsNode(D))
+          // here, to make sure that the statement actually belongs to the
+          // function and not to a nested function. However, forCallable uses
+          // ParentMap which can't be used before the AST is fully constructed.
+          // The original problem doesn't sound like it needs ParentMap though,
+          // maybe there's a more direct solution?
+        ).bind("root_node")
+      )), &CB);
+
+  M.match(*D->getBody(), D->getASTContext());
+
+  return G; // NRVO!
+}
+
+void clang::checkUnsafeBufferUsage(const Decl *D,
+                                   UnsafeBufferUsageHandler &Handler) {
+  assert(D && D->getBody());
+
+  GadgetList G = findGadgets(D);
+  for (const Stmt *S : G) {
+    Handler.handleUnsafeOperation(S);
+  }
+}
Index: clang/lib/Analysis/CMakeLists.txt
===================================================================
--- clang/lib/Analysis/CMakeLists.txt
+++ clang/lib/Analysis/CMakeLists.txt
@@ -32,6 +32,7 @@
   ThreadSafetyLogical.cpp
   ThreadSafetyTIL.cpp
   UninitializedValues.cpp
+  UnsafeBufferUsage.cpp
 
   LINK_LIBS
   clangAST
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11726,4 +11726,8 @@
   "a randomized struct can only be initialized with a designated initializer">;
 def err_cast_from_randomized_struct : Error<
   "casting from randomized structure pointer type %0 to %1">;
+
+// Unsafe buffer usage diagnostics.
+def warn_unsafe_buffer_usage : Warning<"unchecked operation on raw buffer in expression">,
+  InGroup<DiagGroup<"unsafe-buffer-usage">>, DefaultIgnore;
 } // end of sema component.
Index: clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
===================================================================
--- /dev/null
+++ clang/include/clang/Analysis/Analyses/UnsafeBufferUsage.h
@@ -0,0 +1,38 @@
+//===- UnsafeBufferUsage.h - Replace pointers with modern C++ ---*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines an analysis that aids replacing buffer accesses through
+//  raw pointers with safer C++ abstractions such as containers and views/spans.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+namespace clang {
+
+/// The interface that lets the caller handle unsafe buffer usage analysis
+/// results by overriding this class's handle... methods.
+class UnsafeBufferUsageHandler {
+public:
+  UnsafeBufferUsageHandler() = default;
+  virtual ~UnsafeBufferUsageHandler() = default;
+
+  /// Invoked when an unsafe operation over raw pointers is found.
+  virtual void handleUnsafeOperation(const Stmt *Operation) = 0;
+};
+
+// This function invokes the analysis and allows the caller to react to it
+// through the handler class.
+void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler);
+
+} // end namespace clang
+
+#endif /* LLVM_CLANG_ANALYSIS_ANALYSES_UNSAFEBUFFERUSAGE_H */
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to