diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -250,6 +250,7 @@ def GNUFlexibleArrayUnionMember : DiagGroup<"gnu-flexible-array-union-member">; def GNUFoldingConstant : DiagGroup<"gnu-folding-constant">; def FormatInsufficientArgs : DiagGroup<"format-insufficient-args">; +def FormatNSpecifier : DiagGroup<"format-n-specifier">; def FormatExtraArgs : DiagGroup<"format-extra-args">; def FormatZeroLength : DiagGroup<"format-zero-length">; @@ -900,7 +901,7 @@ def Format : DiagGroup<"format", [FormatExtraArgs, FormatZeroLength, NonNull, FormatSecurity, FormatY2K, FormatInvalidSpecifier, - FormatInsufficientArgs]>, + FormatInsufficientArgs, FormatNSpecifier]>, DiagCategory<"Format String Issue">; def FormatNonLiteral : DiagGroup<"format-nonliteral">; def Format2 : DiagGroup<"format=2", 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 @@ -9226,6 +9226,8 @@ def warn_printf_insufficient_data_args : Warning< "more '%%' conversions than data arguments">, InGroup; +def warn_printf_n_specifier : Warning< + "usage of '%%n' is unsafe">, InGroup; def warn_printf_data_arg_not_used : Warning< "data argument not used by format string">, InGroup; def warn_format_invalid_conversion : Warning< 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 @@ -8965,6 +8965,13 @@ return true; } + // %n is unsafe + if (CS.getKind() == ConversionSpecifier::nArg) { + EmitFormatDiagnostic( + S.PDiag(diag::warn_printf_n_specifier), getLocationOfByte(CS.getStart()), /*IsStringLocation*/true, + getSpecifierRange(startSpecifier, specifierLen)); + } + // Only scalars are allowed for os_trace. if (FSType == Sema::FST_OSTrace && (CS.getKind() == ConversionSpecifier::PArg || 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 @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple %itanium_abi_triple -fsyntax-only -fblocks -verify %s -// RUN: %clang_cc1 -triple %itanium_abi_triple -fdiagnostics-parseable-fixits -fblocks %s 2>&1 | FileCheck %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -Wno-format-n-specifier -fsyntax-only -fblocks -verify %s +// RUN: %clang_cc1 -triple %itanium_abi_triple -Wno-format-n-specifier -fdiagnostics-parseable-fixits -fblocks %s 2>&1 | FileCheck %s @class NSString; extern void NSLog(NSString *, ...); diff --git a/clang/test/Misc/warning-wall.c b/clang/test/Misc/warning-wall.c --- a/clang/test/Misc/warning-wall.c +++ b/clang/test/Misc/warning-wall.c @@ -17,6 +17,7 @@ CHECK-NEXT: -Wformat-y2k CHECK-NEXT: -Wformat-invalid-specifier CHECK-NEXT: -Wformat-insufficient-args +CHECK-NEXT: -Wformat-n-specifier CHECK-NEXT: -Wfor-loop-analysis CHECK-NEXT: -Wframe-address CHECK-NEXT: -Wimplicit diff --git a/clang/test/Sema/format-string-percentn.c b/clang/test/Sema/format-string-percentn.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/format-string-percentn.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int printf(const char *restrict, ...); +void percentn(volatile int *n) { + printf("%n", n); //expected-warning{{usage of '%n' is unsafe}} +} diff --git a/clang/test/Sema/format-strings-size_t.c b/clang/test/Sema/format-strings-size_t.c --- a/clang/test/Sema/format-strings-size_t.c +++ b/clang/test/Sema/format-strings-size_t.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-format-n-specifier -fsyntax-only -verify %s int printf(char const *, ...); 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,5 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs %s -// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-format-n-specifier -Wformat-nonliteral -isystem %S/Inputs %s +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-format-n-specifier -Wformat-nonliteral -isystem %S/Inputs -fno-signed-char %s #include #include @@ -644,6 +644,7 @@ test14_bar("%", "%d", p); // expected-warning{{incomplete format specifier}} } +#pragma GCC diagnostic ignored "-Wformat-n-specifier" 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 *'}}