Skip to content

Commit f48e8b6

Browse files
committed
CPP: Add query for detecteing incorrect error checking for scanf
1 parent 8334c6d commit f48e8b6

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
int i, j;
3+
4+
// BAD:The result is only checked against zero
5+
if (scanf("%d %d", &i, &j)) {
6+
use(i);
7+
use(j);
8+
}
9+
10+
// BAD: The result is only checked against zero
11+
if (scanf("%d %d", &i, &j) == 0) {
12+
i = 0;
13+
j = 0;
14+
}
15+
use(i);
16+
use(j);
17+
18+
if (scanf("%d %d", &i, &j) == 2) {
19+
// GOOD: the result is checked against 2
20+
}
21+
22+
// GOOD: the result is compared directly
23+
int r = scanf("%d %d", &i, &j);
24+
if (r < 2) {
25+
return;
26+
}
27+
if (r == 1) {
28+
j = 0;
29+
}
30+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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
10+
improper return-value checking.
11+
</p>
12+
<p>
13+
Specifically, the query flags uses of scanf wehere the reurn value is checked
14+
only against zero.
15+
</p>
16+
<p>
17+
Functions in the <tt>scanf</tt> family return either EOF (a negative value)
18+
in case of IO failure, or the number of items successfully read from the
19+
input. Consequently, a simple check that the return value is nonzero
20+
is not enough.
21+
</p>
22+
</overview>
23+
24+
<recommendation>
25+
<p>
26+
Ensure that all uses of <tt>scanf</tt> check the return value against the expected number of arguments
27+
rather than just against zero
28+
</p>
29+
</recommendation>
30+
31+
<example>
32+
<p>This example shows different ways of guarding a <tt>scanf</tt> output:
33+
</p>
34+
<sample src="IncorrectCheckScanf.cpp" />
35+
</example>
36+
37+
<references>
38+
<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>
39+
<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>
40+
<li>cppreference.com: <a href="https://en.cppreference.com/w/c/io/fscanf">scanf, fscanf, sscanf, scanf_s, fscanf_s, sscanf_s</a>.</li>
41+
</references>
42+
</qhelp>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @name Incorrect return-value check for a 'scanf'-like function
3+
* @description Failing to account for EOF in a call to a scanf-like function can lead to
4+
* undefined behavior.
5+
* @kind problem
6+
* @problem.severity warning
7+
* @security-severity 7.5
8+
* @precision medium
9+
* @id cpp/discarded-scanf
10+
* @tags security
11+
* correctness
12+
* external/cwe/cwe-252
13+
* external/cwe/cwe-253
14+
*/
15+
16+
import cpp
17+
import semmle.code.cpp.commons.Scanf
18+
19+
predicate exprInBooleanContext(Expr e) {
20+
e.getParent() instanceof BinaryLogicalOperation
21+
or
22+
e.getParent() instanceof UnaryLogicalOperation
23+
or
24+
e = any(IfStmt ifStmt).getCondition()
25+
or
26+
e = any(WhileStmt whileStmt).getCondition()
27+
or
28+
exists(EqualityOperation eqOp, Expr other |
29+
eqOp.hasOperands(e, other) and
30+
other.getValue() = "0"
31+
)
32+
or
33+
exists(Variable v |
34+
v.getAnAssignedValue() = e and
35+
forex(Expr use | use = v.getAnAccess() | exprInBooleanContext(use))
36+
)
37+
}
38+
39+
from ScanfFunctionCall call
40+
where
41+
exprInBooleanContext(call) and
42+
not exists(call.getTarget().getDefinition()) // ignore calls where the scanf is defined as they might be different (e.g. linux)
43+
select call, "The result of scanf is onyl checkeck against 0, but it can also return EOF."

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,3 +429,21 @@ void scan_and_static_variable() {
429429
scanf("%d", &i);
430430
use(i); // GOOD: static variables are always 0-initialized
431431
}
432+
433+
void bad_check() {
434+
{
435+
int i = 0;
436+
if (scanf("%d", &i) != 0) {
437+
return;
438+
}
439+
use(i); // GOOD [FALSE POSITIVE]: Technically no security issue, but code is incorrect.
440+
}
441+
{
442+
int i = 0;
443+
int r = scanf("%d", &i);
444+
if (!r) {
445+
return;
446+
}
447+
use(i); // GOOD [FALSE POSITIVE]: Technically no security issue, but code is incorrect.
448+
}
449+
}

0 commit comments

Comments
 (0)