Skip to content

Commit c083c62

Browse files
committed
C++: Explicitly model data flow in through reference return values.
1 parent 76a07f7 commit c083c62

File tree

4 files changed

+69
-7
lines changed

4 files changed

+69
-7
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -83,16 +83,16 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
8383
or
8484
exprToPartialDefinitionStep(nodeFrom.asExpr(), nodeTo.asPartialDefinition())
8585
or
86-
// Reverse taint: if a function model has taint from the qualifier to the
87-
// dereferenced return value, also apply reverse taint from the post-update
88-
// of the return value back to the qualifier. This allows taint to flow 'in'
89-
// through references returned by a modeled function such as `operator[]`.
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[]`.
9090
exists(
9191
TaintFunction f, Call call, FunctionInput inModel, FunctionOutput outModel
9292
|
9393
call.getTarget() = f and
94-
inModel.isQualifierObject() and
95-
outModel.isReturnValueDeref() and
94+
inModel.isReturnValueDeref() and
95+
outModel.isQualifierObject() and
9696
f.hasTaintFlow(inModel, outModel) and
9797
nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = call and
9898
nodeTo.asDefiningArgument() = call.getQualifier()

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,9 @@ class StdSequenceContainerAt extends TaintFunction {
100100
// flow from qualifier to referenced return value
101101
input.isQualifierObject() and
102102
output.isReturnValueDeref()
103+
or
104+
// reverse flow from returned reference to the qualifier
105+
input.isReturnValueDeref() and
106+
output.isQualifierObject()
103107
}
104108
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,9 @@ class StdStringAt extends TaintFunction {
154154
// flow from qualifier to referenced return value
155155
input.isQualifierObject() and
156156
output.isReturnValueDeref()
157+
or
158+
// reverse flow from returned reference to the qualifier
159+
input.isReturnValueDeref() and
160+
output.isQualifierObject()
157161
}
158162
}

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

0 commit comments

Comments
 (0)