[clang] 9b3b3d4 - [Clang][Sema] Add fortify warnings for vsprintf (#230672)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 15 10:58:40 PDT 2026
Author: Venkatesh Srinivasan
Date: 2026-10-15T17:58:32Z
New Revision: 9b3b3d45e35b9c7f18ea293e6ea6ca6106ec915a
URL: https://github.com/llvm/llvm-project/commit/9b3b3d45e35b9c7f18ea293e6ea6ca6106ec915a
DIFF: https://github.com/llvm/llvm-project/commit/9b3b3d45e35b9c7f18ea293e6ea6ca6106ec915a.diff
LOG: [Clang][Sema] Add fortify warnings for vsprintf (#230672)
Extend the `sprintf` `-Wfortify-source` format overflow check in `Sema::checkFortifiedBuiltinMemoryFunction` to also cover `vsprintf`, `__builtin_vsprintf`, `__builtin___vsprintf_chk`, and `__builtin_sprintf`.
Also translate the format string argument index via `Checker.TranslateIndex`, fixing a latent bug when `sprintf` or `__builtin___sprintf_chk` is wrapped with `__attribute__((diagnose_as_builtin(...)))`.
Part of #142230
Assisted-by: Gemini
Added:
Modified:
clang/docs/ReleaseNotes.md
clang/lib/Sema/SemaChecking.cpp
clang/test/Sema/attr-diagnose-as-builtin.c
clang/test/Sema/warn-fortify-source.c
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ed30ac6566d55..9e5d92cae727c 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -415,6 +415,10 @@ features cannot lower the translation-unit ABI level;
- `-Wfortify-source` now diagnoses when `poll`, `ppoll`, or `ppoll64` is called
with a descriptor count whose total size exceeds the `fds` array size.
+- `-Wfortify-source` now diagnoses when `vsprintf`, `__builtin_vsprintf`,
+ `__builtin___vsprintf_chk`, or `__builtin_sprintf` is called with a format
+ string whose output exceeds the destination buffer size.
+
- The `cannot overload a member function` diagnostic now describes the previous
declaration first, matching the order in which the declarations appear in the
source. (#GH219803)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 86f7654f42ff2..51506c62d4e35 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1406,9 +1406,18 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
}
case Builtin::BIsprintf:
- case Builtin::BI__builtin___sprintf_chk: {
- size_t FormatIndex = BuiltinID == Builtin::BIsprintf ? 1 : 3;
- auto *FormatExpr = TheCall->getArg(FormatIndex)->IgnoreParenImpCasts();
+ case Builtin::BI__builtin_sprintf:
+ case Builtin::BI__builtin___sprintf_chk:
+ case Builtin::BIvsprintf:
+ case Builtin::BI__builtin_vsprintf:
+ case Builtin::BI__builtin___vsprintf_chk: {
+ bool IsChk = BuiltinID == Builtin::BI__builtin___sprintf_chk ||
+ BuiltinID == Builtin::BI__builtin___vsprintf_chk;
+ std::optional<unsigned> FormatIndex = Checker.TranslateIndex(IsChk ? 3 : 1);
+ if (!FormatIndex)
+ return;
+ const auto *FormatExpr =
+ TheCall->getArg(*FormatIndex)->IgnoreParenImpCasts();
StringRef FormatStrRef;
size_t StrLen;
@@ -1423,7 +1432,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD,
: diag::warn_format_overflow_non_kprintf;
AccessSize = llvm::APSInt::getUnsigned(H.getSizeLowerBound())
.extOrTrunc(SizeTypeWidth);
- if (BuiltinID == Builtin::BI__builtin___sprintf_chk) {
+ if (IsChk) {
BufferSize = Checker.ComputeExplicitObjectSizeArgument(2);
} else {
BufferSize = Checker.ComputeSizeArgument(0);
diff --git a/clang/test/Sema/attr-diagnose-as-builtin.c b/clang/test/Sema/attr-diagnose-as-builtin.c
index 10962de1a3f0d..3634b1b251893 100644
--- a/clang/test/Sema/attr-diagnose-as-builtin.c
+++ b/clang/test/Sema/attr-diagnose-as-builtin.c
@@ -83,6 +83,15 @@ void warn_mysccanf(void) {
mysscanf("", "%10s", buf); // expected-warning{{'sscanf' may overflow; destination buffer in argument 3 has size 10, but the corresponding specifier may require size 11}}
}
+int my_vsprintf(const char *format, char *str, __builtin_va_list ap) __attribute__((diagnose_as_builtin(__builtin_vsprintf, 2, 1, 3)));
+
+void warn_my_vsprintf(void) {
+ char buf[6];
+ __builtin_va_list list;
+ my_vsprintf("hello", buf, list);
+ my_vsprintf("hello!", buf, list); // expected-warning{{'vsprintf' will always overflow; destination buffer has size 6, but format string expands to at least 7}}
+}
+
#ifdef __cplusplus
template <class T>
diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c
index 73a10070008f4..3e61e73268206 100644
--- a/clang/test/Sema/warn-fortify-source.c
+++ b/clang/test/Sema/warn-fortify-source.c
@@ -27,9 +27,12 @@ extern "C" {
#endif
extern int sprintf(char *str, const char *format, ...);
+extern int vsprintf(char *str, const char *format, __builtin_va_list ap);
#if defined(USE_BUILTINS)
#define memcpy(x,y,z) __builtin_memcpy(x,y,z)
+#define sprintf(x, ...) __builtin_sprintf(x, __VA_ARGS__)
+#define vsprintf(x, y, z) __builtin_vsprintf(x, y, z)
// Also test the Windows winsock2.h signature where len is a signed int.
int recv(int, char *, int, int);
int recvfrom(int, char *, int, int, struct sockaddr *, int *);
@@ -299,6 +302,33 @@ void call_sprintf(void) {
sprintf(buf, "5%.1e", 9.f); // expected-warning {{'sprintf' will always overflow; destination buffer has size 6, but format string expands to at least 8}}
}
+void call_vsprintf_chk(char *buf) {
+ __builtin_va_list list;
+ __builtin___vsprintf_chk(buf, 1, 6, "hell\n", list);
+ __builtin___vsprintf_chk(buf, 1, 5, "hell\n", list); // expected-warning {{'vsprintf' will always overflow; destination buffer has size 5, but format string expands to at least 6}}
+ __builtin___vsprintf_chk(buf, 1, 6, "hell\0 boy", list); // expected-warning {{format string contains '\0' within the string body}}
+ __builtin___vsprintf_chk(buf, 1, 2, "hell\0 boy", list); // expected-warning {{format string contains '\0' within the string body}} \
+ // expected-warning {{'vsprintf' will always overflow; destination buffer has size 2, but format string expands to at least 5}}
+ __builtin___vsprintf_chk(buf, 1, 2, "%d", list);
+ __builtin___vsprintf_chk(buf, 1, 1, "%d", list); // expected-warning {{'vsprintf' will always overflow; destination buffer has size 1, but format string expands to at least 2}}
+ __builtin___vsprintf_chk(buf, 1, 9, "%f", list);
+ __builtin___vsprintf_chk(buf, 1, 8, "%f", list); // expected-warning {{'vsprintf' will always overflow; destination buffer has size 8, but format string expands to at least 9}}
+}
+
+void call_vsprintf(void) {
+ char buf[6];
+ __builtin_va_list list;
+ vsprintf(buf, "hell\0 boy", list); // expected-warning {{format string contains '\0' within the string body}}
+ vsprintf(buf, "hello b\0y", list); // expected-warning {{format string contains '\0' within the string body}} \
+ // expected-warning {{'vsprintf' will always overflow; destination buffer has size 6, but format string expands to at least 8}}
+ vsprintf(buf, "hello", list);
+ vsprintf(buf, "hello!", list); // expected-warning {{'vsprintf' will always overflow; destination buffer has size 6, but format string expands to at least 7}}
+ vsprintf(buf, "1234%d", list);
+ vsprintf(buf, "12345%d", list); // expected-warning {{'vsprintf' will always overflow; destination buffer has size 6, but format string expands to at least 7}}
+ vsprintf(buf, "%.3f", list);
+ vsprintf(buf, "5%.3f", list); // expected-warning {{'vsprintf' will always overflow; destination buffer has size 6, but format string expands to at least 7}}
+}
+
void call_umask(mode_t runtime_mode) {
umask(0);
umask(022);
More information about the cfe-commits
mailing list