Skip to content

Commit f35b7f8

Browse files
committed
C++: Model scanf and fscanf as flow sources
1 parent 85ee4e6 commit f35b7f8

File tree

5 files changed

+101
-17
lines changed

5 files changed

+101
-17
lines changed

cpp/ql/lib/semmle/code/cpp/models/Models.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ private import implementations.StdString
2727
private import implementations.Swap
2828
private import implementations.GetDelim
2929
private import implementations.SmartPointer
30-
private import implementations.Sscanf
30+
private import implementations.Scanf
3131
private import implementations.Send
3232
private import implementations.Recv
3333
private import implementations.Accept

cpp/ql/lib/semmle/code/cpp/models/implementations/Sscanf.qll renamed to cpp/ql/lib/semmle/code/cpp/models/implementations/Scanf.qll

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
3-
* functions. See `semmle.code.cpp.models.Models` for usage information.
2+
* Provides implementation classes modeling the `scanf` family of functions.
3+
* See `semmle.code.cpp.models.Models` for usage information.
44
*/
55

66
import semmle.code.cpp.Function
@@ -9,18 +9,15 @@ import semmle.code.cpp.models.interfaces.ArrayFunction
99
import semmle.code.cpp.models.interfaces.Taint
1010
import semmle.code.cpp.models.interfaces.Alias
1111
import semmle.code.cpp.models.interfaces.SideEffect
12+
import semmle.code.cpp.models.interfaces.FlowSource
1213

1314
/**
14-
* The standard function `sscanf`, `fscanf` and its assorted variants
15+
* The `scanf` family of functions.
1516
*/
16-
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
17-
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
18-
17+
abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction, AliasFunction,
18+
SideEffectFunction {
1919
override predicate hasArrayWithNullTerminator(int bufParam) {
2020
bufParam = this.(ScanfFunction).getFormatParameterIndex()
21-
or
22-
not this instanceof Fscanf and
23-
bufParam = this.(ScanfFunction).getInputParameterIndex()
2421
}
2522

2623
override predicate hasArrayInput(int bufParam) { this.hasArrayWithNullTerminator(bufParam) }
@@ -36,7 +33,7 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S
3633
)
3734
}
3835

39-
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
36+
int getArgsStartPosition() { result = this.getNumberOfParameters() }
4037

4138
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
4239
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
@@ -70,3 +67,40 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S
7067
]
7168
}
7269
}
70+
71+
/**
72+
* The standard function `scanf` and its assorted variants
73+
*/
74+
private class ScanfModel extends ScanfFunctionModel, LocalFlowSourceFunction {
75+
ScanfModel() { this instanceof Scanf }
76+
77+
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
78+
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
79+
description = "Value read by " + this.getName()
80+
}
81+
}
82+
83+
/**
84+
* The standard function `fscanf` and its assorted variants
85+
*/
86+
private class FscanfModel extends ScanfFunctionModel, RemoteFlowSourceFunction {
87+
FscanfModel() { this instanceof Fscanf }
88+
89+
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
90+
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
91+
description = "Value read by " + this.getName()
92+
}
93+
}
94+
95+
/**
96+
* The standard function `sscanf` and its assorted variants
97+
*/
98+
private class SscanfModel extends ScanfFunctionModel {
99+
SscanfModel() { this instanceof Sscanf or this instanceof Snscanf }
100+
101+
override predicate hasArrayWithNullTerminator(int bufParam) {
102+
super.hasArrayWithNullTerminator(bufParam)
103+
or
104+
bufParam = this.(ScanfFunction).getInputParameterIndex()
105+
}
106+
}

cpp/ql/test/library-tests/dataflow/source-sink-tests/local-flow.ql

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,20 @@ class LocalFlowSourceTest extends InlineExpectationsTest {
1111

1212
override predicate hasActualResult(Location location, string element, string tag, string value) {
1313
tag = "local_source" and
14-
value = "" and
15-
exists(LocalFlowSource node |
14+
exists(LocalFlowSource node, int n |
15+
n =
16+
strictcount(LocalFlowSource otherNode |
17+
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
18+
) and
19+
(
20+
n = 1 and value = ""
21+
or
22+
// If there is more than one node on this line
23+
// we specify the location explicitly.
24+
n > 1 and
25+
value =
26+
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
27+
) and
1628
location = node.getLocation() and
1729
element = node.toString()
1830
)

cpp/ql/test/library-tests/dataflow/source-sink-tests/remote-flow.ql

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,20 @@ class RemoteFlowSourceTest extends InlineExpectationsTest {
1111

1212
override predicate hasActualResult(Location location, string element, string tag, string value) {
1313
tag = "remote_source" and
14-
value = "" and
15-
exists(RemoteFlowSource node |
14+
exists(RemoteFlowSource node, int n |
15+
n =
16+
strictcount(RemoteFlowSource otherNode |
17+
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
18+
) and
19+
(
20+
n = 1 and value = ""
21+
or
22+
// If there is more than one node on this line
23+
// we specify the location explicitly.
24+
n > 1 and
25+
value =
26+
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
27+
) and
1628
location = node.getLocation() and
1729
element = node.toString()
1830
)
@@ -26,8 +38,20 @@ class RemoteFlowSinkTest extends InlineExpectationsTest {
2638

2739
override predicate hasActualResult(Location location, string element, string tag, string value) {
2840
tag = "remote_sink" and
29-
value = "" and
30-
exists(RemoteFlowSink node |
41+
exists(RemoteFlowSink node, int n |
42+
n =
43+
strictcount(RemoteFlowSink otherNode |
44+
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
45+
) and
46+
(
47+
n = 1 and value = ""
48+
or
49+
// If there is more than one node on this line
50+
// we specify the location explicitly.
51+
n > 1 and
52+
value =
53+
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
54+
) and
3155
location = node.getLocation() and
3256
element = node.toString()
3357
)

cpp/ql/test/library-tests/dataflow/source-sink-tests/sources-and-sinks.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,17 @@ void test_readv_and_writev(iovec* iovs) {
2626
readv(0, iovs, 16); // $ remote_source
2727
writev(0, iovs, 16); // $ remote_sink
2828
}
29+
30+
struct FILE;
31+
32+
int fscanf(FILE *stream, const char *format, ...);
33+
int scanf(const char *format, ...);
34+
35+
void test_scanf(FILE *stream, int *d, char *buf) {
36+
scanf(""); // Not a local source, as there are no output arguments
37+
fscanf(stream, ""); // Not a remote source, as there are no output arguments
38+
scanf("%d", d); // $ local_source
39+
fscanf(stream, "%d", d); // $ remote_source
40+
scanf("%d %s", d, buf); // $ local_source=40:18 local_source=40:21
41+
fscanf(stream, "%d %s", d, buf); // $ remote_source=41:27 remote_source=41:30
42+
}

0 commit comments

Comments
 (0)