diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -726,7 +726,8 @@ virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, - unsigned specifierLen) { + unsigned specifierLen, + const TargetInfo &Target) { return true; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9369,6 +9369,9 @@ def warn_printf_invalid_objc_flag: Warning< "'%0' is not a valid object format flag">, InGroup; +def warn_printf_narg_not_supported : Warning< + "'%%n' specifier not supported on this platform">, + InGroup; def warn_scanf_scanlist_incomplete : Warning< "no closing ']' for '%%[' in scanf format string">, InGroup; diff --git a/clang/lib/AST/OSLog.cpp b/clang/lib/AST/OSLog.cpp --- a/clang/lib/AST/OSLog.cpp +++ b/clang/lib/AST/OSLog.cpp @@ -56,8 +56,8 @@ } bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, - const char *StartSpecifier, - unsigned SpecifierLen) override { + const char *StartSpecifier, unsigned SpecifierLen, + const TargetInfo &) override { if (!FS.consumesDataArgument() && FS.getConversionSpecifier().getKind() != clang::analyze_format_string::ConversionSpecifier::PrintErrno) diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -428,7 +428,7 @@ continue; // We have a format specifier. Pass it to the callback. if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(), - I - FSR.getStart())) + I - FSR.getStart(), Target)) return true; } assert(I == E && "Format string not exhausted"); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -499,7 +499,8 @@ 1 /* null byte always written by sprintf */) {} bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, - const char *, unsigned SpecifierLen) override { + const char *, unsigned SpecifierLen, + const TargetInfo &) override { const size_t FieldWidth = computeFieldWidth(FS); const size_t Precision = computePrecision(FS); @@ -8909,8 +8910,8 @@ void handleInvalidMaskType(StringRef MaskType) override; bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen) override; + const char *startSpecifier, unsigned specifierLen, + const TargetInfo &Target) override; bool checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, unsigned SpecifierLen, @@ -9169,11 +9170,9 @@ return false; } -bool -CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier - &FS, - const char *startSpecifier, - unsigned specifierLen) { +bool CheckPrintfHandler::HandlePrintfSpecifier( + const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, + unsigned specifierLen, const TargetInfo &Target) { using namespace analyze_format_string; using namespace analyze_printf; @@ -9305,6 +9304,15 @@ } } + const llvm::Triple &Triple = Target.getTriple(); + if (CS.getKind() == ConversionSpecifier::nArg && + (Triple.isAndroid() || Triple.isOSFuchsia())) { + EmitFormatDiagnostic(S.PDiag(diag::warn_printf_narg_not_supported), + getLocationOfByte(CS.getStart()), + /*IsStringLocation*/ false, + getSpecifierRange(startSpecifier, specifierLen)); + } + // Check for invalid use of field width if (!FS.hasValidFieldWidth()) { HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 0, diff --git a/clang/test/FixIt/format.m b/clang/test/FixIt/format.m --- a/clang/test/FixIt/format.m +++ b/clang/test/FixIt/format.m @@ -241,8 +241,13 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:14}:"%f" short x; +#if !defined(__ANDROID__) && !defined(__Fuchsia__) printf("%zn", &x); // expected-warning-re{{format specifies type 'ssize_t *' (aka '{{.+}}') but the argument has type 'short *'}} - // PrintfSpecifier::fixType doesn't handle %n, so a fix-it is not emitted, +#else + printf("%zn", &x); // expected-warning-re{{format specifies type 'ssize_t *' (aka '{{.+}}') but the argument has type 'short *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} +#endif // !defined(__ANDROID__) && !defined(__Fuchsia__) + // PrintfSpecifier::fixType doesn't handle %n, so a fix-it is not emitted, // see the comment in PrintfSpecifier::fixType in PrintfFormatString.cpp. } @@ -269,12 +274,21 @@ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:11-[[@LINE-1]]:14}:"%f" ptrdiff_t p3 = 0; +#if !defined(__ANDROID__) && !defined(__Fuchsia__) printf("%tn", &p3); // No warning. +#else + printf("%tn", &p3); // expected-warning{{'%n' specifier not supported on this platform}} +#endif // !defined(__ANDROID__) && !defined(__Fuchsia__) short x; +#if !defined(__ANDROID__) && !defined(__Fuchsia__) printf("%tn", &x); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'short *'}} // PrintfSpecifier::fixType doesn't handle %n, so a fix-it is not emitted, // see the comment in PrintfSpecifier::fixType in PrintfFormatString.cpp. +#else + printf("%tn", &x); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'short *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} +#endif // !defined(__ANDROID__) && !defined(__Fuchsia__) } void testEnum() { diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c --- a/clang/test/Sema/format-strings.c +++ b/clang/test/Sema/format-strings.c @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s // RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s +// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -triple=x86_64-unknown-fuchsia %s +// RUN: %clang_cc1 -fblocks -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -triple=x86_64-linux-android %s #include #include @@ -118,6 +120,8 @@ printf(i ? "%i\n" : "%i %s %s\n", i, s); // expected-warning{{more '%' conversions than data arguments}} } +#if !defined(__ANDROID__) && !defined(__Fuchsia__) + void check_writeback_specifier() { int x; @@ -154,6 +158,45 @@ // expected-note@-1{{did you mean to use 'll'?}} } +#else + +void check_writeback_specifier() +{ + int x; + printf("%n", &x); // expected-warning{{'%n' specifier not supported on this platform}} + + printf("%hhn", (signed char*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%hhn", (char*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%hhn", (unsigned char*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%hhn", (int*)0); // expected-warning{{format specifies type 'signed char *' but the argument has type 'int *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + + printf("%hn", (short*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%hn", (unsigned short*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%hn", (int*)0); // expected-warning{{format specifies type 'short *' but the argument has type 'int *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + + printf("%n", (int*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%n", (unsigned int*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%n", (char*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'char *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + + printf("%ln", (long*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%ln", (unsigned long*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%ln", (int*)0); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + + printf("%lln", (long long*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%lln", (unsigned long long*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%lln", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + + printf("%qn", (long long*)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%qn", (unsigned long long*)0); // expected-warning{{'%n' specifier not supported on this platform}} +} + +#endif // !defined(__ANDROID__) && !defined(__Fuchsia__) + void check_invalid_specifier(FILE* fp, char *buf) { printf("%s%lb%d","unix",10,20); // expected-warning {{invalid conversion specifier 'b'}} expected-warning {{data argument not used by format string}} @@ -386,14 +429,28 @@ // Bad flag usage printf("%#p", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'p' conversion specifier}} printf("%0d", -1); // no-warning + printf("%-p", (void *) 0); // no-warning +#if !defined(__ANDROID__) && !defined(__Fuchsia__) printf("%#n", (int *) 0); // expected-warning{{flag '#' results in undefined behavior with 'n' conversion specifier}} printf("%-n", (int *) 0); // expected-warning{{flag '-' results in undefined behavior with 'n' conversion specifier}} - printf("%-p", (void *) 0); // no-warning +#else + printf("%#n", (int *) 0); // expected-warning{{flag '#' results in undefined behavior with 'n' conversion specifier}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + printf("%-n", (int *) 0); // expected-warning{{flag '-' results in undefined behavior with 'n' conversion specifier}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} +#endif // !defined(__ANDROID__) && !defined(__Fuchsia__) // Bad optional amount use printf("%.2c", 'a'); // expected-warning{{precision used with 'c' conversion specifier, resulting in undefined behavior}} +#if !defined(__ANDROID__) && !defined(__Fuchsia__) + printf("%1n", (int *) 0); // expected-warning{{field width used with 'n' conversion specifier, resulting in undefined behavior}} + printf("%.9n", (int *) 0); // expected-warning{{precision used with 'n' conversion specifier, resulting in undefined behavior}} +#else printf("%1n", (int *) 0); // expected-warning{{field width used with 'n' conversion specifier, resulting in undefined behavior}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} printf("%.9n", (int *) 0); // expected-warning{{precision used with 'n' conversion specifier, resulting in undefined behavior}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} +#endif // #if !defined(__ANDROID__) && !defined(__Fuchsia__) // Ignored flags printf("% +f", 1.23); // expected-warning{{flag ' ' is ignored when flag '+' is present}} @@ -644,6 +701,8 @@ test14_bar("%", "%d", p); // expected-warning{{incomplete format specifier}} } +#if !defined(__ANDROID__) && !defined(__Fuchsia__) + void test_qualifiers(volatile int *vip, const int *cip, const volatile int *cvip) { printf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}} @@ -660,6 +719,29 @@ printf("%n", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}} } +#else + +void test_qualifiers(volatile int *vip, const int *cip, + const volatile int *cvip) { + printf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + printf("%n", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} + + printf("%n", vip); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%p", cip); // No warning. + printf("%p", cvip); // No warning. + + + typedef int* ip_t; + typedef const int* cip_t; + printf("%n", (ip_t)0); // expected-warning{{'%n' specifier not supported on this platform}} + printf("%n", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}} + // expected-warning@-1 {{'%n' specifier not supported on this platform}} +} + +#endif // #if !defined(__ANDROID__) && !defined(__Fuchsia__) + #pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic warning "-Wformat-security" //