]> granicus.if.org Git - clang/commitdiff
Patch by Cristian Draghici:
authorTed Kremenek <kremenek@apple.com>
Thu, 11 Feb 2010 09:27:41 +0000 (09:27 +0000)
committerTed Kremenek <kremenek@apple.com>
Thu, 11 Feb 2010 09:27:41 +0000 (09:27 +0000)
Enhance the printf format string checking when using the format
specifier flags ' ', '0', '+' with the 'p' or 's' conversions (since
they are nonsensical and undefined).  This is similar to GCC's
checking.

Also warning when a precision is used with the 'p' conversin
specifier, since it has no meaning.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95869 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/Analyses/PrintfFormatString.h
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaChecking.cpp
test/Sema/format-strings.c

index a5c0b23c2d0f34b1a757d88aef6512ba89b16560..0153f724a721eeb5a662c783b1d6c82d52bad5bb 100644 (file)
@@ -73,6 +73,10 @@ public:
   const char *getStart() const {
     return Position;
   }
+  
+  llvm::StringRef getCharacters() const {
+    return llvm::StringRef(getStart(), getLength());
+  }
        
   bool consumesDataArgument() const {
     switch (kind) {
@@ -232,6 +236,7 @@ public:
   bool hasPlusPrefix() const { return (bool) HasPlusPrefix; }
   bool hasAlternativeForm() const { return (bool) HasAlternativeForm; }
   bool hasLeadingZeros() const { return (bool) HasLeadingZeroes; }  
+  bool hasSpacePrefix() const { return (bool) HasSpacePrefix; }
 };
 
 class FormatStringHandler {
index aacaf4e429369190d6bb1063ea3459255009ac1d..84dbaca6de6dba6bb6c258eb608db4ce3503c79d 100644 (file)
@@ -2514,7 +2514,13 @@ def warn_printf_asterisk_width_wrong_type : Warning<
 def warn_printf_asterisk_precision_wrong_type : Warning<
   "field precision should have type %0, but argument has type %1">,
   InGroup<Format>;
-
+def warn_printf_nonsensical_precision: Warning<
+  "precision used in '%0' conversion specifier (where it has no meaning)">,
+  InGroup<Format>;
+def warn_printf_nonsensical_flag: Warning<
+  "flag '%0' results in undefined behavior in '%1' conversion specifier">,
+  InGroup<Format>;
+  
 // CHECK: returning address/reference of stack memory
 def warn_ret_stack_addr : Warning<
   "address of stack memory associated with local variable %0 returned">;
index 845102376169e70401d6580ae865d42677f856a1..3a77552f48289682acc4df9b54c1d275b24868e4 100644 (file)
@@ -1080,6 +1080,9 @@ private:
   bool HandleAmount(const analyze_printf::OptionalAmount &Amt,
                     unsigned MissingArgDiag, unsigned BadTypeDiag,
           const char *startSpecifier, unsigned specifierLen);
+  void HandleFlags(const analyze_printf::FormatSpecifier &FS,
+                   llvm::StringRef flag, llvm::StringRef cspec,
+                   const char *startSpecifier, unsigned specifierLen);
   
   bool MatchType(QualType A, QualType B, bool ignoreSign);
   
@@ -1175,6 +1178,16 @@ bool CheckPrintfHandler::MatchType(QualType A, QualType B, bool ignoreSign) {
   return false;  
 }
 
+void CheckPrintfHandler::HandleFlags(const analyze_printf::FormatSpecifier &FS,
+                                     llvm::StringRef flag,
+                                     llvm::StringRef cspec,
+                                     const char *startSpecifier,
+                                     unsigned specifierLen) {
+  const analyze_printf::ConversionSpecifier &CS = FS.getConversionSpecifier();
+  S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_nonsensical_flag)
+    << flag << cspec << getFormatSpecifierRange(startSpecifier, specifierLen);
+}
+
 bool
 CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt,
                                  unsigned MissingArgDiag,
@@ -1214,7 +1227,8 @@ CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt,
 }
 
 bool
-CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier &FS,
+CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier
+                                            &FS,
                                           const char *startSpecifier,
                                           unsigned specifierLen) {
 
@@ -1262,7 +1276,25 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier
     // Continue checking the other format specifiers.
     return true;
   }
-  
+
+  if (CS.getKind() == ConversionSpecifier::VoidPtrArg) {
+    if (FS.getPrecision().getHowSpecified() != OptionalAmount::NotSpecified)
+      S.Diag(getLocationOfByte(CS.getStart()), 
+             diag::warn_printf_nonsensical_precision)
+        << CS.getCharacters()
+        << getFormatSpecifierRange(startSpecifier, specifierLen);
+  }
+  if (CS.getKind() == ConversionSpecifier::VoidPtrArg || 
+      CS.getKind() == ConversionSpecifier::CStrArg) {    
+    // FIXME: Instead of using "0", "+", etc., eventually get them from
+    // the FormatSpecifier.
+    if (FS.hasLeadingZeros())
+      HandleFlags(FS, "0", CS.getCharacters(), startSpecifier, specifierLen);
+    if (FS.hasPlusPrefix())
+      HandleFlags(FS, "+", CS.getCharacters(), startSpecifier, specifierLen);
+    if (FS.hasSpacePrefix())
+      HandleFlags(FS, " ", CS.getCharacters(), startSpecifier, specifierLen);
+  }  
   
   // The remaining checks depend on the data arguments.
   if (HasVAListArg)
index 5ce3eb036ebfa7514690158eb9ad7753e7172808..1bbc68cd0d5e90236b19aa65dc70bae06b513b8b 100644 (file)
@@ -171,6 +171,18 @@ void test10(int x, float f, int i, long long lli) {
   printf("%f\n", (long double) 1.0); // expected-warning{{conversion specifies type 'double' but the argument has type 'long double'}}
 } 
 
+void test11(void *p, char *s) {
+  printf("%p", p); // no-warning
+  printf("%.4p", p); // expected-warning{{precision used in 'p' conversion specifier (where it has no meaning)}}
+  printf("%+p", p); // expected-warning{{flag '+' results in undefined behavior in 'p' conversion specifier}}
+  printf("% p", p); // expected-warning{{flag ' ' results in undefined behavior in 'p' conversion specifier}}
+  printf("%0p", p); // expected-warning{{flag '0' results in undefined behavior in 'p' conversion specifier}}
+  printf("%s", s); // no-warning
+  printf("%+s", p); // expected-warning{{flag '+' results in undefined behavior in 's' conversion specifier}}
+  printf("% s", p); // expected-warning{{flag ' ' results in undefined behavior in 's' conversion specifier}}
+  printf("%0s", p); // expected-warning{{flag '0' results in undefined behavior in 's' conversion specifier}}
+}
+
 typedef struct __aslclient *aslclient;
 typedef struct __aslmsg *aslmsg;
 int asl_log(aslclient asl, aslmsg msg, int level, const char *format, ...) __attribute__((__format__ (__printf__, 4, 5)));