https://github.com/ThanSin02426 created https://github.com/llvm/llvm-project/pull/182823
This check finds calls to sprintf where the first argument is a fixed-size array and suggests replacing them with snprintf. Fixes #40511 >From 380554139e77ce83bb0c998273e7cae094d3c101 Mon Sep 17 00:00:00 2001 From: ThanSin02426 <[email protected]> Date: Mon, 23 Feb 2026 14:58:47 +0530 Subject: [PATCH] [clang-tidy] Add bugprone-sprintf-to-snprintf check This check finds calls to sprintf where the first argument is a fixed-size array and suggests replacing them with snprintf. Fixes #40511 --- .../bugprone/BugproneTidyModule.cpp | 3 ++ .../clang-tidy/bugprone/CMakeLists.txt | 1 + .../bugprone/SprintfToSnprintfCheck.cpp | 48 +++++++++++++++++++ .../bugprone/SprintfToSnprintfCheck.h | 33 +++++++++++++ clang-tools-extra/docs/ReleaseNotes.rst | 6 +++ .../checks/bugprone/sprintf-to-snprintf.rst | 27 +++++++++++ .../docs/clang-tidy/checks/list.rst | 1 + .../checkers/bugprone/sprintf-to-snprintf.cpp | 16 +++++++ 8 files changed, 135 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 4150442c25d61..46d58b984f97d 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -74,6 +74,7 @@ #include "SignedCharMisuseCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" +#include "SprintfToSnprintfCheck.h" #include "SpuriouslyWakeUpFunctionsCheck.h" #include "StandaloneEmptyCheck.h" #include "StdNamespaceModificationCheck.h" @@ -176,6 +177,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-incorrect-enable-if"); CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>( "bugprone-incorrect-enable-shared-from-this"); + CheckFactories.registerCheck<SprintfToSnprintfCheck>( + "bugprone-sprintf-to-snprintf"); CheckFactories.registerCheck<UnintendedCharOstreamOutputCheck>( "bugprone-unintended-char-ostream-output"); CheckFactories.registerCheck<ReturnConstRefFromParameterCheck>( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index db1256d91d311..21f3c5f00f12f 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -37,6 +37,7 @@ add_clang_library(clangTidyBugproneModule STATIC IncorrectEnableIfCheck.cpp IncorrectEnableSharedFromThisCheck.cpp InvalidEnumDefaultInitializationCheck.cpp + SprintfToSnprintfCheck.cpp UnintendedCharOstreamOutputCheck.cpp ReturnConstRefFromParameterCheck.cpp SuspiciousStringviewDataUsageCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp new file mode 100644 index 0000000000000..e357beb3e182e --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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 "SprintfToSnprintfCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +void SprintfToSnprintfCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr( + callee(functionDecl(hasName("::sprintf"))), + hasArgument( + 0, ignoringParenImpCasts(declRefExpr(to( + varDecl(hasType(constantArrayType())).bind("buffer") + )).bind("arg0")) + ) + ).bind("call"), + this); +} + +void SprintfToSnprintfCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call"); + const auto *Buffer = Result.Nodes.getNodeAs<VarDecl>("buffer"); + + if (!Call || !Buffer) + return; + + StringRef BufferName = Buffer->getName(); + + auto Diag = diag(Call->getBeginLoc(), "use 'snprintf' instead of 'sprintf' for fixed-size character arrays"); + + SourceLocation FuncNameLoc = Call->getExprLoc(); + Diag << FixItHint::CreateReplacement(FuncNameLoc, "snprintf"); + + SourceLocation InsertLoc = Call->getArg(1)->getBeginLoc(); + std::string SizeArg = "sizeof(" + BufferName.str() + "), "; + Diag << FixItHint::CreateInsertion(InsertLoc, SizeArg); +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h new file mode 100644 index 0000000000000..2a9b4dd3c3041 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/SprintfToSnprintfCheck.h @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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_SPRINTFTOSNPRINTFCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFTOSNPRINTFCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/bugprone/sprintf-to-snprintf.html +class SprintfToSnprintfCheck : public ClangTidyCheck { +public: + SprintfToSnprintfCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFTOSNPRINTFCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index e7437e62ee77d..23757aefb13e2 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -97,6 +97,12 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-sprintf-to-snprintf + <clang-tidy/checks/bugprone/sprintf-to-snprintf>` check. + + Finds calls to ``sprintf`` where the destination is a fixed-size character + array and suggests replacing them with the safer ``snprintf``. + - New :doc:`llvm-type-switch-case-types <clang-tidy/checks/llvm/type-switch-case-types>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst new file mode 100644 index 0000000000000..dc57e1736f3bd --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-to-snprintf.rst @@ -0,0 +1,27 @@ +.. title:: clang-tidy - bugprone-sprintf-to-snprintf + +bugprone-sprintf-to-snprintf +============================ + +Finds calls to ``sprintf`` where the destination is a fixed-size character array and replaces them with the safer ``snprintf``. + +It's a common idiom to have a fixed-size buffer of characters allocated on the stack and then to ``printf`` into the buffer. This can easily lead to buffer overflows. This check recommends that the counted version of the function is used instead. + +Example +------- + +.. code-block:: c++ + + void f() { + char buff[80]; + sprintf(buff, "Hello, %s!\n", "world"); + } + +Becomes: + +.. code-block:: c++ + + void f() { + char buff[80]; + snprintf(buff, sizeof(buff), "Hello, %s!\n", "world"); + } \ No newline at end of file diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 0eabd9929dc39..0136a4849a075 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -144,6 +144,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-to-snprintf <bugprone/sprintf-to-snprintf>`, "Yes" :doc:`bugprone-spuriously-wake-up-functions <bugprone/spuriously-wake-up-functions>`, :doc:`bugprone-standalone-empty <bugprone/standalone-empty>`, "Yes" :doc:`bugprone-std-namespace-modification <bugprone/std-namespace-modification>`, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp new file mode 100644 index 0000000000000..f5d6ff5c59ee4 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-to-snprintf.cpp @@ -0,0 +1,16 @@ +// RUN: %check_clang_tidy %s bugprone-sprintf-to-snprintf %t + +extern "C" int sprintf(char *str, const char *format, ...); +extern "C" int snprintf(char *s, unsigned long n, const char *format, ...); + +void f() { + char buff[80]; + sprintf(buff, "Hello, %s!\n", "world"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'snprintf' instead of 'sprintf' for fixed-size character arrays [bugprone-sprintf-to-snprintf] + // CHECK-FIXES: snprintf(buff, sizeof(buff), "Hello, %s!\n", "world"); +} + +void ignore_pointers(char* ptr) { + sprintf(ptr, "Hello"); + // Should not trigger because it's not a fixed-size array. +} \ No newline at end of file _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
