Skip to content

Conversation

NagyDonat
Copy link
Contributor

Previously the analyzer had an undocumented top-level checker group called valist which offered several checkers to detect use of uninitialized va_list objects and leaks of va_lists.

As the responsibilities of these checkers were messily intertwined and va_list is a rarely used language feature, this commit simplifies the situation by consolidating these checkers into a single checker which will be called security.VAList.

Note that I'm choosing the capitalization VAList to be consistent with the example of the AST node type VAArgExpr. I updated many variable names to ensure that ValistChecker.cpp uses this spelling everywhere (in CamelCased names). I'm planning to rename ValistChecker.cpp to VAListChecker.cpp in a follow-up commit.

This commit also adds documentation for this checker in checkers.rst.

Among the test files I preserved the existing separation but I eliminated some duplicated cases now that there is no way to separately enable the old sub-checkers.

For the background of this change see also the discourse thread https://discourse.llvm.org/t/clean-up-valist-checkers/85277/3

Previously the analyzer had an undocumented top-level checker group
called `valist` which offered several checkers to detect use of
uninitialized `va_list` objects and leaks of `va_list`s.

As the responsibilities of these checkers were messily intertwined and
`va_list` is a rarely used language feature, this commit simplifies the
situation by consolidating these checkers into a single checker which
will be called `security.VAList`.

Note that I'm choosing the capitalization `VAList` to be consistent
with the example of the AST node type `VAArgExpr`. I updated many
variable names to ensure that `ValistChecker.cpp` uses this spelling
everywhere (in CamelCased names). I'm planning to rename
`ValistChecker.cpp` to `VAListChecker.cpp` in a follow-up commit.

This commit also adds documentation for this checker in checkers.rst.

Among the test files I preserved the existing separation but I
eliminated some duplicated cases now that there is no way to separately
enable the old sub-checkers.

For the background of this change see also the discourse thread
https://discourse.llvm.org/t/clean-up-valist-checkers/85277/3
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Sep 3, 2025
@llvmbot
Copy link
Member

llvmbot commented Sep 3, 2025

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Donát Nagy (NagyDonat)

Changes

Previously the analyzer had an undocumented top-level checker group called valist which offered several checkers to detect use of uninitialized va_list objects and leaks of va_lists.

As the responsibilities of these checkers were messily intertwined and va_list is a rarely used language feature, this commit simplifies the situation by consolidating these checkers into a single checker which will be called security.VAList.

Note that I'm choosing the capitalization VAList to be consistent with the example of the AST node type VAArgExpr. I updated many variable names to ensure that ValistChecker.cpp uses this spelling everywhere (in CamelCased names). I'm planning to rename ValistChecker.cpp to VAListChecker.cpp in a follow-up commit.

This commit also adds documentation for this checker in checkers.rst.

Among the test files I preserved the existing separation but I eliminated some duplicated cases now that there is no way to separately enable the old sub-checkers.

For the background of this change see also the discourse thread https://discourse.llvm.org/t/clean-up-valist-checkers/85277/3


Patch is 24.57 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/156682.diff

7 Files Affected:

  • (modified) clang/docs/analyzer/checkers.rst (+26)
  • (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4-31)
  • (modified) clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp (+59-109)
  • (modified) clang/test/Analysis/valist-as-lazycompound.c (+1-1)
  • (modified) clang/test/Analysis/valist-uninitialized-no-undef.c (+1-7)
  • (modified) clang/test/Analysis/valist-uninitialized.c (+3-38)
  • (modified) clang/test/Analysis/valist-unterminated.c (+2-8)
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index b2effadacf9f1..ab2bbfaa43738 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -1859,6 +1859,32 @@ this) and always check the return value of these calls.
 
 This check corresponds to SEI CERT Rule `POS36-C <https://wiki.sei.cmu.edu/confluence/display/c/POS36-C.+Observe+correct+revocation+order+while+relinquishing+privileges>`_.
 
+.. _security-VAList:
+
+security.VAList (C, C++)
+""""""""""""""""""""""""
+Reports use of uninitialized (or already released) ``va_list`` objects and
+situations where a ``va_start`` call is not followed by ``va_end``.
+
+Report out of bounds access to memory that is before the start or after the end
+of the accessed region (array, heap-allocated region, string literal etc.).
+This usually means incorrect indexing, but the checker also detects access via
+the operators ``*`` and ``->``.
+
+.. code-block:: c
+
+ int test_use_after_release(int x, ...) {
+   va_list va;
+   va_start(va, x);
+   va_end(va, x);
+   return va_arg(va, int); // warn: va is uninitialized
+ }
+
+ int test_leak(int x, ...) {
+   va_list va;
+   va_start(va, x);
+ } // warn: va is never released
+
 .. _unix-checkers:
 
 unix
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 73f702de581d9..e7468772b7885 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -64,8 +64,6 @@ def Cplusplus : Package<"cplusplus">;
 def CplusplusAlpha : Package<"cplusplus">, ParentPackage<Alpha>;
 def CplusplusOptIn : Package<"cplusplus">, ParentPackage<OptIn>;
 
-def Valist : Package<"valist">;
-
 def DeadCode : Package<"deadcode">;
 def DeadCodeAlpha : Package<"deadcode">, ParentPackage<Alpha>;
 
@@ -803,35 +801,6 @@ def SmartPtrChecker: Checker<"SmartPtr">,
 
 } // end: "alpha.cplusplus"
 
