From 8d31f2930968ddaeba91ffca392116e912f26fc3 Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Thu, 20 Feb 2014 17:05:38 +0000 Subject: [PATCH] Sema: Emit a warning for non-null terminated format strings and other pathological cases. PR18905. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@201795 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 ++ lib/Sema/SemaChecking.cpp | 20 ++++++++++++++++++-- test/Sema/format-strings.c | 15 +++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index a2415f7a95..62ae84595c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -6223,6 +6223,8 @@ def warn_format_string_is_wide_literal : Warning< "format string should not be a wide string">, InGroup; def warn_printf_format_string_contains_null_char : Warning< "format string contains '\\0' within the string body">, InGroup; +def warn_printf_format_string_not_null_terminated : Warning< + "format string is not null-terminated">, InGroup; def warn_printf_asterisk_missing_arg : Warning< "'%select{*|.*}0' specified field %select{width|precision}0 is missing a matching 'int' argument">, InGroup; diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index cfd042f945..f3f08dec97 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -3493,9 +3493,25 @@ void Sema::CheckFormatString(const StringLiteral *FExpr, // Str - The format string. NOTE: this is NOT null-terminated! StringRef StrRef = FExpr->getString(); const char *Str = StrRef.data(); - unsigned StrLen = StrRef.size(); + // Account for cases where the string literal is truncated in a declaration. + const ConstantArrayType *T = Context.getAsConstantArrayType(FExpr->getType()); + assert(T && "String literal not of constant array type!"); + size_t TypeSize = T->getSize().getZExtValue(); + size_t StrLen = std::min(std::max(TypeSize, size_t(1)) - 1, StrRef.size()); const unsigned numDataArgs = Args.size() - firstDataArg; - + + // Emit a warning if the string literal is truncated and does not contain an + // embedded null character. + if (TypeSize <= StrRef.size() && + StrRef.substr(0, TypeSize).find('\0') == StringRef::npos) { + CheckFormatHandler::EmitFormatDiagnostic( + *this, inFunctionCall, Args[format_idx], + PDiag(diag::warn_printf_format_string_not_null_terminated), + FExpr->getLocStart(), + /*IsStringLocation=*/true, OrigFormatExpr->getSourceRange()); + return; + } + // CHECK: empty format string? if (StrLen == 0 && numDataArgs > 0) { CheckFormatHandler::EmitFormatDiagnostic( diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index 6ed665c700..ad7b37c3e1 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -535,6 +535,21 @@ void pr9751() { 0.0); // expected-warning{{format specifies}} } +void pr18905() { + const char s1[] = "s\0%s"; // expected-note{{format string is defined here}} + const char s2[1] = "s"; // expected-note{{format string is defined here}} + const char s3[2] = "s\0%s"; // expected-warning{{initializer-string for char array is too long}} + const char s4[10] = "s"; + const char s5[0] = "%s"; // expected-warning{{initializer-string for char array is too long}} + // expected-note@-1{{format string is defined here}} + + printf(s1); // expected-warning{{format string contains '\0' within the string body}} + printf(s2); // expected-warning{{format string is not null-terminated}} + printf(s3); // no-warning + printf(s4); // no-warning + printf(s5); // expected-warning{{format string is not null-terminated}} +} + void __attribute__((format(strfmon,1,2))) monformat(const char *fmt, ...); void __attribute__((format(strftime,1,0))) dateformat(const char *fmt); -- 2.40.0