C++: Model scanf and fscanf as flow sources

This commit is contained in:
Jeroen Ketema
2022-12-08 12:05:52 +01:00
parent 85ee4e6ca1
commit f35b7f8fe8
5 changed files with 101 additions and 17 deletions

View File

@@ -27,7 +27,7 @@ private import implementations.StdString
private import implementations.Swap
private import implementations.GetDelim
private import implementations.SmartPointer
private import implementations.Sscanf
private import implementations.Scanf
private import implementations.Send
private import implementations.Recv
private import implementations.Accept

View File

@@ -1,6 +1,6 @@
/**
* Provides implementation classes modeling `sscanf`, `fscanf` and various similar
* functions. See `semmle.code.cpp.models.Models` for usage information.
* Provides implementation classes modeling the `scanf` family of functions.
* See `semmle.code.cpp.models.Models` for usage information.
*/
import semmle.code.cpp.Function
@@ -9,18 +9,15 @@ import semmle.code.cpp.models.interfaces.ArrayFunction
import semmle.code.cpp.models.interfaces.Taint
import semmle.code.cpp.models.interfaces.Alias
import semmle.code.cpp.models.interfaces.SideEffect
import semmle.code.cpp.models.interfaces.FlowSource
/**
* The standard function `sscanf`, `fscanf` and its assorted variants
* The `scanf` family of functions.
*/
private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, SideEffectFunction {
SscanfModel() { this instanceof Sscanf or this instanceof Fscanf or this instanceof Snscanf }
abstract private class ScanfFunctionModel extends ArrayFunction, TaintFunction, AliasFunction,
SideEffectFunction {
override predicate hasArrayWithNullTerminator(int bufParam) {
bufParam = this.(ScanfFunction).getFormatParameterIndex()
or
not this instanceof Fscanf and
bufParam = this.(ScanfFunction).getInputParameterIndex()
}
override predicate hasArrayInput(int bufParam) { this.hasArrayWithNullTerminator(bufParam) }
@@ -36,7 +33,7 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S
)
}
private int getArgsStartPosition() { result = this.getNumberOfParameters() }
int getArgsStartPosition() { result = this.getNumberOfParameters() }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
input.isParameterDeref(this.(ScanfFunction).getInputParameterIndex()) and
@@ -70,3 +67,40 @@ private class SscanfModel extends ArrayFunction, TaintFunction, AliasFunction, S
]
}
}
/**
* The standard function `scanf` and its assorted variants
*/
private class ScanfModel extends ScanfFunctionModel, LocalFlowSourceFunction {
ScanfModel() { this instanceof Scanf }
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
description = "Value read by " + this.getName()
}
}
/**
* The standard function `fscanf` and its assorted variants
*/
private class FscanfModel extends ScanfFunctionModel, RemoteFlowSourceFunction {
FscanfModel() { this instanceof Fscanf }
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
description = "Value read by " + this.getName()
}
}
/**
* The standard function `sscanf` and its assorted variants
*/
private class SscanfModel extends ScanfFunctionModel {
SscanfModel() { this instanceof Sscanf or this instanceof Snscanf }
override predicate hasArrayWithNullTerminator(int bufParam) {
super.hasArrayWithNullTerminator(bufParam)
or
bufParam = this.(ScanfFunction).getInputParameterIndex()
}
}

View File

@@ -11,8 +11,20 @@ class LocalFlowSourceTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "local_source" and
value = "" and
exists(LocalFlowSource node |
exists(LocalFlowSource node, int n |
n =
strictcount(LocalFlowSource otherNode |
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
) and
(
n = 1 and value = ""
or
// If there is more than one node on this line
// we specify the location explicitly.
n > 1 and
value =
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
) and
location = node.getLocation() and
element = node.toString()
)

View File

@@ -11,8 +11,20 @@ class RemoteFlowSourceTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "remote_source" and
value = "" and
exists(RemoteFlowSource node |
exists(RemoteFlowSource node, int n |
n =
strictcount(RemoteFlowSource otherNode |
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
) and
(
n = 1 and value = ""
or
// If there is more than one node on this line
// we specify the location explicitly.
n > 1 and
value =
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
) and
location = node.getLocation() and
element = node.toString()
)
@@ -26,8 +38,20 @@ class RemoteFlowSinkTest extends InlineExpectationsTest {
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "remote_sink" and
value = "" and
exists(RemoteFlowSink node |
exists(RemoteFlowSink node, int n |
n =
strictcount(RemoteFlowSink otherNode |
node.getLocation().getStartLine() = otherNode.getLocation().getStartLine()
) and
(
n = 1 and value = ""
or
// If there is more than one node on this line
// we specify the location explicitly.
n > 1 and
value =
node.getLocation().getStartLine().toString() + ":" + node.getLocation().getStartColumn()
) and
location = node.getLocation() and
element = node.toString()
)

View File

@@ -26,3 +26,17 @@ void test_readv_and_writev(iovec* iovs) {
readv(0, iovs, 16); // $ remote_source
writev(0, iovs, 16); // $ remote_sink
}
struct FILE;
int fscanf(FILE *stream, const char *format, ...);
int scanf(const char *format, ...);
void test_scanf(FILE *stream, int *d, char *buf) {
scanf(""); // Not a local source, as there are no output arguments
fscanf(stream, ""); // Not a remote source, as there are no output arguments
scanf("%d", d); // $ local_source
fscanf(stream, "%d", d); // $ remote_source
scanf("%d %s", d, buf); // $ local_source=40:18 local_source=40:21
fscanf(stream, "%d %s", d, buf); // $ remote_source=41:27 remote_source=41:30
}