Skip to content

Commit 4a41bb4

Browse files
committed
Merge branch 'main' into redsun82/swift-open-redirection
2 parents 26ae8f1 + cddb5c5 commit 4a41bb4

File tree

40 files changed

+483
-236
lines changed

40 files changed

+483
-236
lines changed

.github/workflows/check-query-ids.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Check query IDs
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "**/src/**/*.ql"
7+
- misc/scripts/check-query-ids.py
8+
- .github/workflows/check-query-ids.yml
9+
branches:
10+
- main
11+
- "rc/*"
12+
workflow_dispatch:
13+
14+
jobs:
15+
check:
16+
name: Check query IDs
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v3
20+
- name: Check for duplicate query IDs
21+
run: python3 misc/scripts/check-query-ids.py

.github/workflows/swift.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ jobs:
6565
if : ${{ github.event_name == 'pull_request' }}
6666
needs: build-and-test-macos
6767
runs-on: macos-12-xl
68+
timeout-minutes: 60
6869
steps:
6970
- uses: actions/checkout@v3
7071
- uses: ./swift/actions/run-integration-tests

config/identical-files.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,10 @@
470470
"javascript/ql/src/Comments/CommentedOutCodeReferences.inc.qhelp",
471471
"python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
472472
],
473+
"ThreadResourceAbuse qhelp": [
474+
"java/ql/src/experimental/Security/CWE/CWE-400/LocalThreadResourceAbuse.qhelp",
475+
"java/ql/src/experimental/Security/CWE/CWE-400/ThreadResourceAbuse.qhelp"
476+
],
473477
"IDE Contextual Queries": [
474478
"cpp/ql/lib/IDEContextual.qll",
475479
"csharp/ql/lib/IDEContextual.qll",
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 `scanf` and `fscanf` functions and their variants are now recognized as flow sources.

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: 40 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,36 @@ 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 instanceof Scanf {
75+
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
76+
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
77+
description = "Value read by " + this.getName()
78+
}
79+
}
80+
81+
/**
82+
* The standard function `fscanf` and its assorted variants
83+
*/
84+
private class FscanfModel extends ScanfFunctionModel, RemoteFlowSourceFunction instanceof Fscanf {
85+
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
86+
output.isParameterDeref(any(int i | i >= this.getArgsStartPosition())) and
87+
description = "Value read by " + this.getName()
88+
}
89+
}
90+
91+
/**
92+
* The standard function `sscanf` and its assorted variants
93+
*/
94+
private class SscanfModel extends ScanfFunctionModel {
95+
SscanfModel() { this instanceof Sscanf or this instanceof Snscanf }
96+
97+
override predicate hasArrayWithNullTerminator(int bufParam) {
98+
super.hasArrayWithNullTerminator(bufParam)
99+
or
100+
bufParam = this.(ScanfFunction).getInputParameterIndex()
101+
}
102+
}

cpp/ql/src/Security/CWE/CWE-311/CleartextBufferWrite.ql

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,25 @@ import semmle.code.cpp.ir.dataflow.TaintTracking
1919
import DataFlow::PathGraph
2020

2121
/**
22-
* A taint flow configuration for flow from user input to a buffer write.
22+
* A buffer write into a sensitive expression.
23+
*/
24+
class SensitiveBufferWrite extends Expr instanceof BufferWrite::BufferWrite {
25+
SensitiveBufferWrite() { super.getDest() instanceof SensitiveExpr }
26+
27+
/**
28+
* Gets a data source of this operation.
29+
*/
30+
Expr getASource() { result = super.getASource() }
31+
32+
/**
33+
* Gets the destination buffer of this operation.
34+
*/
35+
Expr getDest() { result = super.getDest() }
36+
}
37+
38+
/**
39+
* A taint flow configuration for flow from user input to a buffer write
40+
* into a sensitive expression.
2341
*/
2442
class ToBufferConfiguration extends TaintTracking::Configuration {
2543
ToBufferConfiguration() { this = "ToBufferConfiguration" }
@@ -31,18 +49,17 @@ class ToBufferConfiguration extends TaintTracking::Configuration {
3149
}
3250

3351
override predicate isSink(DataFlow::Node sink) {
34-
exists(BufferWrite::BufferWrite w | w.getASource() = sink.asExpr())
52+
exists(SensitiveBufferWrite w | w.getASource() = sink.asExpr())
3553
}
3654
}
3755

3856
from
39-
ToBufferConfiguration config, BufferWrite::BufferWrite w, DataFlow::PathNode sourceNode,
40-
DataFlow::PathNode sinkNode, FlowSource source, SensitiveExpr dest
57+
ToBufferConfiguration config, SensitiveBufferWrite w, DataFlow::PathNode sourceNode,
58+
DataFlow::PathNode sinkNode, FlowSource source
4159
where
4260
config.hasFlowPath(sourceNode, sinkNode) and
4361
sourceNode.getNode() = source and
44-
w.getASource() = sinkNode.getNode().asExpr() and
45-
dest = w.getDest()
62+
w.getASource() = sinkNode.getNode().asExpr()
4663
select w, sourceNode, sinkNode,
47-
"This write into buffer '" + dest.toString() + "' may contain unencrypted data from $@.", source,
48-
"user input (" + source.getSourceType() + ")"
64+
"This write into buffer '" + w.getDest().toString() + "' may contain unencrypted data from $@.",
65+
source, "user input (" + source.getSourceType() + ")"

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)