Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -48,6 +48,7 @@ #include "SignedCharMisuseCheck.h" #include "SizeofContainerCheck.h" #include "SizeofExpressionCheck.h" +#include "SprintfWithFixedSizeBufferCheck.h" #include "SpuriouslyWakeUpFunctionsCheck.h" #include "StringConstructorCheck.h" #include "StringIntegerAssignmentCheck.h" @@ -153,6 +154,8 @@ "bugprone-sizeof-container"); CheckFactories.registerCheck( "bugprone-sizeof-expression"); + CheckFactories.registerCheck( + "bugprone-sprintf-with-fixed-size-buffer"); CheckFactories.registerCheck( "bugprone-spuriously-wake-up-functions"); CheckFactories.registerCheck( Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -44,6 +44,7 @@ SignedCharMisuseCheck.cpp SizeofContainerCheck.cpp SizeofExpressionCheck.cpp + SprintfWithFixedSizeBufferCheck.cpp SpuriouslyWakeUpFunctionsCheck.cpp StringConstructorCheck.cpp StringIntegerAssignmentCheck.cpp Index: clang-tools-extra/clang-tidy/bugprone/SprintfWithFixedSizeBufferCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/SprintfWithFixedSizeBufferCheck.h @@ -0,0 +1,35 @@ +//===--- SprintfWithFixedSizeBufferCheck.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_SPRINTFWITHFIXEDSIZEBUFFERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFWITHFIXEDSIZEBUFFERCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace bugprone { + +/// Check if `sprintf` is called with a fixed size buffer. Replaced with counted +/// version `snprintf`. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/sprintf-with-fixed-size-buffer.html +class SprintfWithFixedSizeBufferCheck : public ClangTidyCheck { +public: + SprintfWithFixedSizeBufferCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace bugprone +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_SPRINTFWITHFIXEDSIZEBUFFERCHECK_H Index: clang-tools-extra/clang-tidy/bugprone/SprintfWithFixedSizeBufferCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/bugprone/SprintfWithFixedSizeBufferCheck.cpp @@ -0,0 +1,73 @@ +//===--- SprintfWithFixedSizeBufferCheck.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 "SprintfWithFixedSizeBufferCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringRef.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace bugprone { + +void SprintfWithFixedSizeBufferCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr(allOf(hasArgument(0, declRefExpr(hasType(constantArrayType())) + .bind("ConstantArray")), + callee(functionDecl(hasName("::sprintf"))))) + .bind("sprintf"), + this); +} + +void SprintfWithFixedSizeBufferCheck::check( + const MatchFinder::MatchResult &Result) { + const DeclRefExpr *CA = Result.Nodes.getNodeAs("ConstantArray"); + const CallExpr *Call = Result.Nodes.getNodeAs("sprintf"); + + std::string ReplacementText = "snprintf("; + auto arg = Call->getArgs(); + + // Get first argument. + SourceRange SrcRange = arg[0]->getSourceRange(); + ReplacementText += + Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange), + *Result.SourceManager, getLangOpts()); + + // Insert the size of buffer after first argument. + ReplacementText += ", sizeof("; + ReplacementText += + Lexer::getSourceText(CharSourceRange::getTokenRange(CA->getSourceRange()), + *Result.SourceManager, getLangOpts()); + ReplacementText += ")"; + + // Process the remaining arguments. + for (unsigned i = 1; i < Call->getNumArgs(); i++) { + ReplacementText += ", "; + ReplacementText += Lexer::getSourceText( + CharSourceRange::getTokenRange(arg[i]->getSourceRange()), + *Result.SourceManager, getLangOpts()); + } + + ReplacementText += ')'; + + FixItHint hint = + FixItHint::CreateReplacement(Call->getSourceRange(), ReplacementText); + + diag(Call->getBeginLoc(), "string to be written may exceeds the size of " + "buffer; use snprintf instead") + << SourceRange(Call->getBeginLoc(), Call->getEndLoc()) << hint; +} + +} // namespace bugprone +} // namespace tidy +} // namespace clang Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -99,6 +99,11 @@ New checks ^^^^^^^^^^ +- New :doc:`bugprone-sprintf-with-fixed-size-buffer + ` check. + + FIXME: add release notes. + - New :doc:`cppcoreguidelines-avoid-const-or-ref-data-members ` check. Index: clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-with-fixed-size-buffer.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/bugprone/sprintf-with-fixed-size-buffer.rst @@ -0,0 +1,25 @@ +.. title:: clang-tidy - bugprone-sprintf-with-fixed-size-buffer + +bugprone-sprintf-with-fixed-size-buffer +======================================= + +The check finds usage of `sprintf`, which write output string into a fixed-size +array. It will suggest `snprintf` instead. + +It's a common idiom to have a fixed-size buffer of characters allocated on +the stack and then to printf into the buffer. It may be leading to errors if the +string to be written exceeds the size of the array pointed to by buffer. + +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 Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -114,6 +114,7 @@ `bugprone-signed-char-misuse `_, `bugprone-sizeof-container `_, `bugprone-sizeof-expression `_, + `bugprone-sprintf-with-fixed-size-buffer `_, "Yes" `bugprone-spuriously-wake-up-functions `_, `bugprone-string-constructor `_, "Yes" `bugprone-string-integer-assignment `_, "Yes" Index: clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-with-fixed-size-buffer.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/bugprone/sprintf-with-fixed-size-buffer.cpp @@ -0,0 +1,24 @@ +// RUN: %check_clang_tidy %s bugprone-sprintf-with-fixed-size-buffer %t + +#include + +void f(){ + char buff[80]; + sprintf(buff, "Hello, %s!\n", "world"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: string to be written may exceeds the size of buffer; use snprintf instead [bugprone-sprintf-with-fixed-size-buffer] + // CHECK-FIXES: snprintf(buff, sizeof(buff), "Hello, %s!\n", "world"); +} + + +char sbuff[80]; + +void f2(){ + sprintf(sbuff, "Hello, %s!\n", "world"); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: string to be written may exceeds the size of buffer; use snprintf instead [bugprone-sprintf-with-fixed-size-buffer] + // CHECK-FIXES: snprintf(sbuff, sizeof(sbuff), "Hello, %s!\n", "world"); +} + +void f3(){ + char *dynamic_sized_buff = nullptr; + sprintf(dynamic_sized_buff, "Hello, %s!\n", "world"); +}