diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index be5c9c24a9ce4..9e119a3ea801a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -272,6 +272,9 @@ Attribute Changes in Clang attribute, allowing the attribute to only be attached to the declaration. Prior, this would be treated as an error where the definition and declaration would have differing types. +- New format attributes ``gnu_printf``, ``gnu_scanf``, ``gnu_strftime`` and ``gnu_strfmon`` are added + as aliases for ``printf``, ``scanf``, ``strftime`` and ``strfmon``. (#GH16219) + Improvements to Clang's diagnostics ----------------------------------- - Added a separate diagnostic group ``-Wfunction-effect-redeclarations``, for the more pedantic diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d017d1f829015..5edfc29d93781 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -503,7 +503,6 @@ enum class FormatStringType { FreeBSDKPrintf, OSTrace, OSLog, - Syslog, Unknown }; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 0723516b38253..27e3456bc6681 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -6866,11 +6866,12 @@ StringRef Sema::GetFormatStringTypeName(FormatStringType FST) { FormatStringType Sema::GetFormatStringType(StringRef Flavor) { return llvm::StringSwitch(Flavor) - .Case("scanf", FormatStringType::Scanf) - .Cases("printf", "printf0", "syslog", FormatStringType::Printf) + .Cases("gnu_scanf", "scanf", FormatStringType::Scanf) + .Cases("gnu_printf", "printf", "printf0", "syslog", + FormatStringType::Printf) .Cases("NSString", "CFString", FormatStringType::NSString) - .Case("strftime", FormatStringType::Strftime) - .Case("strfmon", FormatStringType::Strfmon) + .Cases("gnu_strftime", "strftime", FormatStringType::Strftime) + .Cases("gnu_strfmon", "strfmon", FormatStringType::Strfmon) .Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FormatStringType::Kprintf) .Case("freebsd_kprintf", FormatStringType::FreeBSDKPrintf) @@ -6990,7 +6991,6 @@ bool Sema::CheckFormatArguments(ArrayRef Args, case FormatStringType::Kprintf: case FormatStringType::FreeBSDKPrintf: case FormatStringType::Printf: - case FormatStringType::Syslog: Diag(FormatLoc, diag::note_format_security_fixit) << FixItHint::CreateInsertion(FormatLoc, "\"%s\", "); break; @@ -9120,8 +9120,7 @@ static void CheckFormatString( if (Type == FormatStringType::Printf || Type == FormatStringType::NSString || Type == FormatStringType::Kprintf || Type == FormatStringType::FreeBSDKPrintf || - Type == FormatStringType::OSLog || Type == FormatStringType::OSTrace || - Type == FormatStringType::Syslog) { + Type == FormatStringType::OSLog || Type == FormatStringType::OSTrace) { bool IsObjC = Type == FormatStringType::NSString || Type == FormatStringType::OSTrace; if (ReferenceFormatString == nullptr) { @@ -9157,8 +9156,7 @@ bool Sema::CheckFormatStringsCompatible( if (Type != FormatStringType::Printf && Type != FormatStringType::NSString && Type != FormatStringType::Kprintf && Type != FormatStringType::FreeBSDKPrintf && - Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace && - Type != FormatStringType::Syslog) + Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace) return true; bool IsObjC = @@ -9192,8 +9190,7 @@ bool Sema::ValidateFormatString(FormatStringType Type, if (Type != FormatStringType::Printf && Type != FormatStringType::NSString && Type != FormatStringType::Kprintf && Type != FormatStringType::FreeBSDKPrintf && - Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace && - Type != FormatStringType::Syslog) + Type != FormatStringType::OSLog && Type != FormatStringType::OSTrace) return true; FormatStringLiteral RefLit = Str; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index b876911384f6f..a8dfa4d7df2d5 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3629,10 +3629,11 @@ static FormatAttrKind getFormatAttrKind(StringRef Format) { // Check for formats that get handled specially. .Case("NSString", NSStringFormat) .Case("CFString", CFStringFormat) - .Case("strftime", StrftimeFormat) + .Cases("gnu_strftime", "strftime", StrftimeFormat) // Otherwise, check for supported formats. - .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat) + .Cases("gnu_scanf", "scanf", "gnu_printf", "printf", "printf0", + "gnu_strfmon", "strfmon", SupportedFormat) .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat) .Cases("kprintf", "syslog", SupportedFormat) // OpenBSD. .Case("freebsd_kprintf", SupportedFormat) // FreeBSD. diff --git a/clang/test/Sema/attr-format.c b/clang/test/Sema/attr-format.c index 5b9e4d02bbaf9..820abd8ec527e 100644 --- a/clang/test/Sema/attr-format.c +++ b/clang/test/Sema/attr-format.c @@ -106,3 +106,11 @@ void b2(const char *a, ...) __attribute__((format(syslog, 1, 1))); // expecte void c2(const char *a, ...) __attribute__((format(syslog, 0, 2))); // expected-error {{'format' attribute parameter 2 is out of bounds}} void d2(const char *a, int c) __attribute__((format(syslog, 1, 2))); // expected-warning {{GCC requires a function with the 'format' attribute to be variadic}} void e2(char *str, int c, ...) __attribute__((format(syslog, 2, 3))); // expected-error {{format argument not a string type}} + +// gnu_printf +// same as format(printf(...))... +void a2(const char *a, ...) __attribute__((format(gnu_printf, 1, 2))); // no-error +void b2(const char *a, ...) __attribute__((format(gnu_printf, 1, 1))); // expected-error {{'format' attribute parameter 3 is out of bounds}} +void c2(const char *a, ...) __attribute__((format(gnu_printf, 0, 2))); // expected-error {{'format' attribute parameter 2 is out of bounds}} +void d2(const char *a, int c) __attribute__((format(gnu_printf, 1, 2))); // expected-warning {{GCC requires a function with the 'format' attribute to be variadic}} +void e2(char *str, int c, ...) __attribute__((format(gnu_printf, 2, 3))); // expected-error {{format argument not a string type}} diff --git a/clang/test/Sema/format-strings-scanf.c b/clang/test/Sema/format-strings-scanf.c index d1f694f3595cf..22c1cce2f989b 100644 --- a/clang/test/Sema/format-strings-scanf.c +++ b/clang/test/Sema/format-strings-scanf.c @@ -30,6 +30,7 @@ int fscanf(FILE * restrict, const char * restrict, ...) ; int scanf(const char * restrict, ...) ; int sscanf(const char * restrict, const char * restrict, ...) ; int my_scanf(const char * restrict, ...) __attribute__((__format__(__scanf__, 1, 2))); +int my_gnu_scanf(const char * restrict, ...) __attribute__((__format__(gnu_scanf, 1, 2))); int vscanf(const char * restrict, va_list); int vfscanf(FILE * restrict, const char * restrict, va_list); @@ -98,6 +99,7 @@ void test_variants(int *i, const char *s, ...) { fscanf(f, "%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}} sscanf(buf, "%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}} my_scanf("%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}} + my_gnu_scanf("%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}} va_list ap; va_start(ap, s); diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c index 4bff30c313c8f..103dd8ab5a85c 100644 --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -678,15 +678,21 @@ void pr18905(void) { } void __attribute__((format(strfmon,1,2))) monformat(const char *fmt, ...); +void __attribute__((format(gnu_strfmon,1,2))) gnu_monformat(const char *fmt, ...); void __attribute__((format(strftime,1,0))) dateformat(const char *fmt); +void __attribute__((format(gnu_strftime,1,0))) gnu_dateformat(const char *fmt); // Other formats void test_other_formats(void) { char *str = ""; monformat("", 1); // expected-warning{{format string is empty}} monformat(str); // expected-warning{{format string is not a string literal (potentially insecure)}} + gnu_monformat("", 1); // expected-warning{{format string is empty}} + gnu_monformat(str); // expected-warning{{format string is not a string literal (potentially insecure)}} dateformat(""); // expected-warning{{format string is empty}} dateformat(str); // no-warning (using strftime non-literal is not unsafe) + gnu_dateformat(""); // expected-warning{{format string is empty}} + gnu_dateformat(str); // no-warning (using strftime non-literal is not unsafe) } // Do not warn about unused arguments coming from system headers.