-
-//===----------------------------------------------------------------------===//
-// Valist checkers.
-//===----------------------------------------------------------------------===//
-
-let ParentPackage = Valist in {
-
-def ValistBase : Checker<"ValistBase">,
-  HelpText<"Gathers information about va_lists.">,
-  Documentation<NotDocumented>,
-  Hidden;
-
-def UninitializedChecker : Checker<"Uninitialized">,
-  HelpText<"Check for usages of uninitialized (or already released) va_lists.">,
-  Dependencies<[ValistBase]>,
-  Documentation<NotDocumented>;
-
-def UnterminatedChecker : Checker<"Unterminated">,
-  HelpText<"Check for va_lists which are not released by a va_end call.">,
-  Dependencies<[ValistBase]>,
-  Documentation<NotDocumented>;
-
-def CopyToSelfChecker : Checker<"CopyToSelf">,
-  HelpText<"Check for va_lists which are copied onto itself.">,
-  Dependencies<[ValistBase]>,
-  Documentation<NotDocumented>;
-
-} // end : "valist"
-
 //===----------------------------------------------------------------------===//
 // Deadcode checkers.
 //===----------------------------------------------------------------------===//
@@ -1006,6 +975,10 @@ let ParentPackage = Security in {
                  "'setuid(getuid())' (CERT: POS36-C)">,
         Documentation<HasDocumentation>;
 
+  def VAListChecker : Checker<"VAList">,
+                      HelpText<"Warn on misuse of va_list objects">,
+                      Documentation<HasDocumentation>;
+
 } // end "security"
 
 let ParentPackage = ENV in {
diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index bd2f88c7b1bcc..2355abdaa060c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -27,28 +27,21 @@ REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *)
 namespace {
 typedef SmallVector<const MemRegion *, 2> RegionVector;
 
-class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
+class VAListChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
                                      check::DeadSymbols> {
-  mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess;
+  const BugType LeakBug{this, "Leaked va_list", categories::MemoryError,
+                        /*SuppressOnSink=*/true};
+  const BugType UninitAccessBug{this, "Uninitialized va_list",
+                                categories::MemoryError};
 
   struct VAListAccepter {
     CallDescription Func;
-    int VAListPos;
+    int ParamIndex;
   };
   static const SmallVector<VAListAccepter, 15> VAListAccepters;
   static const CallDescription VaStart, VaEnd, VaCopy;
 
 public:
-  enum CheckKind {
-    CK_Uninitialized,
-    CK_Unterminated,
-    CK_CopyToSelf,
-    CK_NumCheckKinds
-  };
-
-  bool ChecksEnabled[CK_NumCheckKinds] = {false};
-  CheckerNameRef CheckNames[CK_NumCheckKinds];
-
   void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const;
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
@@ -61,17 +54,16 @@ class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
 
   void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg,
                                  CheckerContext &C) const;
-  void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1,
-                           StringRef Msg2, CheckerContext &C, ExplodedNode *N,
-                           bool ReportUninit = false) const;
+  void reportLeaked(const RegionVector &Leaked, StringRef Msg1, StringRef Msg2,
+                    CheckerContext &C, ExplodedNode *N) const;
 
   void checkVAListStartCall(const CallEvent &Call, CheckerContext &C,
                             bool IsCopy) const;
   void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const;
 
-  class ValistBugVisitor : public BugReporterVisitor {
+  class VAListBugVisitor : public BugReporterVisitor {
   public:
-    ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false)
+    VAListBugVisitor(const MemRegion *Reg, bool IsLeak = false)
         : Reg(Reg), IsLeak(IsLeak) {}
     void Profile(llvm::FoldingSetNodeID &ID) const override {
       static int X = 0;
@@ -99,8 +91,8 @@ class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
   };
 };
 
