| //= ScanfFormatString.cpp - 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 scanf and friends. The structure of format |
| // strings for fscanf() are described in C99 7.19.6.2. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/Analysis/Analyses/FormatString.h" |
| #include "FormatStringParsing.h" |
| |
| 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_scanf::ConversionSpecifier; |
| using clang::analyze_scanf::ScanfSpecifier; |
| |
| typedef clang::analyze_format_string::SpecifierResult<ScanfSpecifier> |
| ScanfSpecifierResult; |
| |
| static bool ParseScanList(FormatStringHandler &H, |
| ConversionSpecifier &CS, |
| const char *&Beg, const char *E) { |
| const char *I = Beg; |
| const char *start = I - 1; |
| UpdateOnReturn <const char*> UpdateBeg(Beg, I); |
| |
| // No more characters? |
| if (I == E) { |
| H.HandleIncompleteScanList(start, I); |
| return true; |
| } |
| |
| // Special case: ']' is the first character. |
| if (*I == ']') { |
| if (++I == E) { |
| H.HandleIncompleteScanList(start, I); |
| return true; |
| } |
| } |
| |
| // Look for a ']' character which denotes the end of the scan list. |
| while (*I != ']') { |
| if (++I == E) { |
| H.HandleIncompleteScanList(start, I); |
| return true; |
| } |
| } |
| |
| CS.setEndScanList(I); |
| return false; |
| } |
| |
| // FIXME: Much of this is copy-paste from ParsePrintfSpecifier. |
| // We can possibly refactor. |
| static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, |
| const char *&Beg, |
| const char *E, |
| unsigned &argIndex) { |
| |
| using namespace clang::analyze_scanf; |
| const char *I = Beg; |
| const char *Start = 0; |
| UpdateOnReturn <const char*> UpdateBeg(Beg, I); |
| |
| // Look for a '%' character that indicates the start of a format specifier. |
| for ( ; I != E ; ++I) { |
| char c = *I; |
| if (c == '\0') { |
| // Detect spurious null characters, which are likely errors. |
| H.HandleNullChar(I); |
| return true; |
| } |
| if (c == '%') { |
| Start = I++; // Record the start of the format specifier. |
| break; |
| } |
| } |
| |
| // No format specifier found? |
| if (!Start) |
| return false; |
| |
| if (I == E) { |
| // No more characters left? |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| |
| ScanfSpecifier FS; |
| if (ParseArgPosition(H, FS, Start, I, E)) |
| return true; |
| |
| if (I == E) { |
| // No more characters left? |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| |
| // Look for '*' flag if it is present. |
| if (*I == '*') { |
| FS.setSuppressAssignment(I); |
| if (++I == E) { |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| } |
| |
| // Look for the field width (if any). Unlike printf, this is either |
| // a fixed integer or isn't present. |
| const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount(I, E); |
| if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) { |
| assert(Amt.getHowSpecified() == OptionalAmount::Constant); |
| FS.setFieldWidth(Amt); |
| |
| if (I == E) { |
| // No more characters left? |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| } |
| |
| // Look for the length modifier. |
| if (ParseLengthModifier(FS, I, E) && I == E) { |
| // No more characters left? |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| |
| // Detect spurious null characters, which are likely errors. |
| if (*I == '\0') { |
| H.HandleNullChar(I); |
| return true; |
| } |
| |
| // Finally, look for the conversion specifier. |
| const char *conversionPosition = I++; |
| ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; |
| switch (*conversionPosition) { |
| default: |
| break; |
| case '%': k = ConversionSpecifier::PercentArg; break; |
| case 'A': k = ConversionSpecifier::AArg; break; |
| case 'E': k = ConversionSpecifier::EArg; break; |
| case 'F': k = ConversionSpecifier::FArg; break; |
| case 'G': k = ConversionSpecifier::GArg; break; |
| case 'X': k = ConversionSpecifier::XArg; break; |
| case 'a': k = ConversionSpecifier::aArg; break; |
| case 'd': k = ConversionSpecifier::dArg; break; |
| case 'e': k = ConversionSpecifier::eArg; break; |
| case 'f': k = ConversionSpecifier::fArg; break; |
| case 'g': k = ConversionSpecifier::gArg; break; |
| case 'i': k = ConversionSpecifier::iArg; break; |
| case 'n': k = ConversionSpecifier::ConsumedSoFarArg; break; |
| case 'c': k = ConversionSpecifier::cArg; break; |
| case 'C': k = ConversionSpecifier::CArg; break; |
| case 'S': k = ConversionSpecifier::SArg; break; |
| case '[': k = ConversionSpecifier::ScanListArg; break; |
| } |
| ConversionSpecifier CS(conversionPosition, k); |
| if (k == ConversionSpecifier::ScanListArg) { |
| if (!ParseScanList(H, CS, I, E)) |
| return true; |
| } |
| FS.setConversionSpecifier(CS); |
| if (CS.consumesDataArgument() && !FS.getSuppressAssignment() |
| && !FS.usesPositionalArg()) |
| FS.setArgIndex(argIndex++); |
| |
| // FIXME: '%' and '*' doesn't make sense. Issue a warning. |
| // FIXME: 'ConsumedSoFar' and '*' doesn't make sense. |
| |
| if (k == ConversionSpecifier::InvalidSpecifier) { |
| // Assume the conversion takes one argument. |
| return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, I - Beg); |
| } |
| return ScanfSpecifierResult(Start, FS); |
| } |
| |
| bool clang::analyze_format_string::ParseScanfString(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 ScanfSpecifierResult &FSR = ParseScanfSpecifier(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()) |
| return true;; |
| // Did we exhaust the string or encounter an error that |
| // we can recover from? |
| if (!FSR.hasValue()) |
| continue; |
| // We have a format specifier. Pass it to the callback. |
| if (!H.HandleScanfSpecifier(FSR.getValue(), FSR.getStart(), |
| I - FSR.getStart())) { |
| return true; |
| } |
| } |
| assert(I == E && "Format string not exhausted"); |
| return false; |
| } |
| |
| |