Skip to content

Commit dff21e0

Browse files
committed
CPP: Fully support positional arguments.
1 parent 2430bf4 commit dff21e0

File tree

3 files changed

+82
-31
lines changed

3 files changed

+82
-31
lines changed

cpp/ql/src/semmle/code/cpp/commons/Printf.qll

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,15 @@ class FormattingFunctionCall extends Expr {
134134
* Gets the argument corresponding to the nth conversion specifier.
135135
*/
136136
Expr getConversionArgument(int n) {
137-
result = this
138-
.getFormatArgument(this.getFormat().(FormatLiteral).getFormatArgumentIndexFor(n, 2))
137+
exists(FormatLiteral fl |
138+
fl = this.getFormat() and
139+
(
140+
result = this.getFormatArgument(fl.getParameterFieldPositional(n))
141+
or
142+
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 2)) and
143+
not exists(fl.getParameterFieldPositional(n))
144+
)
145+
)
139146
}
140147

141148
/**
@@ -144,8 +151,15 @@ class FormattingFunctionCall extends Expr {
144151
* an explicit minimum field width).
145152
*/
146153
Expr getMinFieldWidthArgument(int n) {
147-
result = this
148-
.getFormatArgument(this.getFormat().(FormatLiteral).getFormatArgumentIndexFor(n, 0))
154+
exists(FormatLiteral fl |
155+
fl = this.getFormat() and
156+
(
157+
result = this.getFormatArgument(fl.getMinFieldWidthPositional(n))
158+
or
159+
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 0)) and
160+
not exists(fl.getMinFieldWidthPositional(n))
161+
)
162+
)
149163
}
150164

151165
/**
@@ -154,8 +168,15 @@ class FormattingFunctionCall extends Expr {
154168
* precision).
155169
*/
156170
Expr getPrecisionArgument(int n) {
157-
result = this
158-
.getFormatArgument(this.getFormat().(FormatLiteral).getFormatArgumentIndexFor(n, 1))
171+
exists(FormatLiteral fl |
172+
fl = this.getFormat() and
173+
(
174+
result = this.getFormatArgument(fl.getPrecisionPositional(n))
175+
or
176+
result = this.getFormatArgument(fl.getFormatArgumentIndexFor(n, 1)) and
177+
not exists(fl.getPrecisionPositional(n))
178+
)
179+
)
159180
}
160181

161182
/**
@@ -354,6 +375,14 @@ class FormatLiteral extends Literal {
354375
*/
355376
string getParameterField(int n) { this.parseConvSpec(n, _, result, _, _, _, _, _) }
356377

