diff options
-rw-r--r-- | include/clang/Analysis/Analyses/FormatString.h | 620 | ||||
-rw-r--r-- | include/clang/Analysis/Analyses/PrintfFormatString.h | 445 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 19 | ||||
-rw-r--r-- | lib/Analysis/CMakeLists.txt | 2 | ||||
-rw-r--r-- | lib/Analysis/FormatString.cpp | 380 | ||||
-rw-r--r-- | lib/Analysis/PrintfFormatString.cpp | 448 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 17 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 488 |
8 files changed, 1360 insertions, 1059 deletions
diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h new file mode 100644 index 0000000000..f79300df9f --- /dev/null +++ b/include/clang/Analysis/Analyses/FormatString.h @@ -0,0 +1,620 @@ +//= FormatString.h - Analysis of printf/fprintf format strings --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs for analyzing the format strings of printf, fscanf, +// and friends. +// +// The structure of format strings for fprintf are described in C99 7.19.6.1. +// +// The structure of format strings for fscanf are described in C99 7.19.6.2. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_FORMAT_H +#define LLVM_CLANG_FORMAT_H + +#include "clang/AST/CanonicalType.h" + +namespace clang { + +//===----------------------------------------------------------------------===// +/// Common components of both fprintf and fscanf format strings. +namespace analyze_format_string { + +/// 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; +}; + +/// Represents the length modifier in a format string in scanf/printf. +class LengthModifier { +public: + enum Kind { + None, + AsChar, // 'hh' + AsShort, // 'h' + AsLong, // 'l' + AsLongLong, // 'll', 'q' (BSD, deprecated) + AsIntMax, // 'j' + AsSizeT, // 'z' + AsPtrDiff, // 't' + AsLongDouble, // 'L' + AsWideChar = AsLong // for '%ls', only makes sense for printf + }; + + LengthModifier() + : Position(0), kind(None) {} + LengthModifier(const char *pos, Kind k) + : Position(pos), kind(k) {} + + const char *getStart() const { + return Position; + } + + unsigned getLength() const { + switch (kind) { + default: + return 1; + case AsLongLong: + case AsChar: + return 2; + case None: + return 0; + } + } + + Kind getKind() const { return kind; } + void setKind(Kind k) { kind = k; } + + const char *toString() const; + +private: + const char *Position; + Kind kind; +}; + +class ArgTypeResult { +public: + enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, + CStrTy, WCStrTy }; +private: + const Kind K; + QualType T; + ArgTypeResult(bool) : K(InvalidTy) {} +public: + ArgTypeResult(Kind k = UnknownTy) : K(k) {} + ArgTypeResult(QualType t) : K(SpecificTy), T(t) {} + ArgTypeResult(CanQualType t) : K(SpecificTy), T(t) {} + + static ArgTypeResult Invalid() { return ArgTypeResult(true); } + + bool isValid() const { return K != InvalidTy; } + + const QualType *getSpecificType() const { + return K == SpecificTy ? &T : 0; + } + + bool matchesType(ASTContext &C, QualType argTy) const; + + bool matchesAnyObjCObjectRef() const { return K == ObjCPointerTy; } + + QualType getRepresentativeType(ASTContext &C) const; +}; + +class OptionalAmount { +public: + enum HowSpecified { NotSpecified, Constant, Arg, Invalid }; + + OptionalAmount(HowSpecified howSpecified, + unsigned amount, + const char *amountStart, + unsigned amountLength, + bool usesPositionalArg) + : start(amountStart), length(amountLength), hs(howSpecified), amt(amount), + UsesPositionalArg(usesPositionalArg), UsesDotPrefix(0) {} + + OptionalAmount(bool valid = true) + : start(0),length(0), hs(valid ? NotSpecified : Invalid), amt(0), + UsesPositionalArg(0), UsesDotPrefix(0) {} + + bool isInvalid() const { + return hs == Invalid; + } + + HowSpecified getHowSpecified() const { return hs; } + void setHowSpecified(HowSpecified h) { hs = h; } + + bool hasDataArgument() const { return hs == Arg; } + + unsigned getArgIndex() const { + assert(hasDataArgument()); + return amt; + } + + unsigned getConstantAmount() const { + assert(hs == Constant); + return amt; + } + + const char *getStart() const { + // We include the . character if it is given. + return start - UsesDotPrefix; + } + + unsigned getConstantLength() const { + assert(hs == Constant); + return length + UsesDotPrefix; + } + + ArgTypeResult getArgType(ASTContext &Ctx) const; + + void toString(llvm::raw_ostream &os) const; + + bool usesPositionalArg() const { return (bool) UsesPositionalArg; } + unsigned getPositionalArgIndex() const { + assert(hasDataArgument()); + return amt + 1; + } + + bool usesDotPrefix() const { return UsesDotPrefix; } + void setUsesDotPrefix() { UsesDotPrefix = true; } + +private: + const char *start; + unsigned length; + HowSpecified hs; + unsigned amt; + bool UsesPositionalArg : 1; + bool UsesDotPrefix; +}; + + +class FormatSpecifier { +protected: + LengthModifier LM; + OptionalAmount FieldWidth; + /// Positional arguments, an IEEE extension: + /// IEEE Std 1003.1, 2004 Edition + /// http://www.opengroup.org/onlinepubs/009695399/functions/printf.html + bool UsesPositionalArg; + unsigned argIndex; +public: + FormatSpecifier() : UsesPositionalArg(false), argIndex(0) {} + + void setLengthModifier(LengthModifier lm) { + LM = lm; + } + + void setUsesPositionalArg() { UsesPositionalArg = true; } + + void setArgIndex(unsigned i) { + // assert(CS.consumesDataArgument()); + argIndex = i; + } + + unsigned getArgIndex() const { + //assert(CS.consumesDataArgument()); + return argIndex; + } + + unsigned getPositionalArgIndex() const { + //assert(CS.consumesDataArgument()); + return argIndex + 1; + } + + const LengthModifier &getLengthModifier() const { + return LM; + } + + const OptionalAmount &getFieldWidth() const { + return FieldWidth; + } + + void setFieldWidth(const OptionalAmount &Amt) { + FieldWidth = Amt; + } + + bool usesPositionalArg() const { return UsesPositionalArg; } +}; + +} // end analyze_format_string namespace + +//===----------------------------------------------------------------------===// +/// Pieces specific to fprintf format strings. + +namespace analyze_printf { + +class ConversionSpecifier { +public: + enum Kind { + InvalidSpecifier = 0, + // C99 conversion specifiers. + dArg, // 'd' + IntAsCharArg, // 'c' + iArg, // 'i', + oArg, // 'o', + uArg, // 'u', + xArg, // 'x', + XArg, // 'X', + fArg, // 'f', + FArg, // 'F', + eArg, // 'e', + EArg, // 'E', + gArg, // 'g', + GArg, // 'G', + aArg, // 'a', + AArg, // 'A', + CStrArg, // 's' + VoidPtrArg, // 'p' + OutIntPtrArg, // 'n' + PercentArg, // '%' + // MacOS X unicode extensions. + CArg, // 'C' + UnicodeStrArg, // 'S' + // Objective-C specific specifiers. + ObjCObjArg, // '@' + // GlibC specific specifiers. + PrintErrno, // 'm' + // Specifier ranges. + IntArgBeg = dArg, + IntArgEnd = iArg, + UIntArgBeg = oArg, + UIntArgEnd = XArg, + DoubleArgBeg = fArg, + DoubleArgEnd = AArg, + C99Beg = IntArgBeg, + C99End = DoubleArgEnd, + ObjCBeg = ObjCObjArg, + ObjCEnd = ObjCObjArg + }; + + ConversionSpecifier() + : Position(0), kind(InvalidSpecifier) {} + + ConversionSpecifier(const char *pos, Kind k) + : Position(pos), kind(k) {} + + const char *getStart() const { + return Position; + } + + llvm::StringRef getCharacters() const { + return llvm::StringRef(getStart(), getLength()); + } + + bool consumesDataArgument() const { + switch (kind) { + case PercentArg: + case PrintErrno: + return false; + default: + return true; + } + } + + bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; } + bool isIntArg() const { return kind >= dArg && kind <= iArg; } + bool isUIntArg() const { return kind >= oArg && kind <= XArg; } + bool isDoubleArg() const { return kind >= fArg && kind <= AArg; } + Kind getKind() const { return kind; } + void setKind(Kind k) { kind = k; } + unsigned getLength() const { + // Conversion specifiers currently only are represented by + // single characters, but we be flexible. + return 1; + } + const char *toString() const; + +private: + const char *Position; + Kind kind; +}; + +using analyze_format_string::ArgTypeResult; +using analyze_format_string::LengthModifier; +using analyze_format_string::OptionalAmount; +using analyze_format_string::OptionalFlag; + +class PrintfSpecifier : public analyze_format_string::FormatSpecifier { + OptionalFlag IsLeftJustified; // '-' + OptionalFlag HasPlusPrefix; // '+' + OptionalFlag HasSpacePrefix; // ' ' + OptionalFlag HasAlternativeForm; // '#' + OptionalFlag HasLeadingZeroes; // '0' + ConversionSpecifier CS; + OptionalAmount Precision; +public: + PrintfSpecifier() : + IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "), + HasAlternativeForm("#"), HasLeadingZeroes("0") {} + + static PrintfSpecifier Parse(const char *beg, const char *end); + + // Methods for incrementally constructing the PrintfSpecifier. + void setConversionSpecifier(const ConversionSpecifier &cs) { + CS = cs; + } + 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; } + + // Methods for querying the format specifier. + + const ConversionSpecifier &getConversionSpecifier() const { + return CS; + } + + void setPrecision(const OptionalAmount &Amt) { + Precision = Amt; + Precision.setUsesDotPrefix(); + } + + const OptionalAmount &getPrecision() const { + return Precision; + } + + /// \brief Returns the builtin type that a data argument + /// paired with this format specifier should have. This method + /// will return null if the format specifier does not have + /// a matching data argument or the matching argument matches + /// more than one type. + ArgTypeResult getArgType(ASTContext &Ctx) const; + + 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 + /// was not successful. + 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; +}; +} // end analyze_printf namespace + +//===----------------------------------------------------------------------===// +/// Pieces specific to fscanf format strings. + +namespace analyze_scanf { + +class ConversionSpecifier { +public: + enum Kind { + InvalidSpecifier = 0, + // C99 conversion specifiers. + dArg, // 'd' + iArg, // 'i', + oArg, // 'o', + uArg, // 'u', + xArg, // 'x', + XArg, // 'X', + fArg, // 'f', + FArg, // 'F', + eArg, // 'e', + EArg, // 'E', + gArg, // 'g', + GArg, // 'G', + aArg, // 'a', + AArg, // 'A', + sArg, // 's', // match sequence of non-write-space characters + VoidPtrArg, // 'p' + cArg, // 'c', differs from printf, writes array of characters + ConsumedSoFarArg, // 'n', differs from printf, writes back args consumed + PercentArg, // '%' + ScanListArg, // '[' followed by scan list + // IEEE Std 1003.1 extensions. + CArg, // 'C', same as writing 'lc' + SArg, // 'S', same as writing 'ls' + // Specifier ranges. + IntArgBeg = dArg, + IntArgEnd = iArg, + UIntArgBeg = oArg, + UIntArgEnd = XArg, + DoubleArgBeg = fArg, + DoubleArgEnd = AArg + }; + + ConversionSpecifier() + : Position(0), EndScanList(0), kind(InvalidSpecifier) {} + + ConversionSpecifier(const char *pos, Kind k) + : Position(pos), EndScanList(0), kind(k) {} + + const char *getStart() const { + return Position; + } + + void setEndScanList(const char *pos) { EndScanList = pos; } + + llvm::StringRef getCharacters() const { + return llvm::StringRef(getStart(), getLength()); + } + + bool consumesDataArgument() const { + return kind != PercentArg; + } + + bool isIntArg() const { return kind >= dArg && kind <= iArg; } + bool isUIntArg() const { return kind >= oArg && kind <= XArg; } + bool isDoubleArg() const { return kind >= fArg && kind <= AArg; } + Kind getKind() const { return kind; } + void setKind(Kind k) { kind = k; } + + unsigned getLength() const { + return EndScanList ? EndScanList - Position : 1; + } + + const char *toString() const; + +private: + const char *Position; + const char *EndScanList; + Kind kind; +}; + +using analyze_format_string::LengthModifier; +using analyze_format_string::OptionalAmount; +using analyze_format_string::OptionalFlag; + +class ScanfSpecifier : public analyze_format_string::FormatSpecifier { + OptionalFlag SuppressAssignment; // '*' + ConversionSpecifier CS; +public: + ScanfSpecifier() : SuppressAssignment("*") {} + + void setSuppressAssignment(const char *position) { + SuppressAssignment = true; + SuppressAssignment.setPosition(position); + } + + const OptionalFlag &getSuppressAssignment() const { + return SuppressAssignment; + } + + void setConversionSpecifier(const ConversionSpecifier &cs) { + CS = cs; + } + + const ConversionSpecifier &getConversionSpecifier() const { + return CS; + } + + bool consumesDataArgument() const { + return CS.consumesDataArgument() && !SuppressAssignment; + } + + static ScanfSpecifier Parse(const char *beg, const char *end); + +}; + +} // end analyze_scanf namespace + +//===----------------------------------------------------------------------===// +// Parsing and processing of format strings (both fprintf and fscanf). + +namespace analyze_format_string { + +enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 }; + +class FormatStringHandler { +public: + FormatStringHandler() {} + virtual ~FormatStringHandler(); + + virtual void HandleNullChar(const char *nullCharacter) {} + + virtual void HandleInvalidPosition(const char *startPos, unsigned posLen, + PositionContext p) {} + + virtual void HandleZeroPosition(const char *startPos, unsigned posLen) {} + + virtual void HandleIncompleteSpecifier(const char *startSpecifier, + unsigned specifierLen) {} + + // Printf-specific handlers. + + virtual bool HandleInvalidPrintfConversionSpecifier( + const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + return true; + } + + virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + return true; + } + + // Scanf-specific handlers. + + virtual bool HandleInvalidScanfConversionSpecifier( + const analyze_scanf::ScanfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + return true; + } + + virtual bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + return true; + } + + virtual void HandleIncompleteScanList(const char *start, const char *end) {} +}; + +bool ParsePrintfString(FormatStringHandler &H, + const char *beg, const char *end); + +bool ParseScanfString(FormatStringHandler &H, + const char *beg, const char *end); + +} // end analyze_format_string namespace +} // end clang namespace +#endif diff --git a/include/clang/Analysis/Analyses/PrintfFormatString.h b/include/clang/Analysis/Analyses/PrintfFormatString.h deleted file mode 100644 index d907637d39..0000000000 --- a/include/clang/Analysis/Analyses/PrintfFormatString.h +++ /dev/null @@ -1,445 +0,0 @@ -//==- PrintfFormatStrings.h - Analysis of printf format strings --*- C++ -*-==// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Handling of format string in printf and friends. The structure of format -// strings for fprintf() are described in C99 7.19.6.1. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_CLANG_FPRINTF_FORMAT_H -#define LLVM_CLANG_FPRINTF_FORMAT_H - -#include "clang/AST/CanonicalType.h" - -namespace clang { - -class ASTContext; - -namespace analyze_printf { - -class ArgTypeResult { -public: - enum Kind { UnknownTy, InvalidTy, SpecificTy, ObjCPointerTy, CPointerTy, - CStrTy, WCStrTy }; -private: - const Kind K; - QualType T; - ArgTypeResult(bool) : K(InvalidTy) {} -public: - ArgTypeResult(Kind k = UnknownTy) : K(k) {} - ArgTypeResult(QualType t) : K(SpecificTy), T(t) {} - ArgTypeResult(CanQualType t) : K(SpecificTy), T(t) {} - - static ArgTypeResult Invalid() { return ArgTypeResult(true); } - - bool isValid() const { return K != InvalidTy; } - - const QualType *getSpecificType() const { - return K == SpecificTy ? &T : 0; - } - - bool matchesType(ASTContext &C, QualType argTy) const; - - bool matchesAnyObjCObjectRef() const { return K == ObjCPointerTy; } - - QualType getRepresentativeType(ASTContext &C) const; -}; - -class ConversionSpecifier { -public: - enum Kind { - InvalidSpecifier = 0, - // C99 conversion specifiers. - dArg, // 'd' - IntAsCharArg, // 'c' - iArg, // 'i', - oArg, // 'o', - uArg, // 'u', - xArg, // 'x', - XArg, // 'X', - fArg, // 'f', - FArg, // 'F', - eArg, // 'e', - EArg, // 'E', - gArg, // 'g', - GArg, // 'G', - aArg, // 'a', - AArg, // 'A', - CStrArg, // 's' - VoidPtrArg, // 'p' - OutIntPtrArg, // 'n' - PercentArg, // '%' - // MacOS X unicode extensions. - CArg, // 'C' - UnicodeStrArg, // 'S' - // Objective-C specific specifiers. - ObjCObjArg, // '@' - // GlibC specific specifiers. - PrintErrno, // 'm' - // Specifier ranges. - IntArgBeg = dArg, - IntArgEnd = iArg, - UIntArgBeg = oArg, - UIntArgEnd = XArg, - DoubleArgBeg = fArg, - DoubleArgEnd = AArg, - C99Beg = IntArgBeg, - C99End = DoubleArgEnd, - ObjCBeg = ObjCObjArg, - ObjCEnd = ObjCObjArg - }; - - ConversionSpecifier() - : Position(0), kind(InvalidSpecifier) {} - - ConversionSpecifier(const char *pos, Kind k) - : Position(pos), kind(k) {} - - const char *getStart() const { - return Position; - } - - llvm::StringRef getCharacters() const { - return llvm::StringRef(getStart(), getLength()); - } - - bool consumesDataArgument() const { - switch (kind) { - case PercentArg: - case PrintErrno: - return false; - default: - return true; - } - } - - bool isObjCArg() const { return kind >= ObjCBeg && kind <= ObjCEnd; } - bool isIntArg() const { return kind >= dArg && kind <= iArg; } - bool isUIntArg() const { return kind >= oArg && kind <= XArg; } - bool isDoubleArg() const { return kind >= fArg && kind <= AArg; } - Kind getKind() const { return kind; } - void setKind(Kind k) { kind = k; } - unsigned getLength() const { - // Conversion specifiers currently only are represented by - // single characters, but we be flexible. - return 1; - } - const char *toString() const; - -private: - const char *Position; - Kind kind; -}; - -class LengthModifier { -public: - enum Kind { - None, - AsChar, // 'hh' - AsShort, // 'h' - AsLong, // 'l' - AsLongLong, // 'll', 'q' (BSD, deprecated) - AsIntMax, // 'j' - AsSizeT, // 'z' - AsPtrDiff, // 't' - AsLongDouble, // 'L' - AsWideChar = AsLong // for '%ls' - }; - - LengthModifier() - : Position(0), kind(None) {} - LengthModifier(const char *pos, Kind k) - : Position(pos), kind(k) {} - - const char *getStart() const { - return Position; - } - - unsigned getLength() const { - switch (kind) { - default: - return 1; - case AsLongLong: - case AsChar: - return 2; - case None: - return 0; - } - } - - Kind getKind() const { return kind; } - void setKind(Kind k) { kind = k; } - - const char *toString() const; - -private: - const char *Position; - Kind kind; -}; - -class OptionalAmount { -public: - enum HowSpecified { NotSpecified, Constant, Arg, Invalid }; - - OptionalAmount(HowSpecified howSpecified, - unsigned amount, - const char *amountStart, - unsigned amountLength, - bool usesPositionalArg) - : start(amountStart), length(amountLength), hs(howSpecified), amt(amount), - UsesPositionalArg(usesPositionalArg), UsesDotPrefix(0) {} - - OptionalAmount(bool valid = true) - : start(0),length(0), hs(valid ? NotSpecified : Invalid), amt(0), - UsesPositionalArg(0), UsesDotPrefix(0) {} - - bool isInvalid() const { - return hs == Invalid; - } - - HowSpecified getHowSpecified() const { return hs; } - void setHowSpecified(HowSpecified h) { hs = h; } - - bool hasDataArgument() const { return hs == Arg; } - - unsigned getArgIndex() const { - assert(hasDataArgument()); - return amt; - } - - unsigned getConstantAmount() const { - assert(hs == Constant); - return amt; - } - - const char *getStart() const { - // We include the . character if it is given. - return start - UsesDotPrefix; - } - - unsigned getConstantLength() const { - assert(hs == Constant); - return length + UsesDotPrefix; - } - - ArgTypeResult getArgType(ASTContext &Ctx) const; - - void toString(llvm::raw_ostream &os) const; - - bool usesPositionalArg() const { return (bool) UsesPositionalArg; } - unsigned getPositionalArgIndex() const { - assert(hasDataArgument()); - return amt + 1; - } - - bool usesDotPrefix() const { return UsesDotPrefix; } - void setUsesDotPrefix() { UsesDotPrefix = true; } - -private: - const char *start; - unsigned length; - HowSpecified hs; - unsigned amt; - bool UsesPositionalArg : 1; - bool UsesDotPrefix; -}; - -// 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; - 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 - bool UsesPositionalArg; - unsigned argIndex; - ConversionSpecifier CS; - OptionalAmount FieldWidth; - OptionalAmount Precision; -public: - FormatSpecifier() : - IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "), - HasAlternativeForm("#"), HasLeadingZeroes("0"), UsesPositionalArg(false), - argIndex(0) {} - - static FormatSpecifier Parse(const char *beg, const char *end); - - // Methods for incrementally constructing the FormatSpecifier. - void setConversionSpecifier(const ConversionSpecifier &cs) { - CS = cs; - } - void setLengthModifier(LengthModifier lm) { - LM = lm; - } - 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()); - argIndex = i; - } - - unsigned getArgIndex() const { - assert(CS.consumesDataArgument()); - return argIndex; - } - - unsigned getPositionalArgIndex() const { - assert(CS.consumesDataArgument()); - return argIndex + 1; - } - - // Methods for querying the format specifier. - - const ConversionSpecifier &getConversionSpecifier() const { - return CS; - } - - const LengthModifier &getLengthModifier() const { - return LM; - } - - const OptionalAmount &getFieldWidth() const { - return FieldWidth; - } - - void setFieldWidth(const OptionalAmount &Amt) { - FieldWidth = Amt; - } - - void setPrecision(const OptionalAmount &Amt) { - Precision = Amt; - Precision.setUsesDotPrefix(); - } - - const OptionalAmount &getPrecision() const { - return Precision; - } - - /// \brief Returns the builtin type that a data argument - /// paired with this format specifier should have. This method - /// will return null if the format specifier does not have - /// a matching data argument or the matching argument matches - /// more than one type. - ArgTypeResult getArgType(ASTContext &Ctx) const; - - 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 - /// was not successful. - 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 }; - -class FormatStringHandler { -public: - FormatStringHandler() {} - virtual ~FormatStringHandler(); - - virtual void HandleIncompleteFormatSpecifier(const char *startSpecifier, - unsigned specifierLen) {} - - virtual void HandleNullChar(const char *nullCharacter) {} - - virtual void HandleInvalidPosition(const char *startPos, unsigned posLen, - PositionContext p) {} - - virtual void HandleZeroPosition(const char *startPos, unsigned posLen) {} - - virtual bool - HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen) { return true; } - - virtual bool HandleFormatSpecifier(const analyze_printf::FormatSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen) { - return true; - } -}; - -bool ParseFormatString(FormatStringHandler &H, - const char *beg, const char *end); - -} // end printf namespace -} // end clang namespace -#endif diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 01a37fb6f2..e854674750 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2183,10 +2183,10 @@ def err_builtin_direct_init_more_than_one_arg : Error< "initializer of a builtin type can only take one argument">; def err_value_init_for_array_type : Error< "array types cannot be value-initialized">; -def warn_printf_nonliteral_noargs : Warning< +def warn_format_nonliteral_noargs : Warning< "format string is not a string literal (potentially insecure)">, InGroup<FormatSecurity>; -def warn_printf_nonliteral : Warning< +def warn_format_nonliteral : Warning< "format string is not a string literal">, InGroup<FormatNonLiteral>, DefaultIgnore; @@ -2936,29 +2936,32 @@ def warn_printf_invalid_conversion : Warning< "invalid conversion specifier '%0'">, InGroup<Format>; def warn_printf_incomplete_specifier : Warning< "incomplete format specifier">, InGroup<Format>; -def warn_printf_missing_format_string : Warning< +def warn_missing_format_string : Warning< "format string missing">, InGroup<Format>; +def warn_scanf_nonzero_width : Warning< + "conversion specifies 0 input characters for field width">, + InGroup<Format>; def warn_printf_conversion_argument_type_mismatch : Warning< "conversion specifies type %0 but the argument has type %1">, InGroup<Format>; def warn_printf_positional_arg_exceeds_data_args : Warning < "data argument position '%0' exceeds the number of data arguments (%1)">, InGroup<Format>; -def warn_printf_zero_positional_specifier : Warning< +def warn_format_zero_positional_specifier : Warning< "position arguments in format strings start counting at 1 (not 0)">, InGroup<Format>; -def warn_printf_invalid_positional_specifier : Warning< +def warn_format_invalid_positional_specifier : Warning< "invalid position specified for %select{field width|field precision}0">, InGroup<Format>; -def warn_printf_mix_positional_nonpositional_args : Warning< +def warn_format_mix_positional_nonpositional_args : Warning< "cannot mix positional and non-positional arguments in format string">, InGroup<Format>; def warn_null_arg : Warning< "null passed to a callee which requires a non-null argument">, InGroup<NonNull>; -def warn_printf_empty_format_string : Warning< +def warn_empty_format_string : Warning< "format string is empty">, InGroup<FormatZeroLength>; -def warn_printf_format_string_is_wide_literal : Warning< +def warn_format_string_is_wide_literal : Warning< "format string should not be a wide string">, InGroup<Format>; def warn_printf_format_string_contains_null_char : Warning< "format string contains '\\0' within the string body">, InGroup<Format>; diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt index f2916c2068..d24ae889d5 100644 --- a/lib/Analysis/CMakeLists.txt +++ b/lib/Analysis/CMakeLists.txt @@ -3,9 +3,11 @@ set(LLVM_NO_RTTI 1) add_clang_library(clangAnalysis AnalysisContext.cpp CFG.cpp + FormatString.cpp LiveVariables.cpp PrintfFormatString.cpp ReachableCode.cpp + ScanfFormatString.cpp UninitializedValues.cpp ) diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp new file mode 100644 index 0000000000..dcd18db16c --- /dev/null +++ b/lib/Analysis/FormatString.cpp @@ -0,0 +1,380 @@ +// FormatString.cpp - Common stuff for handling printf/scanf formats -*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Shared details for processing format strings of printf and scanf +// (and friends). +// +//===----------------------------------------------------------------------===// + +#include "FormatStringParsing.h" + +using clang::analyze_format_string::ArgTypeResult; +using clang::analyze_format_string::FormatStringHandler; +using clang::analyze_format_string::FormatSpecifier; +using clang::analyze_format_string::LengthModifier; +using clang::analyze_format_string::OptionalAmount; +using clang::analyze_format_string::PositionContext; +using namespace clang; + +// Key function to FormatStringHandler. +FormatStringHandler::~FormatStringHandler() {} + +//===----------------------------------------------------------------------===// +// Functions for parsing format strings components in both printf and +// scanf format strings. +//===----------------------------------------------------------------------===// + +OptionalAmount +clang::analyze_format_string::ParseAmount(const char *&Beg, const char *E) { + const char *I = Beg; + UpdateOnReturn <const char*> UpdateBeg(Beg, I); + + unsigned accumulator = 0; + bool hasDigits = false; + + for ( ; I != E; ++I) { + char c = *I; + if (c >= '0' && c <= '9') { + hasDigits = true; + accumulator = (accumulator * 10) + (c - '0'); + continue; + } + + if (hasDigits) + return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg, + false); + + break; + } + + return OptionalAmount(); +} + +OptionalAmount +clang::analyze_format_string::ParseNonPositionAmount(const char *&Beg, + const char *E, + unsigned &argIndex) { + if (*Beg == '*') { + ++Beg; + return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); + } + + return ParseAmount(Beg, E); +} + +OptionalAmount +clang::analyze_format_string::ParsePositionAmount(FormatStringHandler &H, + const char *Start, + const char *&Beg, + const char *E, + PositionContext p) { + if (*Beg == '*') { + const char *I = Beg + 1; + const OptionalAmount &Amt = ParseAmount(I, E); + + if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { + H.HandleInvalidPosition(Beg, I - Beg, p); + return OptionalAmount(false); + } + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return OptionalAmount(false); + } + + assert(Amt.getHowSpecified() == OptionalAmount::Constant); + + if (*I == '$') { + // Handle positional arguments + + // Special case: '*0$', since this is an easy mistake. + if (Amt.getConstantAmount() == 0) { + H.HandleZeroPosition(Beg, I - Beg + 1); + return OptionalAmount(false); + } + + const char *Tmp = Beg; + Beg = ++I; + + return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1, + Tmp, 0, true); + } + + H.HandleInvalidPosition(Beg, I - Beg, p); + return OptionalAmount(false); + } + + return ParseAmount(Beg, E); +} + + +bool +clang::analyze_format_string::ParseFieldWidth(FormatStringHandler &H, + FormatSpecifier &CS, + const char *Start, + const char *&Beg, const char *E, + unsigned *argIndex) { + // FIXME: Support negative field widths. + if (argIndex) { + CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex)); + } + else { + const OptionalAmount Amt = + ParsePositionAmount(H, Start, Beg, E, + analyze_format_string::FieldWidthPos); + + if (Amt.isInvalid()) + return true; + CS.setFieldWidth(Amt); + } + return false; +} + +bool +clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, + FormatSpecifier &FS, + const char *Start, + const char *&Beg, + const char *E) { + const char *I = Beg; + + const OptionalAmount &Amt = ParseAmount(I, E); + + if (I == E) { + // No more characters left? + H.HandleIncompleteSpecifier(Start, E - Start); + return true; + } + + if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') { + // Special case: '%0$', since this is an easy mistake. + if (Amt.getConstantAmount() == 0) { + H.HandleZeroPosition(Start, I - Start); + return true; + } + + FS.setArgIndex(Amt.getConstantAmount() - 1); + FS.setUsesPositionalArg(); + // Update the caller's pointer if we decided to consume + // these characters. + Beg = I; + return false; + } + + return false; +} + +bool +clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, + const char *&I, + const char *E) { + LengthModifier::Kind lmKind = LengthModifier::None; + const char *lmPosition = I; + switch (*I) { + default: + return false; + case 'h': + ++I; + lmKind = (I != E && *I == 'h') ? + ++I, LengthModifier::AsChar : LengthModifier::AsShort; + break; + case 'l': + ++I; + lmKind = (I != E && *I == 'l') ? + ++I, LengthModifier::AsLongLong : LengthModifier::AsLong; + break; + case 'j': lmKind = LengthModifier::AsIntMax; ++I; break; + case 'z': lmKind = LengthModifier::AsSizeT; ++I; break; + case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break; + case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break; + case 'q': lmKind = LengthModifier::AsLongLong; ++I; break; + } + LengthModifier lm(lmPosition, lmKind); + FS.setLengthModifier(lm); + return true; +} + +//===----------------------------------------------------------------------===// +// Methods on ArgTypeResult. +//===----------------------------------------------------------------------===// + +bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { + switch (K) { + case InvalidTy: + assert(false && "ArgTypeResult must be valid"); + return true; + + case UnknownTy: + return true; + + case SpecificTy: { + argTy = C.getCanonicalType(argTy).getUnqualifiedType(); + if (T == argTy) + return true; + if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + default: + break; + case BuiltinType::Char_S: + case BuiltinType::SChar: + return T == C.UnsignedCharTy; + case BuiltinType::Char_U: + case BuiltinType::UChar: + return T == C.SignedCharTy; + case BuiltinType::Short: + return T == C.UnsignedShortTy; + case BuiltinType::UShort: + return T == C.ShortTy; + case BuiltinType::Int: + return T == C.UnsignedIntTy; + case BuiltinType::UInt: + return T == C.IntTy; + case BuiltinType::Long: + return T == C.UnsignedLongTy; + case BuiltinType::ULong: + return T == C.LongTy; + case BuiltinType::LongLong: + return T == C.UnsignedLongLongTy; + case BuiltinType::ULongLong: + return T == C.LongLongTy; + } + return false; + } + + case CStrTy: { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + QualType pointeeTy = PT->getPointeeType(); + if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) + switch (BT->getKind()) { + case BuiltinType::Void: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + return true; + default: + break; + } + + return false; + } + + case WCStrTy: { + const PointerType *PT = argTy->getAs<PointerType>(); + if (!PT) + return false; + QualType pointeeTy = + C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType(); + return pointeeTy == C.getWCharType(); + } + + case CPointerTy: + return argTy->getAs<PointerType>() != NULL || + argTy->getAs<ObjCObjectPointerType>() != NULL; + + case ObjCPointerTy: + return argTy->getAs<ObjCObjectPointerType>() != NULL; + } + + // FIXME: Should be unreachable, but Clang is currently emitting + // a warning. + return false; +} + +QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { + switch (K) { + case InvalidTy: + assert(false && "No representative type for Invalid ArgTypeResult"); + // Fall-through. + case UnknownTy: + return QualType(); + case SpecificTy: + return T; + case CStrTy: + return C.getPointerType(C.CharTy); + case WCStrTy: + return C.getPointerType(C.getWCharType()); + case ObjCPointerTy: + return C.ObjCBuiltinIdTy; + case CPointerTy: + return C.VoidPtrTy; + } + + // FIXME: Should be unreachable, but Clang is currently emitting + // a warning. + return QualType(); +} + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +ArgTypeResult +analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const { + return Ctx.IntTy; +} + +//===----------------------------------------------------------------------===// +// Methods on LengthModifier. +//===----------------------------------------------------------------------===// + +const char * +analyze_format_string::LengthModifier::toString() const { + switch (kind) { + case AsChar: + return "hh"; + case AsShort: + return "h"; + case AsLong: // or AsWideChar + return "l"; + case AsLongLong: + return "ll"; + case AsIntMax: + return "j"; + case AsSizeT: + return "z"; + case AsPtrDiff: + return "t"; + case AsLongDouble: + return "L"; + case None: + return ""; + } + return NULL; +} + +//===----------------------------------------------------------------------===// +// Methods on OptionalAmount. +//===----------------------------------------------------------------------===// + +void +analyze_format_string::OptionalAmount::toString(llvm::raw_ostream &os) const { + switch (hs) { + case Invalid: + case NotSpecified: + return; + case Arg: + if (UsesDotPrefix) + os << "."; + if (usesPositionalArg()) + os << "*" << getPositionalArgIndex() << "$"; + else + os << "*"; + break; + case Constant: + if (UsesDotPrefix) + os << "."; + os << amt; + break; + } +} + diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp index 558d38af0c..584fc12002 100644 --- a/lib/Analysis/PrintfFormatString.cpp +++ b/lib/Analysis/PrintfFormatString.cpp @@ -1,4 +1,4 @@ -//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==// +//== PrintfFormatString.cpp - Analysis of printf format strings --*- C++ -*-==// // // The LLVM Compiler Infrastructure // @@ -12,141 +12,28 @@ // //===----------------------------------------------------------------------===// -#include "clang/Analysis/Analyses/PrintfFormatString.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Type.h" -#include "llvm/Support/raw_ostream.h" +#include "clang/Analysis/Analyses/FormatString.h" +#include "FormatStringParsing.h" -using clang::analyze_printf::ArgTypeResult; -using clang::analyze_printf::FormatSpecifier; -using clang::analyze_printf::FormatStringHandler; -using clang::analyze_printf::OptionalAmount; -using clang::analyze_printf::PositionContext; +using clang::analyze_format_string::ArgTypeResult; +using clang::analyze_format_string::FormatStringHandler; +using clang::analyze_format_string::LengthModifier; +using clang::analyze_format_string::OptionalAmount; using clang::analyze_printf::ConversionSpecifier; -using clang::analyze_printf::LengthModifier; +using clang::analyze_printf::PrintfSpecifier; using namespace clang; -namespace { -class FormatSpecifierResult { - FormatSpecifier FS; - const char *Start; - bool Stop; -public: - FormatSpecifierResult(bool stop = false) - : Start(0), Stop(stop) {} - FormatSpecifierResult(const char *start, - const FormatSpecifier &fs) - : FS(fs), Start(start), Stop(false) {} - - const char *getStart() const { return Start; } - bool shouldStop() const { return Stop; } - bool hasValue() const { return Start != 0; } - const FormatSpecifier &getValue() const { - assert(hasValue()); - return FS; - } - const FormatSpecifier &getValue() { return FS; } -}; -} // end anonymous namespace - -template <typename T> -class UpdateOnReturn { - T &ValueToUpdate; - const T &ValueToCopy; -public: - UpdateOnReturn(T &valueToUpdate, const T &valueToCopy) - : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {} - - ~UpdateOnReturn() { - ValueToUpdate = ValueToCopy; - } -}; +typedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier> + PrintfSpecifierResult; //===----------------------------------------------------------------------===// // Methods for parsing format strings. //===----------------------------------------------------------------------===// -static OptionalAmount ParseAmount(const char *&Beg, const char *E) { - const char *I = Beg; - UpdateOnReturn <const char*> UpdateBeg(Beg, I); - - unsigned accumulator = 0; - bool hasDigits = false; - - for ( ; I != E; ++I) { - char c = *I; - if (c >= '0' && c <= '9') { - hasDigits = true; - accumulator = (accumulator * 10) + (c - '0'); - continue; - } - - if (hasDigits) - return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg, - false); - - break; - } - - return OptionalAmount(); -} - -static OptionalAmount ParseNonPositionAmount(const char *&Beg, const char *E, - unsigned &argIndex) { - if (*Beg == '*') { - ++Beg; - return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); - } - - return ParseAmount(Beg, E); -} - -static OptionalAmount ParsePositionAmount(FormatStringHandler &H, - const char *Start, - const char *&Beg, const char *E, - PositionContext p) { - if (*Beg == '*') { - const char *I = Beg + 1; - const OptionalAmount &Amt = ParseAmount(I, E); - - if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { - H.HandleInvalidPosition(Beg, I - Beg, p); - return OptionalAmount(false); - } - - if (I== E) { - // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); - return OptionalAmount(false); - } - - assert(Amt.getHowSpecified() == OptionalAmount::Constant); - - if (*I == '$') { - // Handle positional arguments - - // Special case: '*0$', since this is an easy mistake. - if (Amt.getConstantAmount() == 0) { - H.HandleZeroPosition(Beg, I - Beg + 1); - return OptionalAmount(false); - } - - const char *Tmp = Beg; - Beg = ++I; - - return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1, - Tmp, 0, true); - } - - H.HandleInvalidPosition(Beg, I - Beg, p); - return OptionalAmount(false); - } - - return ParseAmount(Beg, E); -} +using analyze_format_string::ParseNonPositionAmount; -static bool ParsePrecision(FormatStringHandler &H, FormatSpecifier &FS, +static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, const char *Start, const char *&Beg, const char *E, unsigned *argIndex) { if (argIndex) { @@ -154,7 +41,7 @@ static bool ParsePrecision(FormatStringHandler &H, FormatSpecifier &FS, } else { const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E, - analyze_printf::PrecisionPos); + analyze_format_string::PrecisionPos); if (Amt.isInvalid()) return true; FS.setPrecision(Amt); @@ -162,57 +49,7 @@ static bool ParsePrecision(FormatStringHandler &H, FormatSpecifier &FS, return false; } -static bool ParseFieldWidth(FormatStringHandler &H, FormatSpecifier &FS, - const char *Start, const char *&Beg, const char *E, - unsigned *argIndex) { - // FIXME: Support negative field widths. - if (argIndex) { - FS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex)); - } - else { - const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E, - analyze_printf::FieldWidthPos); - if (Amt.isInvalid()) - return true; - FS.setFieldWidth(Amt); - } - return false; -} - -static bool ParseArgPosition(FormatStringHandler &H, - FormatSpecifier &FS, const char *Start, - const char *&Beg, const char *E) { - - using namespace clang::analyze_printf; - const char *I = Beg; - - const OptionalAmount &Amt = ParseAmount(I, E); - - if (I == E) { - // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); - return true; - } - - if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') { - // Special case: '%0$', since this is an easy mistake. - if (Amt.getConstantAmount() == 0) { - H.HandleZeroPosition(Start, I - Start); - return true; - } - - FS.setArgIndex(Amt.getConstantAmount() - 1); - FS.setUsesPositionalArg(); - // Update the caller's pointer if we decided to consume - // these characters. - Beg = I; - return false; - } - - return false; -} - -static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, +static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, const char *&Beg, const char *E, unsigned &argIndex) { @@ -243,17 +80,17 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, if (I == E) { // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } - FormatSpecifier FS; + PrintfSpecifier FS; if (ParseArgPosition(H, FS, Start, I, E)) return true; if (I == E) { // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } @@ -274,7 +111,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, if (I == E) { // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } @@ -285,7 +122,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, if (I == E) { // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } @@ -293,7 +130,7 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, if (*I == '.') { ++I; if (I == E) { - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } @@ -303,39 +140,15 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, if (I == E) { // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } } // Look for the length modifier. - LengthModifier::Kind lmKind = LengthModifier::None; - const char *lmPosition = I; - switch (*I) { - default: - break; - case 'h': - ++I; - lmKind = (I != E && *I == 'h') ? - ++I, LengthModifier::AsChar : LengthModifier::AsShort; - break; - case 'l': - ++I; - lmKind = (I != E && *I == 'l') ? - ++I, LengthModifier::AsLongLong : LengthModifier::AsLong; - break; - case 'j': lmKind = LengthModifier::AsIntMax; ++I; break; - case 'z': lmKind = LengthModifier::AsSizeT; ++I; break; - case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break; - case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break; - case 'q': lmKind = LengthModifier::AsLongLong; ++I; break; - } - LengthModifier lm(lmPosition, lmKind); - FS.setLengthModifier(lm); - - if (I == E) { + if (ParseLengthModifier(FS, I, E) && I == E) { // No more characters left? - H.HandleIncompleteFormatSpecifier(Start, E - Start); + H.HandleIncompleteSpecifier(Start, E - Start); return true; } @@ -386,19 +199,20 @@ static FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H, if (k == ConversionSpecifier::InvalidSpecifier) { // Assume the conversion takes one argument. - return !H.HandleInvalidConversionSpecifier(FS, Beg, I - Beg); + return !H.HandleInvalidPrintfConversionSpecifier(FS, Beg, I - Beg); } - return FormatSpecifierResult(Start, FS); + return PrintfSpecifierResult(Start, FS); } -bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H, - const char *I, const char *E) { +bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, + const char *I, + const char *E) { unsigned argIndex = 0; // Keep looking for a format specifier until we have exhausted the string. while (I != E) { - const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E, argIndex); + const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex); // Did a fail-stop error of any kind occur when parsing the specifier? // If so, don't do any more processing. if (FSR.shouldStop()) @@ -408,7 +222,7 @@ bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H, if (!FSR.hasValue()) continue; // We have a format specifier. Pass it to the callback. - if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(), + if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(), I - FSR.getStart())) return true; } @@ -416,129 +230,6 @@ bool clang::analyze_printf::ParseFormatString(FormatStringHandler &H, return false; } -FormatStringHandler::~FormatStringHandler() {} - -//===----------------------------------------------------------------------===// -// Methods on ArgTypeResult. -//===----------------------------------------------------------------------===// - -bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { - switch (K) { - case InvalidTy: - assert(false && "ArgTypeResult must be valid"); - return true; - - case UnknownTy: - return true; - - case SpecificTy: { - argTy = C.getCanonicalType(argTy).getUnqualifiedType(); - if (T == argTy) - return true; - if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) - switch (BT->getKind()) { - default: - break; - case BuiltinType::Char_S: - case BuiltinType::SChar: - return T == C.UnsignedCharTy; - case BuiltinType::Char_U: - case BuiltinType::UChar: - return T == C.SignedCharTy; - case BuiltinType::Short: - return T == C.UnsignedShortTy; - case BuiltinType::UShort: - return T == C.ShortTy; - case BuiltinType::Int: - return T == C.UnsignedIntTy; - case BuiltinType::UInt: - return T == C.IntTy; - case BuiltinType::Long: - return T == C.UnsignedLongTy; - case BuiltinType::ULong: - return T == C.LongTy; - case BuiltinType::LongLong: - return T == C.UnsignedLongLongTy; - case BuiltinType::ULongLong: - return T == C.LongLongTy; - } - return false; - } - - case CStrTy: { - const PointerType *PT = argTy->getAs<PointerType>(); - if (!PT) - return false; - QualType pointeeTy = PT->getPointeeType(); - if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) - switch (BT->getKind()) { - case BuiltinType::Void: - case BuiltinType::Char_U: - case BuiltinType::UChar: - case BuiltinType::Char_S: - case BuiltinType::SChar: - return true; - default: - break; - } - - return false; - } - - case WCStrTy: { - const PointerType *PT = argTy->getAs<PointerType>(); - if (!PT) - return false; - QualType pointeeTy = - C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType(); - return pointeeTy == C.getWCharType(); - } - - case CPointerTy: - return argTy->getAs<PointerType>() != NULL || - argTy->getAs<ObjCObjectPointerType>() != NULL; - - case ObjCPointerTy: - return argTy->getAs<ObjCObjectPointerType>() != NULL; - } - - // FIXME: Should be unreachable, but Clang is currently emitting - // a warning. - return false; -} - -QualType ArgTypeResult::getRepresentativeType(ASTContext &C) const { - switch (K) { - case InvalidTy: - assert(false && "No representative type for Invalid ArgTypeResult"); - // Fall-through. - case UnknownTy: - return QualType(); - case SpecificTy: - return T; - case CStrTy: - return C.getPointerType(C.CharTy); - case WCStrTy: - return C.getPointerType(C.getWCharType()); - case ObjCPointerTy: - return C.ObjCBuiltinIdTy; - case CPointerTy: - return C.VoidPtrTy; - } - - // FIXME: Should be unreachable, but Clang is currently emitting - // a warning. - return QualType(); -} - -//===----------------------------------------------------------------------===// -// Methods on OptionalAmount. -//===----------------------------------------------------------------------===// - -ArgTypeResult OptionalAmount::getArgType(ASTContext &Ctx) const { - return Ctx.IntTy; -} - //===----------------------------------------------------------------------===// // Methods on ConversionSpecifier. //===----------------------------------------------------------------------===// @@ -579,63 +270,10 @@ const char *ConversionSpecifier::toString() const { } //===----------------------------------------------------------------------===// -// Methods on LengthModifier. -//===----------------------------------------------------------------------===// - -const char *LengthModifier::toString() const { - switch (kind) { - case AsChar: - return "hh"; - case AsShort: - return "h"; - case AsLong: // or AsWideChar - return "l"; - case AsLongLong: - return "ll"; - case AsIntMax: - return "j"; - case AsSizeT: - return "z"; - case AsPtrDiff: - return "t"; - case AsLongDouble: - return "L"; - case None: - return ""; - } - return NULL; -} - -//===----------------------------------------------------------------------===// -// Methods on OptionalAmount. -//===----------------------------------------------------------------------===// - -void OptionalAmount::toString(llvm::raw_ostream &os) const { - switch (hs) { - case Invalid: - case NotSpecified: - return; - case Arg: - if (UsesDotPrefix) - os << "."; - if (usesPositionalArg()) - os << "*" << getPositionalArgIndex() << "$"; - else - os << "*"; - break; - case Constant: - if (UsesDotPrefix) - os << "."; - os << amt; - break; - } -} - -//===----------------------------------------------------------------------===// -// Methods on FormatSpecifier. +// Methods on PrintfSpecifier. //===----------------------------------------------------------------------===// -ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { +ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const { if (!CS.consumesDataArgument()) return ArgTypeResult::Invalid(); @@ -702,7 +340,7 @@ ArgTypeResult FormatSpecifier::getArgType(ASTContext &Ctx) const { return ArgTypeResult(); } -bool FormatSpecifier::fixType(QualType QT) { +bool PrintfSpecifier::fixType(QualType QT) { // Handle strings first (char *, wchar_t *) if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) { CS.setKind(ConversionSpecifier::CStrArg); @@ -783,9 +421,9 @@ bool FormatSpecifier::fixType(QualType QT) { return true; } -void FormatSpecifier::toString(llvm::raw_ostream &os) const { +void PrintfSpecifier::toString(llvm::raw_ostream &os) const { // Whilst some features have no defined order, we are using the order - // appearing in the C99 standard (ISO/IEC 9899:1999 (E) ¤7.19.6.1) + // appearing in the C99 standard (ISO/IEC 9899:1999 (E) ¤7.19.6.1) os << "%"; // Positional args @@ -810,7 +448,7 @@ void FormatSpecifier::toString(llvm::raw_ostream &os) const { os << CS.toString(); } -bool FormatSpecifier::hasValidPlusPrefix() const { +bool PrintfSpecifier::hasValidPlusPrefix() const { if (!HasPlusPrefix) return true; @@ -833,7 +471,7 @@ bool FormatSpecifier::hasValidPlusPrefix() const { } } -bool FormatSpecifier::hasValidAlternativeForm() const { +bool PrintfSpecifier::hasValidAlternativeForm() const { if (!HasAlternativeForm) return true; @@ -856,7 +494,7 @@ bool FormatSpecifier::hasValidAlternativeForm() const { } } -bool FormatSpecifier::hasValidLeadingZeros() const { +bool PrintfSpecifier::hasValidLeadingZeros() const { if (!HasLeadingZeroes) return true; @@ -883,7 +521,7 @@ bool FormatSpecifier::hasValidLeadingZeros() const { } } -bool FormatSpecifier::hasValidSpacePrefix() const { +bool PrintfSpecifier::hasValidSpacePrefix() const { if (!HasSpacePrefix) return true; @@ -906,7 +544,7 @@ bool FormatSpecifier::hasValidSpacePrefix() const { } } -bool FormatSpecifier::hasValidLeftJustified() const { +bool PrintfSpecifier::hasValidLeftJustified() const { if (!IsLeftJustified) return true; @@ -920,7 +558,7 @@ bool FormatSpecifier::hasValidLeftJustified() const { } } -bool FormatSpecifier::hasValidLengthModifier() const { +bool PrintfSpecifier::hasValidLengthModifier() const { switch (LM.getKind()) { case LengthModifier::None: return true; @@ -988,7 +626,7 @@ bool FormatSpecifier::hasValidLengthModifier() const { return false; } -bool FormatSpecifier::hasValidPrecision() const { +bool PrintfSpecifier::hasValidPrecision() const { if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) return true; @@ -1015,7 +653,7 @@ bool FormatSpecifier::hasValidPrecision() const { return false; } } -bool FormatSpecifier::hasValidFieldWidth() const { +bool PrintfSpecifier::hasValidFieldWidth() const { if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified) return true; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 8336918c13..64b1b0dd24 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -4618,16 +4618,23 @@ private: OwningExprResult SemaBuiltinAtomicOverloaded(OwningExprResult TheCallResult); bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum, llvm::APSInt &Result); + bool SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, bool HasVAListArg, unsigned format_idx, - unsigned firstDataArg); - void CheckPrintfString(const StringLiteral *FExpr, const Expr *OrigFormatExpr, + unsigned firstDataArg, bool isPrintf); + + void CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr, const CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg); + unsigned format_idx, unsigned firstDataArg, + bool isPrintf); + void CheckNonNullArguments(const NonNullAttr *NonNull, const CallExpr *TheCall); - void CheckPrintfArguments(const CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg); + + void CheckPrintfScanfArguments(const CallExpr *TheCall, bool HasVAListArg, + unsigned format_idx, unsigned firstDataArg, + bool isPrintf); + void CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc); void CheckFloatComparison(SourceLocation loc, Expr* lex, Expr* rex); diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 7a39f058c5..d789730715 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "Sema.h" -#include "clang/Analysis/Analyses/PrintfFormatString.h" +#include "clang/Analysis/Analyses/FormatString.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclObjC.h" @@ -334,10 +334,13 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { // Printf checking. if (const FormatAttr *Format = FDecl->getAttr<FormatAttr>()) { - if (CheckablePrintfAttr(Format, TheCall)) { + const bool b = Format->getType() == "scanf"; + if (b || CheckablePrintfAttr(Format, TheCall)) { bool HasVAListArg = Format->getFirstArg() == 0; - CheckPrintfArguments(TheCall, HasVAListArg, Format->getFormatIdx() - 1, - HasVAListArg ? 0 : Format->getFirstArg() - 1); + CheckPrintfScanfArguments(TheCall, HasVAListArg, + Format->getFormatIdx() - 1, + HasVAListArg ? 0 : Format->getFirstArg() - 1, + !b); } } @@ -362,12 +365,13 @@ bool Sema::CheckBlockCall(NamedDecl *NDecl, CallExpr *TheCall) { if (!Ty->isBlockPointerType()) return false; - if (!CheckablePrintfAttr(Format, TheCall)) + const bool b = Format->getType() == "scanf"; + if (!b && !CheckablePrintfAttr(Format, TheCall)) return false; bool HasVAListArg = Format->getFirstArg() == 0; - CheckPrintfArguments(TheCall, HasVAListArg, Format->getFormatIdx() - 1, - HasVAListArg ? 0 : Format->getFirstArg() - 1); + CheckPrintfScanfArguments(TheCall, HasVAListArg, Format->getFormatIdx() - 1, + HasVAListArg ? 0 : Format->getFirstArg() - 1, !b); return false; } @@ -941,29 +945,31 @@ bool Sema::SemaBuiltinLongjmp(CallExpr *TheCall) { // Handle i > 1 ? "x" : "y", recursivelly bool Sema::SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg) { + unsigned format_idx, unsigned firstDataArg, + bool isPrintf) { + if (E->isTypeDependent() || E->isValueDependent()) return false; switch (E->getStmtClass()) { case Stmt::ConditionalOperatorClass: { const ConditionalOperator *C = cast<ConditionalOperator>(E); - return SemaCheckStringLiteral(C->getTrueExpr(), TheCall, - HasVAListArg, format_idx, firstDataArg) - && SemaCheckStringLiteral(C->getRHS(), TheCall, - HasVAListArg, format_idx, firstDataArg); + return SemaCheckStringLiteral(C->getTrueExpr(), TheCall, HasVAListArg, + format_idx, firstDataArg, isPrintf) + && SemaCheckStringLiteral(C->getRHS(), TheCall, HasVAListArg, + format_idx, firstDataArg, isPrintf); } case Stmt::ImplicitCastExprClass: { const ImplicitCastExpr *Expr = cast<ImplicitCastExpr>(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, - format_idx, firstDataArg); + format_idx, firstDataArg, isPrintf); } case Stmt::ParenExprClass: { const ParenExpr *Expr = cast<ParenExpr>(E); return SemaCheckStringLiteral(Expr->getSubExpr(), TheCall, HasVAListArg, - format_idx, firstDataArg); + format_idx, firstDataArg, isPrintf); } case Stmt::DeclRefExprClass: { @@ -985,7 +991,8 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, if (isConstant) { if (const Expr *Init = VD->getAnyInitializer()) return SemaCheckStringLiteral(Init, TheCall, - HasVAListArg, format_idx, firstDataArg); + HasVAListArg, format_idx, firstDataArg, + isPrintf); } // For vprintf* functions (i.e., HasVAListArg==true), we add a @@ -1025,7 +1032,7 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, const Expr *Arg = CE->getArg(ArgIndex - 1); return SemaCheckStringLiteral(Arg, TheCall, HasVAListArg, - format_idx, firstDataArg); + format_idx, firstDataArg, isPrintf); } } } @@ -1043,8 +1050,8 @@ bool Sema::SemaCheckStringLiteral(const Expr *E, const CallExpr *TheCall, StrE = cast<StringLiteral>(E); if (StrE) { - CheckPrintfString(StrE, E, TheCall, HasVAListArg, format_idx, - firstDataArg); + CheckFormatString(StrE, E, TheCall, HasVAListArg, format_idx, + firstDataArg, isPrintf); return true; } @@ -1069,55 +1076,13 @@ Sema::CheckNonNullArguments(const NonNullAttr *NonNull, } } -/// CheckPrintfArguments - Check calls to printf (and similar functions) for -/// correct use of format strings. -/// -/// HasVAListArg - A predicate indicating whether the printf-like -/// function is passed an explicit va_arg argument (e.g., vprintf) -/// -/// format_idx - The index into Args for the format string. -/// -/// Improper format strings to functions in the printf family can be -/// the source of bizarre bugs and very serious security holes. A -/// good source of information is available in the following paper -/// (which includes additional references): -/// -/// FormatGuard: Automatic Protection From printf Format String -/// Vulnerabilities, Proceedings of the 10th USENIX Security Symposium, 2001. -/// -/// TODO: -/// Functionality implemented: -/// -/// We can statically check the following properties for string -/// literal format strings for non v.*printf functions (where the -/// arguments are passed directly): -// -/// (1) Are the number of format conversions equal to the number of -/// data arguments? -/// -/// (2) Does each format conversion correctly match the type of the -/// corresponding data argument? -/// -/// Moreover, for all printf functions we can: -/// -/// (3) Check for a missing format string (when not caught by type checking). -/// -/// (4) Check for no-operation flags; e.g. using "#" with format -/// conversion 'c' (TODO) -/// -/// (5) Check the use of '%n', a major source of security holes. -/// -/// (6) Check for malformed format conversions that don't specify anything. -/// -/// (7) Check for empty format strings. e.g: printf(""); -/// -/// (8) Check that the format string is a wide literal. -/// -/// All of these checks can be done by parsing the format string. -/// +/// CheckPrintfScanfArguments - Check calls to printf and scanf (and similar +/// functions) for correct use of format strings. void -Sema::CheckPrintfArguments(const CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg) { +Sema::CheckPrintfScanfArguments(const CallExpr *TheCall, bool HasVAListArg, + unsigned format_idx, unsigned firstDataArg, + bool isPrintf) { + const Expr *Fn = TheCall->getCallee(); // The way the format attribute works in GCC, the implicit this argument @@ -1132,9 +1097,9 @@ Sema::CheckPrintfArguments(const CallExpr *TheCall, bool HasVAListArg, --firstDataArg; } - // CHECK: printf-like function is called with no format string. + // CHECK: printf/scanf-like function is called with no format string. if (format_idx >= TheCall->getNumArgs()) { - Diag(TheCall->getRParenLoc(), diag::warn_printf_missing_format_string) + Diag(TheCall->getRParenLoc(), diag::warn_missing_format_string) << Fn->getSourceRange(); return; } @@ -1154,23 +1119,24 @@ Sema::CheckPrintfArguments(const CallExpr *TheCall, bool HasVAListArg, // ObjC string uses the same format specifiers as C string, so we can use // the same format string checking logic for both ObjC and C strings. if (SemaCheckStringLiteral(OrigFormatExpr, TheCall, HasVAListArg, format_idx, - firstDataArg)) + firstDataArg, isPrintf)) return; // Literal format string found, check done! // If there are no arguments specified, warn with -Wformat-security, otherwise // warn only with -Wformat-nonliteral. if (TheCall->getNumArgs() == format_idx+1) Diag(TheCall->getArg(format_idx)->getLocStart(), - diag::warn_printf_nonliteral_noargs) + diag::warn_format_nonliteral_noargs) << OrigFormatExpr->getSourceRange(); else Diag(TheCall->getArg(format_idx)->getLocStart(), - diag::warn_printf_nonliteral) + diag::warn_format_nonliteral) << OrigFormatExpr->getSourceRange(); } namespace { -class CheckPrintfHandler : public analyze_printf::FormatStringHandler { +class CheckFormatHandler : public analyze_format_string::FormatStringHandler { +protected: Sema &S; const StringLiteral *FExpr; const Expr *OrigFormatExpr; @@ -1185,7 +1151,7 @@ class CheckPrintfHandler : public analyze_printf::FormatStringHandler { bool usesPositionalArgs; bool atFirstArg; public: - CheckPrintfHandler(Sema &s, const StringLiteral *fexpr, + CheckFormatHandler(Sema &s, const StringLiteral *fexpr, const Expr *origFormatExpr, unsigned firstDataArg, unsigned numDataArgs, bool isObjCLiteral, const char *beg, bool hasVAListArg, @@ -1203,55 +1169,33 @@ public: void DoneProcessing(); - void HandleIncompleteFormatSpecifier(const char *startSpecifier, - unsigned specifierLen); - - bool - HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen); - + void HandleIncompleteSpecifier(const char *startSpecifier, + unsigned specifierLen); + virtual void HandleInvalidPosition(const char *startSpecifier, unsigned specifierLen, - analyze_printf::PositionContext p); + analyze_format_string::PositionContext p); virtual void HandleZeroPosition(const char *startPos, unsigned posLen); void HandleNullChar(const char *nullCharacter); - bool HandleFormatSpecifier(const analyze_printf::FormatSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen); -private: +protected: SourceRange getFormatStringRange(); - CharSourceRange getFormatSpecifierRange(const char *startSpecifier, - unsigned specifierLen); + CharSourceRange getSpecifierRange(const char *startSpecifier, + unsigned specifierLen); SourceLocation getLocationOfByte(const char *x); - bool HandleAmount(const analyze_printf::OptionalAmount &Amt, unsigned k, - 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; }; } -SourceRange CheckPrintfHandler::getFormatStringRange() { +SourceRange CheckFormatHandler::getFormatStringRange() { return OrigFormatExpr->getSourceRange(); } -CharSourceRange CheckPrintfHandler:: -getFormatSpecifierRange(const char *startSpecifier, unsigned specifierLen) { +CharSourceRange CheckFormatHandler:: +getSpecifierRange(const char *startSpecifier, unsigned specifierLen) { SourceLocation Start = getLocationOfByte(startSpecifier); SourceLocation End = getLocationOfByte(startSpecifier + specifierLen - 1); @@ -1261,43 +1205,109 @@ getFormatSpecifierRange(const char *startSpecifier, unsigned specifierLen) { return CharSourceRange::getCharRange(Start, End); } -SourceLocation CheckPrintfHandler::getLocationOfByte(const char *x) { +SourceLocation CheckFormatHandler::getLocationOfByte(const char *x) { return S.getLocationOfStringLiteralByte(FExpr, x - Beg); } -void CheckPrintfHandler:: -HandleIncompleteFormatSpecifier(const char *startSpecifier, - unsigned specifierLen) { +void CheckFormatHandler::HandleIncompleteSpecifier(const char *startSpecifier, + unsigned specifierLen){ SourceLocation Loc = getLocationOfByte(startSpecifier); S.Diag(Loc, diag::warn_printf_incomplete_specifier) - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getSpecifierRange(startSpecifier, specifierLen); } void -CheckPrintfHandler::HandleInvalidPosition(const char *startPos, unsigned posLen, - analyze_printf::PositionContext p) { +CheckFormatHandler::HandleInvalidPosition(const char *startPos, unsigned posLen, + analyze_format_string::PositionContext p) { SourceLocation Loc = getLocationOfByte(startPos); - S.Diag(Loc, diag::warn_printf_invalid_positional_specifier) - << (unsigned) p << getFormatSpecifierRange(startPos, posLen); + S.Diag(Loc, diag::warn_format_invalid_positional_specifier) + << (unsigned) p << getSpecifierRange(startPos, posLen); } -void CheckPrintfHandler::HandleZeroPosition(const char *startPos, +void CheckFormatHandler::HandleZeroPosition(const char *startPos, unsigned posLen) { SourceLocation Loc = getLocationOfByte(startPos); - S.Diag(Loc, diag::warn_printf_zero_positional_specifier) - << getFormatSpecifierRange(startPos, posLen); + S.Diag(Loc, diag::warn_format_zero_positional_specifier) + << getSpecifierRange(startPos, posLen); } -bool CheckPrintfHandler:: -HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, - const char *startSpecifier, - unsigned specifierLen) { +void CheckFormatHandler::HandleNullChar(const char *nullCharacter) { + // The presence of a null character is likely an error. + S.Diag(getLocationOfByte(nullCharacter), + diag::warn_printf_format_string_contains_null_char) + << getFormatStringRange(); +} + +const Expr *CheckFormatHandler::getDataArg(unsigned i) const { + return TheCall->getArg(FirstDataArg + i); +} +void CheckFormatHandler::DoneProcessing() { + // Does the number of data arguments exceed the number of + // format conversions in the format string? + if (!HasVAListArg) { + // Find any arguments that weren't covered. + CoveredArgs.flip(); + signed notCoveredArg = CoveredArgs.find_first(); + if (notCoveredArg >= 0) { + assert((unsigned)notCoveredArg < NumDataArgs); + S.Diag(getDataArg((unsigned) notCoveredArg)->getLocStart(), + diag::warn_printf_data_arg_not_used) + << getFormatStringRange(); + } + } +} + +//===--- CHECK: Printf format string checking ------------------------------===// + +namespace { +class CheckPrintfHandler : public CheckFormatHandler { +public: + CheckPrintfHandler(Sema &s, const StringLiteral *fexpr, + const Expr *origFormatExpr, unsigned firstDataArg, + unsigned numDataArgs, bool isObjCLiteral, + const char *beg, bool hasVAListArg, + const CallExpr *theCall, unsigned formatIdx) + : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, + numDataArgs, isObjCLiteral, beg, hasVAListArg, + theCall, formatIdx) {} + + + bool HandleInvalidPrintfConversionSpecifier( + const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen); + + bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen); + + bool HandleAmount(const analyze_format_string::OptionalAmount &Amt, unsigned k, + const char *startSpecifier, unsigned specifierLen); + void HandleInvalidAmount(const analyze_printf::PrintfSpecifier &FS, + const analyze_printf::OptionalAmount &Amt, + unsigned type, + const char *startSpecifier, unsigned specifierLen); + void HandleFlag(const analyze_printf::PrintfSpecifier &FS, + const analyze_printf::OptionalFlag &flag, + const char *startSpecifier, unsigned specifierLen); + void HandleIgnoredFlag(const analyze_printf::PrintfSpecifier &FS, + const analyze_printf::OptionalFlag &ignoredFlag, + const analyze_printf::OptionalFlag &flag, + const char *startSpecifier, unsigned specifierLen); +}; +} + +bool CheckPrintfHandler::HandleInvalidPrintfConversionSpecifier( + const analyze_printf::PrintfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + unsigned argIndex = FS.getArgIndex(); bool keepGoing = true; if (argIndex < NumDataArgs) { - // Consider the argument coverered, even though the specifier doesn't - // make sense. + // Consider the argument coverered, even though the specifier doesn't + // make sense. CoveredArgs.set(argIndex); } else { @@ -1308,32 +1318,21 @@ HandleInvalidConversionSpecifier(const analyze_printf::FormatSpecifier &FS, // gibberish when trying to match arguments. keepGoing = false; } - + const analyze_printf::ConversionSpecifier &CS = - FS.getConversionSpecifier(); + FS.getConversionSpecifier(); SourceLocation Loc = getLocationOfByte(CS.getStart()); S.Diag(Loc, diag::warn_printf_invalid_conversion) - << llvm::StringRef(CS.getStart(), CS.getLength()) - << getFormatSpecifierRange(startSpecifier, specifierLen); - + << llvm::StringRef(CS.getStart(), CS.getLength()) + << getSpecifierRange(startSpecifier, specifierLen); + return keepGoing; } -void CheckPrintfHandler::HandleNullChar(const char *nullCharacter) { - // The presence of a null character is likely an error. - S.Diag(getLocationOfByte(nullCharacter), - diag::warn_printf_format_string_contains_null_char) - << getFormatStringRange(); -} - -const Expr *CheckPrintfHandler::getDataArg(unsigned i) const { - return TheCall->getArg(FirstDataArg + i); -} - -bool -CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt, - unsigned k, const char *startSpecifier, - unsigned specifierLen) { +bool CheckPrintfHandler::HandleAmount( + const analyze_format_string::OptionalAmount &Amt, + unsigned k, const char *startSpecifier, + unsigned specifierLen) { if (Amt.hasDataArgument()) { if (!HasVAListArg) { @@ -1341,7 +1340,7 @@ CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt, if (argIndex >= NumDataArgs) { S.Diag(getLocationOfByte(Amt.getStart()), diag::warn_printf_asterisk_missing_arg) - << k << getFormatSpecifierRange(startSpecifier, specifierLen); + << k << getSpecifierRange(startSpecifier, specifierLen); // Don't do any more checking. We will just emit // spurious errors. return false; @@ -1363,7 +1362,7 @@ CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt, diag::warn_printf_asterisk_wrong_type) << k << ATR.getRepresentativeType(S.Context) << T - << getFormatSpecifierRange(startSpecifier, specifierLen) + << getSpecifierRange(startSpecifier, specifierLen) << Arg->getSourceRange(); // Don't do any more checking. We will just emit // spurious errors. @@ -1375,7 +1374,7 @@ CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt, } void CheckPrintfHandler::HandleInvalidAmount( - const analyze_printf::FormatSpecifier &FS, + const analyze_printf::PrintfSpecifier &FS, const analyze_printf::OptionalAmount &Amt, unsigned type, const char *startSpecifier, @@ -1387,8 +1386,8 @@ void CheckPrintfHandler::HandleInvalidAmount( diag::warn_printf_nonsensical_optional_amount) << type << CS.toString() - << getFormatSpecifierRange(startSpecifier, specifierLen) - << FixItHint::CreateRemoval(getFormatSpecifierRange(Amt.getStart(), + << getSpecifierRange(startSpecifier, specifierLen) + << FixItHint::CreateRemoval(getSpecifierRange(Amt.getStart(), Amt.getConstantLength())); break; @@ -1397,12 +1396,12 @@ void CheckPrintfHandler::HandleInvalidAmount( diag::warn_printf_nonsensical_optional_amount) << type << CS.toString() - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getSpecifierRange(startSpecifier, specifierLen); break; } } -void CheckPrintfHandler::HandleFlag(const analyze_printf::FormatSpecifier &FS, +void CheckPrintfHandler::HandleFlag(const analyze_printf::PrintfSpecifier &FS, const analyze_printf::OptionalFlag &flag, const char *startSpecifier, unsigned specifierLen) { @@ -1411,12 +1410,12 @@ void CheckPrintfHandler::HandleFlag(const analyze_printf::FormatSpecifier &FS, S.Diag(getLocationOfByte(flag.getPosition()), diag::warn_printf_nonsensical_flag) << flag.toString() << CS.toString() - << getFormatSpecifierRange(startSpecifier, specifierLen) - << FixItHint::CreateRemoval(getFormatSpecifierRange(flag.getPosition(), 1)); + << getSpecifierRange(startSpecifier, specifierLen) + << FixItHint::CreateRemoval(getSpecifierRange(flag.getPosition(), 1)); } void CheckPrintfHandler::HandleIgnoredFlag( - const analyze_printf::FormatSpecifier &FS, + const analyze_printf::PrintfSpecifier &FS, const analyze_printf::OptionalFlag &ignoredFlag, const analyze_printf::OptionalFlag &flag, const char *startSpecifier, @@ -1425,13 +1424,13 @@ void CheckPrintfHandler::HandleIgnoredFlag( S.Diag(getLocationOfByte(ignoredFlag.getPosition()), diag::warn_printf_ignored_flag) << ignoredFlag.toString() << flag.toString() - << getFormatSpecifierRange(startSpecifier, specifierLen) - << FixItHint::CreateRemoval(getFormatSpecifierRange( + << getSpecifierRange(startSpecifier, specifierLen) + << FixItHint::CreateRemoval(getSpecifierRange( ignoredFlag.getPosition(), 1)); } bool -CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier +CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS, const char *startSpecifier, unsigned specifierLen) { @@ -1446,8 +1445,8 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier else if (usesPositionalArgs != FS.usesPositionalArg()) { // Cannot mix-and-match positional and non-positional arguments. S.Diag(getLocationOfByte(CS.getStart()), - diag::warn_printf_mix_positional_nonpositional_args) - << getFormatSpecifierRange(startSpecifier, specifierLen); + diag::warn_format_mix_positional_nonpositional_args) + << getSpecifierRange(startSpecifier, specifierLen); return false; } @@ -1481,7 +1480,8 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier // Check for using an Objective-C specific conversion specifier // in a non-ObjC literal. if (!IsObjCLiteral && CS.isObjCArg()) { - return HandleInvalidConversionSpecifier(FS, startSpecifier, specifierLen); + return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier, + specifierLen); } // Check for invalid use of field width @@ -1522,15 +1522,15 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier S.Diag(getLocationOfByte(LM.getStart()), diag::warn_printf_nonsensical_length) << LM.toString() << CS.toString() - << getFormatSpecifierRange(startSpecifier, specifierLen) - << FixItHint::CreateRemoval(getFormatSpecifierRange(LM.getStart(), + << getSpecifierRange(startSpecifier, specifierLen) + << FixItHint::CreateRemoval(getSpecifierRange(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); + << getSpecifierRange(startSpecifier, specifierLen); // Continue checking the other format specifiers. return true; } @@ -1544,12 +1544,12 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_positional_arg_exceeds_data_args) << (argIndex+1) << NumDataArgs - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getSpecifierRange(startSpecifier, specifierLen); } else { S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_insufficient_data_args) - << getFormatSpecifierRange(startSpecifier, specifierLen); + << getSpecifierRange(startSpecifier, specifierLen); } // Don't do any more checking. @@ -1570,7 +1570,7 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier return true; // We may be able to offer a FixItHint if it is a supported type. - FormatSpecifier fixedFS = FS; + PrintfSpecifier fixedFS = FS; bool success = fixedFS.fixType(Ex->getType()); if (success) { @@ -1582,17 +1582,17 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_conversion_argument_type_mismatch) << ATR.getRepresentativeType(S.Context) << Ex->getType() - << getFormatSpecifierRange(startSpecifier, specifierLen) + << getSpecifierRange(startSpecifier, specifierLen) << Ex->getSourceRange() << FixItHint::CreateReplacement( - getFormatSpecifierRange(startSpecifier, specifierLen), + getSpecifierRange(startSpecifier, specifierLen), os.str()); } else { S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_conversion_argument_type_mismatch) << ATR.getRepresentativeType(S.Context) << Ex->getType() - << getFormatSpecifierRange(startSpecifier, specifierLen) + << getSpecifierRange(startSpecifier, specifierLen) << Ex->getSourceRange(); } } @@ -1600,54 +1600,150 @@ CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier return true; } -void CheckPrintfHandler::DoneProcessing() { - // Does the number of data arguments exceed the number of - // format conversions in the format string? - if (!HasVAListArg) { - // Find any arguments that weren't covered. - CoveredArgs.flip(); - signed notCoveredArg = CoveredArgs.find_first(); - if (notCoveredArg >= 0) { - assert((unsigned)notCoveredArg < NumDataArgs); - S.Diag(getDataArg((unsigned) notCoveredArg)->getLocStart(), - diag::warn_printf_data_arg_not_used) - << getFormatStringRange(); +//===--- CHECK: Scanf format string checking ------------------------------===// + +namespace { +class CheckScanfHandler : public CheckFormatHandler { +public: + CheckScanfHandler(Sema &s, const StringLiteral *fexpr, + const Expr *origFormatExpr, unsigned firstDataArg, + unsigned numDataArgs, bool isObjCLiteral, + const char *beg, bool hasVAListArg, + const CallExpr *theCall, unsigned formatIdx) + : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg, + numDataArgs, isObjCLiteral, beg, hasVAListArg, + theCall, formatIdx) {} + + bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen); +}; +} + +bool CheckScanfHandler::HandleScanfSpecifier( + const analyze_scanf::ScanfSpecifier &FS, + const char *startSpecifier, + unsigned specifierLen) { + + using namespace analyze_scanf; + using namespace analyze_format_string; + + const ConversionSpecifier &CS = FS.getConversionSpecifier(); + + // FIXME: Handle case where '%' and '*' don't consume an argument. + // This needs to be done for the printf case as well. + if (atFirstArg) { + atFirstArg = false; + usesPositionalArgs = FS.usesPositionalArg(); + } + else if (usesPositionalArgs != FS.usesPositionalArg()) { + // Cannot mix-and-match positional and non-positional arguments. + S.Diag(getLocationOfByte(CS.getStart()), + diag::warn_format_mix_positional_nonpositional_args) + << getSpecifierRange(startSpecifier, specifierLen); + return false; + } + + // Check if the field with is non-zero. + const OptionalAmount &Amt = FS.getFieldWidth(); + if (Amt.getHowSpecified() == OptionalAmount::Constant) { + if (Amt.getConstantAmount() == 0) { + const CharSourceRange &R = getSpecifierRange(Amt.getStart(), + Amt.getConstantLength()); + S.Diag(getLocationOfByte(Amt.getStart()), + diag::warn_scanf_nonzero_width) + << R << FixItHint::CreateRemoval(R); } } + + if (!FS.consumesDataArgument()) { + // FIXME: Technically specifying a precision or field width here + // makes no sense. Worth issuing a warning at some point. + return true; + } + + // Consume the argument. + unsigned argIndex = FS.getArgIndex(); + if (argIndex < NumDataArgs) { + // The check to see if the argIndex is valid will come later. + // We set the bit here because we may exit early from this + // function if we encounter some other error. + CoveredArgs.set(argIndex); + } + + // FIXME: Check that the length modifier is valid with the given + // conversion specifier. + + // The remaining checks depend on the data arguments. + if (HasVAListArg) + return true; + + if (argIndex >= NumDataArgs) { + if (FS.usesPositionalArg()) { + S.Diag(getLocationOfByte(CS.getStart()), + diag::warn_printf_positional_arg_exceeds_data_args) + << (argIndex+1) << NumDataArgs + << getSpecifierRange(startSpecifier, specifierLen); + } + else { + S.Diag(getLocationOfByte(CS.getStart()), + diag::warn_printf_insufficient_data_args) + << getSpecifierRange(startSpecifier, specifierLen); + } + + // Don't do any more checking. + return false; + } + + // FIXME: Check that the argument type matches the format specifier. + + return true; } -void Sema::CheckPrintfString(const StringLiteral *FExpr, +void Sema::CheckFormatString(const StringLiteral *FExpr, const Expr *OrigFormatExpr, const CallExpr *TheCall, bool HasVAListArg, - unsigned format_idx, unsigned firstDataArg) { - + unsigned format_idx, unsigned firstDataArg, + bool isPrintf) { + // CHECK: is the format string a wide literal? if (FExpr->isWide()) { Diag(FExpr->getLocStart(), - diag::warn_printf_format_string_is_wide_literal) + diag::warn_format_string_is_wide_literal) << OrigFormatExpr->getSourceRange(); return; } - + // Str - The format string. NOTE: this is NOT null-terminated! const char *Str = FExpr->getStrData(); - + // CHECK: empty format string? unsigned StrLen = FExpr->getByteLength(); - + if (StrLen == 0) { - Diag(FExpr->getLocStart(), diag::warn_printf_empty_format_string) + Diag(FExpr->getLocStart(), diag::warn_empty_format_string) << OrigFormatExpr->getSourceRange(); return; } - - CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, - TheCall->getNumArgs() - firstDataArg, - isa<ObjCStringLiteral>(OrigFormatExpr), Str, - HasVAListArg, TheCall, format_idx); - - if (!analyze_printf::ParseFormatString(H, Str, Str + StrLen)) - H.DoneProcessing(); + + if (isPrintf) { + CheckPrintfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, + TheCall->getNumArgs() - firstDataArg, + isa<ObjCStringLiteral>(OrigFormatExpr), Str, + HasVAListArg, TheCall, format_idx); + + if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen)) + H.DoneProcessing(); + } + else { + CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, + TheCall->getNumArgs() - firstDataArg, + isa<ObjCStringLiteral>(OrigFormatExpr), Str, + HasVAListArg, TheCall, format_idx); + + if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen)) + H.DoneProcessing(); + } } //===--- CHECK: Return Address of Stack Variable --------------------------===// |