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); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