378+
/**
379+
* Gets the parameter field of the nth conversion specifier (if it has one) as a
380+
* zero-based number.
381+
*/
382+
int getParameterFieldPositional(int n) {
383+
result = this.getParameterField(n).regexpCapture("([0-9]*)\\$", 1).toInt() - 1
384+
}
385+
357386
/**
358387
* Gets the flags of the nth conversion specifier.
359388
*/
@@ -423,6 +452,14 @@ class FormatLiteral extends Literal {
423452
*/
424453
int getMinFieldWidth(int n) { result = this.getMinFieldWidthOpt(n).toInt() }
425454

455+
/**
456+
* Gets the zero-based parameter number of the minimum field width of the nth
457+
* conversion specifier, if it is implicit and uses a positional parameter.
458+
*/
459+
int getMinFieldWidthPositional(int n) {
460+
result = this.getMinFieldWidthOpt(n).regexpCapture("\\*([0-9]*)\\$", 1).toInt() - 1
461+
}
462+
426463
/**
427464
* Gets the precision of the nth conversion specifier (empty string if none is given).
428465
*/
@@ -453,6 +490,14 @@ class FormatLiteral extends Literal {
453490
else result = this.getPrecisionOpt(n).regexpCapture("\\.([0-9]*)", 1).toInt()
454491
}
455492

493+
/**
494+
* Gets the zero-based parameter number of the precision of the nth conversion
495+
* specifier, if it is implicit and uses a positional parameter.
496+
*/
497+
int getPrecisionPositional(int n) {
498+
result = this.getPrecisionOpt(n).regexpCapture("\\.\\*([0-9]*)\\$", 1).toInt() - 1
499+
}
500+
456501
/**
457502
* Gets the length flag of the nth conversion specifier.
458503
*/

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_signed_chars/WrongTypeFormatArguments.expected

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616
| printf1.h:114:18:114:18 | d | This argument should be of type 'long double' but is of type 'double' |
1717
| printf1.h:147:19:147:19 | i | This argument should be of type 'long long' but is of type 'int' |
1818
| printf1.h:148:19:148:20 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
19-
| printf1.h:159:18:159:18 | i | This argument should be of type 'char *' but is of type 'int' |
2019
| printf1.h:160:18:160:18 | i | This argument should be of type 'char *' but is of type 'int' |
20+
| printf1.h:161:21:161:21 | s | This argument should be of type 'int' but is of type 'char *' |
2121
| printf1.h:167:17:167:17 | i | This argument should be of type 'char *' but is of type 'int' |
2222
| printf1.h:168:18:168:18 | i | This argument should be of type 'char *' but is of type 'int' |
2323
| printf1.h:169:19:169:19 | i | This argument should be of type 'char *' but is of type 'int' |
@@ -35,18 +35,24 @@
3535
| printf1.h:192:19:192:19 | s | This argument should be of type 'int' but is of type 'char *' |
3636
| printf1.h:193:22:193:22 | s | This argument should be of type 'int' but is of type 'char *' |
3737
| printf1.h:194:25:194:25 | i | This argument should be of type 'char *' but is of type 'int' |
38-
| printf1.h:213:28:213:28 | s | This argument should be of type 'int' but is of type 'char *' |
38+
| printf1.h:198:24:198:24 | s | This argument should be of type 'int' but is of type 'char *' |
39+
| printf1.h:199:21:199:21 | i | This argument should be of type 'char *' but is of type 'int' |
40+
| printf1.h:202:26:202:26 | s | This argument should be of type 'int' but is of type 'char *' |
41+
| printf1.h:203:23:203:23 | i | This argument should be of type 'char *' but is of type 'int' |
42+
| printf1.h:206:25:206:25 | s | This argument should be of type 'int' but is of type 'char *' |
43+
| printf1.h:207:22:207:22 | i | This argument should be of type 'char *' but is of type 'int' |
44+
| printf1.h:210:26:210:26 | s | This argument should be of type 'int' but is of type 'char *' |
45+
| printf1.h:211:23:211:23 | i | This argument should be of type 'char *' but is of type 'int' |
3946
| printf1.h:214:28:214:28 | s | This argument should be of type 'int' but is of type 'char *' |
4047
| printf1.h:215:28:215:28 | s | This argument should be of type 'int' but is of type 'char *' |
41-
| printf1.h:216:28:216:28 | s | This argument should be of type 'int' but is of type 'char *' |
48+
| printf1.h:216:25:216:25 | i | This argument should be of type 'char *' but is of type 'int' |
4249
| printf1.h:221:18:221:18 | s | This argument should be of type 'int' but is of type 'char *' |
4350
| printf1.h:222:20:222:20 | s | This argument should be of type 'int' but is of type 'char *' |
44-
| printf1.h:233:22:233:22 | s | This argument should be of type 'int' but is of type 'char *' |
45-
| printf1.h:233:25:233:25 | i | This argument should be of type 'char *' but is of type 'int' |
46-
| printf1.h:234:22:234:22 | s | This argument should be of type 'int' but is of type 'char *' |
51+
| printf1.h:225:23:225:23 | i | This argument should be of type 'char *' but is of type 'int' |
52+
| printf1.h:228:24:228:24 | i | This argument should be of type 'char *' but is of type 'int' |
53+
| printf1.h:231:25:231:25 | i | This argument should be of type 'char *' but is of type 'int' |
4754
| printf1.h:234:25:234:25 | i | This argument should be of type 'char *' but is of type 'int' |
4855
| printf1.h:235:22:235:22 | s | This argument should be of type 'int' but is of type 'char *' |
49-
| printf1.h:235:25:235:25 | i | This argument should be of type 'char *' but is of type 'int' |
5056
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
5157
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
5258
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |

cpp/ql/test/query-tests/Likely Bugs/Format/WrongTypeFormatArguments/Linux_signed_chars/printf1.h

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,9 @@ void complexFormatSymbols(int i, const char *s)
156156
{
157157
// positional arguments
158158
printf("%1$i", i, s); // GOOD
159-
printf("%2$s", i, s); // GOOD [FALSE POSITIVE]
159+
printf("%2$s", i, s); // GOOD
160160
printf("%1$s", i, s); // BAD
161-
printf("%2$i", i, s); // BAD [NOT DETECTED]
161+
printf("%2$i", i, s); // BAD
162162

163163
// width / precision
164164
printf("%4i", i); // GOOD
@@ -195,22 +195,22 @@ void complexFormatSymbols(int i, const char *s)
195195

196196
// positional arguments mixed with variable width / precision
197197
printf("%2$*1$s", i, s); // GOOD
198-
printf("%2$*2$s", i, s); // BAD [NOT DETECTED]
199-
printf("%1$*1$s", i, s); // BAD [NOT DETECTED]
198+
printf("%2$*2$s", i, s); // BAD
199+
printf("%1$*1$s", i, s); // BAD
200200

201201
printf("%2$*1$.4s", i, s); // GOOD
202-
printf("%2$*2$.4s", i, s); // BAD [NOT DETECTED]
203-
printf("%1$*1$.4s", i, s); // BAD [NOT DETECTED]
202+
printf("%2$*2$.4s", i, s); // BAD
203+
printf("%1$*1$.4s", i, s); // BAD
204204

205205
printf("%2$.*1$s", i, s); // GOOD
206-
printf("%2$.*2$s", i, s); // BAD [NOT DETECTED]
207-
printf("%1$.*1$s", i, s); // BAD [NOT DETECTED]
206+
printf("%2$.*2$s", i, s); // BAD
207+
printf("%1$.*1$s", i, s); // BAD
208208

209209
printf("%2$4.*1$s", i, s); // GOOD
210-
printf("%2$4.*2$s", i, s); // BAD [NOT DETECTED]
211-
printf("%1$4.*1$s", i, s); // BAD [NOT DETECTED]
210+
printf("%2$4.*2$s", i, s); // BAD
211+
printf("%1$4.*1$s", i, s); // BAD
212212

213-
printf("%2$*1$.*1$s", i, s); // GOOD [FALSE POSITIVE]
213+
printf("%2$*1$.*1$s", i, s); // GOOD
214214
printf("%2$*2$.*1$s", i, s); // BAD
215215
printf("%2$*1$.*2$s", i, s); // BAD
216216
printf("%1$*1$.*1$s", i, s); // BAD
@@ -222,15 +222,15 @@ void complexFormatSymbols(int i, const char *s)
222222
printf("%1$-4i", s); // BAD
223223

224224
printf("%1$-4s", s, i); // GOOD
225-
printf("%2$-4s", s, i); // BAD [NOT DETECTED]
225+
printf("%2$-4s", s, i); // BAD
226226

227227
printf("%1$-.4s", s, i); // GOOD
228-
printf("%2$-.4s", s, i); // BAD [NOT DETECTED]
228+
printf("%2$-.4s", s, i); // BAD
229229

230230
printf("%1$-4.4s", s, i); // GOOD
231-
printf("%2$-4.4s", s, i); // BAD [NOT DETECTED]
232-
233-
printf("%1$-*2$s", s, i); // GOOD [FALSE POSITIVE x2]
234-
printf("%2$-*2$s", s, i); // BAD [ADDITIONAL RESULT IS A FALSE POSITIVE]
235-
printf("%1$-*1$s", s, i); // BAD [ADDITIONAL RESULT IS A FALSE POSITIVE]
231+
printf("%2$-4.4s", s, i); // BAD
232+
233+
printf("%1$-*2$s", s, i); // GOOD
234+
printf("%2$-*2$s", s, i); // BAD
235+
printf("%1$-*1$s", s, i); // BAD
236236
}

0 commit comments

Comments
 (0)