Skip to content

Commit f60abd8

Browse files
authored
Merge pull request #4125 from geoffw0/oparray2
C++: Model operator[]
2 parents 00316dc + 3f04530 commit f60abd8

File tree

13 files changed

+551
-40
lines changed

13 files changed

+551
-40
lines changed

cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
484484
// Expr -> Expr
485485
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
486486
or
487+
// Assignment -> LValue post-update node
488+
//
489+
// This is used for assignments whose left-hand side is not a variable
490+
// assignment or a storeStep but is still modeled by other means. It could be
491+
// a call to `operator*` or `operator[]` where taint should flow to the
492+
// post-update node of the qualifier.
493+
exists(AssignExpr assign |
494+
nodeFrom.asExpr() = assign and
495+
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
496+
)
497+
or
487498
// Node -> FlowVar -> VariableAccess
488499
exists(FlowVar var |
489500
(

cpp/ql/src/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,19 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
8282
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
8383
or
8484
exprToPartialDefinitionStep(nodeFrom.asExpr(), nodeTo.asPartialDefinition())
85+
or
86+
// Reverse taint: taint that flows from the post-update node of a reference
87+
// returned by a function call, back into the qualifier of that function.
88+
// This allows taint to flow 'in' through references returned by a modeled
89+
// function such as `operator[]`.
90+
exists(TaintFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
91+
call.getTarget() = f and
92+
inModel.isReturnValueDeref() and
93+
outModel.isQualifierObject() and
94+
f.hasTaintFlow(inModel, outModel) and
95+
nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = call and
96+
nodeTo.asDefiningArgument() = call.getQualifier()
97+
)
8598
}
8699

87100
/**

cpp/ql/src/semmle/code/cpp/models/implementations/StdContainer.qll

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ import semmle.code.cpp.models.interfaces.Taint
1414
*/
1515
class StdSequenceContainerConstructor extends Constructor, TaintFunction {
1616
StdSequenceContainerConstructor() {
17-
this.getDeclaringType().hasQualifiedName("std", "vector") or
18-
this.getDeclaringType().hasQualifiedName("std", "deque") or
19-
this.getDeclaringType().hasQualifiedName("std", "list") or
20-
this.getDeclaringType().hasQualifiedName("std", "forward_list")
17+
this.getDeclaringType().hasQualifiedName("std", ["vector", "deque", "list", "forward_list"])
2118
}
2219

2320
/**
@@ -42,10 +39,8 @@ class StdSequenceContainerConstructor extends Constructor, TaintFunction {
4239
class StdSequenceContainerPush extends TaintFunction {
4340
StdSequenceContainerPush() {
4441
this.hasQualifiedName("std", "vector", "push_back") or
45-
this.hasQualifiedName("std", "deque", "push_back") or
46-
this.hasQualifiedName("std", "deque", "push_front") or
47-
this.hasQualifiedName("std", "list", "push_back") or
48-
this.hasQualifiedName("std", "list", "push_front") or
42+
this.hasQualifiedName("std", "deque", ["push_back", "push_front"]) or
43+
this.hasQualifiedName("std", "list", ["push_back", "push_front"]) or
4944
this.hasQualifiedName("std", "forward_list", "push_front")
5045
}
5146

@@ -61,14 +56,10 @@ class StdSequenceContainerPush extends TaintFunction {
6156
*/
6257
class StdSequenceContainerFrontBack extends TaintFunction {
6358
StdSequenceContainerFrontBack() {
64-
this.hasQualifiedName("std", "array", "front") or
65-
this.hasQualifiedName("std", "array", "back") or
66-
this.hasQualifiedName("std", "vector", "front") or
67-
this.hasQualifiedName("std", "vector", "back") or
68-
this.hasQualifiedName("std", "deque", "front") or
69-
this.hasQualifiedName("std", "deque", "back") or
70-
this.hasQualifiedName("std", "list", "front") or
71-
this.hasQualifiedName("std", "list", "back") or
59+
this.hasQualifiedName("std", "array", ["front", "back"]) or
60+
this.hasQualifiedName("std", "vector", ["front", "back"]) or
61+
this.hasQualifiedName("std", "deque", ["front", "back"]) or
62+
this.hasQualifiedName("std", "list", ["front", "back"]) or
7263
this.hasQualifiedName("std", "forward_list", "front")
7364
}
7465

@@ -84,11 +75,7 @@ class StdSequenceContainerFrontBack extends TaintFunction {
8475
*/
8576
class StdSequenceContainerSwap extends TaintFunction {
8677
StdSequenceContainerSwap() {
87-
this.hasQualifiedName("std", "array", "swap") or
88-
this.hasQualifiedName("std", "vector", "swap") or
89-
this.hasQualifiedName("std", "deque", "swap") or
90-
this.hasQualifiedName("std", "list", "swap") or
91-
this.hasQualifiedName("std", "forward_list", "swap")
78+
this.hasQualifiedName("std", ["array", "vector", "deque", "list", "forward_list"], "swap")
9279
}
9380

9481
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -100,3 +87,22 @@ class StdSequenceContainerSwap extends TaintFunction {
10087
output.isQualifierObject()
10188
}
10289
}
90+
91+
/**
92+
* The standard container functions `at` and `operator[]`.
93+
*/
94+
class StdSequenceContainerAt extends TaintFunction {
95+
StdSequenceContainerAt() {
96+
this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
97+
}
98+
99+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
100+
// flow from qualifier to referenced return value
101+
input.isQualifierObject() and
102+
output.isReturnValueDeref()
103+
or
104+
// reverse flow from returned reference to the qualifier
105+
input.isReturnValueDeref() and
106+
output.isQualifierObject()
107+
}
108+
}

cpp/ql/src/semmle/code/cpp/models/implementations/StdString.qll

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,7 @@ class StdBasicString extends TemplateClass {
1111
* The `std::string` functions `c_str` and `data`.
1212
*/
1313
class StdStringCStr extends TaintFunction {
14-
StdStringCStr() {
15-
this.hasQualifiedName("std", "basic_string", "c_str") or
16-
this.hasQualifiedName("std", "basic_string", "data")
17-
}
14+
StdStringCStr() { this.hasQualifiedName("std", "basic_string", ["c_str", "data"]) }
1815

1916
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
2017
// flow from string itself (qualifier) to return value
@@ -49,10 +46,7 @@ class StdStringPlus extends TaintFunction {
4946
*/
5047
class StdStringAppend extends TaintFunction {
5148
StdStringAppend() {
52-
this.hasQualifiedName("std", "basic_string", "operator+=") or
53-
this.hasQualifiedName("std", "basic_string", "append") or
54-
this.hasQualifiedName("std", "basic_string", "insert") or
55-
this.hasQualifiedName("std", "basic_string", "replace")
49+
this.hasQualifiedName("std", "basic_string", ["operator+=", "append", "insert", "replace"])
5650
}
5751

5852
/**
@@ -145,3 +139,20 @@ class StdStringSwap extends TaintFunction {
145139
output.isQualifierObject()
146140
}
147141
}
142+
143+
/**
144+
* The `std::string` functions `at` and `operator[]`.
145+
*/
146+
class StdStringAt extends TaintFunction {
147+
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
148+
149+
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
150+
// flow from qualifier to referenced return value
151+
input.isQualifierObject() and
152+
output.isReturnValueDeref()
153+
or
154+
// reverse flow from returned reference to the qualifier
155+
input.isReturnValueDeref() and
156+
output.isQualifierObject()
157+
}
158+
}

cpp/ql/src/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ private newtype TFunctionInput =
1010
TInParameter(ParameterIndex i) or
1111
TInParameterDeref(ParameterIndex i) or
1212
TInQualifierObject() or
13-
TInQualifierAddress()
13+
TInQualifierAddress() or
14+
TInReturnValueDeref()
1415

1516
/**
1617
* An input to a function. This can be:
@@ -106,6 +107,31 @@ class FunctionInput extends TFunctionInput {
106107
* (with type `C const *`) on entry to the function.
107108
*/
108109
predicate isQualifierAddress() { none() }
110+
111+
/**
112+
* Holds if this is the input value pointed to by the return value of a
113+
* function, if the function returns a pointer, or the input value referred
114+
* to by the return value of a function, if the function returns a reference.
115+
*
116+
* Example:
117+
* ```
118+
* char* getPointer();
119+
* float& getReference();
120+
* int getInt();
121+
* ```
122+
* - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
123+
* value of `*getPointer()` (with type `char`).
124+
* - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
125+
* value of `getReference()` (with type `float`).
126+
* - There is no `FunctionInput` of `getInt()` for which
127+
* `isReturnValueDeref()` holds because the return type of `getInt()` is
128+
* neither a pointer nor a reference.
129+
*
130+
* Note that data flows in through function return values are relatively
131+
* rare, but they do occur when a function returns a reference to itself,
132+
* part of itself, or one of its other inputs.
133+
*/
134+
predicate isReturnValueDeref() { none() }
109135
}
110136

111137
/**
@@ -199,6 +225,34 @@ class InQualifierAddress extends FunctionInput, TInQualifierAddress {
199225
override predicate isQualifierAddress() { any() }
200226
}
201227

228+
/**
229+
* The input value pointed to by the return value of a function, if the
230+
* function returns a pointer, or the input value referred to by the return
231+
* value of a function, if the function returns a reference.
232+
*
233+
* Example:
234+
* ```
235+
* char* getPointer();
236+
* float& getReference();
237+
* int getInt();
238+
* ```
239+
* - `InReturnValueDeref` represents the value of `*getPointer()` (with type
240+
* `char`).
241+
* - `InReturnValueDeref` represents the value of `getReference()` (with type
242+
* `float`).
243+
* - `InReturnValueDeref` does not represent the return value of `getInt()`
244+
* because the return type of `getInt()` is neither a pointer nor a reference.
245+
*
246+
* Note that data flows in through function return values are relatively
247+
* rare, but they do occur when a function returns a reference to itself,
248+
* part of itself, or one of its other inputs.
249+
*/
250+
class InReturnValueDeref extends FunctionInput, TInReturnValueDeref {
251+
override string toString() { result = "InReturnValueDeref" }
252+
253+
override predicate isReturnValueDeref() { any() }
254+
}
255+
202256
private newtype TFunctionOutput =
203257
TOutParameterDeref(ParameterIndex i) or
204258
TOutQualifierObject() or

cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@
77
| example.c:17:19:17:22 | {...} | example.c:26:19:26:24 | coords |
88
| example.c:24:2:24:7 | coords [post update] | example.c:26:2:26:7 | coords |
99
| example.c:24:2:24:7 | coords [post update] | example.c:26:19:26:24 | coords |
10+
| example.c:24:2:24:30 | ... = ... | example.c:24:9:24:9 | x [post update] |
1011
| example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords |
1112
| example.c:24:13:24:18 | coords [post update] | example.c:26:2:26:7 | coords |
1213
| example.c:24:13:24:18 | coords [post update] | example.c:26:19:26:24 | coords |
1314
| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... |
15+
| example.c:24:13:24:30 | ... = ... | example.c:24:20:24:20 | y [post update] |
1416
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
17+
| example.c:26:2:26:25 | ... = ... | example.c:26:9:26:9 | x [post update] |
1518
| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... |
1619
| example.c:26:18:26:24 | ref arg & ... | example.c:26:2:26:7 | coords |
1720
| example.c:26:18:26:24 | ref arg & ... | example.c:26:19:26:24 | coords [inner post update] |

0 commit comments

Comments
 (0)