-const SmallVector<ValistChecker::VAListAccepter, 15>
-    ValistChecker::VAListAccepters = {{{CDM::CLibrary, {"vfprintf"}, 3}, 2},
+const SmallVector<VAListChecker::VAListAccepter, 15>
+    VAListChecker::VAListAccepters = {{{CDM::CLibrary, {"vfprintf"}, 3}, 2},
                                       {{CDM::CLibrary, {"vfscanf"}, 3}, 2},
                                       {{CDM::CLibrary, {"vprintf"}, 2}, 1},
                                       {{CDM::CLibrary, {"vscanf"}, 2}, 1},
@@ -116,14 +108,14 @@ const SmallVector<ValistChecker::VAListAccepter, 15>
                                       // vsnprintf, vsprintf has no wide version
                                       {{CDM::CLibrary, {"vswscanf"}, 3}, 2}};
 
-const CallDescription ValistChecker::VaStart(CDM::CLibrary,
+const CallDescription VAListChecker::VaStart(CDM::CLibrary,
                                              {"__builtin_va_start"}, /*Args=*/2,
                                              /*Params=*/1),
-    ValistChecker::VaCopy(CDM::CLibrary, {"__builtin_va_copy"}, 2),
-    ValistChecker::VaEnd(CDM::CLibrary, {"__builtin_va_end"}, 1);
+    VAListChecker::VaCopy(CDM::CLibrary, {"__builtin_va_copy"}, 2),
+    VAListChecker::VaEnd(CDM::CLibrary, {"__builtin_va_end"}, 1);
 } // end anonymous namespace
 
-void ValistChecker::checkPreCall(const CallEvent &Call,
+void VAListChecker::checkPreCall(const CallEvent &Call,
                                  CheckerContext &C) const {
   if (VaStart.matches(Call))
     checkVAListStartCall(Call, C, false);
@@ -137,8 +129,8 @@ void ValistChecker::checkPreCall(const CallEvent &Call,
         continue;
       bool Symbolic;
       const MemRegion *VAList =
-          getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
-                            Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
+          getVAListAsRegion(Call.getArgSVal(FuncInfo.ParamIndex),
+                            Call.getArgExpr(FuncInfo.ParamIndex), Symbolic, C);
       if (!VAList)
         return;
 
@@ -159,17 +151,17 @@ void ValistChecker::checkPreCall(const CallEvent &Call,
   }
 }
 
-const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
+const MemRegion *VAListChecker::getVAListAsRegion(SVal SV, const Expr *E,
                                                   bool &IsSymbolic,
                                                   CheckerContext &C) const {
   const MemRegion *Reg = SV.getAsRegion();
   if (!Reg)
     return nullptr;
   // TODO: In the future this should be abstracted away by the analyzer.
-  bool VaListModelledAsArray = false;
+  bool VAListModelledAsArray = false;
   if (const auto *Cast = dyn_cast<CastExpr>(E)) {
     QualType Ty = Cast->getType();
-    VaListModelledAsArray =
+    VAListModelledAsArray =
         Ty->isPointerType() && Ty->getPointeeType()->isRecordType();
   }
   if (const auto *DeclReg = Reg->getAs<DeclRegion>()) {
@@ -179,17 +171,16 @@ const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
   IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>();
   // Some VarRegion based VA lists reach here as ElementRegions.
   const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
-  return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
+  return (EReg && VAListModelledAsArray) ? EReg->getSuperRegion() : Reg;
 }
 
-void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
+void VAListChecker::checkPreStmt(const VAArgExpr *VAA,
                                  CheckerContext &C) const {
   ProgramStateRef State = C.getState();
-  const Expr *VASubExpr = VAA->getSubExpr();
-  SVal VAListSVal = C.getSVal(VASubExpr);
+  const Expr *ArgExpr = VAA->getSubExpr();
+  SVal VAListSVal = C.getSVal(ArgExpr);
   bool Symbolic;
-  const MemRegion *VAList =
-      getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
+  const MemRegion *VAList = getVAListAsRegion(VAListSVal, ArgExpr, Symbolic, C);
   if (!VAList)
     return;
   if (Symbolic)
@@ -199,20 +190,19 @@ void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
         VAList, "va_arg() is called on an uninitialized va_list", C);
 }
 
-void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
+void VAListChecker::checkDeadSymbols(SymbolReaper &SR,
                                      CheckerContext &C) const {
   ProgramStateRef State = C.getState();
-  InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
-  RegionVector LeakedVALists;
-  for (auto Reg : TrackedVALists) {
+  InitializedVAListsTy Tracked = State->get<InitializedVALists>();
+  RegionVector Leaked;
+  for (const MemRegion *Reg : Tracked) {
     if (SR.isLiveRegion(Reg))
       continue;
-    LeakedVALists.push_back(Reg);
+    Leaked.push_back(Reg);
     State = State->remove<InitializedVALists>(Reg);
   }
   if (ExplodedNode *N = C.addTransition(State))
-    reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C,
-                        N);
+    reportLeaked(Leaked, "Initialized va_list", " is leaked", C, N);
 }
 
 // This function traverses the exploded graph backwards and finds the node where
@@ -220,7 +210,7 @@ void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
 // It is not likely that there are several different va_lists that belongs to
 // different stack frames, so that case is not yet handled.
 const ExplodedNode *
-ValistChecker::getStartCallSite(const ExplodedNode *N,
+VAListChecker::getStartCallSite(const ExplodedNode *N,
                                 const MemRegion *Reg) const {
   const LocationContext *LeakContext = N->getLocationContext();
   const ExplodedNode *StartCallNode = N;
@@ -244,42 +234,21 @@ ValistChecker::getStartCallSite(const ExplodedNode *N,
   return StartCallNode;
 }
 
-void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
+void VAListChecker::reportUninitializedAccess(const MemRegion *VAList,
                                               StringRef Msg,
                                               CheckerContext &C) const {
-  if (!ChecksEnabled[CK_Uninitialized])
-    return;
   if (ExplodedNode *N = C.generateErrorNode()) {
-    if (!BT_uninitaccess)
-      BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
-                                        "Uninitialized va_list",
-                                        categories::MemoryError));
-    auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N);
+    auto R = std::make_unique<PathSensitiveBugReport>(UninitAccessBug, Msg, N);
     R->markInteresting(VAList);
-    R->addVisitor(std::make_unique<ValistBugVisitor>(VAList));
+    R->addVisitor(std::make_unique<VAListBugVisitor>(VAList));
     C.emitReport(std::move(R));
   }
 }
 
-void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
-                                        StringRef Msg1, StringRef Msg2,
-                                        CheckerContext &C, ExplodedNode *N,
-                                        bool ReportUninit) const {
-  if (!(ChecksEnabled[CK_Unterminated] ||
-        (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
-    return;
-  for (auto Reg : LeakedVALists) {
-    if (!BT_leakedvalist) {
-      // FIXME: maybe creating a new check name for this type of bug is a better
-      // solution.
-      BT_leakedvalist.reset(new BugType(
-          static_cast<StringRef>(CheckNames[CK_Unterminated]).empty()
-              ? CheckNames[CK_Uninitialized]
-              : CheckNames[CK_Unterminated],
-          "Leaked va_list", categories::MemoryError,
-          /*SuppressOnSink=*/true));
-    }
-
+void VAListChecker::reportLeaked(const RegionVector &Leaked, StringRef Msg1,
+                                 StringRef Msg2, CheckerContext &C,
+                                 ExplodedNode *N) const {
+  for (const MemRegion *Reg : Leaked) {
     const ExplodedNode *StartNode = getStartCallSite(N, Reg);
     PathDiagnosticLocation LocUsedForUniqueing;
 
@@ -296,15 +265,15 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
     OS << Msg2;
 
     auto R = std::make_unique<PathSensitiveBugReport>(
-        *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
+        LeakBug, OS.str(), N, LocUsedForUniqueing,
         StartNode->getLocationContext()->getDecl());
     R->markInteresting(Reg);
-    R->addVisitor(std::make_unique<ValistBugVisitor>(Reg, true));
+    R->addVisitor(std::make_unique<VAListBugVisitor>(Reg, true));
     C.emitReport(std::move(R));
   }
 }
 
-void ValistChecker::checkVAListStartCall(const CallEvent &Call,
+void VAListChecker::checkVAListStartCall(const CallEvent &Call,
                                          CheckerContext &C, bool IsCopy) const {
   bool Symbolic;
   const MemRegion *VAList =
@@ -318,20 +287,19 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call,
     const MemRegion *Arg2 =
         getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C);
     if (Arg2) {
-      if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
-        RegionVector LeakedVALists{VAList};
+      if (VAList == Arg2) {
+        RegionVector Leaked{VAList};
         if (ExplodedNode *N = C.addTransition(State))
-          reportLeakedVALists(LeakedVALists, "va_list",
-                              " is copied onto itself", C, N, true);
+          reportLeaked(Leaked, "va_list", " is copied onto itself", C, N);
         return;
-      } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
+      }
+      if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
         if (State->contains<InitializedVALists>(VAList)) {
           State = State->remove<InitializedVALists>(VAList);
-          RegionVector LeakedVALists{VAList};
+          RegionVector Leaked{VAList};
           if (ExplodedNode *N = C.addTransition(State))
-            reportLeakedVALists(LeakedVALists, "Initialized va_list",
-                                " is overwritten by an uninitialized one", C, N,
-                                true);
+            reportLeaked(Leaked, "Initialized va_list",
+                         " is overwritten by an uninitialized one", C, N);
         } else {
           reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C);
         }
@@ -340,10 +308,10 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call,
     }
   }
   if (State->contains<InitializedVALists>(VAList)) {
-    RegionVector LeakedVALists{VAList};
+    RegionVector Leaked{VAList};
     if (ExplodedNode *N = C.addTransition(State))
-      reportLeakedVALists(LeakedVALists, "Initialized va_list",
-                          " is initialized again", C, N);
+      reportLeaked(Leaked, "Initialized va_list", " is initialized again", C,
+                   N);
     return;
   }
 
@@ -351,7 +319,7 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call,
   C.addTransition(State);
 }
 
-void ValistChecker::checkVAListEndCall(const CallEvent &Call,
+void VAListChecker::checkVAListEndCall(const CallEvent &Call,
                                        CheckerContext &C) const {
   bool Symbolic;
   const MemRegion *VAList =
@@ -374,7 +342,7 @@ void ValistChecker::checkVAListEndCall(const CallEvent &Call,
   C.addTransition(State);
 }
 
-PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
+PathDiagnosticPieceRef VAListChecker::VAListBugVisitor::VisitNode(
     const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
   ProgramStateRef State = N->getState();
   ProgramStateRef StatePrev = N->getFirstPred()->getState();
@@ -399,26 +367,8 @@ PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
   return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
 }
 
-void ento::registerValistBase(CheckerManager &mgr) {
-  mgr.registerChecker<ValistChecker>();
-}
-
-bool ento::shouldRegisterValistBase(const CheckerManager &mgr) {
-  return true;
+void ento::registerVAListChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<VAListChecker>();
 }
 
-#define REGISTER_CHECKER(name)                                                 \
-  void ento::register##name##Checker(CheckerManager &mgr) {                    \
-    ValistChecker *checker = mgr.getChecker<ValistChecker>();                  \
-    checker->ChecksEnabled[ValistChecker::CK_##name] = true;                   \
-    checker->CheckNames[ValistChecker::CK_##name] =                            \
-        mgr.getCurrentCheckerName();                                           \
-  }                                                                            \
-                                                                               \
-  bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) {            \
-    return true;                                                               \
-  }
-
-REGISTER_CHECKER(Uninitialized)
-REGISTER_CHECKER(Unterminated)
-REGISTER_CHECKER(CopyToSelf)
+bool ento::shouldRegisterVAListChecker(const CheckerManager &) { return true; }
diff --git a/clang/test/Analysis/valist-as-lazycompound.c b/clang/test/Analysis/valist-as-lazycompound.c
index 3218e03a9c657..35a137321a67f 100644
--- a/clang/test/Analysis/valist-as-lazycompound.c
+++ b/clang/test/Analysis/valist-as-lazycompound.c
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -triple gcc-linaro-arm-linux-gnueabihf -analyzer-checker=core,valist.Uninitialized,valist.CopyToSelf -analyzer-output=text -verify %s
+// RUN: %clang_analyze_cc1 -triple gcc-linaro-arm-linux-gnueabihf -analyzer-checker=core,security.VAList -analyzer-output=text -verify %s
 // expected-no-diagnostics
 
 typedef unsigned int size_t;
diff --git a/clang/test/Analysis/valist-uninitialized-no-undef.c b/clang/test/Analysis/valist-uninitialized-no-undef.c
index 48631429be646..b8add29fc4803 100644
--- a/clang/test/Analysis/valist-uninitialized-no-undef.c
+++ b/clang/test/Analysis/valist-uninitialized-no-undef.c
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -triple x86_64...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Sep 3, 2025

@llvm/pr-subscribers-clang

Author: Donát Nagy (NagyDonat)

Changes

Previously the analyzer had an undocumented top-level checker group called valist which offered several checkers to detect use of uninitialized va_list objects and leaks of va_lists.

As the responsibilities of these checkers were messily intertwined and va_list is a rarely used language feature, this commit simplifies the situation by consolidating these checkers into a single checker which will be called security.VAList.

Note that I'm choosing the capitalization VAList to be consistent with the example of the AST node type VAArgExpr. I updated many variable names to ensure that ValistChecker.cpp uses this spelling everywhere (in CamelCased names). I'm planning to rename ValistChecker.cpp to VAListChecker.cpp in a follow-up commit.

This commit also adds documentation for this checker in checkers.rst.

Among the test files I preserved the existing separation but I eliminated some duplicated cases now that there is no way to separately enable the old sub-checkers.

For the background of this change see also the discourse thread https://discourse.llvm.org/t/clean-up-valist-checkers/85277/3


Patch is 24.57 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/156682.diff

7 Files Affected:

  • (modified) clang/docs/analyzer/checkers.rst (+26)
  • (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4-31)
  • (modified) clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp (+59-109)
  • (modified) clang/test/Analysis/valist-as-lazycompound.c (+1-1)
  • (modified) clang/test/Analysis/valist-uninitialized-no-undef.c (+1-7)
  • (modified) clang/test/Analysis/valist-uninitialized.c (+3-38)
  • (modified) clang/test/Analysis/valist-unterminated.c (+2-8)
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index b2effadacf9f1..ab2bbfaa43738 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -1859,6 +1859,32 @@ this) and always check the return value of these calls.
 
 This check corresponds to SEI CERT Rule `POS36-C <https://wiki.sei.cmu.edu/confluence/display/c/POS36-C.+Observe+correct+revocation+order+while+relinquishing+privileges>`_.
 
+.. _security-VAList:
+
+security.VAList (C, C++)
+""""""""""""""""""""""""
+Reports use of uninitialized (or already released) ``va_list`` objects and
+situations where a ``va_start`` call is not followed by ``va_end``.
+
+Report out of bounds access to memory that is before the start or after the end
+of the accessed region (array, heap-allocated region, string literal etc.).
+This usually means incorrect indexing, but the checker also detects access via
+the operators ``*`` and ``->``.
+
+.. code-block:: c
+
+ int test_use_after_release(int x, ...) {
+   va_list va;
+   va_start(va, x);
+   va_end(va, x);
+   return va_arg(va, int); // warn: va is uninitialized
+ }
+
+ int test_leak(int x, ...) {
+   va_list va;
+   va_start(va, x);
+ } // warn: va is never released
+
 .. _unix-checkers:
 
 unix
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 73f702de581d9..e7468772b7885 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -64,8 +64,6 @@ def Cplusplus : Package<"cplusplus">;
 def CplusplusAlpha : Package<"cplusplus">, ParentPackage<Alpha>;
 def CplusplusOptIn : Package<"cplusplus">, ParentPackage<OptIn>;
 
-def Valist : Package<"valist">;
-
 def DeadCode : Package<"deadcode">;
 def DeadCodeAlpha : Package<"deadcode">, ParentPackage<Alpha>;
 
@@ -803,35 +801,6 @@ def SmartPtrChecker: Checker<"SmartPtr">,
 
 } // end: "alpha.cplusplus"
 
-
-//===----------------------------------------------------------------------===//
-// Valist checkers.
-//===----------------------------------------------------------------------===//
-
-let ParentPackage = Valist in {
-
-def ValistBase : Checker<"ValistBase">,
-  HelpText<"Gathers information about va_lists.">,
-  Documentation<NotDocumented>,
-  Hidden;
-
-def UninitializedChecker : Checker<"Uninitialized">,
-  HelpText<"Check for usages of uninitialized (or already released) va_lists.">,
-  Dependencies<[ValistBase]>,
-  Documentation<NotDocumented>;
-
-def UnterminatedChecker : Checker<"Unterminated">,
-  HelpText<"Check for va_lists which are not released by a va_end call.">,
-  Dependencies<[ValistBase]>,
-  Documentation<NotDocumented>;
-
-def CopyToSelfChecker : Checker<"CopyToSelf">,
-  HelpText<"Check for va_lists which are copied onto itself.">,
-  Dependencies<[ValistBase]>,
-  Documentation<NotDocumented>;
-
-} // end : "valist"
-
 //===----------------------------------------------------------------------===//
 // Deadcode checkers.
 //===----------------------------------------------------------------------===//
@@ -1006,6 +975,10 @@ let ParentPackage = Security in {
                  "'setuid(getuid())' (CERT: POS36-C)">,
         Documentation<HasDocumentation>;
 
+  def VAListChecker : Checker<"VAList">,
+                      HelpText<"Warn on misuse of va_list objects">,
+                      Documentation<HasDocumentation>;
+
 } // end "security"
 
 let ParentPackage = ENV in {
diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index bd2f88c7b1bcc..2355abdaa060c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -27,28 +27,21 @@ REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *)
 namespace {
 typedef SmallVector<const MemRegion *, 2> RegionVector;
 
-class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
+class VAListChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
                                      check::DeadSymbols> {
-  mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess;
+  const BugType LeakBug{this, "Leaked va_list", categories::MemoryError,
+                        /*SuppressOnSink=*/true};
+  const BugType UninitAccessBug{this, "Uninitialized va_list",
+                                categories::MemoryError};
 
   struct VAListAccepter {
     CallDescription Func;
-    int VAListPos;
+    int ParamIndex;
   };
   static const SmallVector<VAListAccepter, 15> VAListAccepters;
   static const CallDescription VaStart, VaEnd, VaCopy;
 
 public:
-  enum CheckKind {
-    CK_Uninitialized,
-    CK_Unterminated,
-    CK_CopyToSelf,
-    CK_NumCheckKinds
-  };
-
-  bool ChecksEnabled[CK_NumCheckKinds] = {false};
-  CheckerNameRef CheckNames[CK_NumCheckKinds];
-
   void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const;
   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
@@ -61,17 +54,16 @@ class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
 
   void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg,
                                  CheckerContext &C) const;
-  void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1,
-                           StringRef Msg2, CheckerContext &C, ExplodedNode *N,
-                           bool ReportUninit = false) const;
+  void reportLeaked(const RegionVector &Leaked, StringRef Msg1, StringRef Msg2,
+                    CheckerContext &C, ExplodedNode *N) const;
 
   void checkVAListStartCall(const CallEvent &Call, CheckerContext &C,
                             bool IsCopy) const;
   void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const;
 
-  class ValistBugVisitor : public BugReporterVisitor {
+  class VAListBugVisitor : public BugReporterVisitor {
   public:
-    ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false)
+    VAListBugVisitor(const MemRegion *Reg, bool IsLeak = false)
         : Reg(Reg), IsLeak(IsLeak) {}
     void Profile(llvm::FoldingSetNodeID &ID) const override {
       static int X = 0;
@@ -99,8 +91,8 @@ class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
   };
 };
 
-const SmallVector<ValistChecker::VAListAccepter, 15>
-    ValistChecker::VAListAccepters = {{{CDM::CLibrary, {"vfprintf"}, 3}, 2},
+const SmallVector<VAListChecker::VAListAccepter, 15>
+    VAListChecker::VAListAccepters = {{{CDM::CLibrary, {"vfprintf"}, 3}, 2},
                                       {{CDM::CLibrary, {"vfscanf"}, 3}, 2},
                                       {{CDM::CLibrary, {"vprintf"}, 2}, 1},
                                       {{CDM::CLibrary, {"vscanf"}, 2}, 1},
@@ -116,14 +108,14 @@ const SmallVector<ValistChecker::VAListAccepter, 15>
                                       // vsnprintf, vsprintf has no wide version
                                       {{CDM::CLibrary, {"vswscanf"}, 3}, 2}};
 
-const CallDescription ValistChecker::VaStart(CDM::CLibrary,
+const CallDescription VAListChecker::VaStart(CDM::CLibrary,
                                              {"__builtin_va_start"}, /*Args=*/2,
                                              /*Params=*/1),
-    ValistChecker::VaCopy(CDM::CLibrary, {"__builtin_va_copy"}, 2),
-    ValistChecker::VaEnd(CDM::CLibrary, {"__builtin_va_end"}, 1);
+    VAListChecker::VaCopy(CDM::CLibrary, {"__builtin_va_copy"}, 2),
+    VAListChecker::VaEnd(CDM::CLibrary, {"__builtin_va_end"}, 1);
 } // end anonymous namespace
 
-void ValistChecker::checkPreCall(const CallEvent &Call,
+void VAListChecker::checkPreCall(const CallEvent &Call,
                                  CheckerContext &C) const {
   if (VaStart.matches(Call))
     checkVAListStartCall(Call, C, false);
@@ -137,8 +129,8 @@ void ValistChecker::checkPreCall(const CallEvent &Call,
         continue;
       bool Symbolic;
       const MemRegion *VAList =
-          getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
-                            Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
+          getVAListAsRegion(Call.getArgSVal(FuncInfo.ParamIndex),
+                            Call.getArgExpr(FuncInfo.ParamIndex), Symbolic, C);
       if (!VAList)
         return;
 
@@ -159,17 +151,17 @@ void ValistChecker::checkPreCall(const CallEvent &Call,
   }
 }
 
-const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
+const MemRegion *VAListChecker::getVAListAsRegion(SVal SV, const Expr *E,
                                                   bool &IsSymbolic,
                                                   CheckerContext &C) const {
   const MemRegion *Reg = SV.getAsRegion();
   if (!Reg)
     return nullptr;
   // TODO: In the future this should be abstracted away by the analyzer.
-  bool VaListModelledAsArray = false;
+  bool VAListModelledAsArray = false;
   if (const auto *Cast = dyn_cast<CastExpr>(E)) {
     QualType Ty = Cast->getType();
-    VaListModelledAsArray =
+    VAListModelledAsArray =
         Ty->isPointerType() && Ty->getPointeeType()->isRecordType();
   }
   if (const auto *DeclReg = Reg->getAs<DeclRegion>()) {
@@ -179,17 +171,16 @@ const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
   IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>();
   // Some VarRegion based VA lists reach here as ElementRegions.
   const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
-  return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
+  return (EReg && VAListModelledAsArray) ? EReg->getSuperRegion() : Reg;
 }
 
-void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
+void VAListChecker::checkPreStmt(const VAArgExpr *VAA,
                                  CheckerContext &C) const {
   ProgramStateRef State = C.getState();
-  const Expr *VASubExpr = VAA->getSubExpr();
-  SVal VAListSVal = C.getSVal(VASubExpr);
+  const Expr *ArgExpr = VAA->getSubExpr();
+  SVal VAListSVal = C.getSVal(ArgExpr);
   bool Symbolic;
-  const MemRegion *VAList =
-      getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
+  const MemRegion *VAList = getVAListAsRegion(VAListSVal, ArgExpr, Symbolic, C);
   if (!VAList)
     return;
   if (Symbolic)
@@ -199,20 +190,19 @@ void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
         VAList, "va_arg() is called on an uninitialized va_list", C);
 }
 
-void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
+void VAListChecker::checkDeadSymbols(SymbolReaper &SR,
                                      CheckerContext &C) const {
   ProgramStateRef State = C.getState();
-  InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
-  RegionVector LeakedVALists;
-  for (auto Reg : TrackedVALists) {
+  InitializedVAListsTy Tracked = State->get<InitializedVALists>();
+  RegionVector Leaked;
+  for (const MemRegion *Reg : Tracked) {
     if (SR.isLiveRegion(Reg))
       continue;
-    LeakedVALists.push_back(Reg);
+    Leaked.push_back(Reg);
     State = State->remove<InitializedVALists>(Reg);
   }
   if (ExplodedNode *N = C.addTransition(State))
-    reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C,
-                        N);
+    reportLeaked(Leaked, "Initialized va_list", " is leaked", C, N);
 }
 
 // This function traverses the exploded graph backwards and finds the node where
@@ -220,7 +210,7 @@ void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
 // It is not likely that there are several different va_lists that belongs to
 // different stack frames, so that case is not yet handled.
 const ExplodedNode *
-ValistChecker::getStartCallSite(const ExplodedNode *N,
+VAListChecker::getStartCallSite(const ExplodedNode *N,
                                 const MemRegion *Reg) const {
   const LocationContext *LeakContext = N->getLocationContext();
   const ExplodedNode *StartCallNode = N;
@@ -244,42 +234,21 @@ ValistChecker::getStartCallSite(const ExplodedNode *N,
   return StartCallNode;
 }
 
-void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
+void VAListChecker::reportUninitializedAccess(const MemRegion *VAList,
                                               StringRef Msg,
                                               CheckerContext &C) const {
-  if (!ChecksEnabled[CK_Uninitialized])
-    return;
   if (ExplodedNode *N = C.generateErrorNode()) {
-    if (!BT_uninitaccess)
-      BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
-                                        "Uninitialized va_list",
-                                        categories::MemoryError));
-    auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N);
+    auto R = std::make_unique<PathSensitiveBugReport>(UninitAccessBug, Msg, N);
     R->markInteresting(VAList);
-    R->addVisitor(std::make_unique<ValistBugVisitor>(VAList));
+    R->addVisitor(std::make_unique<VAListBugVisitor>(VAList));
     C.emitReport(std::move(R));
   }
 }
 
-void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
-                                        StringRef Msg1, StringRef Msg2,
-                                        CheckerContext &C, ExplodedNode *N,
-                                        bool ReportUninit) const {
-  if (!(ChecksEnabled[CK_Unterminated] ||
-        (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
-    return;
-  for (auto Reg : LeakedVALists) {
-    if (!BT_leakedvalist) {
-      // FIXME: maybe creating a new check name for this type of bug is a better
-      // solution.
-      BT_leakedvalist.reset(new BugType(
-          static_cast<StringRef>(CheckNames[CK_Unterminated]).empty()
-              ? CheckNames[CK_Uninitialized]
-              : CheckNames[CK_Unterminated],
-          "Leaked va_list", categories::MemoryError,
-          /*SuppressOnSink=*/true));
-    }
-
+void VAListChecker::reportLeaked(const RegionVector &Leaked, StringRef Msg1,
+                                 StringRef Msg2, CheckerContext &C,
+                                 ExplodedNode *N) const {
+  for (const MemRegion *Reg : Leaked) {
     const ExplodedNode *StartNode = getStartCallSite(N, Reg);
     PathDiagnosticLocation LocUsedForUniqueing;
 
@@ -296,15 +265,15 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
     OS << Msg2;
 
     auto R = std::make_unique<PathSensitiveBugReport>(
-        *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
+        LeakBug, OS.str(), N, LocUsedForUniqueing,
         StartNode->getLocationContext()->getDecl());
     R->markInteresting(Reg);
-    R->addVisitor(std::make_unique<ValistBugVisitor>(Reg, true));
+    R->addVisitor(std::make_unique<VAListBugVisitor>(Reg, true));
     C.emitReport(std::move(R));
   }
 }
 
-void ValistChecker::checkVAListStartCall(const CallEvent &Call,
+void VAListChecker::checkVAListStartCall(const CallEvent &Call,
                                          CheckerContext &C, bool IsCopy) const {
   bool Symbolic;
   const MemRegion *VAList =
@@ -318,20 +287,19 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call,
     const MemRegion *Arg2 =
         getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C);
     if (Arg2) {
-      if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
-        RegionVector LeakedVALists{VAList};
+      if (VAList == Arg2) {
+        RegionVector Leaked{VAList};
         if (ExplodedNode *N = C.addTransition(State))
-          reportLeakedVALists(LeakedVALists, "va_list",
-                              " is copied onto itself", C, N, true);
+          reportLeaked(Leaked, "va_list", " is copied onto itself", C, N);
         return;
-      } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
+      }
+      if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
         if (State->contains<InitializedVALists>(VAList)) {
           State = State->remove<InitializedVALists>(VAList);
-          RegionVector LeakedVALists{VAList};
+          RegionVector Leaked{VAList};
           if (ExplodedNode *N = C.addTransition(State))
-            reportLeakedVALists(LeakedVALists, "Initialized va_list",
-                                " is overwritten by an uninitialized one", C, N,
-                                true);
+            reportLeaked(Leaked, "Initialized va_list",
+                         " is overwritten by an uninitialized one", C, N);
         } else {
           reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C);
         }
@@ -340,10 +308,10 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call,
     }
   }
   if (State->contains<InitializedVALists>(VAList)) {
-    RegionVector LeakedVALists{VAList};
+    RegionVector Leaked{VAList};
     if (ExplodedNode *N = C.addTransition(State))
-      reportLeakedVALists(LeakedVALists, "Initialized va_list",
-                          " is initialized again", C, N);
+      reportLeaked(Leaked, "Initialized va_list", " is initialized again", C,
+                   N);
     return;
   }
 
@@ -351,7 +319,7 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call,
   C.addTransition(State);
 }
 
-void ValistChecker::checkVAListEndCall(const CallEvent &Call,
+void VAListChecker::checkVAListEndCall(const CallEvent &Call,
                                        CheckerContext &C) const {
   bool Symbolic;
   const MemRegion *VAList =
@@ -374,7 +342,7 @@ void ValistChecker::checkVAListEndCall(const CallEvent &Call,
   C.addTransition(State);
 }
 
-PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
+PathDiagnosticPieceRef VAListChecker::VAListBugVisitor::VisitNode(
     const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
   ProgramStateRef State = N->getState();
   ProgramStateRef StatePrev = N->getFirstPred()->getState();
@@ -399,26 +367,8 @@ PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode(
   return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
 }
 
-void ento::registerValistBase(CheckerManager &mgr) {
-  mgr.registerChecker<ValistChecker>();
-}
-
-bool ento::shouldRegisterValistBase(const CheckerManager &mgr) {
-  return true;
+void ento::registerVAListChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<VAListChecker>();
 }
 
-#define REGISTER_CHECKER(name)                                                 \
-  void ento::register##name##Checker(CheckerManager &mgr) {                    \
-    ValistChecker *checker = mgr.getChecker<ValistChecker>();                  \
-    checker->ChecksEnabled[ValistChecker::CK_##name] = true;                   \
-    checker->CheckNames[ValistChecker::CK_##name] =                            \
-        mgr.getCurrentCheckerName();                                           \
-  }                                                                            \
-                                                                               \
-  bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) {            \
-    return true;                                                               \
-  }
-
-REGISTER_CHECKER(Uninitialized)
-REGISTER_CHECKER(Unterminated)
-REGISTER_CHECKER(CopyToSelf)
+bool ento::shouldRegisterVAListChecker(const CheckerManager &) { return true; }
diff --git a/clang/test/Analysis/valist-as-lazycompound.c b/clang/test/Analysis/valist-as-lazycompound.c
index 3218e03a9c657..35a137321a67f 100644
--- a/clang/test/Analysis/valist-as-lazycompound.c
+++ b/clang/test/Analysis/valist-as-lazycompound.c
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -triple gcc-linaro-arm-linux-gnueabihf -analyzer-checker=core,valist.Uninitialized,valist.CopyToSelf -analyzer-output=text -verify %s
+// RUN: %clang_analyze_cc1 -triple gcc-linaro-arm-linux-gnueabihf -analyzer-checker=core,security.VAList -analyzer-output=text -verify %s
 // expected-no-diagnostics
 
 typedef unsigned int size_t;
diff --git a/clang/test/Analysis/valist-uninitialized-no-undef.c b/clang/test/Analysis/valist-uninitialized-no-undef.c
index 48631429be646..b8add29fc4803 100644
--- a/clang/test/Analysis/valist-uninitialized-no-undef.c
+++ b/clang/test/Analysis/valist-uninitialized-no-undef.c
@@ -1,4 +1,4 @@
-// RUN: %clang_analyze_cc1 -triple x86_64...
[truncated]

Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I skipped the tests though.
Let a couple of remarks along the way. Nothing major.

int test_use_after_release(int x, ...) {
va_list va;
va_start(va, x);
va_end(va, x);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
va_end(va, x);
va_end(va);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, thanks for noticing!

int test_leak(int x, ...) {
va_list va;
va_start(va, x);
} // warn: va is never released
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual diag phrases this as Initialized va_list 'va' is leaked, so uses the phrase leaked instead of released.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, updated to "va is leaked".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants