[cfe-commits] r106233 - in /cfe/trunk: include/clang/Analysis/Analyses/PrintfFormatString.h include/clang/Basic/DiagnosticSemaKinds.td lib/Analysis/PrintfFormatString.cpp lib/Sema/SemaChecking.cpp test/Sema/format-strings.c

Tom Care tcare at apple.com
Thu Jun 17 12:00:27 PDT 2010


Author: tcare
Date: Thu Jun 17 14:00:27 2010
New Revision: 106233

URL: http://llvm.org/viewvc/llvm-project?rev=106233&view=rev
Log:
Bug 7377: Fixed several bad printf format string bugs.
- Added warning for undefined behavior when using field specifier
- Added warning for undefined behavior when using length modifier
- Fixed warnings for invalid flags
- Added warning for ignored flags
- Added fixits for the above warnings
- Fixed accuracy of detecting several undefined behavior conditions
- Receive normal warnings in addition to security warnings when using %n
- Fix bug where '+' flag would remain on unsigned conversion suggestions

Summary of changes:
- Added expanded tests
- Added/expanded warnings
- Added position info to OptionalAmounts for fixits
- Extracted optional flags to a wrapper class with position info for fixits
- Added several methods to validate a FormatSpecifier by component, each checking for undefined behavior
- Fixed conversion specifier checking to conform to C99 standard
- Added hooks to detect the invalid states in CheckPrintfHandler::HandleFormatSpecifier

Note: warnings involving the ' ' (space) flag are temporarily disabled until whitespace highlighting no longer triggers assertions. I will make a post about this on cfe-dev shortly.

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

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

Modified: cfe/trunk/include/clang/Analysis/Analyses/PrintfFormatString.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Analysis/Analyses/PrintfFormatString.h?rev=106233&r1=106232&r2=106233&view=diff
==============================================================================
--- cfe/trunk/include/clang/Analysis/Analyses/PrintfFormatString.h (original)
+++ cfe/trunk/include/clang/Analysis/Analyses/PrintfFormatString.h Thu Jun 17 14:00:27 2010
@@ -189,12 +189,13 @@
   OptionalAmount(HowSpecified howSpecified,
                  unsigned amount,
                  const char *amountStart,
+                 unsigned amountLength,
                  bool usesPositionalArg)
-    : start(amountStart), hs(howSpecified), amt(amount),
+    : start(amountStart), length(amountLength), hs(howSpecified), amt(amount),
       UsesPositionalArg(usesPositionalArg) {}
 
   OptionalAmount(bool valid = true)
-    : start(0), hs(valid ? NotSpecified : Invalid), amt(0),
+    : start(0),length(0), hs(valid ? NotSpecified : Invalid), amt(0),
       UsesPositionalArg(0) {}
 
   bool isInvalid() const {
@@ -220,6 +221,11 @@
     return start;
   }
 
+  unsigned getConstantLength() const {
+    assert(hs == Constant);
+    return length;
+  }
+
   ArgTypeResult getArgType(ASTContext &Ctx) const;
 
   void toString(llvm::raw_ostream &os) const;
@@ -232,30 +238,62 @@
 
 private:
   const char *start;
+  unsigned length;
   HowSpecified hs;
   unsigned amt;
   bool UsesPositionalArg : 1;
 };
 
+// Class representing optional flags with location and representation
+// information.
+class OptionalFlag {
+public:
+  OptionalFlag(const char *Representation)
+      : representation(Representation), flag(false) {}
+  bool isSet() { return flag; }
+  void set() { flag = true; }
+  void clear() { flag = false; }
+  void setPosition(const char *position) {
+    assert(position);
+    this->position = position;
+  }
+  const char *getPosition() const {
+    assert(position);
+    return position;
+  }
+  const char *toString() const { return representation; }
+
+  // Overloaded operators for bool like qualities
+  operator bool() const { return flag; }
+  OptionalFlag& operator=(const bool &rhs) {
+    flag = rhs;
+    return *this;  // Return a reference to myself.
+  }
+private:
+  const char *representation;
+  const char *position;
+  bool flag;
+};
+
 class FormatSpecifier {
   LengthModifier LM;
-  unsigned IsLeftJustified : 1;
-  unsigned HasPlusPrefix : 1;
-  unsigned HasSpacePrefix : 1;
-  unsigned HasAlternativeForm : 1;
-  unsigned HasLeadingZeroes : 1;
+  OptionalFlag IsLeftJustified; // '-'
+  OptionalFlag HasPlusPrefix; // '+'
+  OptionalFlag HasSpacePrefix; // ' '
+  OptionalFlag HasAlternativeForm; // '#'
+  OptionalFlag HasLeadingZeroes; // '0'
   /// Positional arguments, an IEEE extension:
   ///  IEEE Std 1003.1, 2004 Edition
   ///  http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
-  unsigned UsesPositionalArg : 1;
+  bool UsesPositionalArg;
   unsigned argIndex;
   ConversionSpecifier CS;
   OptionalAmount FieldWidth;
   OptionalAmount Precision;
 public:
   FormatSpecifier() :
-    IsLeftJustified(0), HasPlusPrefix(0), HasSpacePrefix(0),
-    HasAlternativeForm(0), HasLeadingZeroes(0), UsesPositionalArg(0),
+    IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "),
+    HasAlternativeForm("#"), HasLeadingZeroes("0"), UsesPositionalArg(false),
     argIndex(0) {}
 
   static FormatSpecifier Parse(const char *beg, const char *end);
@@ -267,12 +305,27 @@
   void setLengthModifier(LengthModifier lm) {
     LM = lm;
   }
-  void setIsLeftJustified() { IsLeftJustified = 1; }
-  void setHasPlusPrefix() { HasPlusPrefix = 1; }
-  void setHasSpacePrefix() { HasSpacePrefix = 1; }
-  void setHasAlternativeForm() { HasAlternativeForm = 1; }
-  void setHasLeadingZeros() { HasLeadingZeroes = 1; }
-  void setUsesPositionalArg() { UsesPositionalArg = 1; }
+  void setIsLeftJustified(const char *position) {
+    IsLeftJustified = true;
+    IsLeftJustified.setPosition(position);
+  }
+  void setHasPlusPrefix(const char *position) {
+    HasPlusPrefix = true;
+    HasPlusPrefix.setPosition(position);
+  }
+  void setHasSpacePrefix(const char *position) {
+    HasSpacePrefix = true;
+    HasSpacePrefix.setPosition(position);
+  }
+  void setHasAlternativeForm(const char *position) {
+    HasAlternativeForm = true;
+    HasAlternativeForm.setPosition(position);
+  }
+  void setHasLeadingZeros(const char *position) {
+    HasLeadingZeroes = true;
+    HasLeadingZeroes.setPosition(position);
+  }
+  void setUsesPositionalArg() { UsesPositionalArg = true; }
 
   void setArgIndex(unsigned i) {
     assert(CS.consumesDataArgument());
@@ -295,7 +348,7 @@
     return CS;
   }
 
-  LengthModifier getLengthModifier() const {
+  const LengthModifier &getLengthModifier() const {
     return LM;
   }
 
@@ -322,12 +375,12 @@
   /// more than one type.
   ArgTypeResult getArgType(ASTContext &Ctx) const;
 
-  bool isLeftJustified() const { return (bool) IsLeftJustified; }
-  bool hasPlusPrefix() const { return (bool) HasPlusPrefix; }
-  bool hasAlternativeForm() const { return (bool) HasAlternativeForm; }
-  bool hasLeadingZeros() const { return (bool) HasLeadingZeroes; }
-  bool hasSpacePrefix() const { return (bool) HasSpacePrefix; }
-  bool usesPositionalArg() const { return (bool) UsesPositionalArg; }
+  const OptionalFlag &isLeftJustified() const { return IsLeftJustified; }
+  const OptionalFlag &hasPlusPrefix() const { return HasPlusPrefix; }
+  const OptionalFlag &hasAlternativeForm() const { return HasAlternativeForm; }
+  const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; }
+  const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; }
+  bool usesPositionalArg() const { return UsesPositionalArg; }
 
   /// Changes the specifier and length according to a QualType, retaining any
   /// flags or options. Returns true on success, or false when a conversion
@@ -335,6 +388,17 @@
   bool fixType(QualType QT);
 
   void toString(llvm::raw_ostream &os) const;
+
+  // Validation methods - to check if any element results in undefined behavior
+  bool hasValidPlusPrefix() const;
+  bool hasValidAlternativeForm() const;
+  bool hasValidLeadingZeros() const;
+  bool hasValidSpacePrefix() const;
+  bool hasValidLeftJustified() const;
+
+  bool hasValidLengthModifier() const;
+  bool hasValidPrecision() const;
+  bool hasValidFieldWidth() const;
 };
 
 enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 };

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=106233&r1=106232&r2=106233&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Thu Jun 17 14:00:27 2010
@@ -2910,11 +2910,17 @@
 def warn_printf_asterisk_wrong_type : Warning<
   "field %select{width|precision}0 should have type %1, but argument has type %2">,
   InGroup<Format>;
-def warn_printf_nonsensical_precision: Warning<
-  "precision used in '%0' conversion specifier (where it has no meaning)">,
+def warn_printf_nonsensical_optional_amount: Warning<
+  "%select{field width|precision}0 used with '%1' conversion specifier, resulting in undefined behavior">,
   InGroup<Format>;
 def warn_printf_nonsensical_flag: Warning<
-  "flag '%0' results in undefined behavior in '%1' conversion specifier">,
+  "flag '%0' results in undefined behavior with '%1' conversion specifier">,
+  InGroup<Format>;
+def warn_printf_nonsensical_length: Warning<
+  "length modifier '%0' results in undefined behavior or no effect with '%1' conversion specifier">,
+  InGroup<Format>;
+def warn_printf_ignored_flag: Warning<
+  "flag '%0' is ignored when flag '%1' is present">,
   InGroup<Format>;
   
 // CHECK: returning address/reference of stack memory

Modified: cfe/trunk/lib/Analysis/PrintfFormatString.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Analysis/PrintfFormatString.cpp?rev=106233&r1=106232&r2=106233&view=diff
==============================================================================
--- cfe/trunk/lib/Analysis/PrintfFormatString.cpp (original)
+++ cfe/trunk/lib/Analysis/PrintfFormatString.cpp Thu Jun 17 14:00:27 2010
@@ -83,7 +83,8 @@
     }
 
     if (hasDigits)
-      return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, 0);
+      return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg,
+          false);
 
     break;
   }
@@ -95,7 +96,7 @@
                                              unsigned &argIndex) {
   if (*Beg == '*') {
     ++Beg;
-    return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0);
+    return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false);
   }
 
   return ParseAmount(Beg, E);
@@ -135,7 +136,7 @@
       Beg = ++I;
 
       return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1,
-                            Tmp, 1);
+                            Tmp, 0, true);
     }
 
     H.HandleInvalidPosition(Beg, I - Beg, p);
