[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,264 @@
+//===--===//
+//
+// 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 "UnsafeFormatStringCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+#include
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringLiteral OptionNameCustomPrintfFunctions =
+"CustomPrintfFunctions";
+static constexpr llvm::StringLiteral OptionNameCustomScanfFunctions =
+"CustomScanfFunctions";
+
+static constexpr llvm::StringLiteral BuiltInFormatBind = "format";
+static constexpr llvm::StringLiteral BuiltInCallBind = "call";
+static constexpr llvm::StringLiteral PrintfCallBind = "printfcall";
+static constexpr llvm::StringLiteral ScanfCallBind = "scanfcall";
+
+static std::vector
+parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
+ const std::vector Functions =
+ utils::options::parseStringList(Option);
+ std::vector Result;
+ Result.reserve(Functions.size());
+
+ for (const StringRef Function : Functions) {
+if (Function.empty())
+ continue;
+const auto [Name, ParamCount] = Function.split(',');
+unsigned long Count = 0;
+if (Name.trim().empty() || ParamCount.trim().empty() ||
+ParamCount.trim().getAsInteger(10, Count)) {
+ Context->configurationDiag(
+ "invalid configuration value for option '%0'; "
+ "expected , ; pairs.")
+ << OptionNameCustomPrintfFunctions;
+ continue;
+}
+Result.push_back(
+{Name.trim().str(),
+ matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
+ Count});
+ }
+
+ return Result;
+}
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+ CustomPrintfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomPrintfFunctions, ""), Context)),
+ CustomScanfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomScanfFunctions, ""), Context)) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+hasDeclContext(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind(BuiltInFormatBind)),
+hasArgument(1, stringLiteral().bind(BuiltInFormatBind
+ .bind(BuiltInCallBind),
+ this);
+
+ if (!CustomPrintfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomPrintfFunctions.size());
+
+for (const auto &Entry : CustomPrintfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(PrintfCallBind),
+ this);
+ }
+
+ if (!CustomScanfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomScanfFunctions.size());
+
+for (const auto &Entry : CustomScanfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(ScanfCallBind),
+ this);
+ }
+}
+
+void UnsafeFormatStringCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, OptionNameCustomPrintfFunctions, "");
+ Options.store(Opts, OptionNameCustomScanfFunctions, "");
+}
+
+const StringLiteral *UnsafeFormatStringCheck::getFormatLiteral(
+const CallExpr *Call, const std::vector &CustomFunctions)
{
+ const auto *FD = cast(Call->getDirectCallee());
+ if (!FD)
+return nullptr;
+ for (const auto &Entry : CustomFunctions) {
+if (Entry.Pattern.match(*FD)) {
+ if (Entry.FormatStringLocation >= Call->getNumArgs())
+return nullptr;
+ const Expr *Arg =
+ Call->getArg(Entry.FormatStringLocation)->IgnoreImpCasts();
+ return dyn_cast(Arg)
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/vbvictor edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
vbvictor wrote: > maybe bugprone-unbounded-format-string ? Sounds good to me https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,114 @@
+.. title:: clang-tidy - bugprone-unsafe-format-string
+
+bugprone-unsafe-format-string
+=
+
+Detects usage of vulnerable ``printf`` and ``scanf``-like format string
+functions with unbounded ``%s`` specifiers that can cause buffer overflows.
+
+The check identifies calls to format string functions like ``sprintf``,
``scanf``,
+and their variants that use ``%s`` format specifiers without proper limits.
+This can lead to buffer overflow vulnerabilities when the input string is
longer
+than the destination buffer.
+
+Format Specifier Behavior
+-
+
+The check distinguishes between different function families:
+
+**scanf family functions**: Field width limits input length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - safe (reads at most 99 characters)
+
+**sprintf family functions**: Precision limits output length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - unsafe (minimum width, no maximum)
+ - ``%.99s`` - safe (outputs at most 99 characters)
+ - ``%10.99s`` - safe (minimum 10 chars, maximum 99 chars)
+
+Examples
+
+
+.. code-block:: c
+
+ char buffer[100];
+ const char* input = "user input";
+
+ // Unsafe sprintf usage
+ sprintf(buffer, "%s", input); // No limit
+ sprintf(buffer, "%99s", input);// Field width is minimum, not maximum
+
+ // Safe sprintf usage
+ sprintf(buffer, "%.99s", input); // Precision limits to 99 chars
+ sprintf(buffer, "%10.99s", input); // Min 10, max 99 chars
+
+ // Unsafe scanf usage
+ scanf("%s", buffer); // No limit
+
+ // Safe scanf usage
+ scanf("%99s", buffer); // Field width limits to 99 chars
+
+ // Safe alternative: use safer functions
+ snprintf(buffer, sizeof(buffer), "%s", input);
+
+
+Checked Functions
+-
+
+The check detects unsafe format strings in these functions:
+
+**sprintf family** (precision ``.N`` provides safety):
+* ``sprintf``, ``vsprintf``
+
+**scanf family** (field width ``N`` provides safety):
+* ``scanf``, ``fscanf``, ``sscanf``
+* ``vscanf``, ``vfscanf``, ``vsscanf``
+* ``wscanf``, ``fwscanf``, ``swscanf``
+* ``vwscanf``, ``vfwscanf``, ``vswscanf``
+
+Configuration
+-
+
+The checker offers 2 configuration options.
+
+* `CustomPrintfFunctions` The user can specify own printf-like functions
EugeneZelenko wrote:
```suggestion
.. option:: CustomPrintfFunctions
The user can specify own ``printf``-like functions
```
Same for other option.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/zwuis edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,264 @@
+//===--===//
+//
+// 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 "UnsafeFormatStringCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+#include
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringLiteral OptionNameCustomPrintfFunctions =
+"CustomPrintfFunctions";
+static constexpr llvm::StringLiteral OptionNameCustomScanfFunctions =
+"CustomScanfFunctions";
+
+static constexpr llvm::StringLiteral BuiltInFormatBind = "format";
+static constexpr llvm::StringLiteral BuiltInCallBind = "call";
+static constexpr llvm::StringLiteral PrintfCallBind = "printfcall";
+static constexpr llvm::StringLiteral ScanfCallBind = "scanfcall";
+
+static std::vector
+parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
+ const std::vector Functions =
+ utils::options::parseStringList(Option);
+ std::vector Result;
+ Result.reserve(Functions.size());
+
+ for (const StringRef Function : Functions) {
+if (Function.empty())
+ continue;
+const auto [Name, ParamCount] = Function.split(',');
+unsigned long Count = 0;
+if (Name.trim().empty() || ParamCount.trim().empty() ||
+ParamCount.trim().getAsInteger(10, Count)) {
+ Context->configurationDiag(
+ "invalid configuration value for option '%0'; "
+ "expected , ; pairs.")
+ << OptionNameCustomPrintfFunctions;
+ continue;
+}
+Result.push_back(
+{Name.trim().str(),
+ matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
+ Count});
+ }
+
+ return Result;
+}
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+ CustomPrintfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomPrintfFunctions, ""), Context)),
+ CustomScanfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomScanfFunctions, ""), Context)) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+hasDeclContext(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind(BuiltInFormatBind)),
+hasArgument(1, stringLiteral().bind(BuiltInFormatBind
+ .bind(BuiltInCallBind),
+ this);
+
+ if (!CustomPrintfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomPrintfFunctions.size());
+
+for (const auto &Entry : CustomPrintfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(PrintfCallBind),
+ this);
+ }
+
+ if (!CustomScanfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomScanfFunctions.size());
+
+for (const auto &Entry : CustomScanfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(ScanfCallBind),
+ this);
+ }
+}
+
+void UnsafeFormatStringCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, OptionNameCustomPrintfFunctions, "");
+ Options.store(Opts, OptionNameCustomScanfFunctions, "");
+}
+
+const StringLiteral *UnsafeFormatStringCheck::getFormatLiteral(
+const CallExpr *Call, const std::vector &CustomFunctions)
{
+ const auto *FD = cast(Call->getDirectCallee());
+ if (!FD)
+return nullptr;
+ for (const auto &Entry : CustomFunctions) {
+if (Entry.Pattern.match(*FD)) {
+ if (Entry.FormatStringLocation >= Call->getNumArgs())
+return nullptr;
+ const Expr *Arg =
+ Call->getArg(Entry.FormatStringLocation)->IgnoreImpCasts();
+ return dyn_cast(Arg)
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,114 @@
+.. title:: clang-tidy - bugprone-unsafe-format-string
+
+bugprone-unsafe-format-string
+=
+
+Detects usage of vulnerable ``printf`` and ``scanf``-like format string
+functions with unbounded ``%s`` specifiers that can cause buffer overflows.
+
+The check identifies calls to format string functions like ``sprintf``,
``scanf``,
+and their variants that use ``%s`` format specifiers without proper limits.
+This can lead to buffer overflow vulnerabilities when the input string is
longer
+than the destination buffer.
+
+Format Specifier Behavior
+-
+
+The check distinguishes between different function families:
+
+**scanf family functions**: Field width limits input length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - safe (reads at most 99 characters)
+
+**sprintf family functions**: Precision limits output length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - unsafe (minimum width, no maximum)
+ - ``%.99s`` - safe (outputs at most 99 characters)
+ - ``%10.99s`` - safe (minimum 10 chars, maximum 99 chars)
+
+Examples
+
+
+.. code-block:: c
+
+ char buffer[100];
+ const char* input = "user input";
+
+ // Unsafe sprintf usage
+ sprintf(buffer, "%s", input); // No limit
+ sprintf(buffer, "%99s", input);// Field width is minimum, not maximum
+
+ // Safe sprintf usage
+ sprintf(buffer, "%.99s", input); // Precision limits to 99 chars
+ sprintf(buffer, "%10.99s", input); // Min 10, max 99 chars
+
+ // Unsafe scanf usage
+ scanf("%s", buffer); // No limit
+
+ // Safe scanf usage
+ scanf("%99s", buffer); // Field width limits to 99 chars
+
+ // Safe alternative: use safer functions
+ snprintf(buffer, sizeof(buffer), "%s", input);
+
+
+Checked Functions
+-
+
+The check detects unsafe format strings in these functions:
+
+**sprintf family** (precision ``.N`` provides safety):
+* ``sprintf``, ``vsprintf``
+
+**scanf family** (field width ``N`` provides safety):
+* ``scanf``, ``fscanf``, ``sscanf``
+* ``vscanf``, ``vfscanf``, ``vsscanf``
+* ``wscanf``, ``fwscanf``, ``swscanf``
+* ``vwscanf``, ``vfwscanf``, ``vswscanf``
+
+Configuration
+-
+
+The checker offers 2 configuration options.
+
EugeneZelenko wrote:
```suggestion
```
Not needed.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,264 @@
+//===--===//
+//
+// 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 "UnsafeFormatStringCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+#include
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringLiteral OptionNameCustomPrintfFunctions =
zwuis wrote:
Use `StringRef`. See #172765.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,264 @@
+//===--===//
+//
+// 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 "UnsafeFormatStringCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+#include
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringLiteral OptionNameCustomPrintfFunctions =
+"CustomPrintfFunctions";
+static constexpr llvm::StringLiteral OptionNameCustomScanfFunctions =
+"CustomScanfFunctions";
+
+static constexpr llvm::StringLiteral BuiltInFormatBind = "format";
+static constexpr llvm::StringLiteral BuiltInCallBind = "call";
+static constexpr llvm::StringLiteral PrintfCallBind = "printfcall";
+static constexpr llvm::StringLiteral ScanfCallBind = "scanfcall";
+
+static std::vector
+parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
+ const std::vector Functions =
+ utils::options::parseStringList(Option);
+ std::vector Result;
+ Result.reserve(Functions.size());
+
+ for (const StringRef Function : Functions) {
+if (Function.empty())
+ continue;
+const auto [Name, ParamCount] = Function.split(',');
+unsigned long Count = 0;
+if (Name.trim().empty() || ParamCount.trim().empty() ||
+ParamCount.trim().getAsInteger(10, Count)) {
+ Context->configurationDiag(
+ "invalid configuration value for option '%0'; "
+ "expected , ; pairs.")
+ << OptionNameCustomPrintfFunctions;
+ continue;
+}
+Result.push_back(
+{Name.trim().str(),
+ matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
+ Count});
+ }
+
+ return Result;
+}
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+ CustomPrintfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomPrintfFunctions, ""), Context)),
+ CustomScanfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomScanfFunctions, ""), Context)) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+hasDeclContext(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind(BuiltInFormatBind)),
+hasArgument(1, stringLiteral().bind(BuiltInFormatBind
+ .bind(BuiltInCallBind),
+ this);
+
+ if (!CustomPrintfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomPrintfFunctions.size());
+
+for (const auto &Entry : CustomPrintfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(PrintfCallBind),
+ this);
+ }
+
+ if (!CustomScanfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomScanfFunctions.size());
+
+for (const auto &Entry : CustomScanfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(ScanfCallBind),
+ this);
+ }
+}
+
+void UnsafeFormatStringCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, OptionNameCustomPrintfFunctions, "");
+ Options.store(Opts, OptionNameCustomScanfFunctions, "");
+}
+
+const StringLiteral *UnsafeFormatStringCheck::getFormatLiteral(
+const CallExpr *Call, const std::vector &CustomFunctions)
{
+ const auto *FD = cast(Call->getDirectCallee());
zwuis wrote:
`getDirectCallee()` already returns `FunctionDecl *`.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/zwuis commented: > coauthors: [email protected], [email protected] FYI https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors > > Can we give this check a more specific name rather than using "unsafe"? > > Any suggestions? > > maybe bugprone-unbounded-format-string ? +1 if other reviewers don't have a plan to extend this check in future. https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/vbvictor edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/zwuis edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@
+//===*- 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_UNSAFEFORMATSTRINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFORMATSTRINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of vulnerable format string functions with unbounded %s
+/// specifiers that can cause buffer overflows.
+///
+/// For the user-facing documentation see:
+///
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
+class UnsafeFormatStringCheck : public ClangTidyCheck {
+public:
+ UnsafeFormatStringCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool hasUnboundedStringSpecifier(StringRef Fmt, bool IsScanfFamily);
zwuis wrote:
> ... in .cpp file ...
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
dkrupp wrote: I have executed the checker on several open source projects. There are a few findings. 1) In twin, the destination buffer size is calculated properly (I think), but using snprintf or width specifier would not hurt. 2) Openssl the write length to `sfx` is protected with the if condition. 3) The sprintf usage in sqlite could be problematic. The source string size is not trivially visible and the write length of the destination buffer is not protected. Using snprintf or width specifier would make the write much safer. | Project | Reports | |-|-| | codechecker | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=codechecker_v6.25.1_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | memcached | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=memcached_1.6.38_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | tmux | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=tmux_3.5a_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | curl | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=curl_curl-8_12_1_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | twin | [3 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=twin_v0.9.0_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | vim | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=vim_v9.1.1232_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | openssl | [2 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=openssl_openssl-3.4.1_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | sqlite | [1 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=sqlite_version-3.49.1_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | postgres | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=postgres_REL_17_4_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | tinyxml2 | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=tinyxml2_11.0.0_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | libwebm | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=libwebm_libwebm-1.0.0.31_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | | xerces | [0 reports](https://codechecker-demo.eastus.cloudapp.azure.com/Default/reports?run=xerces_v3.3.0_ednikru_dkrupp-bugprone-unsafe-format-string-pr_a8d28b5) | https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
dkrupp wrote: > Can we give this check a more specific name rather than using "unsafe"? Any suggestions? maybe bugprone-unbounded-format-string ? https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,111 @@
+.. title:: clang-tidy - bugprone-unsafe-format-string
+
+bugprone-unsafe-format-string
+=
+
+Detects usage of vulnerable format string functions with unbounded ``%s``
+specifiers that can cause buffer overflows.
+
+The check identifies calls to format string functions like ``sprintf``,
``scanf``,
+and their variants that use ``%s`` format specifiers without proper limits.
+This can lead to buffer overflow vulnerabilities when the input string is
longer
+than the destination buffer.
+
+Format Specifier Behavior
+-
+
+The check distinguishes between different function families:
+
+**scanf family functions**: Field width limits input length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - safe (reads at most 99 characters)
+
+**sprintf family functions**: Precision limits output length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - unsafe (minimum width, no maximum)
+ - ``%.99s`` - safe (outputs at most 99 characters)
+ - ``%10.99s`` - safe (minimum 10 chars, maximum 99 chars)
+
+Examples
+
+
+.. code-block:: c
+
+ char buffer[100];
+ const char* input = "user input";
+
+ // Unsafe sprintf usage
+ sprintf(buffer, "%s", input); // No limit
+ sprintf(buffer, "%99s", input);// Field width is minimum, not maximum
+
+ // Safe sprintf usage
+ sprintf(buffer, "%.99s", input); // Precision limits to 99 chars
+ sprintf(buffer, "%10.99s", input); // Min 10, max 99 chars
+
+ // Unsafe scanf usage
+ scanf("%s", buffer); // No limit
+
+ // Safe scanf usage
+ scanf("%99s", buffer); // Field width limits to 99 chars
+
+ // Safe alternative: use safer functions
+ snprintf(buffer, sizeof(buffer), "%s", input);
+
+
+Checked Functions
+-
+
+The check detects unsafe format strings in these functions:
+
+**sprintf family** (precision ``.N`` provides safety):
+* ``sprintf``, ``vsprintf``
+
+**scanf family** (field width ``N`` provides safety):
+* ``scanf``, ``fscanf``, ``sscanf``
+* ``vscanf``, ``vfscanf``, ``vsscanf``
+* ``wscanf``, ``fwscanf``, ``swscanf``
+* ``vwscanf``, ``vfwscanf``, ``vswscanf``
+
+Configuration
+-
+
+The checker offers 2 configuration options.
+
+* `CustomPrintfFunctions` The user can specify own printf-like functions with
dangerous format string parameter.
+* `CustomScanfFunctions` The user can specify own scanf-like functions with
dangerous format string parameter.
+
+Format:
+Both options have the following format.
+.. code::
+
+ bugprone-unsafe-functions.CustomPrintfFunctions="
+ functionRegex1, format-string-position;
+ functionRegex2, format-string-position;
+ ...
+ "
+
+The first parameter in the pairs is a function regular expression matching the
function name,
+the second parameter is the count of the format string literal argument.
+
+The following configuration will give warning:
+
+.. code::
+
+ bugprone-unsafe-format-string.CustomPrintfFunctions: 'mysprintf, 0;'
+ bugprone-unsafe-format-string.CustomScanfFunctions: 'myscanf, 1;'
+
+ extern int myscanf( const char* format, ... );
+ extern int mysprintf( char* buffer, const char* format, ... );
+ void test() {
+char buffer[100];
+const char* input = "user input";
+mysprintf(buffer, "%s", input); // warning
+myscanf("%s", buffer); //warning
+ }
zwuis wrote:
Please document the default value of options.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/zwuis commented: Can we give this check a more specific name rather than using "unsafe"? https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/zwuis edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,111 @@ +.. title:: clang-tidy - bugprone-unsafe-format-string + +bugprone-unsafe-format-string += + +Detects usage of vulnerable format string functions with unbounded ``%s`` EugeneZelenko wrote: Please synchronize with statement in Release Notes. https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,33 @@
+// RUN: %check_clang_tidy %s bugprone-unsafe-format-string %t --\
+// RUN: -config="{CheckOptions:
{bugprone-unsafe-format-string.CustomPrintfFunctions: 'mysprintf, 1;',
bugprone-unsafe-format-string.CustomScanfFunctions: 'myscanf, 0;' }}"\
+// RUN: -- -isystem %S/Inputs/unsafe-format-string
+
+#include
+
+extern int myscanf( const char* format, ... );
+extern int mysprintf( char* buffer, const char* format, ... );
+
+void test_sprintf() {
+ char buffer[100];
+ const char* input = "user input";
+
+ /* Positive: unsafe %s without field width */
+ mysprintf(buffer, "%s", input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ mysprintf(buffer, "%.99s", input);
+ /*no warning*/
+}
+
+
+void test_scanf() {
+ char buffer[100];
+
+ /* Positive: unsafe %s without field width */
+ myscanf("%s", buffer);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
field width may cause buffer overflow; consider using '%Ns' where N limits
input length [bugprone-unsafe-format-string]
+
+ /*Negative: safe %s with field width */
+ myscanf("%99s", buffer);
+ /* no-warning */
+}
EugeneZelenko wrote:
Please add newline.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,111 @@
+.. title:: clang-tidy - bugprone-unsafe-format-string
+
+bugprone-unsafe-format-string
+=
+
+Detects usage of vulnerable format string functions with unbounded ``%s``
+specifiers that can cause buffer overflows.
+
+The check identifies calls to format string functions like ``sprintf``,
``scanf``,
+and their variants that use ``%s`` format specifiers without proper limits.
+This can lead to buffer overflow vulnerabilities when the input string is
longer
+than the destination buffer.
+
+Format Specifier Behavior
+-
+
+The check distinguishes between different function families:
+
+**scanf family functions**: Field width limits input length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - safe (reads at most 99 characters)
+
+**sprintf family functions**: Precision limits output length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - unsafe (minimum width, no maximum)
+ - ``%.99s`` - safe (outputs at most 99 characters)
+ - ``%10.99s`` - safe (minimum 10 chars, maximum 99 chars)
+
+Examples
+
+
+.. code-block:: c
+
+ char buffer[100];
+ const char* input = "user input";
+
+ // Unsafe sprintf usage
+ sprintf(buffer, "%s", input); // No limit
+ sprintf(buffer, "%99s", input);// Field width is minimum, not maximum
+
+ // Safe sprintf usage
+ sprintf(buffer, "%.99s", input); // Precision limits to 99 chars
+ sprintf(buffer, "%10.99s", input); // Min 10, max 99 chars
+
+ // Unsafe scanf usage
+ scanf("%s", buffer); // No limit
+
+ // Safe scanf usage
+ scanf("%99s", buffer); // Field width limits to 99 chars
+
+ // Safe alternative: use safer functions
+ snprintf(buffer, sizeof(buffer), "%s", input);
+
+
+Checked Functions
+-
+
+The check detects unsafe format strings in these functions:
+
+**sprintf family** (precision ``.N`` provides safety):
+* ``sprintf``, ``vsprintf``
+
+**scanf family** (field width ``N`` provides safety):
+* ``scanf``, ``fscanf``, ``sscanf``
+* ``vscanf``, ``vfscanf``, ``vsscanf``
+* ``wscanf``, ``fwscanf``, ``swscanf``
+* ``vwscanf``, ``vfwscanf``, ``vswscanf``
+
+Configuration
+-
+
+The checker offers 2 configuration options.
+
+* `CustomPrintfFunctions` The user can specify own printf-like functions with
dangerous format string parameter.
+* `CustomScanfFunctions` The user can specify own scanf-like functions with
dangerous format string parameter.
EugeneZelenko wrote:
```suggestion
* `CustomPrintfFunctions` The user can specify own ``printf``-like functions
with dangerous format string parameter.
* `CustomScanfFunctions` The user can specify own ``scanf``-like functions with
dangerous format string parameter.
```
80-characters limit, please.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,264 @@
+//===--===//
+//
+// 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 "UnsafeFormatStringCheck.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+#include
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+static constexpr llvm::StringLiteral OptionNameCustomPrintfFunctions =
+"CustomPrintfFunctions";
+static constexpr llvm::StringLiteral OptionNameCustomScanfFunctions =
+"CustomScanfFunctions";
+
+static constexpr llvm::StringLiteral BuiltInFormatBind = "format";
+static constexpr llvm::StringLiteral BuiltInCallBind = "call";
+static constexpr llvm::StringLiteral PrintfCallBind = "printfcall";
+static constexpr llvm::StringLiteral ScanfCallBind = "scanfcall";
+
+static std::vector
+parseCheckedFunctions(StringRef Option, ClangTidyContext *Context) {
+ const std::vector Functions =
+ utils::options::parseStringList(Option);
+ std::vector Result;
+ Result.reserve(Functions.size());
+
+ for (const StringRef Function : Functions) {
+if (Function.empty())
+ continue;
+const auto [Name, ParamCount] = Function.split(',');
+unsigned long Count = 0;
+if (Name.trim().empty() || ParamCount.trim().empty() ||
+ParamCount.trim().getAsInteger(10, Count)) {
+ Context->configurationDiag(
+ "invalid configuration value for option '%0'; "
+ "expected , ; pairs.")
+ << OptionNameCustomPrintfFunctions;
+ continue;
+}
+Result.push_back(
+{Name.trim().str(),
+ matchers::MatchesAnyListedNameMatcher::NameMatcher(Name.trim()),
+ Count});
+ }
+
+ return Result;
+}
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context),
+ CustomPrintfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomPrintfFunctions, ""), Context)),
+ CustomScanfFunctions(parseCheckedFunctions(
+ Options.get(OptionNameCustomScanfFunctions, ""), Context)) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+hasDeclContext(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind(BuiltInFormatBind)),
+hasArgument(1, stringLiteral().bind(BuiltInFormatBind
+ .bind(BuiltInCallBind),
+ this);
+
+ if (!CustomPrintfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomPrintfFunctions.size());
+
+for (const auto &Entry : CustomPrintfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(PrintfCallBind),
+ this);
+ }
+
+ if (!CustomScanfFunctions.empty()) {
+std::vector FunctionNames;
+FunctionNames.reserve(CustomScanfFunctions.size());
+
+for (const auto &Entry : CustomScanfFunctions)
+ FunctionNames.emplace_back(Entry.Name);
+
+auto CustomFunctionsMatcher =
matchers::matchesAnyListedName(FunctionNames);
+
+Finder->addMatcher(callExpr(callee((functionDecl(CustomFunctionsMatcher
+ .bind(ScanfCallBind),
+ this);
+ }
+}
+
+void UnsafeFormatStringCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, OptionNameCustomPrintfFunctions, "");
+ Options.store(Opts, OptionNameCustomScanfFunctions, "");
+}
+
+const StringLiteral *UnsafeFormatStringCheck::getFormatLiteral(
+const CallExpr *Call, const std::vector &CustomFunctions)
{
+ auto *FD = cast(Call->getDirectCallee());
EugeneZelenko wrote:
`const`?
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,111 @@
+.. title:: clang-tidy - bugprone-unsafe-format-string
+
+bugprone-unsafe-format-string
+=
+
+Detects usage of vulnerable format string functions with unbounded ``%s``
+specifiers that can cause buffer overflows.
+
+The check identifies calls to format string functions like ``sprintf``,
``scanf``,
+and their variants that use ``%s`` format specifiers without proper limits.
+This can lead to buffer overflow vulnerabilities when the input string is
longer
+than the destination buffer.
+
+Format Specifier Behavior
+-
+
+The check distinguishes between different function families:
+
+**scanf family functions**: Field width limits input length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - safe (reads at most 99 characters)
+
+**sprintf family functions**: Precision limits output length
+ - ``%s`` - unsafe (no limit)
+ - ``%99s`` - unsafe (minimum width, no maximum)
+ - ``%.99s`` - safe (outputs at most 99 characters)
+ - ``%10.99s`` - safe (minimum 10 chars, maximum 99 chars)
+
+Examples
+
+
+.. code-block:: c
+
+ char buffer[100];
+ const char* input = "user input";
+
+ // Unsafe sprintf usage
+ sprintf(buffer, "%s", input); // No limit
+ sprintf(buffer, "%99s", input);// Field width is minimum, not maximum
+
+ // Safe sprintf usage
+ sprintf(buffer, "%.99s", input); // Precision limits to 99 chars
+ sprintf(buffer, "%10.99s", input); // Min 10, max 99 chars
+
+ // Unsafe scanf usage
+ scanf("%s", buffer); // No limit
+
+ // Safe scanf usage
+ scanf("%99s", buffer); // Field width limits to 99 chars
+
+ // Safe alternative: use safer functions
+ snprintf(buffer, sizeof(buffer), "%s", input);
+
+
+Checked Functions
+-
+
+The check detects unsafe format strings in these functions:
+
+**sprintf family** (precision ``.N`` provides safety):
+* ``sprintf``, ``vsprintf``
+
+**scanf family** (field width ``N`` provides safety):
+* ``scanf``, ``fscanf``, ``sscanf``
+* ``vscanf``, ``vfscanf``, ``vsscanf``
+* ``wscanf``, ``fwscanf``, ``swscanf``
+* ``vwscanf``, ``vfwscanf``, ``vswscanf``
+
+Configuration
+-
+
+The checker offers 2 configuration options.
+
+* `CustomPrintfFunctions` The user can specify own printf-like functions with
dangerous format string parameter.
+* `CustomScanfFunctions` The user can specify own scanf-like functions with
dangerous format string parameter.
+
+Format:
+Both options have the following format.
+.. code::
EugeneZelenko wrote:
```suggestion
Both options have the following format:
.. code::
```
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -202,6 +202,12 @@ New checks Detects default initialization (to 0) of variables with ``enum`` type where the enum has no enumerator with value of 0. +- New :doc:`bugprone-unsafe-format-string + ` check. + + Detects usage of vulnerable printf and scanf-like format + string functions with unbounded ``%s`` specifiers that can cause buffer overflows. EugeneZelenko wrote: ```suggestion Detects usage of vulnerable ``printf`` and ``scanf``-like format string functions with unbounded ``%s`` specifiers that can cause buffer overflows. ``` https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/9] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
dkrupp wrote: > Missing Release Notes entry. > > How about custom `printf/scanf` functions? > > Are similar functionality covered by `-Wformat` or Clang Static Analyzer? Release notes extended. https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
dkrupp wrote: > Missing Release Notes entry. > > How about custom `printf/scanf` functions? > > Are similar functionality covered by `-Wformat` or Clang Static Analyzer? There are related checkers, but they warn for different cases -Wformat detects different cases. -Wformat-overflow is the closest, but it detects cases where the width specifier is present, but proven to be larger than the destination buffer. This check however always requieres the width specifier to be present. Clang Static Analyzer has a checker bans certain unsafe functions altogether, like scanf : https://clang.llvm.org/docs/analyzer/checkers.html#security-insecureapi-deprecatedorunsafebufferhandling-c this check is less strict. https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/8] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@
+//===*- 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_UNSAFEFORMATSTRINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFORMATSTRINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of vulnerable format string functions with unbounded %s
+/// specifiers that can cause buffer overflows.
+///
+/// For the user-facing documentation see:
+///
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
+class UnsafeFormatStringCheck : public ClangTidyCheck {
+public:
+ UnsafeFormatStringCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool hasUnboundedStringSpecifier(StringRef Fmt, bool IsScanfFamily);
dkrupp wrote:
done
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/7] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@
+//===*- 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_UNSAFEFORMATSTRINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFORMATSTRINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of vulnerable format string functions with unbounded %s
+/// specifiers that can cause buffer overflows.
+///
+/// For the user-facing documentation see:
+///
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
+class UnsafeFormatStringCheck : public ClangTidyCheck {
+public:
+ UnsafeFormatStringCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool hasUnboundedStringSpecifier(StringRef Fmt, bool IsScanfFamily);
+ std::string getSafeAlternative(StringRef FunctionName);
dkrupp wrote:
removed
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
dkrupp wrote:
I added now to config options CustomPrintfFunctions and CustomScanfFunctions.
We need both, as the format string handling is different.
Also extended the documentation and the test.
please check. thanks.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/6] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/5] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@
+//===*- 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_UNSAFEFORMATSTRINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFORMATSTRINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of vulnerable format string functions with unbounded %s
+/// specifiers that can cause buffer overflows.
+///
+/// For the user-facing documentation see:
+///
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
+class UnsafeFormatStringCheck : public ClangTidyCheck {
+public:
+ UnsafeFormatStringCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool hasUnboundedStringSpecifier(StringRef Fmt, bool IsScanfFamily);
+ std::string getSafeAlternative(StringRef FunctionName);
vbvictor wrote:
is this used?
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@
+//===*- 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_UNSAFEFORMATSTRINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFORMATSTRINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of vulnerable format string functions with unbounded %s
+/// specifiers that can cause buffer overflows.
+///
+/// For the user-facing documentation see:
+///
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
+class UnsafeFormatStringCheck : public ClangTidyCheck {
+public:
+ UnsafeFormatStringCheck(StringRef Name, ClangTidyContext *Context);
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ bool hasUnboundedStringSpecifier(StringRef Fmt, bool IsScanfFamily);
vbvictor wrote:
Could we make this as pure `static` function in .cpp file or mark it as static
here because we don't use class state
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
vbvictor wrote: > @vbvictor @EugeneZelenko thanks for your review. I tried to address your > comments. Would you push the changes since linter is still broken but comment is marked as resolved? https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
vbvictor wrote:
I think it's reasonable to add it in current PR if you are not opposed to it.
`CustomPrintFunctions` seems good for me thought comma-separation style is a
bit odd for clang-tidy but I don't see a better approach for now.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,151 @@
+//===--===//
+//
+// 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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(
+ callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+hasDeclContext(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ assert(Call && Format);
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } else if (Format->getCharByteWidth() == 2) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF16ToUTF8String(Format->getBytes(), FormatString);
+ } else if (Format->getCharByteWidth() == 4) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF32ToUTF8String(Format->getBytes(), FormatString);
+ }
+
+ const auto *Callee = cast(Call->getCalleeDecl());
+ const StringRef FunctionName = Callee->getName();
+
+ const bool IsScanfFamily = FunctionName.contains("scanf");
+
+ if (!hasUnboundedStringSpecifier(FormatString, IsScanfFamily))
+return;
+
+ diag(Call->getBeginLoc(),
+ IsScanfFamily
+ ? "format specifier '%%s' without field width may cause buffer "
+ "overflow; consider using '%%Ns' where N limits input length"
+ : "format specifier '%%s' without precision may cause buffer "
+ "overflow; consider using '%%.Ns' where N limits output length")
+ << Call->getSourceRange();
+}
+
+bool UnsafeFormatStringCheck::hasUnboundedStringSpecifier(StringRef Fmt,
+ bool IsScanfFamily) {
+ size_t Pos = 0;
+ size_t N = Fmt.size();
vbvictor wrote:
It's reported in
https://github.com/llvm/llvm-project/pull/168691#issuecomment-3552061977
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp commented: @vbvictor @EugeneZelenko thanks for your review. I tried to address your comments. https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
dkrupp wrote:
These functions are now matched against function int std namespace and globally.
I can imagine introducing 2 config variables:
CustomPrintfFunctions
CustomScanfFunctions
These would be a list of regex definable functions like in
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-functions.html#cmdoption-arg-CustomFunctions
CustomPrintFunctions: "mysprintf, 0; mylogger, 1;"
Where the first argument is a regex matching the function name and the second
parameter would be indicating which parameter contains the format string (0 is
the first parameter).
We need to distinguish between scanf like and printf like functions, as their
format strings behave differently.
Is that what you mean?
Should this be added in this PR or as a later improvement in a follow-up PR?
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/4] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,243 @@
+// RUN: %check_clang_tidy %s bugprone-unsafe-format-string %t -- -- -isystem
%S/Inputs/unsafe-format-string
+
+#include
+
+void test_sprintf() {
+ char buffer[100];
+ const char* input = "user input";
+
+ /* Positive: unsafe %s without field width */
+ sprintf(buffer, "%s", input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ /* Positive: field width doesn't prevent overflow in sprintf */
+ sprintf(buffer, "%99s", input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ /* Positive: dynamic field width doesn't prevent overflow */
+ sprintf(buffer, "%*s", 10, input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ /*Negative: precision limits string length */
+ sprintf(buffer, "%.99s", input);
+ /* no-warning */
dkrupp wrote:
I would keep the comments then show test intent.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } else if (Format->getCharByteWidth() == 2) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF16ToUTF8String(Format->getBytes(), FormatString);
+ } else if (Format->getCharByteWidth() == 4) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF32ToUTF8String(Format->getBytes(), FormatString);
+ }
+
+ const auto *Callee = cast(Call->getCalleeDecl());
+ StringRef FunctionName = Callee->getName();
+
+ bool IsScanfFamily = FunctionName.contains("scanf");
+
+ if (!hasUnboundedStringSpecifier(FormatString, IsScanfFamily))
+return;
+
+ auto Diag =
+ diag(
+ Call->getBeginLoc(),
+ IsScanfFamily
+ ? "format specifier '%%s' without field width may cause buffer "
+"overflow; consider using '%%Ns' where N limits input length"
+ : "format specifier '%%s' without precision may cause buffer "
+"overflow; consider using '%%.Ns' where N limits output
length")
+ << Call->getSourceRange();
+}
+
+bool UnsafeFormatStringCheck::hasUnboundedStringSpecifier(StringRef Fmt,
dkrupp wrote:
This parser was implemented particularly for this checker.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } else if (Format->getCharByteWidth() == 2) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF16ToUTF8String(Format->getBytes(), FormatString);
+ } else if (Format->getCharByteWidth() == 4) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF32ToUTF8String(Format->getBytes(), FormatString);
+ }
+
+ const auto *Callee = cast(Call->getCalleeDecl());
+ StringRef FunctionName = Callee->getName();
+
+ bool IsScanfFamily = FunctionName.contains("scanf");
+
+ if (!hasUnboundedStringSpecifier(FormatString, IsScanfFamily))
+return;
+
+ auto Diag =
vbvictor wrote:
No need to declare variable
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
vbvictor wrote:
Use `hasDeclContext(translationUnitDecl())`.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
vbvictor wrote:
We expect matchers to work, no need for this (or you can place assert).
```suggestion
```
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,243 @@
+// RUN: %check_clang_tidy %s bugprone-unsafe-format-string %t -- -- -isystem
%S/Inputs/unsafe-format-string
+
+#include
+
+void test_sprintf() {
+ char buffer[100];
+ const char* input = "user input";
+
+ /* Positive: unsafe %s without field width */
+ sprintf(buffer, "%s", input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ /* Positive: field width doesn't prevent overflow in sprintf */
+ sprintf(buffer, "%99s", input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ /* Positive: dynamic field width doesn't prevent overflow */
+ sprintf(buffer, "%*s", 10, input);
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: format specifier '%s' without
precision may cause buffer overflow; consider using '%.Ns' where N limits
output length [bugprone-unsafe-format-string]
+
+ /*Negative: precision limits string length */
+ sprintf(buffer, "%.99s", input);
+ /* no-warning */
vbvictor wrote:
I think this is unnecessary to write in test files in general, but no strong
objection
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } else if (Format->getCharByteWidth() == 2) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF16ToUTF8String(Format->getBytes(), FormatString);
+ } else if (Format->getCharByteWidth() == 4) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF32ToUTF8String(Format->getBytes(), FormatString);
+ }
+
+ const auto *Callee = cast(Call->getCalleeDecl());
+ StringRef FunctionName = Callee->getName();
+
+ bool IsScanfFamily = FunctionName.contains("scanf");
+
+ if (!hasUnboundedStringSpecifier(FormatString, IsScanfFamily))
+return;
+
+ auto Diag =
+ diag(
+ Call->getBeginLoc(),
+ IsScanfFamily
+ ? "format specifier '%%s' without field width may cause buffer "
+"overflow; consider using '%%Ns' where N limits input length"
+ : "format specifier '%%s' without precision may cause buffer "
+"overflow; consider using '%%.Ns' where N limits output
length")
+ << Call->getSourceRange();
+}
+
+bool UnsafeFormatStringCheck::hasUnboundedStringSpecifier(StringRef Fmt,
vbvictor wrote:
Is part of this was copied somewhere from clang codebase or purely hand-crafted?
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
vbvictor wrote:
This list should be converted into an option in case users use custom
printf/scanf-like functions
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,73 @@ +.. title:: clang-tidy - bugprone-unsafe-format-string + +bugprone-unsafe-format-string +== EugeneZelenko wrote: ```suggestion bugprone-unsafe-format-string = ``` https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@
+//===--- UnsafeFormatStringCheck.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_UNSAFEFORMATSTRINGCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNSAFEFORMATSTRINGCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::bugprone {
+
+/// Detects usage of vulnerable format string functions with unbounded %s
+/// specifiers that can cause buffer overflows.
+///
+/// For the user-facing documentation see:
+///
http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
EugeneZelenko wrote:
```suggestion
///
https://clang.llvm.org/extra/clang-tidy/checks/bugprone/unsafe-format-string.html
```
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,57 @@
+#pragma clang system_header
+
+#ifdef __cplusplus
+#define restrict /*restrict*/
+#endif
+
+#ifndef __cplusplus
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+typedef __typeof(sizeof(int)) size_t;
+typedef long long __int64_t;
+typedef __int64_t __darwin_off_t;
+typedef __darwin_off_t fpos_t;
+typedef int off_t;
+typedef long ssize_t;
+
+typedef struct _FILE FILE;
+
+extern FILE *stdin;
+extern FILE *stdout;
+extern FILE *stderr;
+
+typedef __builtin_va_list va_list;
+#define va_start(ap, param) __builtin_va_start(ap, param)
+#define va_end(ap) __builtin_va_end(ap)
+#define va_arg(ap, type)__builtin_va_arg(ap, type)
+#define va_copy(dst, src) __builtin_va_copy(dst, src)
+
+
+#ifdef __cplusplus
+namespace std {
+#endif
+extern int fscanf ( FILE *restrict stream, const char *restrict format, ... );
+extern int scanf ( const char *restrict format, ... );
+extern int sscanf ( const char *restrict s, const char *restrict format, ...);
+extern int vscanf( const char *restrict format, va_list vlist );
+extern int vfscanf ( FILE *restrict stream, const char *restrict format,
va_list arg );
+
+extern int vsscanf( const char *restrict buffer, const char *restrict format,
va_list vlist );
+extern int vwscanf( const wchar_t* format, va_list vlist );
+extern int vfwscanf( FILE* stream, const wchar_t* format, va_list vlist );
+extern int vswscanf( const wchar_t* buffer, const wchar_t* format, va_list
vlist );
+extern int swscanf (const wchar_t* ws, const wchar_t* format, ...);
+extern int wscanf( const wchar_t *format, ... );
+extern int fwscanf( FILE *stream, const wchar_t *format, ... );
+
+extern int printf( const char* format, ... );
+extern int sprintf( char* buffer, const char* format, ... );
+extern int vsprintf (char * s, const char * format, va_list arg );
+extern int vsnprintf (char * s, size_t n, const char * format, va_list arg );
+extern int fprintf( FILE* stream, const char* format, ... );
+extern int snprintf( char* restrict buffer, size_t bufsz,
+ const char* restrict format, ... );
+#ifdef __cplusplus
+} //namespace std {
+#endif
EugeneZelenko wrote:
Please add newline.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC UnhandledSelfAssignmentCheck.cpp UniquePtrArrayMismatchCheck.cpp UnsafeFunctionsCheck.cpp + UnsafeFormatStringCheck.cpp EugeneZelenko wrote: Ditto. https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
EugeneZelenko wrote:
Should be before `bugprone-unsafe-functions`.
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,73 @@ +.. title:: clang-tidy - bugprone-unsafe-format-string + +bugprone-unsafe-format-string +== + +Detects usage of vulnerable format string functions with unbounded ``%s`` +specifiers that can cause buffer overflows. + +The check identifies calls to format string functions like ``sprintf``, ``scanf``, +and their variants that use ``%s`` format specifiers without proper limits. +This can lead to buffer overflow vulnerabilities when the input string is longer +than the destination buffer. + +Format Specifier Behavior +-- EugeneZelenko wrote: ```suggestion Format Specifier Behavior - ``` https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } else if (Format->getCharByteWidth() == 2) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF16ToUTF8String(Format->getBytes(), FormatString);
+ } else if (Format->getCharByteWidth() == 4) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF32ToUTF8String(Format->getBytes(), FormatString);
+ }
+
+ const auto *Callee = cast(Call->getCalleeDecl());
+ StringRef FunctionName = Callee->getName();
EugeneZelenko wrote:
```suggestion
const StringRef FunctionName = Callee->getName();
```
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@ +//===--- UnsafeFormatStringCheck.cpp - clang-tidy ---===// EugeneZelenko wrote: ```suggestion //===--===// ``` https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,34 @@ +//===--- UnsafeFormatStringCheck.h - clang-tidy ---*- C++ -*-===// EugeneZelenko wrote: ```suggestion //===--===// ``` https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
@@ -0,0 +1,153 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } else if (Format->getCharByteWidth() == 2) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF16ToUTF8String(Format->getBytes(), FormatString);
+ } else if (Format->getCharByteWidth() == 4) {
+// Handle wide strings by converting to narrow string for analysis
+convertUTF32ToUTF8String(Format->getBytes(), FormatString);
+ }
+
+ const auto *Callee = cast(Call->getCalleeDecl());
+ StringRef FunctionName = Callee->getName();
+
+ bool IsScanfFamily = FunctionName.contains("scanf");
EugeneZelenko wrote:
```suggestion
const bool IsScanfFamily = FunctionName.contains("scanf");
```
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/EugeneZelenko commented: Missing Release Notes entry. How about custom `printf/scanf` functions? Are similar functionality covered by `-Wformat` or Clang Static Analyzer? https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/EugeneZelenko edited https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp updated
https://github.com/llvm/llvm-project/pull/168691
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH 1/3] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
github-actions[bot] wrote: # :penguin: Linux x64 Test Results * 3054 tests passed * 7 tests skipped https://github.com/llvm/llvm-project/pull/168691 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
github-actions[bot] wrote:
:warning: C/C++ code linter clang-tidy found issues in your code. :warning:
You can test this locally with the following command:
```bash
git diff -U0 origin/main...HEAD --
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp |
python3 clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py \
-path build -p1 -quiet
```
View the output from clang-tidy here.
```
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h:28:8: warning:
function
'clang::tidy::bugprone::UnsafeFormatStringCheck::hasUnboundedStringSpecifier'
has a definition with different parameter names
[readability-inconsistent-declaration-parameter-name]
28 | bool hasUnboundedStringSpecifier(StringRef FormatString, bool
IsScanfFamily);
|^
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp:71:31: note:
the definition seen here
71 | bool UnsafeFormatStringCheck::hasUnboundedStringSpecifier(StringRef Fmt,
| ^
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h:28:8: note:
differing parameters are named here: ('FormatString'), in definition: ('Fmt')
28 | bool hasUnboundedStringSpecifier(StringRef FormatString, bool
IsScanfFamily);
|^
| Fmt
```
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
github-actions[bot] wrote:
:warning: C/C++ code formatter, clang-format found issues in your code.
:warning:
You can test this locally with the following command:
``bash
git-clang-format --diff origin/main HEAD --extensions cpp,h,c --
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
--diff_from_common_commit
``
:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:
View the diff from clang-format here.
``diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 15e8a6c3a..940d13bf6 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -106,8 +106,8 @@
#include "UnhandledSelfAssignmentCheck.h"
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
-#include "UnsafeFunctionsCheck.h"
#include "UnsafeFormatStringCheck.h"
+#include "UnsafeFunctionsCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
index cd8f6b85e..48a05aa27 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -61,11 +61,15 @@ void UnsafeFormatStringCheck::check(const
MatchFinder::MatchResult &Result) {
if (!hasUnboundedStringSpecifier(FormatString, IsScanfFamily))
return;
- auto Diag = diag(Call->getBeginLoc(),
- IsScanfFamily
- ? "format specifier '%%s' without field width may cause
buffer overflow; consider using '%%Ns' where N limits input length"
- : "format specifier '%%s' without precision may cause
buffer overflow; consider using '%%.Ns' where N limits output length")
- << Call->getSourceRange();
+ auto Diag =
+ diag(
+ Call->getBeginLoc(),
+ IsScanfFamily
+ ? "format specifier '%%s' without field width may cause buffer "
+"overflow; consider using '%%Ns' where N limits input length"
+ : "format specifier '%%s' without precision may cause buffer "
+"overflow; consider using '%%.Ns' where N limits output
length")
+ << Call->getSourceRange();
}
bool UnsafeFormatStringCheck::hasUnboundedStringSpecifier(StringRef Fmt,
``
https://github.com/llvm/llvm-project/pull/168691
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
llvmbot wrote:
@llvm/pr-subscribers-clang-tools-extra
Author: Daniel Krupp (dkrupp)
Changes
Adds a new check which warns for the usage of unbounded %s format string
specifiers in the sprintf and scanf family functions, which may cause buffer
overlfow.
---
Patch is 24.95 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/168691.diff
8 Files Affected:
- (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3)
- (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1)
- (added) clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
(+149)
- (added) clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h (+34)
- (added)
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
(+73)
- (added)
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
(+57)
- (added)
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
(+243)
- (added)
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
(+58)
``diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
llvmbot wrote:
@llvm/pr-subscribers-clang-tidy
Author: Daniel Krupp (dkrupp)
Changes
Adds a new check which warns for the usage of unbounded %s format string
specifiers in the sprintf and scanf family functions, which may cause buffer
overlfow.
---
Patch is 24.95 KiB, truncated to 20.00 KiB below, full version:
https://github.com/llvm/llvm-project/pull/168691.diff
8 Files Affected:
- (modified) clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp (+3)
- (modified) clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt (+1)
- (added) clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
(+149)
- (added) clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h (+34)
- (added)
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
(+73)
- (added)
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
(+57)
- (added)
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
(+243)
- (added)
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
(+58)
``diff
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasParent(translationUnitDecl(),
+ anyOf(hasArgument(0, stringLiteral().bind("format")),
+ hasArgument(1, stringLiteral().bind("format"
+ .bind("call"),
+ this);
+}
+
+void UnsafeFormatStringCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs("call");
+ const auto *Format = Result.Nodes.getNodeAs("format");
+
+ if (!Call || !Format)
+return;
+
+ std::string FormatString;
+ if (Format->getCharByteWidth() == 1) {
+FormatString = Format->getString().str();
+ } e
[clang-tools-extra] [clang-tidy] New bugprone-unsafe-format-string check (PR #168691)
https://github.com/dkrupp created
https://github.com/llvm/llvm-project/pull/168691
Adds a new check which warns for the usage of unbounded %s format string
specifiers in the sprintf and scanf family functions, which may cause buffer
overlfow.
>From 03433eec012de3ad51e44d0d96721d004ef1c7d7 Mon Sep 17 00:00:00 2001
From: Daniel Krupp
Date: Thu, 30 Oct 2025 13:39:23 +0100
Subject: [PATCH] [clang-tidy] New bugprone-unsafe-format-string check
Adds a new check which warns for the usage of unbounded %s format string
specifiers
in the sprintf and scanf family functions, which may cause buffer overlfow.
---
.../bugprone/BugproneTidyModule.cpp | 3 +
.../clang-tidy/bugprone/CMakeLists.txt| 1 +
.../bugprone/UnsafeFormatStringCheck.cpp | 149 +++
.../bugprone/UnsafeFormatStringCheck.h| 34 +++
.../checks/bugprone/unsafe-format-string.rst | 73 ++
.../system-header-simulator.h | 57
.../checkers/bugprone/unsafe-format-string.c | 243 ++
.../bugprone/unsafe-format-string.cpp | 58 +
8 files changed, 618 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/bugprone/unsafe-format-string.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unsafe-format-string/system-header-simulator.h
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.c
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/bugprone/unsafe-format-string.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 6859dc97c112a..15e8a6c3afb6a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -107,6 +107,7 @@
#include "UnintendedCharOstreamOutputCheck.h"
#include "UniquePtrArrayMismatchCheck.h"
#include "UnsafeFunctionsCheck.h"
+#include "UnsafeFormatStringCheck.h"
#include "UnusedLocalNonTrivialVariableCheck.h"
#include "UnusedRaiiCheck.h"
#include "UnusedReturnValueCheck.h"
@@ -308,6 +309,8 @@ class BugproneModule : public ClangTidyModule {
"bugprone-crtp-constructor-accessibility");
CheckFactories.registerCheck(
"bugprone-unsafe-functions");
+CheckFactories.registerCheck(
+"bugprone-unsafe-format-string");
CheckFactories.registerCheck(
"bugprone-unused-local-non-trivial-variable");
CheckFactories.registerCheck("bugprone-unused-raii");
diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index db1256d91d311..0e9439423ce5a 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -108,6 +108,7 @@ add_clang_library(clangTidyBugproneModule STATIC
UnhandledSelfAssignmentCheck.cpp
UniquePtrArrayMismatchCheck.cpp
UnsafeFunctionsCheck.cpp
+ UnsafeFormatStringCheck.cpp
UnusedLocalNonTrivialVariableCheck.cpp
UnusedRaiiCheck.cpp
UnusedReturnValueCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
new file mode 100644
index 0..cd8f6b85ee1e2
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/UnsafeFormatStringCheck.cpp
@@ -0,0 +1,149 @@
+//===--- UnsafeFormatStringCheck.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 "UnsafeFormatStringCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "llvm/Support/ConvertUTF.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+UnsafeFormatStringCheck::UnsafeFormatStringCheck(StringRef Name,
+ ClangTidyContext *Context)
+: ClangTidyCheck(Name, Context) {}
+
+void UnsafeFormatStringCheck::registerMatchers(MatchFinder *Finder) {
+ // Matches sprintf and scanf family functions in std namespace in C++ and
+ // globally in C.
+ auto VulnerableFunctions =
+ hasAnyName("sprintf", "vsprintf", "scanf", "fscanf", "sscanf", "vscanf",
+ "vfscanf", "vsscanf", "wscanf", "fwscanf", "swscanf",
+ "vwscanf", "vfwscanf", "vswscanf");
+ Finder->addMatcher(
+ callExpr(callee(functionDecl(VulnerableFunctions,
+ anyOf(isInStdNamespace(),
+ hasPa
