forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathScanf.qll
More file actions
278 lines (235 loc) · 8.82 KB
/
Scanf.qll
File metadata and controls
278 lines (235 loc) · 8.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/**
* A library for dealing with scanf-like formatting strings. This is similar to
* printf.qll but the format specification for scanf is quite different.
*/
import semmle.code.cpp.Type
/**
* A `scanf`-like standard library function.
*/
abstract class ScanfFunction extends Function {
/**
* Gets the position at which the input string or stream parameter occurs,
* if this function does not read from standard input.
*/
abstract int getInputParameterIndex();
/**
* Gets the position at which the format parameter occurs.
*/
abstract int getFormatParameterIndex();
/**
* Holds if the default meaning of `%s` is a `wchar_t*` string
* (rather than a `char*`).
*/
predicate isWideCharDefault() { exists(this.getName().indexOf("wscanf")) }
}
/**
* The standard function `scanf` (and variations).
*/
class Scanf extends ScanfFunction instanceof TopLevelFunction {
Scanf() {
this.hasGlobalOrStdOrBslName("scanf") or // scanf(format, args...)
this.hasGlobalOrStdOrBslName("wscanf") or // wscanf(format, args...)
this.hasGlobalName("_scanf_l") or // _scanf_l(format, locale, args...)
this.hasGlobalName("_wscanf_l")
}
override int getInputParameterIndex() { none() }
override int getFormatParameterIndex() { result = 0 }
}
/**
* The standard function `fscanf` (and variations).
*/
class Fscanf extends ScanfFunction instanceof TopLevelFunction {
Fscanf() {
this.hasGlobalOrStdOrBslName("fscanf") or // fscanf(src_stream, format, args...)
this.hasGlobalOrStdOrBslName("fwscanf") or // fwscanf(src_stream, format, args...)
this.hasGlobalName("_fscanf_l") or // _fscanf_l(src_stream, format, locale, args...)
this.hasGlobalName("_fwscanf_l")
}
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 1 }
}
/**
* The standard function `sscanf` (and variations).
*/
class Sscanf extends ScanfFunction instanceof TopLevelFunction {
Sscanf() {
this.hasGlobalOrStdOrBslName("sscanf") or // sscanf(src_stream, format, args...)
this.hasGlobalOrStdOrBslName("swscanf") or // swscanf(src, format, args...)
this.hasGlobalName("_sscanf_l") or // _sscanf_l(src, format, locale, args...)
this.hasGlobalName("_swscanf_l")
}
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 1 }
}
/**
* The standard(ish) function `snscanf` (and variations).
*/
class Snscanf extends ScanfFunction instanceof TopLevelFunction {
Snscanf() {
this.hasGlobalName("_snscanf") or // _snscanf(src, max_amount, format, args...)
this.hasGlobalName("_snwscanf") or // _snwscanf(src, max_amount, format, args...)
this.hasGlobalName("_snscanf_l") or // _snscanf_l(src, max_amount, format, locale, args...)
this.hasGlobalName("_snwscanf_l")
}
override int getInputParameterIndex() { result = 0 }
override int getFormatParameterIndex() { result = 2 }
/**
* Gets the position at which the maximum number of characters in the
* input string is specified.
*/
int getInputLengthParameterIndex() { result = 1 }
}
/**
* A call to one of the `scanf` functions.
*/
class ScanfFunctionCall extends FunctionCall {
ScanfFunctionCall() { this.getTarget() instanceof ScanfFunction }
/**
* Gets the `scanf`-like function that is called.
*/
ScanfFunction getScanfFunction() { result = this.getTarget() }
/**
* Gets the position at which the input string or stream parameter occurs,
* if this function call does not read from standard input.
*/
int getInputParameterIndex() { result = this.getScanfFunction().getInputParameterIndex() }
/**
* Gets the position at which the format parameter occurs.
*/
int getFormatParameterIndex() { result = this.getScanfFunction().getFormatParameterIndex() }
/**
* Gets the format expression used in this call.
*/
Expr getFormat() { result = this.getArgument(this.getFormatParameterIndex()) }
/**
* Holds if the default meaning of `%s` is a `wchar_t*` string
* (rather than a `char*`).
*/
predicate isWideCharDefault() { this.getScanfFunction().isWideCharDefault() }
/**
* Gets the output argument at position `n` in the vararg list of this call.
*
* The range of `n` is from `0` to `this.getNumberOfOutputArguments() - 1`.
*/
Expr getOutputArgument(int n) {
result = this.getArgument(this.getTarget().getNumberOfParameters() + n) and
n >= 0
}
/**
* Gets an output argument given to this call in vararg position.
*/
Expr getAnOutputArgument() { result = this.getOutputArgument(_) }
/**
* Gets the number of output arguments present in this call.
*/
int getNumberOfOutputArguments() {
result = this.getNumberOfArguments() - this.getTarget().getNumberOfParameters()
}
}
/**
* A class to represent format strings that occur as arguments to invocations of `scanf` functions.
*/
class ScanfFormatLiteral extends Expr {
ScanfFormatLiteral() {
exists(ScanfFunctionCall sfc | sfc.getFormat() = this) and
this.isConstant()
}
/** the function call where this format string is used */
ScanfFunctionCall getUse() { result.getFormat() = this }
/** Holds if the default meaning of `%s` is a `wchar_t*` (rather than a `char*`). */
predicate isWideCharDefault() { this.getUse().getTarget().(ScanfFunction).isWideCharDefault() }
/**
* Gets the format string itself, transformed as follows:
* - '%%' is replaced with '_'
* (this avoids accidentally processing them as format specifiers)
* - '%*' is replaced with '_'
* (%*any is matched but not assigned to an argument)
*/
string getFormat() { result = this.getValue().replaceAll("%%", "_").replaceAll("%*", "_") }
/**
* Gets the number of conversion specifiers (not counting `%%` and `%*`...).
*/
int getNumConvSpec() { result = count(this.getFormat().indexOf("%")) }
/**
* Gets the position in the string at which the nth conversion specifier starts.
*/
int getConvSpecOffset(int n) {
n = 0 and result = this.getFormat().indexOf("%", 0, 0)
or
n > 0 and
exists(int p |
n = p + 1 and result = this.getFormat().indexOf("%", 0, this.getConvSpecOffset(p) + 2)
)
}
/**
* Gets the nth conversion specifier string.
*/
private string getConvSpecString(int n) {
n >= 0 and result = "%" + this.getFormat().splitAt("%", n + 1)
}
/**
* Gets the regular expression to match each individual part of a conversion specifier.
*/
private string getMaxWidthRegexp() { result = "(?:[1-9][0-9]*)?" }
private string getLengthRegexp() { result = "(?:hh?|ll?|L|q|j|z|t)?" }
private string getConvCharRegexp() { result = "[aAcCdeEfFgGimnopsSuxX]" }
/**
* Gets the regular expression used for matching a whole conversion specifier.
*/
string getConvSpecRegexp() {
// capture groups: 1 - entire conversion spec, including "%"
// 2 - maximum width
// 3 - length modifier
// 4 - conversion character
result =
"(\\%(" + this.getMaxWidthRegexp() + ")(" + this.getLengthRegexp() + ")(" +
this.getConvCharRegexp() + ")).*"
}
/**
* Holds if the arguments are a parsing of a conversion specifier to this
* format string, where `n` is which conversion specifier to parse, `spec` is
* the whole conversion specifier, `width` is the maximum width option of
* this input, `len` is the length flag of this input, and `conv` is the
* conversion character of this input.
*
* Each parameter is the empty string if no value is given by the conversion
* specifier.
*/
predicate parseConvSpec(int n, string spec, string width, string len, string conv) {
exists(string rst, string regexp |
rst = this.getConvSpecString(n) and
regexp = this.getConvSpecRegexp() and
(
spec = rst.regexpCapture(regexp, 1) and
width = rst.regexpCapture(regexp, 2) and
len = rst.regexpCapture(regexp, 3) and
conv = rst.regexpCapture(regexp, 4)
)
)
}
/**
* Gets the maximum width option of the nth input (empty string if none is given).
*/
string getMaxWidthOpt(int n) { this.parseConvSpec(n, _, result, _, _) }
/**
* Gets the maximum width of the nth input.
*/
int getMaxWidth(int n) { result = this.getMaxWidthOpt(n).toInt() }
/**
* Gets the length flag of the nth conversion specifier.
*/
string getLength(int n) { this.parseConvSpec(n, _, _, result, _) }
/**
* Gets the conversion character of the nth conversion specifier.
*/
string getConversionChar(int n) { this.parseConvSpec(n, _, _, _, result) }
/**
* Gets the maximum length of the string that can be produced by the nth
* conversion specifier of this format string; fails if no estimate is
* possible (or implemented).
*/
int getMaxConvertedLength(int n) {
this.getConversionChar(n).toLowerCase() = "s" and
result = this.getMaxWidth(n)
}
}