[clang] [analyzer] Adding taint analysis capability to unix.Malloc checker (PR #92420)

Daniel Krupp via cfe-commits cfe-commits at lists.llvm.org
Mon May 27 07:54:52 PDT 2024


https://github.com/dkrupp updated https://github.com/llvm/llvm-project/pull/92420

>From 80767176cbe8e5717c5f42b113f305d81b635cb9 Mon Sep 17 00:00:00 2001
From: Daniel Krupp <daniel.krupp at ericsson.com>
Date: Tue, 30 Apr 2024 15:20:52 +0200
Subject: [PATCH 1/3] [analyzer] Adding taint analysis capability to
 unix.Malloc checker

unix.Malloc checker will warn if a memory allocation function
(malloc, calloc, realloc, alloca) is called with a tainted
(attacker controlled) size parameter.
A large, maliciously set size value can trigger memory exhaustion.
To get this warning, the alpha.security.taint.TaintPropagation checker
also needs to be switched on.

The warning will only be emitted, if the analyzer cannot prove
that the size is below reasonable bounds (<SIZE_MAX/4).
---
 clang/docs/analyzer/checkers.rst              | 35 +++++++
 .../StaticAnalyzer/Checkers/MallocChecker.cpp | 96 ++++++++++++++++---
 clang/test/Analysis/malloc.c                  | 42 +++++++-
 3 files changed, 159 insertions(+), 14 deletions(-)

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index eb8b58323da4d..0cdf6dcab2875 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -1273,6 +1273,41 @@ Check for memory leaks, double free, and use-after-free problems. Traces memory
 .. literalinclude:: checkers/unix_malloc_example.c
     :language: c
 
+If the ``alpha.security.taint.TaintPropagation`` checker is enabled, the checker
+warns for cases when the ``size`` parameter of the ``malloc`` , ``calloc``,
+``realloc``, ``alloca`` is tainted (potentially attacker controlled). If an
+attacker can inject a large value as the size parameter, memory exhaustion
+denial of service attack can be carried out.
+
+The analyzer emits warning only if it cannot prove that the size parameter is
+within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
+covers the SEI Cert coding standard rule `INT04-C
+<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.
+
+You can silence this warning either by bound checking the ``size`` parameter, or
+by explicitly marking the ``size`` parameter as sanitized. See the
+:ref:`alpha-security-taint-TaintPropagation` checker for more details.
+
+.. code-block:: c
+
+  void t1(void) {
+    size_t size;
+    scanf("%zu", &size);
+    int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
+    free(p);
+  }
+
+  void t3(void) {
+    size_t size;
+    scanf("%zu", &size);
+    if (1024<size)
+      return;
+    int *p = malloc(size); // No warning expected as the the user input is bound
+    free(p);
+  }
+
+.. _unix-MismatchedDeallocator:
+
 .. _unix-MallocSizeof:
 
 unix.MallocSizeof (C)
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index dd204b62dcc04..2cc9205c07814 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -60,6 +60,7 @@
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/Taint.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
@@ -365,6 +366,7 @@ class MallocChecker
   mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
   mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
   mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_TaintedAlloc[CK_NumCheckKinds];
 
 #define CHECK_FN(NAME)                                                         \
   void NAME(const CallEvent &Call, CheckerContext &C) const;
@@ -462,6 +464,13 @@ class MallocChecker
   };
 
   bool isMemCall(const CallEvent &Call) const;
+  void reportTaintBug(StringRef Msg, ProgramStateRef State, CheckerContext &C,
+                      llvm::ArrayRef<SymbolRef> TaintedSyms,
+                      AllocationFamily Family, const Expr *SizeEx) const;
+
+  void CheckTaintedness(CheckerContext &C, const CallEvent &Call,
+                        const SVal SizeSVal, ProgramStateRef State,
+                        AllocationFamily Family) const;
 
   // TODO: Remove mutable by moving the initializtaion to the registry function.
   mutable std::optional<uint64_t> KernelZeroFlagVal;
@@ -521,9 +530,9 @@ class MallocChecker
   /// malloc leaves it undefined.
   /// \param [in] State The \c ProgramState right before allocation.
   /// \returns The ProgramState right after allocation.
-  [[nodiscard]] static ProgramStateRef
+  [[nodiscard]] ProgramStateRef
   MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx,
-               SVal Init, ProgramStateRef State, AllocationFamily Family);
+               SVal Init, ProgramStateRef State, AllocationFamily Family) const;
 
   /// Models memory allocation.
   ///
@@ -534,9 +543,10 @@ class MallocChecker
   /// malloc leaves it undefined.
   /// \param [in] State The \c ProgramState right before allocation.
   /// \returns The ProgramState right after allocation.
-  [[nodiscard]] static ProgramStateRef
-  MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init,
-               ProgramStateRef State, AllocationFamily Family);
+  [[nodiscard]] ProgramStateRef MallocMemAux(CheckerContext &C,
+                                             const CallEvent &Call, SVal Size,
+                                             SVal Init, ProgramStateRef State,
+                                             AllocationFamily Family) const;
 
   // Check if this malloc() for special flags. At present that means M_ZERO or
   // __GFP_ZERO (in which case, treat it like calloc).
@@ -649,8 +659,9 @@ class MallocChecker
   /// \param [in] Call The expression that reallocated memory
   /// \param [in] State The \c ProgramState right before reallocation.
   /// \returns The ProgramState right after allocation.
-  [[nodiscard]] static ProgramStateRef
-  CallocMem(CheckerContext &C, const CallEvent &Call, ProgramStateRef State);
+  [[nodiscard]] ProgramStateRef CallocMem(CheckerContext &C,
+                                          const CallEvent &Call,
+                                          ProgramStateRef State) const;
 
   /// See if deallocation happens in a suspicious context. If so, escape the
   /// pointers that otherwise would have been deallocated and return true.
@@ -1779,7 +1790,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
                                             const CallEvent &Call,
                                             const Expr *SizeEx, SVal Init,
                                             ProgramStateRef State,
-                                            AllocationFamily Family) {
+                                            AllocationFamily Family) const {
   if (!State)
     return nullptr;
 
@@ -1787,10 +1798,71 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
   return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Family);
 }
 
+void MallocChecker::reportTaintBug(StringRef Msg, ProgramStateRef State,
+                                   CheckerContext &C,
+                                   llvm::ArrayRef<SymbolRef> TaintedSyms,
+                                   AllocationFamily Family,
+                                   const Expr *SizeEx) const {
+  if (ExplodedNode *N = C.generateErrorNode(State)) {
+
+    std::optional<MallocChecker::CheckKind> CheckKind =
+        getCheckIfTracked(Family);
+    if (!CheckKind)
+      return;
+    if (!BT_TaintedAlloc[*CheckKind])
+      BT_TaintedAlloc[*CheckKind].reset(new BugType(CheckNames[*CheckKind],
+                                                    "Tainted Memory Allocation",
+                                                    categories::MemoryError));
+    auto R = std::make_unique<PathSensitiveBugReport>(
+        *BT_TaintedAlloc[*CheckKind], Msg, N);
+
+    bugreporter::trackExpressionValue(N, SizeEx, *R);
+    for (auto Sym : TaintedSyms)
+      R->markInteresting(Sym);
+    C.emitReport(std::move(R));
+  }
+}
+
+void MallocChecker::CheckTaintedness(CheckerContext &C, const CallEvent &Call,
+                                     const SVal SizeSVal, ProgramStateRef State,
+                                     AllocationFamily Family) const {
+  std::vector<SymbolRef> TaintedSyms =
+      clang::ento::taint::getTaintedSymbols(State, SizeSVal);
+  if (!TaintedSyms.empty()) {
+    SValBuilder &SVB = C.getSValBuilder();
+    QualType SizeTy = SVB.getContext().getSizeType();
+    QualType CmpTy = SVB.getConditionType();
+    // In case the symbol is tainted, we give a warning if the
+    // size is larger than SIZE_MAX/4
+    BasicValueFactory &BVF = SVB.getBasicValueFactory();
+    const llvm::APSInt MaxValInt = BVF.getMaxValue(SizeTy);
+    NonLoc MaxLength =
+        SVB.makeIntVal(MaxValInt / APSIntType(MaxValInt).getValue(4));
+    std::optional<NonLoc> SizeNL = SizeSVal.getAs<NonLoc>();
+    auto Cmp = SVB.evalBinOpNN(State, BO_GE, *SizeNL, MaxLength, CmpTy)
+                   .getAs<DefinedOrUnknownSVal>();
+    if (!Cmp)
+      return;
+    auto [StateTooLarge, StateNotTooLarge] = State->assume(*Cmp);
+    if (!StateTooLarge && StateNotTooLarge) {
+      // we can prove that size is not too large so ok.
+      return;
+    }
+
+    std::string Callee = "Memory allocation function";
+    if (Call.getCalleeIdentifier())
+      Callee = Call.getCalleeIdentifier()->getName().str();
+    reportTaintBug(
+        Callee + " is called with a tainted (potentially attacker controlled) "
+                 "value. Make sure the value is bound checked.",
+        State, C, TaintedSyms, Family, Call.getArgExpr(0));
+  }
+}
+
 ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
                                             const CallEvent &Call, SVal Size,
                                             SVal Init, ProgramStateRef State,
-                                            AllocationFamily Family) {
+                                            AllocationFamily Family) const {
   if (!State)
     return nullptr;
 
@@ -1819,9 +1891,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
   if (Size.isUndef())
     Size = UnknownVal();
 
-  // TODO: If Size is tainted and we cannot prove that it is within
-  // reasonable bounds, emit a warning that an attacker may
-  // provoke a memory exhaustion error.
+  CheckTaintedness(C, Call, Size, State, AF_Malloc);
 
   // Set the region's extent.
   State = setDynamicExtent(State, RetVal.getAsRegion(),
@@ -2761,7 +2831,7 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call,
 
 ProgramStateRef MallocChecker::CallocMem(CheckerContext &C,
                                          const CallEvent &Call,
-                                         ProgramStateRef State) {
+                                         ProgramStateRef State) const {
   if (!State)
     return nullptr;
 
diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c
index e5cb45ba73352..6dba76a57d83f 100644
--- a/clang/test/Analysis/malloc.c
+++ b/clang/test/Analysis/malloc.c
@@ -3,7 +3,8 @@
 // RUN:   -analyzer-checker=alpha.deadcode.UnreachableCode \
 // RUN:   -analyzer-checker=alpha.core.CastSize \
 // RUN:   -analyzer-checker=unix \
-// RUN:   -analyzer-checker=debug.ExprInspection
+// RUN:   -analyzer-checker=debug.ExprInspection \
+// RUN:   -analyzer-checker=alpha.security.taint.TaintPropagation
 
 #include "Inputs/system-header-simulator.h"
 
@@ -48,6 +49,45 @@ void myfoo(int *p);
 void myfooint(int p);
 char *fooRetPtr(void);
 
+void t1(void) {
+  size_t size;
+  scanf("%zu", &size);
+  int *p = malloc(size); // expected-warning{{malloc is called with a tainted (potentially attacker controlled) value}}
+  free(p);
+}
+
+void t2(void) {
+  size_t size;
+  scanf("%zu", &size);
+  int *p = calloc(size,2); // expected-warning{{calloc is called with a tainted (potentially attacker controlled) value}}
+  free(p);
+}
+
+void t3(void) {
+  size_t size;
+  scanf("%zu", &size);
+  if (1024<size)
+    return;
+  int *p = malloc(size); // No warning expected as the the user input is bound
+  free(p);
+}
+
+void t4(void) {
+  size_t size;
+  int *p = malloc(sizeof(int)); 
+  scanf("%zu", &size);  
+  p = (int*) realloc((void*) p, size); // // expected-warning{{realloc is called with a tainted (potentially attacker controlled) value}}
+  free(p);
+}
+
+void t5(void) {
+  size_t size;
+  int *p = alloca(sizeof(int)); 
+  scanf("%zu", &size);  
+  p = (int*) alloca(size); // // expected-warning{{alloca is called with a tainted (potentially attacker controlled) value}}  
+}
+
+
 void f1(void) {
   int *p = malloc(12);
   return; // expected-warning{{Potential leak of memory pointed to by 'p'}}

>From 9710b984d7a6d2ea018323c17a6ede3fb7fb49f1 Mon Sep 17 00:00:00 2001
From: Daniel Krupp <daniel.krupp at ericsson.com>
Date: Thu, 23 May 2024 10:12:48 +0200
Subject: [PATCH 2/3] Addressing review commnents

- Create a new optional checker optin.taint.TaintMalloc
- Add test case for testing taint diagnostic notes
---
 clang/docs/analyzer/checkers.rst              | 77 ++++++++--------
 .../clang/StaticAnalyzer/Checkers/Checkers.td | 13 +++
 .../StaticAnalyzer/Checkers/MallocChecker.cpp | 87 ++++++++++---------
 clang/test/Analysis/malloc.c                  | 27 +++---
 .../test/Analysis/taint-diagnostic-visitor.c  | 12 ++-
 5 files changed, 124 insertions(+), 92 deletions(-)

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 0cdf6dcab2875..827ba38f020fe 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -599,7 +599,47 @@ Warns when a nullable pointer is returned from a function that has _Nonnull retu
 optin
 ^^^^^
 
-Checkers for portability, performance or coding style specific rules.
+Checkers for portability, performance, optional security and coding style specific rules.
+
+.. _optin-taint-TaintMalloc:
+
+optin.taint.TaintMalloc
+"""""""""""""""""""""""
+
+This checker warns for cases when the ``size`` parameter of the ``malloc`` ,
+``calloc``, ``realloc``, ``alloca`` is tainted (potentially attacker controlled).
+If an attacker can inject a large value as the size parameter, memory exhaustion
+denial of service attack can be carried out.
+
+The ``alpha.security.taint.TaintPropagation`` checker also needs to be enabled for
+this checker to give warnings.
+
+The analyzer emits warning only if it cannot prove that the size parameter is
+within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
+covers the SEI Cert coding standard rule `INT04-C
+<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.
+
+You can silence this warning either by bound checking the ``size`` parameter, or
+by explicitly marking the ``size`` parameter as sanitized. See the
+:ref:`alpha-security-taint-TaintPropagation` checker for more details.
+
+.. code-block:: c
+
+  void t1(void) {
+    size_t size;
+    scanf("%zu", &size);
+    int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
+    free(p);
+  }
+
+  void t3(void) {
+    size_t size = 0;
+    scanf("%zu", &size);
+    if (1024 < size)
+      return;
+    int *p = malloc(size); // No warning expected as the the user input is bound
+    free(p);
+  }
 
 .. _optin-core-EnumCastOutOfRange:
 
@@ -1273,41 +1313,6 @@ Check for memory leaks, double free, and use-after-free problems. Traces memory
 .. literalinclude:: checkers/unix_malloc_example.c
     :language: c
 
-If the ``alpha.security.taint.TaintPropagation`` checker is enabled, the checker
-warns for cases when the ``size`` parameter of the ``malloc`` , ``calloc``,
-``realloc``, ``alloca`` is tainted (potentially attacker controlled). If an
-attacker can inject a large value as the size parameter, memory exhaustion
-denial of service attack can be carried out.
-
-The analyzer emits warning only if it cannot prove that the size parameter is
-within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
-covers the SEI Cert coding standard rule `INT04-C
-<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.
-
-You can silence this warning either by bound checking the ``size`` parameter, or
-by explicitly marking the ``size`` parameter as sanitized. See the
-:ref:`alpha-security-taint-TaintPropagation` checker for more details.
-
-.. code-block:: c
-
-  void t1(void) {
-    size_t size;
-    scanf("%zu", &size);
-    int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
-    free(p);
-  }
-
-  void t3(void) {
-    size_t size;
-    scanf("%zu", &size);
-    if (1024<size)
-      return;
-    int *p = malloc(size); // No warning expected as the the user input is bound
-    free(p);
-  }
-
-.. _unix-MismatchedDeallocator:
-
 .. _unix-MallocSizeof:
 
 unix.MallocSizeof (C)
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 520286b57c9fd..a79032c655c10 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -36,6 +36,8 @@ def CoreAlpha : Package<"core">, ParentPackage<Alpha>;
 // Note: OptIn is *not* intended for checkers that are too noisy to be on by
 // default. Such checkers belong in the alpha package.
 def OptIn : Package<"optin">;
+def TaintOptIn : Package<"taint">, ParentPackage<OptIn>;
+
 def CoreOptIn : Package<"core">, ParentPackage<OptIn>;
 
 // In the Portability package reside checkers for finding code that relies on
@@ -452,6 +454,7 @@ def EnumCastOutOfRangeChecker : Checker<"EnumCastOutOfRange">,
 
 } // end "optin.core"
 
+
 //===----------------------------------------------------------------------===//
 // Unix API checkers.
 //===----------------------------------------------------------------------===//
@@ -625,6 +628,16 @@ def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
 
 } // end "alpha.unix"
 
+let ParentPackage = TaintOptIn in {
+
+def TaintMallocChecker: Checker<"TaintMalloc">,
+  HelpText<"Check for memory allocations, where the size parameter "
+           "might be a tainted (attacker controlled) value.">,
+  Dependencies<[DynamicMemoryModeling]>,
+  Documentation<HasDocumentation>;
+
+} // end "optin.taint"
+
 //===----------------------------------------------------------------------===//
 // C++ checkers.
 //===----------------------------------------------------------------------===//
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 2cc9205c07814..71304f1868a26 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -323,6 +323,7 @@ class MallocChecker
     CK_NewDeleteLeaksChecker,
     CK_MismatchedDeallocatorChecker,
     CK_InnerPointerChecker,
+    CK_TaintMallocChecker,
     CK_NumCheckKinds
   };
 
@@ -366,7 +367,7 @@ class MallocChecker
   mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
   mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
   mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
-  mutable std::unique_ptr<BugType> BT_TaintedAlloc[CK_NumCheckKinds];
+  mutable std::unique_ptr<BugType> BT_TaintedAlloc;
 
 #define CHECK_FN(NAME)                                                         \
   void NAME(const CallEvent &Call, CheckerContext &C) const;
@@ -1803,22 +1804,22 @@ void MallocChecker::reportTaintBug(StringRef Msg, ProgramStateRef State,
                                    llvm::ArrayRef<SymbolRef> TaintedSyms,
                                    AllocationFamily Family,
                                    const Expr *SizeEx) const {
-  if (ExplodedNode *N = C.generateErrorNode(State)) {
 
-    std::optional<MallocChecker::CheckKind> CheckKind =
-        getCheckIfTracked(Family);
-    if (!CheckKind)
-      return;
-    if (!BT_TaintedAlloc[*CheckKind])
-      BT_TaintedAlloc[*CheckKind].reset(new BugType(CheckNames[*CheckKind],
-                                                    "Tainted Memory Allocation",
-                                                    categories::MemoryError));
-    auto R = std::make_unique<PathSensitiveBugReport>(
-        *BT_TaintedAlloc[*CheckKind], Msg, N);
+  if (!ChecksEnabled[CK_TaintMallocChecker])
+    return;
+
+  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+    if (!BT_TaintedAlloc)
+      BT_TaintedAlloc.reset(new BugType(CheckNames[CK_TaintMallocChecker],
+                                        "Tainted Memory Allocation",
+                                        categories::TaintedData));
+    auto R = std::make_unique<PathSensitiveBugReport>(*BT_TaintedAlloc, Msg, N);
 
+    R->addRange(SizeEx->getSourceRange());
     bugreporter::trackExpressionValue(N, SizeEx, *R);
-    for (auto Sym : TaintedSyms)
-      R->markInteresting(Sym);
+    for (auto TaintedSym : TaintedSyms) {
+      R->markInteresting(TaintedSym);
+    }
     C.emitReport(std::move(R));
   }
 }
@@ -1827,36 +1828,37 @@ void MallocChecker::CheckTaintedness(CheckerContext &C, const CallEvent &Call,
                                      const SVal SizeSVal, ProgramStateRef State,
                                      AllocationFamily Family) const {
   std::vector<SymbolRef> TaintedSyms =
-      clang::ento::taint::getTaintedSymbols(State, SizeSVal);
-  if (!TaintedSyms.empty()) {
-    SValBuilder &SVB = C.getSValBuilder();
-    QualType SizeTy = SVB.getContext().getSizeType();
-    QualType CmpTy = SVB.getConditionType();
-    // In case the symbol is tainted, we give a warning if the
-    // size is larger than SIZE_MAX/4
-    BasicValueFactory &BVF = SVB.getBasicValueFactory();
-    const llvm::APSInt MaxValInt = BVF.getMaxValue(SizeTy);
-    NonLoc MaxLength =
-        SVB.makeIntVal(MaxValInt / APSIntType(MaxValInt).getValue(4));
-    std::optional<NonLoc> SizeNL = SizeSVal.getAs<NonLoc>();
-    auto Cmp = SVB.evalBinOpNN(State, BO_GE, *SizeNL, MaxLength, CmpTy)
-                   .getAs<DefinedOrUnknownSVal>();
-    if (!Cmp)
-      return;
-    auto [StateTooLarge, StateNotTooLarge] = State->assume(*Cmp);
-    if (!StateTooLarge && StateNotTooLarge) {
-      // we can prove that size is not too large so ok.
-      return;
-    }
+      taint::getTaintedSymbols(State, SizeSVal);
+  if (TaintedSyms.empty())
+    return;
 
-    std::string Callee = "Memory allocation function";
-    if (Call.getCalleeIdentifier())
-      Callee = Call.getCalleeIdentifier()->getName().str();
-    reportTaintBug(
-        Callee + " is called with a tainted (potentially attacker controlled) "
-                 "value. Make sure the value is bound checked.",
-        State, C, TaintedSyms, Family, Call.getArgExpr(0));
+  SValBuilder &SVB = C.getSValBuilder();
+  QualType SizeTy = SVB.getContext().getSizeType();
+  QualType CmpTy = SVB.getConditionType();
+  // In case the symbol is tainted, we give a warning if the
+  // size is larger than SIZE_MAX/4
+  BasicValueFactory &BVF = SVB.getBasicValueFactory();
+  const llvm::APSInt MaxValInt = BVF.getMaxValue(SizeTy);
+  NonLoc MaxLength =
+      SVB.makeIntVal(MaxValInt / APSIntType(MaxValInt).getValue(4));
+  std::optional<NonLoc> SizeNL = SizeSVal.getAs<NonLoc>();
+  auto Cmp = SVB.evalBinOpNN(State, BO_GE, *SizeNL, MaxLength, CmpTy)
+                 .getAs<DefinedOrUnknownSVal>();
+  if (!Cmp)
+    return;
+  auto [StateTooLarge, StateNotTooLarge] = State->assume(*Cmp);
+  if (!StateTooLarge && StateNotTooLarge) {
+    // we can prove that size is not too large so ok.
+    return;
   }
+
+  std::string Callee = "Memory allocation function";
+  if (Call.getCalleeIdentifier())
+    Callee = Call.getCalleeIdentifier()->getName().str();
+  reportTaintBug(
+      Callee + " is called with a tainted (potentially attacker controlled) "
+               "value. Make sure the value is bound checked.",
+      State, C, TaintedSyms, Family, Call.getArgExpr(0));
 }
 
 ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
@@ -3793,3 +3795,4 @@ REGISTER_CHECKER(MallocChecker)
 REGISTER_CHECKER(NewDeleteChecker)
 REGISTER_CHECKER(NewDeleteLeaksChecker)
 REGISTER_CHECKER(MismatchedDeallocatorChecker)
+REGISTER_CHECKER(TaintMallocChecker)
diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c
index 6dba76a57d83f..70b7f89a8d6d3 100644
--- a/clang/test/Analysis/malloc.c
+++ b/clang/test/Analysis/malloc.c
@@ -4,7 +4,8 @@
 // RUN:   -analyzer-checker=alpha.core.CastSize \
 // RUN:   -analyzer-checker=unix \
 // RUN:   -analyzer-checker=debug.ExprInspection \
-// RUN:   -analyzer-checker=alpha.security.taint.TaintPropagation
+// RUN:   -analyzer-checker=alpha.security.taint.TaintPropagation \
+// RUN:   -analyzer-checker=optin.taint.TaintMalloc
 
 #include "Inputs/system-header-simulator.h"
 
@@ -50,41 +51,41 @@ void myfooint(int p);
 char *fooRetPtr(void);
 
 void t1(void) {
-  size_t size;
+  size_t size = 0;
   scanf("%zu", &size);
   int *p = malloc(size); // expected-warning{{malloc is called with a tainted (potentially attacker controlled) value}}
   free(p);
 }
 
 void t2(void) {
-  size_t size;
+  size_t size = 0;
   scanf("%zu", &size);
   int *p = calloc(size,2); // expected-warning{{calloc is called with a tainted (potentially attacker controlled) value}}
   free(p);
 }
 
 void t3(void) {
-  size_t size;
+  size_t size = 0;
   scanf("%zu", &size);
-  if (1024<size)
+  if (1024 < size)
     return;
   int *p = malloc(size); // No warning expected as the the user input is bound
   free(p);
 }
 
 void t4(void) {
-  size_t size;
-  int *p = malloc(sizeof(int)); 
-  scanf("%zu", &size);  
-  p = (int*) realloc((void*) p, size); // // expected-warning{{realloc is called with a tainted (potentially attacker controlled) value}}
+  size_t size = 0;
+  int *p = malloc(sizeof(int));
+  scanf("%zu", &size);
+  p = (int*) realloc((void*) p, size); // expected-warning{{realloc is called with a tainted (potentially attacker controlled) value}}
   free(p);
 }
 
 void t5(void) {
-  size_t size;
-  int *p = alloca(sizeof(int)); 
-  scanf("%zu", &size);  
-  p = (int*) alloca(size); // // expected-warning{{alloca is called with a tainted (potentially attacker controlled) value}}  
+  size_t size = 0;
+  int *p = alloca(sizeof(int));
+  scanf("%zu", &size);
+  p = (int*) alloca(size); // expected-warning{{alloca is called with a tainted (potentially attacker controlled) value}}
 }
 
 
diff --git a/clang/test/Analysis/taint-diagnostic-visitor.c b/clang/test/Analysis/taint-diagnostic-visitor.c
index b8b3710a7013e..dcec6c5236a5c 100644
--- a/clang/test/Analysis/taint-diagnostic-visitor.c
+++ b/clang/test/Analysis/taint-diagnostic-visitor.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.security.taint,core,alpha.security.ArrayBoundV2 -analyzer-output=text -verify %s
+// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.security.taint,core,alpha.security.ArrayBoundV2,optin.taint.TaintMalloc -analyzer-output=text -verify %s
 
 // This file is for testing enhanced diagnostics produced by the GenericTaintChecker
 
@@ -115,3 +115,13 @@ void multipleTaintedArgs(void) {
   system(buf); // expected-warning {{Untrusted data is passed to a system call}}
                // expected-note at -1{{Untrusted data is passed to a system call}}
 }
+
+void testTaintedMalloc(){
+  size_t size = 0;
+  scanf("%zu", &size); // expected-note {{Value assigned to 'size'}}
+                       // expected-note at -1 {{Taint originated here}}
+                       // expected-note at -2 {{Taint propagated to the 2nd argument}}
+  int *p = malloc(size);// expected-warning{{malloc is called with a tainted (potentially attacker controlled) value}}
+                    // expected-note at -1{{malloc is called with a tainted (potentially attacker controlled) value}}
+  free(p);
+}

>From f6f910202a7075626c9d625ea59c6e4e10932f8e Mon Sep 17 00:00:00 2001
From: Daniel Krupp <daniel.krupp at ericsson.com>
Date: Mon, 27 May 2024 16:54:29 +0200
Subject: [PATCH 3/3] minor review fixes

---
 clang/docs/analyzer/checkers.rst              | 79 +++++++++----------
 .../clang/StaticAnalyzer/Checkers/Checkers.td | 30 ++++---
 2 files changed, 57 insertions(+), 52 deletions(-)

diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 827ba38f020fe..fb1974f6d90d7 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -601,46 +601,6 @@ optin
 
 Checkers for portability, performance, optional security and coding style specific rules.
 
-.. _optin-taint-TaintMalloc:
-
-optin.taint.TaintMalloc
-"""""""""""""""""""""""
-
-This checker warns for cases when the ``size`` parameter of the ``malloc`` ,
-``calloc``, ``realloc``, ``alloca`` is tainted (potentially attacker controlled).
-If an attacker can inject a large value as the size parameter, memory exhaustion
-denial of service attack can be carried out.
-
-The ``alpha.security.taint.TaintPropagation`` checker also needs to be enabled for
-this checker to give warnings.
-
-The analyzer emits warning only if it cannot prove that the size parameter is
-within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
-covers the SEI Cert coding standard rule `INT04-C
-<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.
-
-You can silence this warning either by bound checking the ``size`` parameter, or
-by explicitly marking the ``size`` parameter as sanitized. See the
-:ref:`alpha-security-taint-TaintPropagation` checker for more details.
-
-.. code-block:: c
-
-  void t1(void) {
-    size_t size;
-    scanf("%zu", &size);
-    int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
-    free(p);
-  }
-
-  void t3(void) {
-    size_t size = 0;
-    scanf("%zu", &size);
-    if (1024 < size)
-      return;
-    int *p = malloc(size); // No warning expected as the the user input is bound
-    free(p);
-  }
-
 .. _optin-core-EnumCastOutOfRange:
 
 optin.core.EnumCastOutOfRange (C, C++)
@@ -978,6 +938,45 @@ optin.portability.UnixAPI
 """""""""""""""""""""""""
 Finds implementation-defined behavior in UNIX/Posix functions.
 
+.. _optin-taint-TaintMalloc:
+
+optin.taint.TaintMalloc
+"""""""""""""""""""""""
+
+This checker warns for cases when the ``size`` parameter of the ``malloc`` ,
+``calloc``, ``realloc``, ``alloca`` is tainted (potentially attacker controlled).
+If an attacker can inject a large value as the size parameter, memory exhaustion
+denial of service attack can be carried out.
+
+The ``alpha.security.taint.TaintPropagation`` checker also needs to be enabled for
+this checker to give warnings.
+
+The analyzer emits warning only if it cannot prove that the size parameter is
+within reasonable bounds (``<= SIZE_MAX/4``). This functionality partially
+covers the SEI Cert coding standard rule `INT04-C
+<https://wiki.sei.cmu.edu/confluence/display/c/INT04-C.+Enforce+limits+on+integer+values+originating+from+tainted+sources>`_.
+
+You can silence this warning either by bound checking the ``size`` parameter, or
+by explicitly marking the ``size`` parameter as sanitized. See the
+:ref:`alpha-security-taint-TaintPropagation` checker for more details.
+
+.. code-block:: c
+
+  void t1(void) {
+    size_t size;
+    scanf("%zu", &size);
+    int *p = malloc(size); // warn: malloc is called with a tainted (potentially attacker controlled) value
+    free(p);
+  }
+
+  void t3(void) {
+    size_t size = 0;
+    scanf("%zu", &size);
+    if (1024 < size)
+      return;
+    int *p = malloc(size); // No warning expected as the the user input is bound
+    free(p);
+  }
 
 .. _security-checkers:
 
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index a79032c655c10..35824a89ccb67 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -36,7 +36,6 @@ def CoreAlpha : Package<"core">, ParentPackage<Alpha>;
 // Note: OptIn is *not* intended for checkers that are too noisy to be on by
 // default. Such checkers belong in the alpha package.
 def OptIn : Package<"optin">;
-def TaintOptIn : Package<"taint">, ParentPackage<OptIn>;
 
 def CoreOptIn : Package<"core">, ParentPackage<OptIn>;
 
@@ -45,6 +44,9 @@ def CoreOptIn : Package<"core">, ParentPackage<OptIn>;
 // development, but unwanted for developers who target only a single platform.
 def PortabilityOptIn : Package<"portability">, ParentPackage<OptIn>;
 
+// Optional checkers related to taint security analysis.
+def TaintOptIn : Package<"taint">, ParentPackage<OptIn>;
+
 def Nullability : Package<"nullability">,
   PackageOptions<[
     CmdLineOption<Boolean,
@@ -454,7 +456,6 @@ def EnumCastOutOfRangeChecker : Checker<"EnumCastOutOfRange">,
 
 } // end "optin.core"
 
-
 //===----------------------------------------------------------------------===//
 // Unix API checkers.
 //===----------------------------------------------------------------------===//
@@ -628,16 +629,6 @@ def BlockInCriticalSectionChecker : Checker<"BlockInCriticalSection">,
 
 } // end "alpha.unix"
 
-let ParentPackage = TaintOptIn in {
-
-def TaintMallocChecker: Checker<"TaintMalloc">,
-  HelpText<"Check for memory allocations, where the size parameter "
-           "might be a tainted (attacker controlled) value.">,
-  Dependencies<[DynamicMemoryModeling]>,
-  Documentation<HasDocumentation>;
-
-} // end "optin.taint"
-
 //===----------------------------------------------------------------------===//
 // C++ checkers.
 //===----------------------------------------------------------------------===//
@@ -1730,6 +1721,21 @@ def UnixAPIPortabilityChecker : Checker<"UnixAPI">,
 
 } // end optin.portability
 
+
+//===----------------------------------------------------------------------===//
+// Taint checkers.
+//===----------------------------------------------------------------------===//
+
+let ParentPackage = TaintOptIn in {
+
+def TaintMallocChecker: Checker<"TaintMalloc">,
+  HelpText<"Check for memory allocations, where the size parameter "
+           "might be a tainted (attacker controlled) value.">,
+  Dependencies<[DynamicMemoryModeling]>,
+  Documentation<HasDocumentation>;
+
+} // end "optin.taint"
+
 //===----------------------------------------------------------------------===//
 // NonDeterminism checkers.
 //===----------------------------------------------------------------------===//



More information about the cfe-commits mailing list