@@ -261,11 +262,11 @@
   for ( ; I != E; ++I) {
     switch (*I) {
       default: hasMore = false; break;
-      case '-': FS.setIsLeftJustified(); break;
-      case '+': FS.setHasPlusPrefix(); break;
-      case ' ': FS.setHasSpacePrefix(); break;
-      case '#': FS.setHasAlternativeForm(); break;
-      case '0': FS.setHasLeadingZeros(); break;
+      case '-': FS.setIsLeftJustified(I); break;
+      case '+': FS.setHasPlusPrefix(I); break;
+      case ' ': FS.setHasSpacePrefix(I); break;
+      case '#': FS.setHasAlternativeForm(I); break;
+      case '0': FS.setHasLeadingZeros(I); break;
     }
     if (!hasMore)
       break;
@@ -616,12 +617,12 @@
     return;
   case Arg:
     if (usesPositionalArg())
-      os << ".*" << getPositionalArgIndex() << "$";
+      os << "*" << getPositionalArgIndex() << "$";
     else
-      os << ".*";
+      os << "*";
     break;
   case Constant:
-    os << "." << amt;
+    os << amt;
     break;
   }
 }
@@ -749,6 +750,7 @@
     Precision.setHowSpecified(OptionalAmount::NotSpecified);
     HasAlternativeForm = 0;
     HasLeadingZeroes = 0;
