[clang-tools-extra] Add bugprone-undefined-sprintf-overlap (PR #114244)
Chris Cotter via cfe-commits
cfe-commits at lists.llvm.org
Mon Jan 6 06:43:06 PST 2025
https://github.com/ccotter updated https://github.com/llvm/llvm-project/pull/114244
>From fd914cc82688b122654d2d7ada72007541b197c0 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Wed, 30 Oct 2024 10:54:49 -0400
Subject: [PATCH 01/19] Add bugprone-sprintf-overlap
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt | 1 +
.../bugprone/UndefinedSprintfOverlapCheck.cpp | 93 +++++++++++++++++++
.../bugprone/UndefinedSprintfOverlapCheck.h | 36 +++++++
clang-tools-extra/docs/ReleaseNotes.rst | 6 ++
.../bugprone/undefined-sprintf-overlap.rst | 23 +++++
.../docs/clang-tidy/checks/list.rst | 3 +-
.../bugprone/undefined-sprintf-overlap.cpp | 59 ++++++++++++
8 files changed, 223 insertions(+), 1 deletion(-)
create mode 100644 clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 33ac65e715ce81..ac3a08c80d7ae6 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -86,6 +86,7 @@
#include "TooSmallLoopVariableCheck.h"
#include "UncheckedOptionalAccessCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
+#include "UndefinedSprintfOverlapCheck.h"
#include "UndelegatedConstructorCheck.h"
#include "UnhandledExceptionAtNewCheck.h"
#include "UnhandledSelfAssignmentCheck.h"
@@ -248,6 +249,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-unchecked-optional-access");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
+ CheckFactories.registerCheck<UndefinedSprintfOverlapCheck>(
+ "bugprone-undefined-sprintf-overlap");
CheckFactories.registerCheck<UndelegatedConstructorCheck>(
"bugprone-undelegated-constructor");
CheckFactories.registerCheck<UnhandledSelfAssignmentCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index b0a2318acc0597..2403ed665fd5c7 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -81,6 +81,7 @@ add_clang_library(clangTidyBugproneModule STATIC
TooSmallLoopVariableCheck.cpp
UncheckedOptionalAccessCheck.cpp
UndefinedMemoryManipulationCheck.cpp
+ UndefinedSprintfOverlapCheck.cpp
UndelegatedConstructorCheck.cpp
UnhandledExceptionAtNewCheck.cpp
UnhandledSelfAssignmentCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
new file mode 100644
index 00000000000000..301b6bce0dbbad
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -0,0 +1,93 @@
+//===--- UndefinedSprintfOverlapCheck.cpp - clang-tidy --------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UndefinedSprintfOverlapCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+AST_MATCHER_P(CallExpr, hasAnyOtherArgument,
+ ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
+ for (const auto *Arg : llvm::drop_begin(Node.arguments())) {
+ ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
+ if (InnerMatcher.matches(*Arg, Finder, &Result)) {
+ *Builder = std::move(Result);
+ return true;
+ }
+ }
+ return false;
+}
+
+AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
+ return Builder->removeBindings(
+ [this, &Node](const ast_matchers::internal::BoundNodesMap &Nodes) {
+ const auto &BN = Nodes.getNode(ID);
+ if (const auto *Lit = BN.get<IntegerLiteral>()) {
+ return Lit->getValue() != Node.getValue();
+ }
+ return true;
+ });
+}
+
+UndefinedSprintfOverlapCheck::UndefinedSprintfOverlapCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ SprintfRegex(Options.get("SprintfFunction", "(::std)?::(sn?printf)")) {}
+
+void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
+ auto FirstArg = declRefExpr(to(varDecl().bind("firstArg")));
+ auto OtherRefToArg = declRefExpr(to(varDecl(equalsBoundNode("firstArg"))))
+ .bind("overlappingArg");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
+ allOf(hasArgument(
+ 0, anyOf(FirstArg.bind("firstArgExpr"),
+ arraySubscriptExpr(
+ hasBase(FirstArg),
+ hasIndex(integerLiteral().bind("index")))
+ .bind("firstArgExpr"),
+ memberExpr(member(decl().bind("member")),
+ hasObjectExpression(FirstArg))
+ .bind("firstArgExpr"))),
+ hasAnyOtherArgument(anyOf(
+ OtherRefToArg,
+ arraySubscriptExpr(
+ hasBase(OtherRefToArg),
+ hasIndex(integerLiteral(hasSameValueAs("index")))),
+ memberExpr(member(decl(equalsBoundNode("member"))),
+ hasObjectExpression(OtherRefToArg)))))),
+ this);
+}
+
+void UndefinedSprintfOverlapCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *OverlappingArg =
+ Result.Nodes.getNodeAs<DeclRefExpr>("overlappingArg");
+ const auto *FirstArg = Result.Nodes.getNodeAs<Expr>("firstArgExpr");
+ const auto *FnDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+
+ llvm::StringRef FirstArgText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(
+ FirstArg->getBeginLoc(), FirstArg->getEndLoc()),
+ *Result.SourceManager, getLangOpts());
+
+ diag(OverlappingArg->getLocation(),
+ "argument '%0' overlaps the first argument "
+ "in %1, which is undefined behavior")
+ << FirstArgText << FnDecl;
+}
+
+void UndefinedSprintfOverlapCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "SprintfRegex", SprintfRegex);
+}
+
+} // namespace clang::tidy::bugprone
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
new file mode 100644
index 00000000000000..349b1156671ff0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
@@ -0,0 +1,36 @@
+//===--- UndefinedSprintfOverlapCheck.h - clang-tidy ------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINEDSPRINTFOVERLAPCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINEDSPRINTFOVERLAPCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// FIXME: Write a short description.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/undefined-sprintf-overlap.html
+class UndefinedSprintfOverlapCheck : public ClangTidyCheck {
+public:
+ UndefinedSprintfOverlapCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const std::string SprintfRegex;
+};
+
+} // namespace clang::tidy::bugprone
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINEDSPRINTFOVERLAPCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index ccebf74e8a67e7..7285343971189f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -131,6 +131,12 @@ New checks
Gives warnings for tagged unions, where the number of tags is
different from the number of data members inside the union.
+- New :doc:`bugprone-undefined-sprintf-overlap
+ <clang-tidy/checks/bugprone/undefined-sprintf-overlap>` check.
+
+ Finds calls to sprintf family of functions whose first argument
+ overlaps with a subsequent argument.
+
- New :doc:`portability-template-virtual-member-function
<clang-tidy/checks/portability/template-virtual-member-function>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
new file mode 100644
index 00000000000000..775d99c9f1e0bb
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - bugprone-undefined-sprintf-overlap
+
+bugprone-undefined-sprintf-overlap
+==================================
+
+Warns if any arguments to the sprintf family of functions overlap with the
+first argument.
+
+.. code-block:: c++
+
+ char buf[20] = {"hi"};
+ sprintf(buf, "%s%d", buf, 0);
+
+C99 and POSIX.1-2001 states that if copying were to take place between objects
+that overlap, the result is undefined.
+
+Options
+-------
+
+.. option:: SprintfRegex
+
+ A regex specifying the sprintf family of functions to match on. By default,
+ this is "(::std)?::sn?printf".
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index d731b13fc0df44..41b7d92f94f90c 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -115,8 +115,8 @@ Clang-Tidy Checks
:doc:`bugprone-multiple-new-in-one-expression <bugprone/multiple-new-in-one-expression>`,
:doc:`bugprone-multiple-statement-macro <bugprone/multiple-statement-macro>`,
:doc:`bugprone-no-escape <bugprone/no-escape>`,
- :doc:`bugprone-nondeterministic-pointer-iteration-order <bugprone/nondeterministic-pointer-iteration-order>`,
:doc:`bugprone-non-zero-enum-to-bool-conversion <bugprone/non-zero-enum-to-bool-conversion>`,
+ :doc:`bugprone-nondeterministic-pointer-iteration-order <bugprone/nondeterministic-pointer-iteration-order>`,
:doc:`bugprone-not-null-terminated-result <bugprone/not-null-terminated-result>`, "Yes"
:doc:`bugprone-optional-value-conversion <bugprone/optional-value-conversion>`, "Yes"
:doc:`bugprone-parent-virtual-call <bugprone/parent-virtual-call>`, "Yes"
@@ -153,6 +153,7 @@ Clang-Tidy Checks
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
:doc:`bugprone-unchecked-optional-access <bugprone/unchecked-optional-access>`,
:doc:`bugprone-undefined-memory-manipulation <bugprone/undefined-memory-manipulation>`,
+ :doc:`bugprone-undefined-sprintf-overlap <bugprone/undefined-sprintf-overlap>`, "Yes"
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
new file mode 100644
index 00000000000000..50095346da8a8d
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
@@ -0,0 +1,59 @@
+// RUN: %check_clang_tidy %s bugprone-undefined-sprintf-overlap %t
+
+using size_t = decltype(sizeof(int));
+
+extern "C" int sprintf(char *s, const char *format, ...);
+extern "C" int snprintf(char *s, size_t n, const char *format, ...);
+
+namespace std {
+ int snprintf(char *s, size_t n, const char *format, ...);
+}
+
+struct st_t {
+ char buf[10];
+ char buf2[10];
+};
+
+void first_arg_overlaps() {
+ char buf[10];
+ sprintf(buf, "%s", buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: argument 'buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ snprintf(buf, sizeof(buf), "%s", buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: argument 'buf' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ std::snprintf(buf, sizeof(buf), "%s", buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: argument 'buf' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+
+ char* c = &buf[0];
+ sprintf(c, "%s", c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: argument 'c' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ snprintf(c, sizeof(buf), "%s", c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+
+ snprintf(c, sizeof(buf), "%s%s", c, c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+
+ char buf2[10];
+ snprintf(buf, sizeof(buf), "%s", buf2);
+
+ st_t st1, st2;
+ sprintf(st1.buf, "%s", st1.buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: argument 'st1.buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ sprintf(st1.buf, "%s", st1.buf2);
+ sprintf(st1.buf, "%s", st2.buf);
+
+ st_t* stp;
+ sprintf(stp->buf, "%s", stp->buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: argument 'stp->buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ sprintf((stp->buf), "%s", stp->buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: argument 'stp->buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ stp = &st1;
+ sprintf(stp->buf, "%s", st1.buf);
+
+ char bufs[10][10];
+ sprintf(bufs[1], "%s", bufs[1]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: argument 'bufs[1]' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ sprintf(bufs[0], "%s", bufs[1]);
+
+ char bufss[10][10][10];
+ sprintf(bufss[0][1], "%s", bufss[0][1]);
+}
>From 004719c3f6efbb5e36016ac49ce8983ff7d2b144 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Thu, 31 Oct 2024 11:13:16 -0400
Subject: [PATCH 02/19] Apply suggestions from code review
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 7285343971189f..e746c8fcf675f2 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -134,7 +134,7 @@ New checks
- New :doc:`bugprone-undefined-sprintf-overlap
<clang-tidy/checks/bugprone/undefined-sprintf-overlap>` check.
- Finds calls to sprintf family of functions whose first argument
+ Finds calls to ``sprintf`` family of functions whose first argument
overlaps with a subsequent argument.
- New :doc:`portability-template-virtual-member-function
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
index 775d99c9f1e0bb..3f6640cb7eec89 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
@@ -19,5 +19,5 @@ Options
.. option:: SprintfRegex
- A regex specifying the sprintf family of functions to match on. By default,
- this is "(::std)?::sn?printf".
+ A regex specifying the ``sprintf`` family of functions to match on. By default,
+ this is `(::std)?::sn?printf`.
>From d69c69ad588af74fb1575169713ee726f09638f5 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Thu, 31 Oct 2024 11:43:07 -0400
Subject: [PATCH 03/19] Review feedback
---
.../clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp | 2 +-
clang-tools-extra/docs/ReleaseNotes.rst | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index 301b6bce0dbbad..043e5d8fc2a29d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -29,7 +29,7 @@ AST_MATCHER_P(CallExpr, hasAnyOtherArgument,
AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
return Builder->removeBindings(
[this, &Node](const ast_matchers::internal::BoundNodesMap &Nodes) {
- const auto &BN = Nodes.getNode(ID);
+ const DynTypedNode &BN = Nodes.getNode(ID);
if (const auto *Lit = BN.get<IntegerLiteral>()) {
return Lit->getValue() != Node.getValue();
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index e746c8fcf675f2..98c137a29c13ac 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -134,8 +134,8 @@ New checks
- New :doc:`bugprone-undefined-sprintf-overlap
<clang-tidy/checks/bugprone/undefined-sprintf-overlap>` check.
- Finds calls to ``sprintf`` family of functions whose first argument
- overlaps with a subsequent argument.
+ Warns if any arguments to the sprintf family of functions overlap with the
+ first argument.
- New :doc:`portability-template-virtual-member-function
<clang-tidy/checks/portability/template-virtual-member-function>` check.
>From 1984c94efc9094d26a4dc4103118d11c7bc56e2f Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Thu, 31 Oct 2024 16:59:29 -0400
Subject: [PATCH 04/19] Apply suggestions from code review
Co-authored-by: EugeneZelenko <eugene.zelenko at gmail.com>
---
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 98c137a29c13ac..8caa9713fc9b37 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -134,7 +134,7 @@ New checks
- New :doc:`bugprone-undefined-sprintf-overlap
<clang-tidy/checks/bugprone/undefined-sprintf-overlap>` check.
- Warns if any arguments to the sprintf family of functions overlap with the
+ Warns if any arguments to the ``sprintf`` family of functions overlap with the
first argument.
- New :doc:`portability-template-virtual-member-function
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
index 3f6640cb7eec89..eaa10fd0ad6bd3 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
@@ -3,7 +3,7 @@
bugprone-undefined-sprintf-overlap
==================================
-Warns if any arguments to the sprintf family of functions overlap with the
+Warns if any arguments to the ``sprintf`` family of functions overlap with the
first argument.
.. code-block:: c++
>From 75103a2c1b7d6be4284571df3584f201f9e1e431 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sat, 2 Nov 2024 13:22:58 -0400
Subject: [PATCH 05/19] Remove custom matcher
---
.../bugprone/UndefinedSprintfOverlapCheck.cpp | 45 +++++++------------
1 file changed, 17 insertions(+), 28 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index 043e5d8fc2a29d..f0689fa4f3fee1 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -14,18 +14,6 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-AST_MATCHER_P(CallExpr, hasAnyOtherArgument,
- ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
- for (const auto *Arg : llvm::drop_begin(Node.arguments())) {
- ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder);
- if (InnerMatcher.matches(*Arg, Finder, &Result)) {
- *Builder = std::move(Result);
- return true;
- }
- }
- return false;
-}
-
AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
return Builder->removeBindings(
[this, &Node](const ast_matchers::internal::BoundNodesMap &Nodes) {
@@ -43,27 +31,28 @@ UndefinedSprintfOverlapCheck::UndefinedSprintfOverlapCheck(
SprintfRegex(Options.get("SprintfFunction", "(::std)?::(sn?printf)")) {}
void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
- auto FirstArg = declRefExpr(to(varDecl().bind("firstArg")));
- auto OtherRefToArg = declRefExpr(to(varDecl(equalsBoundNode("firstArg"))))
+ auto FirstArg = declRefExpr(to(varDecl().bind("firstArgDecl")));
+ auto OtherRefToArg = declRefExpr(to(varDecl(equalsBoundNode("firstArgDecl"))))
.bind("overlappingArg");
Finder->addMatcher(
- callExpr(callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
- allOf(hasArgument(
- 0, anyOf(FirstArg.bind("firstArgExpr"),
+ callExpr(
+ callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
+ allOf(hasArgument(
+ 0, expr(anyOf(FirstArg,
arraySubscriptExpr(
hasBase(FirstArg),
- hasIndex(integerLiteral().bind("index")))
- .bind("firstArgExpr"),
+ hasIndex(integerLiteral().bind("index"))),
memberExpr(member(decl().bind("member")),
- hasObjectExpression(FirstArg))
- .bind("firstArgExpr"))),
- hasAnyOtherArgument(anyOf(
- OtherRefToArg,
- arraySubscriptExpr(
- hasBase(OtherRefToArg),
- hasIndex(integerLiteral(hasSameValueAs("index")))),
- memberExpr(member(decl(equalsBoundNode("member"))),
- hasObjectExpression(OtherRefToArg)))))),
+ hasObjectExpression(FirstArg))))
+ .bind("firstArgExpr")),
+ hasAnyArgument(expr(
+ unless(equalsBoundNode("firstArgExpr")),
+ anyOf(OtherRefToArg,
+ arraySubscriptExpr(hasBase(OtherRefToArg),
+ hasIndex(integerLiteral(
+ hasSameValueAs("index")))),
+ memberExpr(member(decl(equalsBoundNode("member"))),
+ hasObjectExpression(OtherRefToArg))))))),
this);
}
>From 84ded5fc966979c55419b3bb32729259d98a6744 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sat, 4 Jan 2025 21:25:46 -0500
Subject: [PATCH 06/19] Review feedback
---
.../bugprone/UndefinedSprintfOverlapCheck.cpp | 55 +++++++++----------
.../bugprone/UndefinedSprintfOverlapCheck.h | 6 +-
.../bugprone/undefined-sprintf-overlap.rst | 2 +-
.../docs/clang-tidy/checks/list.rst | 2 +-
.../bugprone/undefined-sprintf-overlap.cpp | 4 +-
5 files changed, 35 insertions(+), 34 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index f0689fa4f3fee1..82245ebdf4e666 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -18,9 +18,8 @@ AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
return Builder->removeBindings(
[this, &Node](const ast_matchers::internal::BoundNodesMap &Nodes) {
const DynTypedNode &BN = Nodes.getNode(ID);
- if (const auto *Lit = BN.get<IntegerLiteral>()) {
+ if (const auto *Lit = BN.get<IntegerLiteral>())
return Lit->getValue() != Node.getValue();
- }
return true;
});
}
@@ -28,50 +27,46 @@ AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
UndefinedSprintfOverlapCheck::UndefinedSprintfOverlapCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
- SprintfRegex(Options.get("SprintfFunction", "(::std)?::(sn?printf)")) {}
+ SprintfRegex(Options.get("SprintfFunction", "(::std)?::sn?printf")) {}
void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
auto FirstArg = declRefExpr(to(varDecl().bind("firstArgDecl")));
- auto OtherRefToArg = declRefExpr(to(varDecl(equalsBoundNode("firstArgDecl"))))
- .bind("overlappingArg");
+ auto OtherRefToArg = declRefExpr(to(varDecl(equalsBoundNode("firstArgDecl"))));
Finder->addMatcher(
callExpr(
callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
- allOf(hasArgument(
- 0, expr(anyOf(FirstArg,
- arraySubscriptExpr(
- hasBase(FirstArg),
- hasIndex(integerLiteral().bind("index"))),
- memberExpr(member(decl().bind("member")),
- hasObjectExpression(FirstArg))))
- .bind("firstArgExpr")),
- hasAnyArgument(expr(
- unless(equalsBoundNode("firstArgExpr")),
- anyOf(OtherRefToArg,
- arraySubscriptExpr(hasBase(OtherRefToArg),
- hasIndex(integerLiteral(
- hasSameValueAs("index")))),
- memberExpr(member(decl(equalsBoundNode("member"))),
- hasObjectExpression(OtherRefToArg))))))),
+ hasArgument(
+ 0, expr(anyOf(FirstArg,
+ arraySubscriptExpr(
+ hasBase(FirstArg),
+ hasIndex(integerLiteral().bind("index"))),
+ memberExpr(member(decl().bind("member")),
+ hasObjectExpression(FirstArg))))
+ .bind("firstArgExpr")),
+ hasAnyArgument(expr(
+ unless(equalsBoundNode("firstArgExpr")),
+ anyOf(OtherRefToArg,
+ arraySubscriptExpr(hasBase(OtherRefToArg),
+ hasIndex(integerLiteral(
+ hasSameValueAs("index")))),
+ memberExpr(member(decl(equalsBoundNode("member"))),
+ hasObjectExpression(OtherRefToArg)))).bind("secondArgExpr"))),
this);
}
void UndefinedSprintfOverlapCheck::check(
const MatchFinder::MatchResult &Result) {
- const auto *OverlappingArg =
- Result.Nodes.getNodeAs<DeclRefExpr>("overlappingArg");
const auto *FirstArg = Result.Nodes.getNodeAs<Expr>("firstArgExpr");
+ const auto *SecondArg = Result.Nodes.getNodeAs<Expr>("secondArgExpr");
const auto *FnDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
- llvm::StringRef FirstArgText =
- Lexer::getSourceText(CharSourceRange::getTokenRange(
- FirstArg->getBeginLoc(), FirstArg->getEndLoc()),
+ const llvm::StringRef FirstArgText =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(FirstArg->getSourceRange()),
*Result.SourceManager, getLangOpts());
- diag(OverlappingArg->getLocation(),
- "argument '%0' overlaps the first argument "
- "in %1, which is undefined behavior")
- << FirstArgText << FnDecl;
+ diag(SecondArg->getBeginLoc(),
+ "argument '%0' overlaps the first argument in %1, which is undefined behavior")
+ << FirstArgText << FnDecl << FirstArg->getSourceRange() << SecondArg->getSourceRange();
}
void UndefinedSprintfOverlapCheck::storeOptions(
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
index 349b1156671ff0..e31f1752776df0 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
@@ -13,7 +13,8 @@
namespace clang::tidy::bugprone {
-/// FIXME: Write a short description.
+/// Warns if any arguments to the ``sprintf`` family of functions overlap with
+/// the destination buffer (the first argument).
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/undefined-sprintf-overlap.html
@@ -26,6 +27,9 @@ class UndefinedSprintfOverlapCheck : public ClangTidyCheck {
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus || LangOpts.C99;
+ }
private:
const std::string SprintfRegex;
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
index eaa10fd0ad6bd3..2a63e237833189 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
@@ -4,7 +4,7 @@ bugprone-undefined-sprintf-overlap
==================================
Warns if any arguments to the ``sprintf`` family of functions overlap with the
-first argument.
+destination buffer (the first argument).
.. code-block:: c++
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index be26c82800af65..8c169dd59de74d 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -154,7 +154,7 @@ Clang-Tidy Checks
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
:doc:`bugprone-unchecked-optional-access <bugprone/unchecked-optional-access>`,
:doc:`bugprone-undefined-memory-manipulation <bugprone/undefined-memory-manipulation>`,
- :doc:`bugprone-undefined-sprintf-overlap <bugprone/undefined-sprintf-overlap>`, "Yes"
+ :doc:`bugprone-undefined-sprintf-overlap <bugprone/undefined-sprintf-overlap>`
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
index 50095346da8a8d..cf844cc537d83d 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
@@ -33,7 +33,9 @@ void first_arg_overlaps() {
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
char buf2[10];
- snprintf(buf, sizeof(buf), "%s", buf2);
+ sprintf(buf, "%s", buf2);
+ sprintf(buf, "%s", buf2, buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: argument 'buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
st_t st1, st2;
sprintf(st1.buf, "%s", st1.buf);
>From cefd652381c6cf387bd7487bb46ff76f7b03bc34 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sat, 4 Jan 2025 21:29:43 -0500
Subject: [PATCH 07/19] Apply suggestions from code review
Co-authored-by: Nicolas van Kempen <nvankemp at gmail.com>
---
.../clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp | 2 +-
.../checks/bugprone/undefined-sprintf-overlap.rst | 7 +++++--
clang-tools-extra/docs/clang-tidy/checks/list.rst | 2 +-
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index f0689fa4f3fee1..901f18dad53de4 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -63,7 +63,7 @@ void UndefinedSprintfOverlapCheck::check(
const auto *FirstArg = Result.Nodes.getNodeAs<Expr>("firstArgExpr");
const auto *FnDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
- llvm::StringRef FirstArgText =
+ const llvm::StringRef FirstArgText =
Lexer::getSourceText(CharSourceRange::getTokenRange(
FirstArg->getBeginLoc(), FirstArg->getEndLoc()),
*Result.SourceManager, getLangOpts());
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
index eaa10fd0ad6bd3..2a9b582db21275 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
@@ -11,8 +11,11 @@ first argument.
char buf[20] = {"hi"};
sprintf(buf, "%s%d", buf, 0);
-C99 and POSIX.1-2001 states that if copying were to take place between objects
-that overlap, the result is undefined.
+If copying takes place between objects that overlap, the behavior is undefined.
+This is stated in the `C23/N3220 standard
+<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf>`_
+(sections 7.23.6.5 and 7.23.6.6), as well as the `POSIX.1-2024 standard
+<https://pubs.opengroup.org/onlinepubs/9799919799/>`_.
Options
-------
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 41b7d92f94f90c..5b871644921127 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -153,7 +153,7 @@ Clang-Tidy Checks
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
:doc:`bugprone-unchecked-optional-access <bugprone/unchecked-optional-access>`,
:doc:`bugprone-undefined-memory-manipulation <bugprone/undefined-memory-manipulation>`,
- :doc:`bugprone-undefined-sprintf-overlap <bugprone/undefined-sprintf-overlap>`, "Yes"
+ :doc:`bugprone-undefined-sprintf-overlap <bugprone/undefined-sprintf-overlap>`,
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
>From 680b94ed0871bd6e8dc54e5a14fedbfaaf07c557 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sat, 4 Jan 2025 21:32:29 -0500
Subject: [PATCH 08/19] format
---
.../bugprone/UndefinedSprintfOverlapCheck.cpp | 45 ++++++++++---------
1 file changed, 24 insertions(+), 21 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index 82245ebdf4e666..f916321421c83f 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -31,26 +31,28 @@ UndefinedSprintfOverlapCheck::UndefinedSprintfOverlapCheck(
void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
auto FirstArg = declRefExpr(to(varDecl().bind("firstArgDecl")));
- auto OtherRefToArg = declRefExpr(to(varDecl(equalsBoundNode("firstArgDecl"))));
+ auto OtherRefToArg =
+ declRefExpr(to(varDecl(equalsBoundNode("firstArgDecl"))));
Finder->addMatcher(
callExpr(
callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
- hasArgument(
- 0, expr(anyOf(FirstArg,
- arraySubscriptExpr(
- hasBase(FirstArg),
- hasIndex(integerLiteral().bind("index"))),
- memberExpr(member(decl().bind("member")),
+ hasArgument(0,
+ expr(anyOf(FirstArg,
+ arraySubscriptExpr(
+ hasBase(FirstArg),
+ hasIndex(integerLiteral().bind("index"))),
+ memberExpr(member(decl().bind("member")),
hasObjectExpression(FirstArg))))
.bind("firstArgExpr")),
- hasAnyArgument(expr(
- unless(equalsBoundNode("firstArgExpr")),
- anyOf(OtherRefToArg,
- arraySubscriptExpr(hasBase(OtherRefToArg),
- hasIndex(integerLiteral(
- hasSameValueAs("index")))),
- memberExpr(member(decl(equalsBoundNode("member"))),
- hasObjectExpression(OtherRefToArg)))).bind("secondArgExpr"))),
+ hasAnyArgument(
+ expr(unless(equalsBoundNode("firstArgExpr")),
+ anyOf(OtherRefToArg,
+ arraySubscriptExpr(
+ hasBase(OtherRefToArg),
+ hasIndex(integerLiteral(hasSameValueAs("index")))),
+ memberExpr(member(decl(equalsBoundNode("member"))),
+ hasObjectExpression(OtherRefToArg))))
+ .bind("secondArgExpr"))),
this);
}
@@ -60,13 +62,14 @@ void UndefinedSprintfOverlapCheck::check(
const auto *SecondArg = Result.Nodes.getNodeAs<Expr>("secondArgExpr");
const auto *FnDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
- const llvm::StringRef FirstArgText =
- Lexer::getSourceText(CharSourceRange::getTokenRange(FirstArg->getSourceRange()),
- *Result.SourceManager, getLangOpts());
+ const llvm::StringRef FirstArgText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(FirstArg->getSourceRange()),
+ *Result.SourceManager, getLangOpts());
- diag(SecondArg->getBeginLoc(),
- "argument '%0' overlaps the first argument in %1, which is undefined behavior")
- << FirstArgText << FnDecl << FirstArg->getSourceRange() << SecondArg->getSourceRange();
+ diag(SecondArg->getBeginLoc(), "argument '%0' overlaps the first argument in "
+ "%1, which is undefined behavior")
+ << FirstArgText << FnDecl << FirstArg->getSourceRange()
+ << SecondArg->getSourceRange();
}
void UndefinedSprintfOverlapCheck::storeOptions(
>From bb6d7f7c6a43878f144b277bf2b85cf94b3bcf12 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sat, 4 Jan 2025 21:48:40 -0500
Subject: [PATCH 09/19] Add example of incorrect behavior
---
.../checks/bugprone/undefined-sprintf-overlap.rst | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
index 68a2d415d6d56b..ef35dded3335e7 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
@@ -17,6 +17,15 @@ This is stated in the `C23/N3220 standard
(sections 7.23.6.5 and 7.23.6.6), as well as the `POSIX.1-2024 standard
<https://pubs.opengroup.org/onlinepubs/9799919799/>`_.
+In practice, passing the output buffer to an input argument can result in
+incorrect output. For example, Linux with glibc may produce the following.
+
+.. clode-block:: c++
+ char buf[10];
+ sprintf(buf, "%s", "12");
+ sprintf(buf, "%s%s", "34", buf);
+ printf("%s\n", buf); // prints 3434
+
Options
-------
>From 32f9d3fce84be0b920418f0e66259f3e71e3dea4 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 20:42:38 -0500
Subject: [PATCH 10/19] Support more expressions with areStatementsIdentical
---
.../bugprone/UndefinedSprintfOverlapCheck.cpp | 60 ++++++++++++-------
.../bugprone/undefined-sprintf-overlap.cpp | 21 +++++++
2 files changed, 59 insertions(+), 22 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index f916321421c83f..5d40b5a3a98a9e 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "UndefinedSprintfOverlapCheck.h"
+#include "../utils/ASTUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
@@ -24,35 +25,40 @@ AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
});
}
+// Similar to forEachArgumentWithParam. forEachArgumentWithParam does not work
+// with variadic functions like sprintf, since there is no `decl()` to match
+// against in the parameter list `...`.
+AST_MATCHER_P(CallExpr, forEachArgument, ast_matchers::internal::Matcher<Expr>,
+ ArgMatcher) {
+ using namespace clang::ast_matchers::internal;
+ BoundNodesTreeBuilder Result;
+ int ParamIndex = 0;
+ bool Matched = false;
+ for (unsigned ArgIndex = 0; ArgIndex < Node.getNumArgs(); ++ArgIndex) {
+ BoundNodesTreeBuilder ArgMatches(*Builder);
+ if (ArgMatcher.matches(*(Node.getArg(ArgIndex)->IgnoreParenCasts()), Finder,
+ &ArgMatches)) {
+ BoundNodesTreeBuilder ParamMatches(ArgMatches);
+ Result.addMatch(ArgMatches);
+ Matched = true;
+ }
+ ++ParamIndex;
+ }
+ *Builder = std::move(Result);
+ return Matched;
+}
+
UndefinedSprintfOverlapCheck::UndefinedSprintfOverlapCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
SprintfRegex(Options.get("SprintfFunction", "(::std)?::sn?printf")) {}
void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
- auto FirstArg = declRefExpr(to(varDecl().bind("firstArgDecl")));
- auto OtherRefToArg =
- declRefExpr(to(varDecl(equalsBoundNode("firstArgDecl"))));
Finder->addMatcher(
- callExpr(
- callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
- hasArgument(0,
- expr(anyOf(FirstArg,
- arraySubscriptExpr(
- hasBase(FirstArg),
- hasIndex(integerLiteral().bind("index"))),
- memberExpr(member(decl().bind("member")),
- hasObjectExpression(FirstArg))))
- .bind("firstArgExpr")),
- hasAnyArgument(
- expr(unless(equalsBoundNode("firstArgExpr")),
- anyOf(OtherRefToArg,
- arraySubscriptExpr(
- hasBase(OtherRefToArg),
- hasIndex(integerLiteral(hasSameValueAs("index")))),
- memberExpr(member(decl(equalsBoundNode("member"))),
- hasObjectExpression(OtherRefToArg))))
- .bind("secondArgExpr"))),
+ callExpr(callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
+ hasArgument(0, expr().bind("firstArgExpr")),
+ forEachArgument(expr(unless(equalsBoundNode("firstArgExpr")))
+ .bind("secondArgExpr"))),
this);
}
@@ -62,6 +68,16 @@ void UndefinedSprintfOverlapCheck::check(
const auto *SecondArg = Result.Nodes.getNodeAs<Expr>("secondArgExpr");
const auto *FnDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
+ clang::ASTContext &Context = *Result.Context;
+
+ if (!FirstArg || !SecondArg)
+ return;
+
+ if (!utils::areStatementsIdentical(FirstArg, SecondArg, Context))
+ return;
+ if (FirstArg->HasSideEffects(Context) || SecondArg->HasSideEffects(Context))
+ return;
+
const llvm::StringRef FirstArgText = Lexer::getSourceText(
CharSourceRange::getTokenRange(FirstArg->getSourceRange()),
*Result.SourceManager, getLangOpts());
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
index cf844cc537d83d..ae504346a26497 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
@@ -14,6 +14,14 @@ struct st_t {
char buf2[10];
};
+struct st2_t {
+ st_t inner;
+};
+
+struct st3_t {
+ st2_t inner;
+};
+
void first_arg_overlaps() {
char buf[10];
sprintf(buf, "%s", buf);
@@ -31,6 +39,7 @@ void first_arg_overlaps() {
snprintf(c, sizeof(buf), "%s%s", c, c);
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
char buf2[10];
sprintf(buf, "%s", buf2);
@@ -43,6 +52,12 @@ void first_arg_overlaps() {
sprintf(st1.buf, "%s", st1.buf2);
sprintf(st1.buf, "%s", st2.buf);
+ st3_t st3;
+ sprintf(st3.inner.inner.buf, "%s", st3.inner.inner.buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: argument 'st3.inner.inner.buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ sprintf((st3.inner.inner.buf), "%s", st3.inner.inner.buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: argument 'st3.inner.inner.buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+
st_t* stp;
sprintf(stp->buf, "%s", stp->buf);
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: argument 'stp->buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
@@ -58,4 +73,10 @@ void first_arg_overlaps() {
char bufss[10][10][10];
sprintf(bufss[0][1], "%s", bufss[0][1]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: argument 'bufss[0][1]' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+
+ sprintf(bufss[0][0], "%s", bufss[0][1]);
+
+ int i = 0;
+ sprintf(bufss[0][++i], "%s", bufss[0][++i]);
}
>From 357350efcc43ba77131c8b30aae42dc0d32fd499 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 21:45:34 -0500
Subject: [PATCH 11/19] Use argument number instead of argument text
---
.../bugprone/UndefinedSprintfOverlapCheck.cpp | 33 ++++++++++++-------
.../bugprone/undefined-sprintf-overlap.cpp | 30 ++++++++---------
2 files changed, 36 insertions(+), 27 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
index 5d40b5a3a98a9e..f7b6cd21c3396e 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
@@ -58,34 +58,43 @@ void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
callExpr(callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
hasArgument(0, expr().bind("firstArgExpr")),
forEachArgument(expr(unless(equalsBoundNode("firstArgExpr")))
- .bind("secondArgExpr"))),
+ .bind("otherArgExpr")))
+ .bind("call"),
this);
}
void UndefinedSprintfOverlapCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *FirstArg = Result.Nodes.getNodeAs<Expr>("firstArgExpr");
- const auto *SecondArg = Result.Nodes.getNodeAs<Expr>("secondArgExpr");
+ const auto *OtherArg = Result.Nodes.getNodeAs<Expr>("otherArgExpr");
+ const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
const auto *FnDecl = Result.Nodes.getNodeAs<FunctionDecl>("decl");
clang::ASTContext &Context = *Result.Context;
- if (!FirstArg || !SecondArg)
+ if (!FirstArg || !OtherArg || !Call || !FnDecl)
return;
- if (!utils::areStatementsIdentical(FirstArg, SecondArg, Context))
+ if (!utils::areStatementsIdentical(FirstArg, OtherArg, Context))
return;
- if (FirstArg->HasSideEffects(Context) || SecondArg->HasSideEffects(Context))
+ if (FirstArg->HasSideEffects(Context) || OtherArg->HasSideEffects(Context))
return;
- const llvm::StringRef FirstArgText = Lexer::getSourceText(
- CharSourceRange::getTokenRange(FirstArg->getSourceRange()),
- *Result.SourceManager, getLangOpts());
+ std::optional<unsigned> ArgNumber;
+ for (unsigned I = 0; I != Call->getNumArgs(); ++I) {
+ if (Call->getArg(I)->IgnoreUnlessSpelledInSource() == OtherArg) {
+ ArgNumber = I;
+ break;
+ }
+ }
+ if (!ArgNumber)
+ return;
- diag(SecondArg->getBeginLoc(), "argument '%0' overlaps the first argument in "
- "%1, which is undefined behavior")
- << FirstArgText << FnDecl << FirstArg->getSourceRange()
- << SecondArg->getSourceRange();
+ diag(OtherArg->getBeginLoc(),
+ "the %ordinal0 argument in %1 overlaps the 1st argument, "
+ "which is undefined behavior")
+ << *ArgNumber << FnDecl << FirstArg->getSourceRange()
+ << OtherArg->getSourceRange();
}
void UndefinedSprintfOverlapCheck::storeOptions(
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
index ae504346a26497..284adba70d4683 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
@@ -25,55 +25,55 @@ struct st3_t {
void first_arg_overlaps() {
char buf[10];
sprintf(buf, "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: argument 'buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
snprintf(buf, sizeof(buf), "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: argument 'buf' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
std::snprintf(buf, sizeof(buf), "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: argument 'buf' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
char* c = &buf[0];
sprintf(c, "%s", c);
- // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: argument 'c' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
snprintf(c, sizeof(buf), "%s", c);
- // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
snprintf(c, sizeof(buf), "%s%s", c, c);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
- // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: argument 'c' overlaps the first argument in 'snprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
char buf2[10];
sprintf(buf, "%s", buf2);
sprintf(buf, "%s", buf2, buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: argument 'buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
st_t st1, st2;
sprintf(st1.buf, "%s", st1.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: argument 'st1.buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
sprintf(st1.buf, "%s", st1.buf2);
sprintf(st1.buf, "%s", st2.buf);
st3_t st3;
sprintf(st3.inner.inner.buf, "%s", st3.inner.inner.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: argument 'st3.inner.inner.buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
sprintf((st3.inner.inner.buf), "%s", st3.inner.inner.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: argument 'st3.inner.inner.buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
st_t* stp;
sprintf(stp->buf, "%s", stp->buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: argument 'stp->buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
sprintf((stp->buf), "%s", stp->buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: argument 'stp->buf' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
stp = &st1;
sprintf(stp->buf, "%s", st1.buf);
char bufs[10][10];
sprintf(bufs[1], "%s", bufs[1]);
- // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: argument 'bufs[1]' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
sprintf(bufs[0], "%s", bufs[1]);
char bufss[10][10][10];
sprintf(bufss[0][1], "%s", bufss[0][1]);
- // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: argument 'bufss[0][1]' overlaps the first argument in 'sprintf', which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
sprintf(bufss[0][0], "%s", bufss[0][1]);
>From 292752be949bdde798b08e62fb3e55424cca1d89 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 22:11:52 -0500
Subject: [PATCH 12/19] Rename tool
---
.../bugprone/BugproneTidyModule.cpp | 6 +-
.../clang-tidy/bugprone/CMakeLists.txt | 2 +-
...ck.cpp => SprintfArgumentOverlapCheck.cpp} | 12 +--
...pCheck.h => SprintfArgumentOverlapCheck.h} | 14 ++--
clang-tools-extra/docs/ReleaseNotes.rst | 4 +-
...erlap.rst => sprintf-argument-overlap.rst} | 6 +-
.../docs/clang-tidy/checks/list.rst | 2 +-
.../bugprone/sprintf-argument-overlap.cpp | 82 +++++++++++++++++++
.../bugprone/undefined-sprintf-overlap.cpp | 3 +
9 files changed, 108 insertions(+), 23 deletions(-)
rename clang-tools-extra/clang-tidy/bugprone/{UndefinedSprintfOverlapCheck.cpp => SprintfArgumentOverlapCheck.cpp} (90%)
rename clang-tools-extra/clang-tidy/bugprone/{UndefinedSprintfOverlapCheck.h => SprintfArgumentOverlapCheck.h} (67%)
rename clang-tools-extra/docs/clang-tidy/checks/bugprone/{undefined-sprintf-overlap.rst => sprintf-argument-overlap.rst} (88%)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index a85458bf49255c..b229753e0b86a3 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -86,7 +86,7 @@
#include "TooSmallLoopVariableCheck.h"
#include "UncheckedOptionalAccessCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
-#include "UndefinedSprintfOverlapCheck.h"
+#include "SprintfArgumentOverlapCheck.h"
#include "UndelegatedConstructorCheck.h"
#include "UnhandledExceptionAtNewCheck.h"
#include "UnhandledSelfAssignmentCheck.h"
@@ -249,8 +249,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-unchecked-optional-access");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
- CheckFactories.registerCheck<UndefinedSprintfOverlapCheck>(
- "bugprone-undefined-sprintf-overlap");
+ CheckFactories.registerCheck<SprintfArgumentOverlapCheck>(
+ "bugprone-sprintf-argument-overlap");
CheckFactories.registerCheck<UndelegatedConstructorCheck>(
"bugprone-undelegated-constructor");
CheckFactories.registerCheck<UnhandledSelfAssignmentCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index 9efadc0c08751c..dd818866f56b3d 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -83,7 +83,7 @@ add_clang_library(clangTidyBugproneModule STATIC
TooSmallLoopVariableCheck.cpp
UncheckedOptionalAccessCheck.cpp
UndefinedMemoryManipulationCheck.cpp
- UndefinedSprintfOverlapCheck.cpp
+ SprintfArgumentOverlapCheck.cpp
UndelegatedConstructorCheck.cpp
UnhandledExceptionAtNewCheck.cpp
UnhandledSelfAssignmentCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
similarity index 90%
rename from clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
rename to clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
index f7b6cd21c3396e..6d86279191492a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
@@ -1,4 +1,4 @@
-//===--- UndefinedSprintfOverlapCheck.cpp - clang-tidy --------------------===//
+//===--- SprintfArgumentOverlapCheck.cpp - clang-tidy --------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "UndefinedSprintfOverlapCheck.h"
+#include "SprintfArgumentOverlapCheck.h"
#include "../utils/ASTUtils.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Lex/Lexer.h"
@@ -48,12 +48,12 @@ AST_MATCHER_P(CallExpr, forEachArgument, ast_matchers::internal::Matcher<Expr>,
return Matched;
}
-UndefinedSprintfOverlapCheck::UndefinedSprintfOverlapCheck(
+SprintfArgumentOverlapCheck::SprintfArgumentOverlapCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
SprintfRegex(Options.get("SprintfFunction", "(::std)?::sn?printf")) {}
-void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
+void SprintfArgumentOverlapCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
callExpr(callee(functionDecl(matchesName(SprintfRegex)).bind("decl")),
hasArgument(0, expr().bind("firstArgExpr")),
@@ -63,7 +63,7 @@ void UndefinedSprintfOverlapCheck::registerMatchers(MatchFinder *Finder) {
this);
}
-void UndefinedSprintfOverlapCheck::check(
+void SprintfArgumentOverlapCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *FirstArg = Result.Nodes.getNodeAs<Expr>("firstArgExpr");
const auto *OtherArg = Result.Nodes.getNodeAs<Expr>("otherArgExpr");
@@ -97,7 +97,7 @@ void UndefinedSprintfOverlapCheck::check(
<< OtherArg->getSourceRange();
}
-void UndefinedSprintfOverlapCheck::storeOptions(
+void SprintfArgumentOverlapCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "SprintfRegex", SprintfRegex);
}
diff --git a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.h
similarity index 67%
rename from clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
rename to clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.h
index e31f1752776df0..58d8e9cfb022ba 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UndefinedSprintfOverlapCheck.h
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.h
@@ -1,4 +1,4 @@
-//===--- UndefinedSprintfOverlapCheck.h - clang-tidy ------------*- C++ -*-===//
+//===--- SprintfArgumentOverlapCheck.h - clang-tidy -------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINEDSPRINTFOVERLAPCHECK_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINEDSPRINTFOVERLAPCHECK_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFARGUMENTOVERLAPCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFARGUMENTOVERLAPCHECK_H
#include "../ClangTidyCheck.h"
@@ -17,10 +17,10 @@ namespace clang::tidy::bugprone {
/// the destination buffer (the first argument).
///
/// For the user-facing documentation see:
-/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/undefined-sprintf-overlap.html
-class UndefinedSprintfOverlapCheck : public ClangTidyCheck {
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/sprintf-argument-overlap.html
+class SprintfArgumentOverlapCheck : public ClangTidyCheck {
public:
- UndefinedSprintfOverlapCheck(StringRef Name, ClangTidyContext *Context);
+ SprintfArgumentOverlapCheck(StringRef Name, ClangTidyContext *Context);
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
@@ -37,4 +37,4 @@ class UndefinedSprintfOverlapCheck : public ClangTidyCheck {
} // namespace clang::tidy::bugprone
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNDEFINEDSPRINTFOVERLAPCHECK_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFARGUMENTOVERLAPCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index ad60ce3d6ae9d1..a366896ebb23a8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -157,8 +157,8 @@ New checks
Gives warnings for tagged unions, where the number of tags is
different from the number of data members inside the union.
-- New :doc:`bugprone-undefined-sprintf-overlap
- <clang-tidy/checks/bugprone/undefined-sprintf-overlap>` check.
+- New :doc:`bugprone-sprintf-argument-overlap
+ <clang-tidy/checks/bugprone/sprintf-argument-overlap>` check.
Warns if any arguments to the ``sprintf`` family of functions overlap with the
first argument.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst
similarity index 88%
rename from clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
rename to clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst
index ef35dded3335e7..315683f1f99588 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/undefined-sprintf-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst
@@ -1,7 +1,7 @@
-.. title:: clang-tidy - bugprone-undefined-sprintf-overlap
+.. title:: clang-tidy - bugprone-sprintf-argument-overlap
-bugprone-undefined-sprintf-overlap
-==================================
+bugprone-sprintf-argument-overlap
+=================================
Warns if any arguments to the ``sprintf`` family of functions overlap with the
destination buffer (the first argument).
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 765b152ff10b8b..6c8f3b1d7469c6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -154,7 +154,7 @@ Clang-Tidy Checks
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
:doc:`bugprone-unchecked-optional-access <bugprone/unchecked-optional-access>`,
:doc:`bugprone-undefined-memory-manipulation <bugprone/undefined-memory-manipulation>`,
- :doc:`bugprone-undefined-sprintf-overlap <bugprone/undefined-sprintf-overlap>`,
+ :doc:`bugprone-sprintf-argument-overlap <bugprone/sprintf-argument-overlap>`,
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
new file mode 100644
index 00000000000000..ec0d2c9e00ae75
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
@@ -0,0 +1,82 @@
+// RUN: %check_clang_tidy %s bugprone-sprintf-argument-overlap %t
+
+using size_t = decltype(sizeof(int));
+
+extern "C" int sprintf(char *s, const char *format, ...);
+extern "C" int snprintf(char *s, size_t n, const char *format, ...);
+
+namespace std {
+ int snprintf(char *s, size_t n, const char *format, ...);
+}
+
+struct st_t {
+ char buf[10];
+ char buf2[10];
+};
+
+struct st2_t {
+ st_t inner;
+};
+
+struct st3_t {
+ st2_t inner;
+};
+
+void first_arg_overlaps() {
+ char buf[10];
+ sprintf(buf, "%s", buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ snprintf(buf, sizeof(buf), "%s", buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ std::snprintf(buf, sizeof(buf), "%s", buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+
+ char* c = &buf[0];
+ sprintf(c, "%s", c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ snprintf(c, sizeof(buf), "%s", c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+
+ snprintf(c, sizeof(buf), "%s%s", c, c);
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+
+ char buf2[10];
+ sprintf(buf, "%s", buf2);
+ sprintf(buf, "%s", buf2, buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+
+ st_t st1, st2;
+ sprintf(st1.buf, "%s", st1.buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ sprintf(st1.buf, "%s", st1.buf2);
+ sprintf(st1.buf, "%s", st2.buf);
+
+ st3_t st3;
+ sprintf(st3.inner.inner.buf, "%s", st3.inner.inner.buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ sprintf((st3.inner.inner.buf), "%s", st3.inner.inner.buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+
+ st_t* stp;
+ sprintf(stp->buf, "%s", stp->buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ sprintf((stp->buf), "%s", stp->buf);
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ stp = &st1;
+ sprintf(stp->buf, "%s", st1.buf);
+
+ char bufs[10][10];
+ sprintf(bufs[1], "%s", bufs[1]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ sprintf(bufs[0], "%s", bufs[1]);
+
+ char bufss[10][10][10];
+ sprintf(bufss[0][1], "%s", bufss[0][1]);
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+
+ sprintf(bufss[0][0], "%s", bufss[0][1]);
+
+ int i = 0;
+ sprintf(bufss[0][++i], "%s", bufss[0][++i]);
+}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
index 284adba70d4683..5686a1b9278d46 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
@@ -30,6 +30,9 @@ void first_arg_overlaps() {
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
std::snprintf(buf, sizeof(buf), "%s", buf);
// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ sprintf(buf+1, "%s", (buf+1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
+ sprintf(buf+1, "%s", buf+2);
char* c = &buf[0];
sprintf(c, "%s", c);
>From 3e1abf85468419615108f3e3173ebd61e3b28cee Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 22:16:10 -0500
Subject: [PATCH 13/19] tidy up
---
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp | 2 +-
.../clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index b229753e0b86a3..fc69390766415a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -63,6 +63,7 @@
#include "SignedCharMisuseCheck.h"
#include "SizeofContainerCheck.h"
#include "SizeofExpressionCheck.h"
+#include "SprintfArgumentOverlapCheck.h"
#include "SpuriouslyWakeUpFunctionsCheck.h"
#include "StandaloneEmptyCheck.h"
#include "StringConstructorCheck.h"
@@ -86,7 +87,6 @@
#include "TooSmallLoopVariableCheck.h"
#include "UncheckedOptionalAccessCheck.h"
#include "UndefinedMemoryManipulationCheck.h"
-#include "SprintfArgumentOverlapCheck.h"
#include "UndelegatedConstructorCheck.h"
#include "UnhandledExceptionAtNewCheck.h"
#include "UnhandledSelfAssignmentCheck.h"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
index 5686a1b9278d46..b613497a472311 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
@@ -33,6 +33,7 @@ void first_arg_overlaps() {
sprintf(buf+1, "%s", (buf+1));
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
sprintf(buf+1, "%s", buf+2);
+ sprintf(buf+1, "%s", buf[1]);
char* c = &buf[0];
sprintf(c, "%s", c);
>From c8f57642d5ba4f70ee9c05f85575c22c8c7e74fa Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 23:04:43 -0500
Subject: [PATCH 14/19] Fix rename operation
---
.../bugprone/sprintf-argument-overlap.cpp | 4 +
.../bugprone/undefined-sprintf-overlap.cpp | 86 -------------------
2 files changed, 4 insertions(+), 86 deletions(-)
delete mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
index ec0d2c9e00ae75..0726efed7b0a03 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
@@ -30,6 +30,10 @@ void first_arg_overlaps() {
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
std::snprintf(buf, sizeof(buf), "%s", buf);
// CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ sprintf(buf+1, "%s", (buf+1));
+ // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ sprintf(buf+1, "%s", buf+2);
+ sprintf(buf+1, "%s", buf[1]);
char* c = &buf[0];
sprintf(c, "%s", c);
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
deleted file mode 100644
index b613497a472311..00000000000000
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/undefined-sprintf-overlap.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-// RUN: %check_clang_tidy %s bugprone-undefined-sprintf-overlap %t
-
-using size_t = decltype(sizeof(int));
-
-extern "C" int sprintf(char *s, const char *format, ...);
-extern "C" int snprintf(char *s, size_t n, const char *format, ...);
-
-namespace std {
- int snprintf(char *s, size_t n, const char *format, ...);
-}
-
-struct st_t {
- char buf[10];
- char buf2[10];
-};
-
-struct st2_t {
- st_t inner;
-};
-
-struct st3_t {
- st2_t inner;
-};
-
-void first_arg_overlaps() {
- char buf[10];
- sprintf(buf, "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- snprintf(buf, sizeof(buf), "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- std::snprintf(buf, sizeof(buf), "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- sprintf(buf+1, "%s", (buf+1));
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- sprintf(buf+1, "%s", buf+2);
- sprintf(buf+1, "%s", buf[1]);
-
- char* c = &buf[0];
- sprintf(c, "%s", c);
- // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- snprintf(c, sizeof(buf), "%s", c);
- // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
-
- snprintf(c, sizeof(buf), "%s%s", c, c);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
-
- char buf2[10];
- sprintf(buf, "%s", buf2);
- sprintf(buf, "%s", buf2, buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
-
- st_t st1, st2;
- sprintf(st1.buf, "%s", st1.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- sprintf(st1.buf, "%s", st1.buf2);
- sprintf(st1.buf, "%s", st2.buf);
-
- st3_t st3;
- sprintf(st3.inner.inner.buf, "%s", st3.inner.inner.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- sprintf((st3.inner.inner.buf), "%s", st3.inner.inner.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
-
- st_t* stp;
- sprintf(stp->buf, "%s", stp->buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- sprintf((stp->buf), "%s", stp->buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- stp = &st1;
- sprintf(stp->buf, "%s", st1.buf);
-
- char bufs[10][10];
- sprintf(bufs[1], "%s", bufs[1]);
- // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
- sprintf(bufs[0], "%s", bufs[1]);
-
- char bufss[10][10][10];
- sprintf(bufss[0][1], "%s", bufss[0][1]);
- // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-undefined-sprintf-overlap]
-
- sprintf(bufss[0][0], "%s", bufss[0][1]);
-
- int i = 0;
- sprintf(bufss[0][++i], "%s", bufss[0][++i]);
-}
>From a699e1d7d2e90ba6c5ee16a9d4a42bd496577b70 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 23:08:01 -0500
Subject: [PATCH 15/19] Fix sort
---
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp | 4 ++--
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt | 2 +-
clang-tools-extra/docs/clang-tidy/checks/list.rst | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index fc69390766415a..fe6cc78d32f4eb 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -205,6 +205,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-sizeof-container");
CheckFactories.registerCheck<SizeofExpressionCheck>(
"bugprone-sizeof-expression");
+ CheckFactories.registerCheck<SprintfArgumentOverlapCheck>(
+ "bugprone-sprintf-argument-overlap");
CheckFactories.registerCheck<SpuriouslyWakeUpFunctionsCheck>(
"bugprone-spuriously-wake-up-functions");
CheckFactories.registerCheck<StandaloneEmptyCheck>(
@@ -249,8 +251,6 @@ class BugproneModule : public ClangTidyModule {
"bugprone-unchecked-optional-access");
CheckFactories.registerCheck<UndefinedMemoryManipulationCheck>(
"bugprone-undefined-memory-manipulation");
- CheckFactories.registerCheck<SprintfArgumentOverlapCheck>(
- "bugprone-sprintf-argument-overlap");
CheckFactories.registerCheck<UndelegatedConstructorCheck>(
"bugprone-undelegated-constructor");
CheckFactories.registerCheck<UnhandledSelfAssignmentCheck>(
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index dd818866f56b3d..3ae2afb6de6e44 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -60,6 +60,7 @@ add_clang_library(clangTidyBugproneModule STATIC
SizeofContainerCheck.cpp
SizeofExpressionCheck.cpp
SmartPtrArrayMismatchCheck.cpp
+ SprintfArgumentOverlapCheck.cpp
SpuriouslyWakeUpFunctionsCheck.cpp
StandaloneEmptyCheck.cpp
StringConstructorCheck.cpp
@@ -83,7 +84,6 @@ add_clang_library(clangTidyBugproneModule STATIC
TooSmallLoopVariableCheck.cpp
UncheckedOptionalAccessCheck.cpp
UndefinedMemoryManipulationCheck.cpp
- SprintfArgumentOverlapCheck.cpp
UndelegatedConstructorCheck.cpp
UnhandledExceptionAtNewCheck.cpp
UnhandledSelfAssignmentCheck.cpp
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 6c8f3b1d7469c6..f8626305716f92 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -131,6 +131,7 @@ Clang-Tidy Checks
:doc:`bugprone-signed-char-misuse <bugprone/signed-char-misuse>`,
:doc:`bugprone-sizeof-container <bugprone/sizeof-container>`,
:doc:`bugprone-sizeof-expression <bugprone/sizeof-expression>`,
+ :doc:`bugprone-sprintf-argument-overlap <bugprone/sprintf-argument-overlap>`,
:doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`,
:doc:`bugprone-standalone-empty <bugprone/standalone-empty>`, "Yes"
:doc:`bugprone-string-constructor <bugprone/string-constructor>`, "Yes"
@@ -154,7 +155,6 @@ Clang-Tidy Checks
:doc:`bugprone-too-small-loop-variable <bugprone/too-small-loop-variable>`,
:doc:`bugprone-unchecked-optional-access <bugprone/unchecked-optional-access>`,
:doc:`bugprone-undefined-memory-manipulation <bugprone/undefined-memory-manipulation>`,
- :doc:`bugprone-sprintf-argument-overlap <bugprone/sprintf-argument-overlap>`,
:doc:`bugprone-undelegated-constructor <bugprone/undelegated-constructor>`,
:doc:`bugprone-unhandled-exception-at-new <bugprone/unhandled-exception-at-new>`,
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
>From b62b61558dd84b72a093a84b45bc76be06b52851 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Sun, 5 Jan 2025 23:36:32 -0500
Subject: [PATCH 16/19] Fix arg index
---
.../bugprone/SprintfArgumentOverlapCheck.cpp | 8 ++---
.../bugprone/sprintf-argument-overlap.cpp | 32 +++++++++----------
2 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
index 6d86279191492a..a3a6aa56e5785e 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
@@ -80,20 +80,20 @@ void SprintfArgumentOverlapCheck::check(
if (FirstArg->HasSideEffects(Context) || OtherArg->HasSideEffects(Context))
return;
- std::optional<unsigned> ArgNumber;
+ std::optional<unsigned> ArgIndex;
for (unsigned I = 0; I != Call->getNumArgs(); ++I) {
if (Call->getArg(I)->IgnoreUnlessSpelledInSource() == OtherArg) {
- ArgNumber = I;
+ ArgIndex = I;
break;
}
}
- if (!ArgNumber)
+ if (!ArgIndex)
return;
diag(OtherArg->getBeginLoc(),
"the %ordinal0 argument in %1 overlaps the 1st argument, "
"which is undefined behavior")
- << *ArgNumber << FnDecl << FirstArg->getSourceRange()
+ << (*ArgIndex+1) << FnDecl << FirstArg->getSourceRange()
<< OtherArg->getSourceRange();
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
index 0726efed7b0a03..96752175ca5059 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-argument-overlap.cpp
@@ -25,59 +25,59 @@ struct st3_t {
void first_arg_overlaps() {
char buf[10];
sprintf(buf, "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
snprintf(buf, sizeof(buf), "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
std::snprintf(buf, sizeof(buf), "%s", buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:41: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf(buf+1, "%s", (buf+1));
- // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf(buf+1, "%s", buf+2);
sprintf(buf+1, "%s", buf[1]);
char* c = &buf[0];
sprintf(c, "%s", c);
- // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
snprintf(c, sizeof(buf), "%s", c);
- // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:34: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
snprintf(c, sizeof(buf), "%s%s", c, c);
- // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 3rd argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
- // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:36: warning: the 4th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: the 5th argument in 'snprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
char buf2[10];
sprintf(buf, "%s", buf2);
sprintf(buf, "%s", buf2, buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: the 4th argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
st_t st1, st2;
sprintf(st1.buf, "%s", st1.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf(st1.buf, "%s", st1.buf2);
sprintf(st1.buf, "%s", st2.buf);
st3_t st3;
sprintf(st3.inner.inner.buf, "%s", st3.inner.inner.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:38: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf((st3.inner.inner.buf), "%s", st3.inner.inner.buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:40: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
st_t* stp;
sprintf(stp->buf, "%s", stp->buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf((stp->buf), "%s", stp->buf);
- // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
stp = &st1;
sprintf(stp->buf, "%s", st1.buf);
char bufs[10][10];
sprintf(bufs[1], "%s", bufs[1]);
- // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf(bufs[0], "%s", bufs[1]);
char bufss[10][10][10];
sprintf(bufss[0][1], "%s", bufss[0][1]);
- // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: the 2nd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
+ // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: the 3rd argument in 'sprintf' overlaps the 1st argument, which is undefined behavior [bugprone-sprintf-argument-overlap]
sprintf(bufss[0][0], "%s", bufss[0][1]);
>From 74c3a7b79ee8b445b5187beb9b9878c25cc494a3 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Mon, 6 Jan 2025 00:39:30 -0500
Subject: [PATCH 17/19] format
---
.../clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp | 2 +-
clang-tools-extra/docs/ReleaseNotes.rst | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
index a3a6aa56e5785e..7acea23359ac28 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
@@ -93,7 +93,7 @@ void SprintfArgumentOverlapCheck::check(
diag(OtherArg->getBeginLoc(),
"the %ordinal0 argument in %1 overlaps the 1st argument, "
"which is undefined behavior")
- << (*ArgIndex+1) << FnDecl << FirstArg->getSourceRange()
+ << (*ArgIndex + 1) << FnDecl << FirstArg->getSourceRange()
<< OtherArg->getSourceRange();
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index a366896ebb23a8..63f933189f7cd8 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -164,7 +164,6 @@ New checks
first argument.
- New :doc:`modernize-use-integer-sign-comparison
-
<clang-tidy/checks/modernize/use-integer-sign-comparison>` check.
Replace comparisons between signed and unsigned integers with their safe
>From 27ca8d4a54633706462a536a55e12c0702c8753a Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Mon, 6 Jan 2025 09:21:58 -0500
Subject: [PATCH 18/19] fix docs
---
.../checks/bugprone/sprintf-argument-overlap.rst | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst
index 315683f1f99588..58ee20a2aeb09e 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-argument-overlap.rst
@@ -20,11 +20,12 @@ This is stated in the `C23/N3220 standard
In practice, passing the output buffer to an input argument can result in
incorrect output. For example, Linux with glibc may produce the following.
-.. clode-block:: c++
- char buf[10];
- sprintf(buf, "%s", "12");
- sprintf(buf, "%s%s", "34", buf);
- printf("%s\n", buf); // prints 3434
+.. code-block:: c++
+
+ char buf[10];
+ sprintf(buf, "%s", "12");
+ sprintf(buf, "%s%s", "34", buf);
+ printf("%s\n", buf); // prints 3434
Options
-------
>From 21af986faa56722c539a839cf51bbdb7fc907e13 Mon Sep 17 00:00:00 2001
From: Chris Cotter <ccotter14 at bloomberg.net>
Date: Mon, 6 Jan 2025 09:42:38 -0500
Subject: [PATCH 19/19] Remove unused matcher
---
.../bugprone/SprintfArgumentOverlapCheck.cpp | 10 ----------
1 file changed, 10 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
index 7acea23359ac28..a8455a4bb7bbab 100644
--- a/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/SprintfArgumentOverlapCheck.cpp
@@ -15,16 +15,6 @@ using namespace clang::ast_matchers;
namespace clang::tidy::bugprone {
-AST_MATCHER_P(IntegerLiteral, hasSameValueAs, std::string, ID) {
- return Builder->removeBindings(
- [this, &Node](const ast_matchers::internal::BoundNodesMap &Nodes) {
- const DynTypedNode &BN = Nodes.getNode(ID);
- if (const auto *Lit = BN.get<IntegerLiteral>())
- return Lit->getValue() != Node.getValue();
- return true;
- });
-}
-
// Similar to forEachArgumentWithParam. forEachArgumentWithParam does not work
// with variadic functions like sprintf, since there is no `decl()` to match
// against in the parameter list `...`.
More information about the cfe-commits
mailing list