diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index eddf9c50033e1..086a88eba6e1b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -965,6 +965,10 @@ def warn_fortify_strlen_overflow: Warning< " but the source string has length %2 (including NUL byte)">, InGroup; +def warn_fortify_literal_copy_too_large : Warning< + "copying %0 bytes into buffer of size %1 (including null terminator)">, + InGroup; + def subst_format_overflow : TextSubstitution< "'%0' will always overflow; destination buffer has size %1," " but format string expands to at least %2">; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index de8b965144971..974607648b939 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -1247,6 +1247,7 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, std::optional DestinationSize; unsigned DiagID = 0; bool IsChkVariant = false; + bool UseLiteralCopyOverflowDiag = false; auto GetFunctionName = [&]() { std::string FunctionNameStr = @@ -1276,6 +1277,10 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, DiagID = diag::warn_fortify_strlen_overflow; SourceSize = ComputeStrLenArgument(1); DestinationSize = ComputeSizeArgument(0); + UseLiteralCopyOverflowDiag = + (BuiltinID == Builtin::BI__builtin_strcpy || + BuiltinID == Builtin::BIstrcpy) && + isa(TheCall->getArg(1)->IgnoreParenCasts()); break; } @@ -1286,6 +1291,9 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, SourceSize = ComputeStrLenArgument(1); DestinationSize = ComputeExplicitObjectSizeArgument(2); IsChkVariant = true; + UseLiteralCopyOverflowDiag = + BuiltinID == Builtin::BI__builtin___strcpy_chk && + isa(TheCall->getArg(1)->IgnoreParenCasts()); break; } @@ -1470,6 +1478,14 @@ void Sema::checkFortifiedBuiltinMemoryFunction(FunctionDecl *FD, SmallString<16> SourceStr; DestinationSize->toString(DestinationStr, /*Radix=*/10); SourceSize->toString(SourceStr, /*Radix=*/10); + + if (UseLiteralCopyOverflowDiag) { + DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall, + PDiag(diag::warn_fortify_literal_copy_too_large) + << SourceStr << DestinationStr); + return; + } + DiagRuntimeBehavior(TheCall->getBeginLoc(), TheCall, PDiag(DiagID) << FunctionName << DestinationStr << SourceStr); diff --git a/clang/test/Sema/warn-fortify-literal-copy-overflow.c b/clang/test/Sema/warn-fortify-literal-copy-overflow.c new file mode 100644 index 0000000000000..c3e96dbae217c --- /dev/null +++ b/clang/test/Sema/warn-fortify-literal-copy-overflow.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -Wfortify-source -verify %s + +char *strcpy(char *, const char *); + +void literal_strcpy_overflow(void) { + char buf[4]; + char ok[5]; + strcpy(buf, "abcd"); // expected-warning{{copying 5 bytes into buffer of size 4 (including null terminator)}} + strcpy(ok, "abcd"); +}