blob: d65352ac879ed5dec78aa758b10bc5e16f832c9f [file] [log] [blame]
Ted Kremenekd9c904d2010-07-16 02:11:31 +00001//= ScanfFormatString.cpp - Analysis of printf format strings --*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Handling of format string in scanf and friends. The structure of format
11// strings for fscanf() are described in C99 7.19.6.2.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Analysis/Analyses/FormatString.h"
16#include "FormatStringParsing.h"
17
18using clang::analyze_format_string::ArgTypeResult;
19using clang::analyze_format_string::FormatStringHandler;
20using clang::analyze_format_string::LengthModifier;
21using clang::analyze_format_string::OptionalAmount;
Ted Kremenek1e51c202010-07-20 20:04:47 +000022using clang::analyze_format_string::ConversionSpecifier;
Ted Kremenek6ecb9502010-07-20 20:04:27 +000023using clang::analyze_scanf::ScanfConversionSpecifier;
Ted Kremenekd9c904d2010-07-16 02:11:31 +000024using clang::analyze_scanf::ScanfSpecifier;
25
26typedef clang::analyze_format_string::SpecifierResult<ScanfSpecifier>
27 ScanfSpecifierResult;
28
29static bool ParseScanList(FormatStringHandler &H,
Ted Kremenek6ecb9502010-07-20 20:04:27 +000030 ScanfConversionSpecifier &CS,
Ted Kremenekd9c904d2010-07-16 02:11:31 +000031 const char *&Beg, const char *E) {
32 const char *I = Beg;
33 const char *start = I - 1;
34 UpdateOnReturn <const char*> UpdateBeg(Beg, I);
35
36 // No more characters?
37 if (I == E) {
38 H.HandleIncompleteScanList(start, I);
39 return true;
40 }
41
42 // Special case: ']' is the first character.
43 if (*I == ']') {
44 if (++I == E) {
Ted Kremenekb7c21012010-07-16 18:28:03 +000045 H.HandleIncompleteScanList(start, I - 1);
Ted Kremenekd9c904d2010-07-16 02:11:31 +000046 return true;
47 }
48 }
49
50 // Look for a ']' character which denotes the end of the scan list.
51 while (*I != ']') {
52 if (++I == E) {
Ted Kremenekb7c21012010-07-16 18:28:03 +000053 H.HandleIncompleteScanList(start, I - 1);
Ted Kremenekd9c904d2010-07-16 02:11:31 +000054 return true;
55 }
56 }
57
58 CS.setEndScanList(I);
59 return false;
60}
61
62// FIXME: Much of this is copy-paste from ParsePrintfSpecifier.
63// We can possibly refactor.
64static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
65 const char *&Beg,
66 const char *E,
67 unsigned &argIndex) {
68
69 using namespace clang::analyze_scanf;
70 const char *I = Beg;
71 const char *Start = 0;
72 UpdateOnReturn <const char*> UpdateBeg(Beg, I);
73
74 // Look for a '%' character that indicates the start of a format specifier.
75 for ( ; I != E ; ++I) {
76 char c = *I;
77 if (c == '\0') {
78 // Detect spurious null characters, which are likely errors.
79 H.HandleNullChar(I);
80 return true;
81 }
82 if (c == '%') {
83 Start = I++; // Record the start of the format specifier.
84 break;
85 }
86 }
87
88 // No format specifier found?
89 if (!Start)
90 return false;
91
92 if (I == E) {
93 // No more characters left?
94 H.HandleIncompleteSpecifier(Start, E - Start);
95 return true;
96 }
97
98 ScanfSpecifier FS;
99 if (ParseArgPosition(H, FS, Start, I, E))
100 return true;
101
102 if (I == E) {
103 // No more characters left?
104 H.HandleIncompleteSpecifier(Start, E - Start);
105 return true;
106 }
107
108 // Look for '*' flag if it is present.
109 if (*I == '*') {
110 FS.setSuppressAssignment(I);
111 if (++I == E) {
112 H.HandleIncompleteSpecifier(Start, E - Start);
113 return true;
114 }
115 }
116
117 // Look for the field width (if any). Unlike printf, this is either
118 // a fixed integer or isn't present.
119 const OptionalAmount &Amt = clang::analyze_format_string::ParseAmount(I, E);
120 if (Amt.getHowSpecified() != OptionalAmount::NotSpecified) {
121 assert(Amt.getHowSpecified() == OptionalAmount::Constant);
122 FS.setFieldWidth(Amt);
123
124 if (I == E) {
125 // No more characters left?
126 H.HandleIncompleteSpecifier(Start, E - Start);
127 return true;
128 }
129 }
130
131 // Look for the length modifier.
132 if (ParseLengthModifier(FS, I, E) && I == E) {
133 // No more characters left?
134 H.HandleIncompleteSpecifier(Start, E - Start);
135 return true;
136 }
137
138 // Detect spurious null characters, which are likely errors.
139 if (*I == '\0') {
140 H.HandleNullChar(I);
141 return true;
142 }
143
144 // Finally, look for the conversion specifier.
145 const char *conversionPosition = I++;
Ted Kremenek6ecb9502010-07-20 20:04:27 +0000146 ScanfConversionSpecifier::Kind k = ScanfConversionSpecifier::InvalidSpecifier;
Ted Kremenekd9c904d2010-07-16 02:11:31 +0000147 switch (*conversionPosition) {
148 default:
149 break;
Ted Kremenek1e51c202010-07-20 20:04:47 +0000150 case '%': k = ConversionSpecifier::PercentArg; break;
151 case 'A': k = ConversionSpecifier::AArg; break;
152 case 'E': k = ConversionSpecifier::EArg; break;
153 case 'F': k = ConversionSpecifier::FArg; break;
154 case 'G': k = ConversionSpecifier::GArg; break;
155 case 'X': k = ConversionSpecifier::XArg; break;
156 case 'a': k = ConversionSpecifier::aArg; break;
157 case 'd': k = ConversionSpecifier::dArg; break;
158 case 'e': k = ConversionSpecifier::eArg; break;
159 case 'f': k = ConversionSpecifier::fArg; break;
160 case 'g': k = ConversionSpecifier::gArg; break;
161 case 'i': k = ConversionSpecifier::iArg; break;
162 case 'n': k = ConversionSpecifier::nArg; break;
163 case 'c': k = ConversionSpecifier::cArg; break;
164 case 'C': k = ConversionSpecifier::CArg; break;
165 case 'S': k = ConversionSpecifier::SArg; break;
166 case '[': k = ConversionSpecifier::ScanListArg; break;
167 case 'u': k = ConversionSpecifier::uArg; break;
168 case 'x': k = ConversionSpecifier::xArg; break;
169 case 'o': k = ConversionSpecifier::oArg; break;
170 case 's': k = ConversionSpecifier::sArg; break;
171 case 'p': k = ConversionSpecifier::pArg; break;
Ted Kremenekd9c904d2010-07-16 02:11:31 +0000172 }
Ted Kremenek6ecb9502010-07-20 20:04:27 +0000173 ScanfConversionSpecifier CS(conversionPosition, k);
174 if (k == ScanfConversionSpecifier::ScanListArg) {
Ted Kremenekd9c904d2010-07-16 02:11:31 +0000175 if (!ParseScanList(H, CS, I, E))
176 return true;
177 }
178 FS.setConversionSpecifier(CS);
179 if (CS.consumesDataArgument() && !FS.getSuppressAssignment()
180 && !FS.usesPositionalArg())
181 FS.setArgIndex(argIndex++);
182
183 // FIXME: '%' and '*' doesn't make sense. Issue a warning.
184 // FIXME: 'ConsumedSoFar' and '*' doesn't make sense.
185
Ted Kremenek6ecb9502010-07-20 20:04:27 +0000186 if (k == ScanfConversionSpecifier::InvalidSpecifier) {
Ted Kremenekd9c904d2010-07-16 02:11:31 +0000187 // Assume the conversion takes one argument.
188 return !H.HandleInvalidScanfConversionSpecifier(FS, Beg, I - Beg);
189 }
190 return ScanfSpecifierResult(Start, FS);
191}
192
193bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H,
194 const char *I,
195 const char *E) {
196
197 unsigned argIndex = 0;
198
199 // Keep looking for a format specifier until we have exhausted the string.
200 while (I != E) {
201 const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex);
202 // Did a fail-stop error of any kind occur when parsing the specifier?
203 // If so, don't do any more processing.
204 if (FSR.shouldStop())
205 return true;;
206 // Did we exhaust the string or encounter an error that
207 // we can recover from?
208 if (!FSR.hasValue())
209 continue;
210 // We have a format specifier. Pass it to the callback.
211 if (!H.HandleScanfSpecifier(FSR.getValue(), FSR.getStart(),
212 I - FSR.getStart())) {
213 return true;
214 }
215 }
216 assert(I == E && "Format string not exhausted");
217 return false;
218}
219
Ted Kremenekf7629052010-07-26 19:45:54 +0000220ArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
221 // FIXME: Fill in.
222 return ArgTypeResult();
223}
224
225
Ted Kremenekd9c904d2010-07-16 02:11:31 +0000226