From f57c413e444b441fa75ae8911d183c19b53bdd56 Mon Sep 17 00:00:00 2001 From: Jean-Daniel Dupas <devlists@shadowlab.org> Date: Tue, 21 Feb 2012 20:00:53 +0000 Subject: [PATCH] =?UTF-8?q?When=20calling=20a=20non=20variadic=20format=20?= =?UTF-8?q?function(vprintf,=20vscanf,=20NSLogv,=20=E2=80=A6),=20warn=20if?= =?UTF-8?q?=20the=20format=20string=20argument=20is=20a=20parameter=20that?= =?UTF-8?q?=20is=20not=20itself=20declared=20as=20a=20format=20string=20wi?= =?UTF-8?q?th=20compatible=20format.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@151080 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaChecking.cpp | 28 +++++++++++++++++------ test/Sema/format-strings.c | 35 ++++++++++++++++++++++++----- test/SemaCXX/format-strings.cpp | 30 ++++++++++++++++++++++--- test/SemaObjC/format-strings-objc.m | 29 ++++++++++++++++++++++-- 4 files changed, 104 insertions(+), 18 deletions(-) diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 1d75ef6e6f5..9454a751550 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -1447,13 +1447,27 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, Expr **Args, // vprintf(fmt, ap); // Do NOT emit a warning about "fmt". // ... // - // - // FIXME: We don't have full attribute support yet, so just check to see - // if the argument is a DeclRefExpr that references a parameter. We'll - // add proper support for checking the attribute later. - if (HasVAListArg) - if (isa<ParmVarDecl>(VD)) - return true; + if (HasVAListArg) { + if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(VD)) { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(PV->getDeclContext())) { + int PVIndex = PV->getFunctionScopeIndex() + 1; + for (specific_attr_iterator<FormatAttr> + i = ND->specific_attr_begin<FormatAttr>(), + e = ND->specific_attr_end<FormatAttr>(); i != e ; ++i) { + FormatAttr *PVFormat = *i; + // adjust for implicit parameter + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(ND)) + if (MD->isInstance()) + ++PVIndex; + // We also check if the formats are compatible. + // We can't pass a 'scanf' string to a 'printf' function. + if (PVIndex == PVFormat->getFormatIdx() && + Type == GetFormatStringType(PVFormat)) + return true; + } + } + } + } } return false; diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index e6ce6e3c4ab..55b8201fd39 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -14,6 +14,8 @@ int vprintf(const char *restrict, va_list); int vsnprintf(char *, size_t, const char *, va_list); int vsprintf(char *restrict, const char *restrict, va_list); // expected-note{{passing argument to parameter here}} +int vscanf(const char *restrict format, va_list arg); + char * global_fmt; void check_string_literal( FILE* fp, const char* s, char *buf, ... ) { @@ -23,21 +25,23 @@ void check_string_literal( FILE* fp, const char* s, char *buf, ... ) { va_start(ap,buf); printf(s); // expected-warning {{format string is not a string literal}} - vprintf(s,ap); // // no-warning + vprintf(s,ap); // expected-warning {{format string is not a string literal}} fprintf(fp,s); // expected-warning {{format string is not a string literal}} - vfprintf(fp,s,ap); // no-warning + vfprintf(fp,s,ap); // expected-warning {{format string is not a string literal}} asprintf(&b,s); // expected-warning {{format string is not a string lit}} - vasprintf(&b,s,ap); // no-warning + vasprintf(&b,s,ap); // expected-warning {{format string is not a string literal}} sprintf(buf,s); // expected-warning {{format string is not a string literal}} snprintf(buf,2,s); // expected-warning {{format string is not a string lit}} __builtin___sprintf_chk(buf,0,-1,s); // expected-warning {{format string is not a string literal}} __builtin___snprintf_chk(buf,2,0,-1,s); // expected-warning {{format string is not a string lit}} - vsprintf(buf,s,ap); // no-warning - vsnprintf(buf,2,s,ap); // no-warning + vsprintf(buf,s,ap); // expected-warning {{format string is not a string lit}} + vsnprintf(buf,2,s,ap); // expected-warning {{format string is not a string lit}} vsnprintf(buf,2,global_fmt,ap); // expected-warning {{format string is not a string literal}} - __builtin___vsnprintf_chk(buf,2,0,-1,s,ap); // no-warning + __builtin___vsnprintf_chk(buf,2,0,-1,s,ap); // expected-warning {{format string is not a string lit}} __builtin___vsnprintf_chk(buf,2,0,-1,global_fmt,ap); // expected-warning {{format string is not a string literal}} + vscanf(s, ap); // expected-warning {{format string is not a string literal}} + // rdar://6079877 printf("abc" "%*d", 1, 1); // no-warning @@ -51,6 +55,25 @@ def" printf("%*d", (unsigned) 1, 1); // no-warning } +__attribute__((__format__ (__printf__, 2, 4))) +void check_string_literal2( FILE* fp, const char* s, char *buf, ... ) { + char * b; + va_list ap; + va_start(ap,buf); + + printf(s); // expected-warning {{format string is not a string literal}} + vprintf(s,ap); // no-warning + fprintf(fp,s); // expected-warning {{format string is not a string literal}} + vfprintf(fp,s,ap); // no-warning + asprintf(&b,s); // expected-warning {{format string is not a string lit}} + vasprintf(&b,s,ap); // no-warning + sprintf(buf,s); // expected-warning {{format string is not a string literal}} + snprintf(buf,2,s); // expected-warning {{format string is not a string lit}} + __builtin___vsnprintf_chk(buf,2,0,-1,s,ap); // no-warning + + vscanf(s, ap); // expected-warning {{format string is not a string literal}} +} + void check_conditional_literal(const char* s, int i) { printf(i == 1 ? "yes" : "no"); // no-warning printf(i == 0 ? (i == 1 ? "yes" : "no") : "dont know"); // no-warning diff --git a/test/SemaCXX/format-strings.cpp b/test/SemaCXX/format-strings.cpp index 0d5b62598d1..74fc7a7810d 100644 --- a/test/SemaCXX/format-strings.cpp +++ b/test/SemaCXX/format-strings.cpp @@ -1,8 +1,11 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral -pedantic %s +#include <stdarg.h> + extern "C" { extern int scanf(const char *restrict, ...); extern int printf(const char *restrict, ...); +extern int vprintf(const char *restrict, va_list); } void f(char **sp, float *fp) { @@ -23,11 +26,12 @@ class Foo { public: const char *gettext(const char *fmt) __attribute__((format_arg(2))); - int scanf(const char *restrict, ...) __attribute__((format(scanf, 2, 3))); - int printf(const char *restrict, ...) __attribute__((format(printf, 2, 3))); + int scanf(const char *, ...) __attribute__((format(scanf, 2, 3))); + int printf(const char *, ...) __attribute__((format(printf, 2, 3))); + int printf2(const char *, ...); static const char *gettext_static(const char *fmt) __attribute__((format_arg(1))); - static int printf_static(const char *restrict, ...) __attribute__((format(printf, 1, 2))); + static int printf_static(const char *fmt, ...) __attribute__((format(printf, 1, 2))); }; void h(int *i) { @@ -52,3 +56,23 @@ void rdar8269537(const char *f) test_null_format(__null); // no-warning test_null_format(f); // expected-warning {{not a string literal}} } + +int Foo::printf(const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + const char * const format = fmt; + vprintf(format, ap); // no-warning + + const char *format2 = fmt; + vprintf(format2, ap); // expected-warning{{format string is not a string literal}} + + return 0; +} + +int Foo::printf2(const char *fmt, ...) { + va_list ap; + va_start(ap,fmt); + vprintf(fmt, ap); // expected-warning{{format string is not a string literal}} + + return 0; +} diff --git a/test/SemaObjC/format-strings-objc.m b/test/SemaObjC/format-strings-objc.m index 675729ca1e9..a2a926c7d01 100644 --- a/test/SemaObjC/format-strings-objc.m +++ b/test/SemaObjC/format-strings-objc.m @@ -9,11 +9,13 @@ // portable to non-Mac platforms. //===----------------------------------------------------------------------===// +#include <stdarg.h> + typedef signed char BOOL; typedef unsigned int NSUInteger; @class NSString, Protocol; -extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); -extern void NSLog(NSString *format, ...) __attribute__((format(__NSString__, 1, 2))); +extern void NSLog(NSString *format, ...); +extern void NSLogv(NSString *format, va_list args); typedef struct _NSZone NSZone; @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; @protocol NSObject - (BOOL)isEqual:(id)object; @end @@ -151,3 +153,26 @@ void test_percent_C() { void test_toll_free_bridging(CFStringRef x) { NSLog(@"%@", x); // no-warning } + +@interface Bar ++ (void)log:(NSString *)fmt, ...; ++ (void)log2:(NSString *)fmt, ... __attribute__((format(NSString, 1, 2))); +@end + +@implementation Bar + ++ (void)log:(NSString *)fmt, ... { + va_list ap; + va_start(ap,fmt); + NSLogv(fmt, ap); // expected-warning{{format string is not a string literal}} + va_end(ap); +} + ++ (void)log2:(NSString *)fmt, ... { + va_list ap; + va_start(ap,fmt); + NSLogv(fmt, ap); // no-warning + va_end(ap); +} + +@end -- GitLab