Skip to content

Commit eaf2949

Browse files
authored
Merge pull request #17054 from geoffw0/scanf
C++: Fix false positives in cpp/incorrectly-checked-scanf
2 parents 741a328 + a98fac0 commit eaf2949

File tree

4 files changed

+139
-5
lines changed

4 files changed

+139
-5
lines changed

cpp/ql/src/Critical/ScanfChecks.qll

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ private string getEofValue() {
3838
private predicate checkedForEof(ScanfFunctionCall call) {
3939
exists(IRGuardCondition gc |
4040
exists(Instruction i | i.getUnconvertedResultExpression() = call |
41-
// call == EOF
42-
gc.comparesEq(valueNumber(i).getAUse(), getEofValue().toInt(), _, _)
41+
exists(int val | gc.comparesEq(valueNumber(i).getAUse(), val, _, _) |
42+
// call == EOF
43+
val = getEofValue().toInt()
44+
or
45+
// call == [any positive number]
46+
val > 0
47+
)
4348
or
44-
// call < 0 (EOF is guaranteed to be negative)
45-
gc.comparesLt(valueNumber(i).getAUse(), 0, true, _)
49+
exists(int val | gc.comparesLt(valueNumber(i).getAUse(), val, true, _) |
50+
// call < [any non-negative number] (EOF is guaranteed to be negative)
51+
val >= 0
52+
)
4653
)
4754
)
4855
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The `cpp/incorrectly-checked-scanf` ("Incorrect return-value check for a 'scanf'-like function") query now produces fewer false positive results.

cpp/ql/test/query-tests/Critical/MissingCheckScanf/MissingCheckScanf.expected

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ edges
3737
| test.cpp:420:19:420:20 | scanf output argument | test.cpp:423:7:423:7 | i | provenance | |
3838
| test.cpp:455:41:455:46 | sscanf output argument | test.cpp:460:6:460:10 | value | provenance | |
3939
| test.cpp:467:20:467:25 | scanf output argument | test.cpp:474:6:474:10 | value | provenance | |
40+
| test.cpp:480:25:480:26 | scanf output argument | test.cpp:484:9:484:9 | i | provenance | |
41+
| test.cpp:491:25:491:26 | scanf output argument | test.cpp:495:8:495:8 | i | provenance | |
42+
| test.cpp:501:25:501:26 | scanf output argument | test.cpp:505:9:505:9 | i | provenance | |
43+
| test.cpp:512:25:512:26 | scanf output argument | test.cpp:516:9:516:9 | i | provenance | |
44+
| test.cpp:525:35:525:36 | sscanf output argument | test.cpp:527:8:527:8 | a | provenance | |
45+
| test.cpp:525:35:525:36 | sscanf output argument | test.cpp:531:8:531:8 | a | provenance | |
46+
| test.cpp:525:39:525:40 | sscanf output argument | test.cpp:528:8:528:8 | b | provenance | |
47+
| test.cpp:525:39:525:40 | sscanf output argument | test.cpp:532:8:532:8 | b | provenance | |
48+
| test.cpp:525:43:525:44 | sscanf output argument | test.cpp:533:8:533:8 | c | provenance | |
49+
| test.cpp:541:35:541:36 | sscanf output argument | test.cpp:543:8:543:8 | d | provenance | |
50+
| test.cpp:541:35:541:36 | sscanf output argument | test.cpp:548:8:548:8 | d | provenance | |
51+
| test.cpp:541:39:541:40 | sscanf output argument | test.cpp:544:8:544:8 | e | provenance | |
52+
| test.cpp:541:39:541:40 | sscanf output argument | test.cpp:549:8:549:8 | e | provenance | |
53+
| test.cpp:541:43:541:44 | sscanf output argument | test.cpp:545:8:545:8 | f | provenance | |
54+
| test.cpp:541:43:541:44 | sscanf output argument | test.cpp:550:8:550:8 | f | provenance | |
4055
nodes
4156
| test.cpp:34:15:34:16 | scanf output argument | semmle.label | scanf output argument |
4257
| test.cpp:35:7:35:7 | i | semmle.label | i |
@@ -114,6 +129,31 @@ nodes
114129
| test.cpp:460:6:460:10 | value | semmle.label | value |
115130
| test.cpp:467:20:467:25 | scanf output argument | semmle.label | scanf output argument |
116131
| test.cpp:474:6:474:10 | value | semmle.label | value |
132+
| test.cpp:480:25:480:26 | scanf output argument | semmle.label | scanf output argument |
133+
| test.cpp:484:9:484:9 | i | semmle.label | i |
134+
| test.cpp:491:25:491:26 | scanf output argument | semmle.label | scanf output argument |
135+
| test.cpp:495:8:495:8 | i | semmle.label | i |
136+
| test.cpp:501:25:501:26 | scanf output argument | semmle.label | scanf output argument |
137+
| test.cpp:505:9:505:9 | i | semmle.label | i |
138+
| test.cpp:512:25:512:26 | scanf output argument | semmle.label | scanf output argument |
139+
| test.cpp:516:9:516:9 | i | semmle.label | i |
140+
| test.cpp:525:35:525:36 | sscanf output argument | semmle.label | sscanf output argument |
141+
| test.cpp:525:39:525:40 | sscanf output argument | semmle.label | sscanf output argument |
142+
| test.cpp:525:43:525:44 | sscanf output argument | semmle.label | sscanf output argument |
143+
| test.cpp:527:8:527:8 | a | semmle.label | a |
144+
| test.cpp:528:8:528:8 | b | semmle.label | b |
145+
| test.cpp:531:8:531:8 | a | semmle.label | a |
146+
| test.cpp:532:8:532:8 | b | semmle.label | b |
147+
| test.cpp:533:8:533:8 | c | semmle.label | c |
148+
| test.cpp:541:35:541:36 | sscanf output argument | semmle.label | sscanf output argument |
149+
| test.cpp:541:39:541:40 | sscanf output argument | semmle.label | sscanf output argument |
150+
| test.cpp:541:43:541:44 | sscanf output argument | semmle.label | sscanf output argument |
151+
| test.cpp:543:8:543:8 | d | semmle.label | d |
152+
| test.cpp:544:8:544:8 | e | semmle.label | e |
153+
| test.cpp:545:8:545:8 | f | semmle.label | f |
154+
| test.cpp:548:8:548:8 | d | semmle.label | d |
155+
| test.cpp:549:8:549:8 | e | semmle.label | e |
156+
| test.cpp:550:8:550:8 | f | semmle.label | f |
117157
subpaths
118158
#select
119159
| test.cpp:35:7:35:7 | i | test.cpp:34:15:34:16 | scanf output argument | test.cpp:35:7:35:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:34:3:34:7 | call to scanf | call to scanf |
@@ -134,3 +174,6 @@ subpaths
134174
| test.cpp:423:7:423:7 | i | test.cpp:420:19:420:20 | scanf output argument | test.cpp:423:7:423:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:420:7:420:11 | call to scanf | call to scanf |
135175
| test.cpp:460:6:460:10 | value | test.cpp:455:41:455:46 | sscanf output argument | test.cpp:460:6:460:10 | value | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:455:12:455:17 | call to sscanf | call to sscanf |
136176
| test.cpp:474:6:474:10 | value | test.cpp:467:20:467:25 | scanf output argument | test.cpp:474:6:474:10 | value | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:467:8:467:12 | call to scanf | call to scanf |
177+
| test.cpp:484:9:484:9 | i | test.cpp:480:25:480:26 | scanf output argument | test.cpp:484:9:484:9 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:480:13:480:17 | call to scanf | call to scanf |
178+
| test.cpp:495:8:495:8 | i | test.cpp:491:25:491:26 | scanf output argument | test.cpp:495:8:495:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:491:13:491:17 | call to scanf | call to scanf |
179+
| test.cpp:545:8:545:8 | f | test.cpp:541:43:541:44 | sscanf output argument | test.cpp:545:8:545:8 | f | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 3. | test.cpp:541:10:541:15 | call to sscanf | call to sscanf |

cpp/ql/test/query-tests/Critical/MissingCheckScanf/test.cpp

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,4 +472,84 @@ void check_for_negative_test() {
472472
return;
473473
}
474474
use(value);
475-
}
475+
}
476+
477+
void multiple_checks() {
478+
{
479+
int i;
480+
int res = scanf("%d", &i);
481+
482+
if (res >= 0) {
483+
if (res != 0) {
484+
use(i); // GOOD: checks return value [FALSE POSITIVE]
485+
}
486+
}
487+
}
488+
489+
{
490+
int i;
491+
int res = scanf("%d", &i);
492+
493+
if (res < 0) return;
494+
if (res != 0) {
495+
use(i); // GOOD: checks return value [FALSE POSITIVE]
496+
}
497+
}
498+
499+
{
500+
int i;
501+
int res = scanf("%d", &i);
502+
503+
if (res >= 1) {
504+
if (res != 0) {
505+
use(i); // GOOD: checks return value
506+
}
507+
}
508+
}
509+
510+
{
511+
int i;
512+
int res = scanf("%d", &i);
513+
514+
if (res == 1) {
515+
if (res != 0) {
516+
use(i); // GOOD: checks return value
517+
}
518+
}
519+
}
520+
}
521+
522+
void switch_cases(const char *data) {
523+
float a, b, c;
524+
525+
switch (sscanf(data, "%f %f %f", &a, &b, &c)) {
526+
case 2:
527+
use(a); // GOOD
528+
use(b); // GOOD
529+
break;
530+
case 3:
531+
use(a); // GOOD
532+
use(b); // GOOD
533+
use(c); // GOOD
534+
break;
535+
default:
536+
break;
537+
}
538+
539+
float d, e, f;
540+
541+
switch (sscanf(data, "%f %f %f", &d, &e, &f)) {
542+
case 2:
543+
use(d); // GOOD
544+
use(e); // GOOD
545+
use(f); // BAD
546+
break;
547+
case 3:
548+
use(d); // GOOD
549+
use(e); // GOOD
550+
use(f); // GOOD
551+
break;
552+
default:
553+
break;
554+
}
555+
}

0 commit comments

Comments
 (0)