+    HasPlusPrefix = 0;
   }
   // Test for Floating type first as LongDouble can pass isUnsignedIntegerType
   else if (QT->isFloatingType()) {
@@ -759,6 +761,7 @@
     Precision.setHowSpecified(OptionalAmount::NotSpecified);
     HasAlternativeForm = 0;
     HasLeadingZeroes = 0;
+    HasPlusPrefix = 0;
   }
   else if (QT->isSignedIntegerType()) {
     CS.setKind(ConversionSpecifier::dArg);
@@ -767,6 +770,7 @@
   else if (QT->isUnsignedIntegerType()) {
     CS.setKind(ConversionSpecifier::uArg);
     HasAlternativeForm = 0;
+    HasPlusPrefix = 0;
   }
   else {
     return false;
@@ -801,3 +805,222 @@
   // Conversion specifier
   os << CS.toString();
 }
+
+bool FormatSpecifier::hasValidPlusPrefix() const {
+  if (!HasPlusPrefix)
+    return true;
+
+  // The plus prefix only makes sense for signed conversions
+  switch (CS.getKind()) {
+  case ConversionSpecifier::dArg:
+  case ConversionSpecifier::iArg:
+  case ConversionSpecifier::fArg:
+  case ConversionSpecifier::FArg:
+  case ConversionSpecifier::eArg:
+  case ConversionSpecifier::EArg:
+  case ConversionSpecifier::gArg:
+  case ConversionSpecifier::GArg:
+  case ConversionSpecifier::aArg:
+  case ConversionSpecifier::AArg:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+bool FormatSpecifier::hasValidAlternativeForm() const {
+  if (!HasAlternativeForm)
+    return true;
+
+  // Alternate form flag only valid with the oxaAeEfFgG conversions
+  switch (CS.getKind()) {
+  case ConversionSpecifier::oArg:
+  case ConversionSpecifier::xArg:
+  case ConversionSpecifier::aArg:
+  case ConversionSpecifier::AArg:
+  case ConversionSpecifier::eArg:
+  case ConversionSpecifier::EArg:
+  case ConversionSpecifier::fArg:
+  case ConversionSpecifier::FArg:
+  case ConversionSpecifier::gArg:
+  case ConversionSpecifier::GArg:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+bool FormatSpecifier::hasValidLeadingZeros() const {
+  if (!HasLeadingZeroes)
+    return true;
+
+  // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
+  switch (CS.getKind()) {
+  case ConversionSpecifier::dArg:
+  case ConversionSpecifier::iArg:
+  case ConversionSpecifier::oArg:
+  case ConversionSpecifier::uArg:
+  case ConversionSpecifier::xArg:
+  case ConversionSpecifier::XArg:
+  case ConversionSpecifier::aArg:
+  case ConversionSpecifier::AArg:
+  case ConversionSpecifier::eArg:
+  case ConversionSpecifier::EArg:
+  case ConversionSpecifier::fArg:
+  case ConversionSpecifier::FArg:
+  case ConversionSpecifier::gArg:
+  case ConversionSpecifier::GArg:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+bool FormatSpecifier::hasValidSpacePrefix() const {
+  if (!HasSpacePrefix)
+    return true;
+
+  // The space prefix only makes sense for signed conversions
+  switch (CS.getKind()) {
+  case ConversionSpecifier::dArg:
+  case ConversionSpecifier::iArg:
+  case ConversionSpecifier::fArg:
+  case ConversionSpecifier::FArg:
+  case ConversionSpecifier::eArg:
+  case ConversionSpecifier::EArg:
+  case ConversionSpecifier::gArg:
+  case ConversionSpecifier::GArg:
+  case ConversionSpecifier::aArg:
+  case ConversionSpecifier::AArg:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
+bool FormatSpecifier::hasValidLeftJustified() const {
+  if (!IsLeftJustified)
+    return true;
+
+  // The left justified flag is valid for all conversions except n
+  switch (CS.getKind()) {
+  case ConversionSpecifier::OutIntPtrArg:
+    return false;
+
+  default:
+    return true;
+  }
+}
+
+bool FormatSpecifier::hasValidLengthModifier() const {
+  switch (LM.getKind()) {
+  case LengthModifier::None:
+    return true;
+
+  // Handle most integer flags
+  case LengthModifier::AsChar:
+  case LengthModifier::AsShort:
+  case LengthModifier::AsLongLong:
+  case LengthModifier::AsIntMax:
+  case LengthModifier::AsSizeT:
+  case LengthModifier::AsPtrDiff:
+    switch (CS.getKind()) {
+    case ConversionSpecifier::dArg:
+    case ConversionSpecifier::iArg:
+    case ConversionSpecifier::oArg:
+    case ConversionSpecifier::uArg:
+    case ConversionSpecifier::xArg:
+    case ConversionSpecifier::XArg:
+    case ConversionSpecifier::OutIntPtrArg:
+      return true;
+    default:
+      return false;
+    }
+
+  // Handle 'l' flag
+  case LengthModifier::AsLong:
+    switch (CS.getKind()) {
+    case ConversionSpecifier::dArg:
+    case ConversionSpecifier::iArg:
+    case ConversionSpecifier::oArg:
+    case ConversionSpecifier::uArg:
+    case ConversionSpecifier::xArg:
+    case ConversionSpecifier::XArg:
+    case ConversionSpecifier::aArg:
+    case ConversionSpecifier::AArg:
+    case ConversionSpecifier::fArg:
+    case ConversionSpecifier::FArg:
+    case ConversionSpecifier::eArg:
+    case ConversionSpecifier::EArg:
+    case ConversionSpecifier::gArg:
+    case ConversionSpecifier::GArg:
+    case ConversionSpecifier::OutIntPtrArg:
+    case ConversionSpecifier::IntAsCharArg:
+    case ConversionSpecifier::CStrArg:
+      return true;
+    default:
+      return false;
+    }
+
+  case LengthModifier::AsLongDouble:
+    switch (CS.getKind()) {
+    case ConversionSpecifier::aArg:
+    case ConversionSpecifier::AArg:
+    case ConversionSpecifier::fArg:
+    case ConversionSpecifier::FArg:
+    case ConversionSpecifier::eArg:
+    case ConversionSpecifier::EArg:
+    case ConversionSpecifier::gArg:
+    case ConversionSpecifier::GArg:
+      return true;
+    default:
+      return false;
+    }
+  }
+  return false;
+}
+
+bool FormatSpecifier::hasValidPrecision() const {
+  if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
+    return true;
+
+  // Precision is only valid with the diouxXaAeEfFgGs conversions
+  switch (CS.getKind()) {
+  case ConversionSpecifier::dArg:
+  case ConversionSpecifier::iArg:
+  case ConversionSpecifier::oArg:
+  case ConversionSpecifier::uArg:
+  case ConversionSpecifier::xArg:
+  case ConversionSpecifier::XArg:
+  case ConversionSpecifier::aArg:
+  case ConversionSpecifier::AArg:
+  case ConversionSpecifier::eArg:
+  case ConversionSpecifier::EArg:
+  case ConversionSpecifier::fArg:
+  case ConversionSpecifier::FArg:
+  case ConversionSpecifier::gArg:
+  case ConversionSpecifier::GArg:
+  case ConversionSpecifier::CStrArg:
+    return true;
+
+  default:
+    return false;
+  }
+}
+bool FormatSpecifier::hasValidFieldWidth() const {
+  if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified)
+      return true;
+
+  // The field width is valid for all conversions except n
+  switch (CS.getKind()) {
+  case ConversionSpecifier::OutIntPtrArg:
+    return false;
+
+  default:
+    return true;
+  }
+}

Modified: cfe/trunk/lib/Sema/SemaChecking.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaChecking.cpp?rev=106233&r1=106232&r2=106233&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaChecking.cpp (original)
+++ cfe/trunk/lib/Sema/SemaChecking.cpp Thu Jun 17 14:00:27 2010
@@ -1200,9 +1200,17 @@
 
   bool HandleAmount(const analyze_printf::OptionalAmount &Amt, unsigned k,
                     const char *startSpecifier, unsigned specifierLen);
-  void HandleFlags(const analyze_printf::FormatSpecifier &FS,
-                   llvm::StringRef flag, llvm::StringRef cspec,
-                   const char *startSpecifier, unsigned specifierLen);
+  void HandleInvalidAmount(const analyze_printf::FormatSpecifier &FS,
+                           const analyze_printf::OptionalAmount &Amt,
+                           unsigned type,
+                           const char *startSpecifier, unsigned specifierLen);
+  void HandleFlag(const analyze_printf::FormatSpecifier &FS,
+                  const analyze_printf::OptionalFlag &flag,
+                  const char *startSpecifier, unsigned specifierLen);
+  void HandleIgnoredFlag(const analyze_printf::FormatSpecifier &FS,
+                         const analyze_printf::OptionalFlag &ignoredFlag,
+                         const analyze_printf::OptionalFlag &flag,
+                         const char *startSpecifier, unsigned specifierLen);
 
   const Expr *getDataArg(unsigned i) const;
 };
@@ -1287,16 +1295,6 @@
   return TheCall->getArg(FirstDataArg + i);
 }
 
-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 k, const char *startSpecifier,
@@ -1341,6 +1339,62 @@
   return true;
 }
 
+void CheckPrintfHandler::HandleInvalidAmount(
+                                      const analyze_printf::FormatSpecifier &FS,
+                                      const analyze_printf::OptionalAmount &Amt,
+                                      unsigned type,
+                                      const char *startSpecifier,
+                                      unsigned specifierLen) {
+  const analyze_printf::ConversionSpecifier &CS = FS.getConversionSpecifier();
+  switch (Amt.getHowSpecified()) {
+  case analyze_printf::OptionalAmount::Constant:
+    S.Diag(getLocationOfByte(Amt.getStart()),
+        diag::warn_printf_nonsensical_optional_amount)
+      << type
+      << CS.toString()
+      << getFormatSpecifierRange(startSpecifier, specifierLen)
+      << FixItHint::CreateRemoval(getFormatSpecifierRange(Amt.getStart(),
+          Amt.getConstantLength()));
+    break;
+
+  default:
+    S.Diag(getLocationOfByte(Amt.getStart()),
+        diag::warn_printf_nonsensical_optional_amount)
+      << type
+      << CS.toString()
+      << getFormatSpecifierRange(startSpecifier, specifierLen);
+    break;
+  }
+}
+
+void CheckPrintfHandler::HandleFlag(const analyze_printf::FormatSpecifier &FS,
+                                    const analyze_printf::OptionalFlag &flag,
+                                    const char *startSpecifier,
+                                    unsigned specifierLen) {
+  // Warn about pointless flag with a fixit removal.
+  const analyze_printf::ConversionSpecifier &CS = FS.getConversionSpecifier();
+  S.Diag(getLocationOfByte(flag.getPosition()),
+      diag::warn_printf_nonsensical_flag)
+    << flag.toString() << CS.toString()
+    << getFormatSpecifierRange(startSpecifier, specifierLen)
+    << FixItHint::CreateRemoval(getFormatSpecifierRange(flag.getPosition(), 1));
+}
+
+void CheckPrintfHandler::HandleIgnoredFlag(
+                                const analyze_printf::FormatSpecifier &FS,
+                                const analyze_printf::OptionalFlag &ignoredFlag,
+                                const analyze_printf::OptionalFlag &flag,
+                                const char *startSpecifier,
+                                unsigned specifierLen) {
+  // Warn about ignored flag with a fixit removal.
+  S.Diag(getLocationOfByte(ignoredFlag.getPosition()),
+      diag::warn_printf_ignored_flag)
+    << ignoredFlag.toString() << flag.toString()
+    << getFormatSpecifierRange(startSpecifier, specifierLen)
+    << FixItHint::CreateRemoval(getFormatSpecifierRange(
+        ignoredFlag.getPosition(), 1));
+}
+
 bool
 CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier
                                             &FS,
@@ -1395,34 +1449,61 @@
     return HandleInvalidConversionSpecifier(FS, startSpecifier, specifierLen);
   }
 
-  // Are we using '%n'?  Issue a warning about this being
-  // a possible security issue.
+  // Check for invalid use of field width
+  if (!FS.hasValidFieldWidth()) {
+    HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 1,
+        startSpecifier, specifierLen);
+  }
+
+  // Check for invalid use of precision
+  if (!FS.hasValidPrecision()) {
+    HandleInvalidAmount(FS, FS.getPrecision(), /* precision */ 1,
+        startSpecifier, specifierLen);
+  }
+
+  // Check each flag does not conflict with any other component.
+  if (!FS.hasValidLeadingZeros())
+    HandleFlag(FS, FS.hasLeadingZeros(), startSpecifier, specifierLen);
+  if (!FS.hasValidPlusPrefix())
+    HandleFlag(FS, FS.hasPlusPrefix(), startSpecifier, specifierLen);
+  // FIXME: the following lines are disabled due to clang assertions on
+  // highlights containing spaces.
+  // if (!FS.hasValidSpacePrefix())
+  //   HandleFlag(FS, FS.hasSpacePrefix(), startSpecifier, specifierLen);
+  if (!FS.hasValidAlternativeForm())
+    HandleFlag(FS, FS.hasAlternativeForm(), startSpecifier, specifierLen);
+  if (!FS.hasValidLeftJustified())
+    HandleFlag(FS, FS.isLeftJustified(), startSpecifier, specifierLen);
+
+  // Check that flags are not ignored by another flag
+  // FIXME: the following lines are disabled due to clang assertions on
+  // highlights containing spaces.
+  //if (FS.hasSpacePrefix() && FS.hasPlusPrefix()) // ' ' ignored by '+'
+  //  HandleIgnoredFlag(FS, FS.hasSpacePrefix(), FS.hasPlusPrefix(),
+  //      startSpecifier, specifierLen);
+  if (FS.hasLeadingZeros() && FS.isLeftJustified()) // '0' ignored by '-'
+    HandleIgnoredFlag(FS, FS.hasLeadingZeros(), FS.isLeftJustified(),
+            startSpecifier, specifierLen);
+
+  // Check the length modifier is valid with the given conversion specifier.
+  const LengthModifier &LM = FS.getLengthModifier();
+  if (!FS.hasValidLengthModifier())
+    S.Diag(getLocationOfByte(LM.getStart()),
+        diag::warn_printf_nonsensical_length)
+      << LM.toString() << CS.toString()
+      << getFormatSpecifierRange(startSpecifier, specifierLen)
+      << FixItHint::CreateRemoval(getFormatSpecifierRange(LM.getStart(),
+          LM.getLength()));
+
+  // Are we using '%n'?
   if (CS.getKind() == ConversionSpecifier::OutIntPtrArg) {
+    // Issue a warning about this being a possible security issue.
     S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_write_back)
       << getFormatSpecifierRange(startSpecifier, specifierLen);
     // 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)
     return true;

Modified: cfe/trunk/test/Sema/format-strings.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/format-strings.c?rev=106233&r1=106232&r2=106233&view=diff
==============================================================================
--- cfe/trunk/test/Sema/format-strings.c (original)
+++ cfe/trunk/test/Sema/format-strings.c Thu Jun 17 14:00:27 2010
@@ -1,4 +1,7 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral %s
+// XFAIL: *
+// FIXME: temporarily marking as expected fail until whitespace highlighting
+// is fixed.
 
 #include <stdarg.h>
 typedef __typeof(sizeof(int)) size_t;
@@ -179,14 +182,14 @@
 void test11(void *p, char *s) {
   printf("%p", p); // no-warning
   printf("%p", 123); // expected-warning{{conversion specifies type 'void *' but the argument has type 'int'}}
-  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("%.4p", p); // expected-warning{{precision used with 'p' conversion specifier, resulting in undefined behavior}}
+  printf("%+p", p); // expected-warning{{flag '+' results in undefined behavior with 'p' conversion specifier}}
+  printf("% p", p); // expected-warning{{flag ' ' results in undefined behavior with 'p' conversion specifier}}
+  printf("%0p", p); // expected-warning{{flag '0' results in undefined behavior with '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}}
+  printf("%+s", p); // expected-warning{{flag '+' results in undefined behavior with 's' conversion specifier}}
+  printf("% s", p); // expected-warning{{flag ' ' results in undefined behavior with 's' conversion specifier}}
+  printf("%0s", p); // expected-warning{{flag '0' results in undefined behavior with 's' conversion specifier}}
 }
 
 void test12(char *b) {
@@ -257,3 +260,29 @@
 void rdar8026030(FILE *fp) {
   fprintf(fp, "\%"); // expected-warning{{incomplete format specifier}}
 }
+
+void bug7377_bad_length_mod_usage() {
+  // Bad length modifiers
+  printf("%hhs", "foo"); // expected-warning{{length modifier 'hh' results in undefined behavior or no effect with 's' conversion specifier}}
+  printf("%1$zp", (void *)0); // expected-warning{{length modifier 'z' results in undefined behavior or no effect with 'p' conversion specifier}}
+  printf("%ls", L"foo"); // no-warning
+  printf("%#.2Lf", (long double)1.234); // no-warning
+
+  // Bad flag usage
+  printf("%#p", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'p' conversion specifier}}
+  printf("%0d", -1); // no-warning
+  printf("%#n", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'n' conversion specifier}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+  printf("%-n", (void *) 0); // expected-warning{{flag '-' results in undefined behavior with 'n' conversion specifier}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+  printf("%-p", (void *) 0); // no-warning
+
+  // Bad optional amount use
+  printf("%.2c", 'a'); // expected-warning{{precision used with 'c' conversion specifier, resulting in undefined behavior}}
+  printf("%1n", (void *) 0); // expected-warning{{precision used with 'n' conversion specifier, resulting in undefined behavior}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+
+  // Ignored flags
+  printf("% +f", 1.23); // expected-warning{{flag ' ' is ignored when flag '+' is present}}
+  printf("%+ f", 1.23); // expected-warning{{flag ' ' is ignored when flag '+' is present}}
+  printf("%0-f", 1.23); // expected-warning{{flag '0' is ignored when flag '-' is present}}
+  printf("%-0f", 1.23); // expected-warning{{flag '0' is ignored when flag '-' is present}}
+  printf("%-+f", 1.23); // no-warning
+}





More information about the cfe-commits mailing list