Skip to content

Commit 170d12b

Browse files
committed
Write MissingCheckScanf.qhelp
1 parent ca162a4 commit 170d12b

File tree

4 files changed

+76
-7
lines changed

4 files changed

+76
-7
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
int i, j, r;
3+
4+
r = scanf("%d %d", &i, &j);
5+
6+
use(i); // BAD: i is not guarded
7+
8+
if (r >= 1) {
9+
use(i); // GOOD: i is guarded correctly
10+
use(j); // BAD: j is guarded incorrectly
11+
}
12+
13+
if (r != 2)
14+
return;
15+
16+
use(j); // GOOD: j is guarded correctly
17+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
7+
<overview>
8+
<p>
9+
This query finds calls of <tt>scanf</tt>-like functions with missing or
10+
improper return-value checking.
11+
</p>
12+
<p>
13+
Specifically, the query flags uses of variables that may have been modified by
14+
<tt>scanf</tt> and subsequently are used without being guarded by a correct
15+
return-value check. A proper check is one that asserts the corresponding
16+
<tt>scanf</tt> to have returned (at least) a certain minimum constant.
17+
</p>
18+
<p>
19+
Functions in the <tt>scanf</tt> family return either EOF (a negative value)
20+
in case of IO failure, or the number of items successfully read from the
21+
input. Consequently, a simple check that the return value is truthy (nonzero)
22+
is not enough.
23+
</p>
24+
<warning>
25+
This query has medium precision because, in the current implementation, it
26+
takes a strict stance on unguarded uses of output variables, and flags them
27+
as problematic even if they had already been initialized.
28+
</warning>
29+
</overview>
30+
31+
<recommendation>
32+
<p>
33+
Ensure that all subsequent uses of <tt>scanf</tt> output arguments occur in a
34+
branch of an <tt>if</tt> statement (or similar), in which it is known that the
35+
corresponding <tt>scanf</tt> call has in fact read all possible items from its
36+
input. This can be done by comparing the return value to a numerical constant.
37+
</p>
38+
</recommendation>
39+
40+
<example>
41+
<p>This example shows different ways of guarding a <tt>scanf</tt> output:
42+
</p>
43+
<sample src="MissingCheckScanf.cpp" />
44+
</example>
45+
46+
<references>
47+
<li>SEI CERT C++ Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/ERR62-CPP.+Detect+errors+when+converting+a+string+to+a+number">ERR62-CPP. Detect errors when converting a string to a number</a>.</li>
48+
<li>SEI CERT C Coding Standard: <a href="https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors">ERR33-C. Detect and handle standard library errors</a>.</li>
49+
<li>cppreference.com: <a href="https://en.cppreference.com/w/c/io/fscanf">scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s</a></li>
50+
</references>
51+
</qhelp>

cpp/ql/src/Critical/MissingCheckScanf.ql

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
/**
2-
* @name Missing return-value check for a scanf-like function
3-
* @description TODO
2+
* @name Missing return-value check for a 'scanf'-like function
3+
* @description Without checking that a call to 'scanf' actually wrote to an
4+
* output variable, reading from it can lead to unexpected behavior.
45
* @kind problem
5-
* @problem.severity warning
6-
* @security-severity TODO
7-
* @precision TODO
6+
* @problem.severity recommendation
7+
* @security-severity 4.5
8+
* @precision medium
89
* @id cpp/missing-check-scanf
910
* @tags security
1011
*/

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,11 +222,11 @@ int main()
222222
if (maybe()) {
223223
break;
224224
}
225-
else if (maybe() && (scanf("%5c %d", c, &d) == 1)) { // GOOD [FALSE POSITIVE]
225+
else if (maybe() && (scanf("%5c %d", c, &d) == 1)) { // GOOD
226226
use(*(int *)c); // GOOD
227227
use(d); // BAD
228228
}
229-
else if ((scanf("%5c %d", c, &d) == 1) && maybe()) { // GOOD [FALSE POSITIVE]
229+
else if ((scanf("%5c %d", c, &d) == 1) && maybe()) { // GOOD
230230
use(*(int *)c); // GOOD
231231
use(d); // BAD
232232
}

0 commit comments

Comments
 (0)