Skip to content

Commit c56d5eb

Browse files
authored
Merge pull request #4295 from rdmarsh2/rdmarsh2/cpp/ir-qualifier-flow
C++: Improved qualifier flow in IR taint tracking
2 parents 724baaf + 947ad02 commit c56d5eb

File tree

9 files changed

+270
-230
lines changed

9 files changed

+270
-230
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,12 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
717717
iTo = outNode and
718718
outNode = getSideEffectFor(call, index)
719719
)
720-
// TODO: add write side effects for qualifiers
720+
or
721+
exists(WriteSideEffectInstruction outNode |
722+
modelOut.isQualifierObject() and
723+
iTo = outNode and
724+
outNode = getSideEffectFor(call, -1)
725+
)
721726
) and
722727
(
723728
exists(int index |
@@ -733,7 +738,12 @@ private predicate modelFlow(Operand opFrom, Instruction iTo) {
733738
or
734739
modelIn.isQualifierAddress() and
735740
opFrom = call.getThisArgumentOperand()
736-
// TODO: add read side effects for qualifiers
741+
or
742+
exists(ReadSideEffectInstruction read |
743+
modelIn.isQualifierObject() and
744+
read = getSideEffectFor(call, -1) and
745+
opFrom = read.getSideEffectOperand()
746+
)
737747
)
738748
)
739749
}

cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,31 @@ private import semmle.code.cpp.ir.dataflow.DataFlow
99
/**
1010
* Gets the instruction that goes into `input` for `call`.
1111
*/
12-
Instruction callInput(CallInstruction call, FunctionInput input) {
12+
DataFlow::Node callInput(CallInstruction call, FunctionInput input) {
1313
// A positional argument
1414
exists(int index |
15-
result = call.getPositionalArgument(index) and
15+
result.asInstruction() = call.getPositionalArgument(index) and
1616
input.isParameter(index)
1717
)
1818
or
1919
// A value pointed to by a positional argument
2020
exists(ReadSideEffectInstruction read |
21-
result = read and
21+
result.asOperand() = read.getSideEffectOperand() and
2222
read.getPrimaryInstruction() = call and
2323
input.isParameterDeref(read.getIndex())
2424
)
2525
or
2626
// The qualifier pointer
27-
result = call.getThisArgument() and
27+
result.asInstruction() = call.getThisArgument() and
2828
input.isQualifierAddress()
29-
//TODO: qualifier deref
29+
or
30+
// The qualifier object
31+
exists(ReadSideEffectInstruction read |
32+
result.asOperand() = read.getSideEffectOperand() and
33+
read.getPrimaryInstruction() = call and
34+
read.getIndex() = -1 and
35+
input.isQualifierObject()
36+
)
3037
}
3138

3239
/**
@@ -43,5 +50,13 @@ Instruction callOutput(CallInstruction call, FunctionOutput output) {
4350
effect.getPrimaryInstruction() = call and
4451
output.isParameterDeref(effect.getIndex())
4552
)
46-
// TODO: qualifiers, return value dereference
53+
or
54+
// The side effect of a call on the qualifier object
55+
exists(WriteSideEffectInstruction effect |
56+
result = effect and
57+
effect.getPrimaryInstruction() = call and
58+
effect.getIndex() = -1 and
59+
output.isQualifierObject()
60+
)
61+
// TODO: return value dereference
4762
}

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

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
1919
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
2020
* different objects.
2121
*/
22+
cached
2223
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
2324
localInstructionTaintStep(nodeFrom.asInstruction(), nodeTo.asInstruction())
25+
or
26+
modeledTaintStep(nodeFrom, nodeTo)
2427
}
2528

2629
/**
@@ -49,8 +52,6 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no
4952
or
5053
nodeTo.(LoadInstruction).getSourceAddress() = nodeFrom
5154
or
52-
modeledInstructionTaintStep(nodeFrom, nodeTo)
53-
or
5455
// Flow through partial reads of arrays and unions
5556
nodeTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = nodeFrom and
5657
not nodeFrom.isResultConflated() and
@@ -109,10 +110,17 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
109110
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
110111
* modeled function.
111112
*/
112-
predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut) {
113+
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
113114
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
114-
instrIn = callInput(call, modelIn) and
115-
instrOut = callOutput(call, modelOut) and
115+
(
116+
nodeIn = callInput(call, modelIn)
117+
or
118+
exists(int n |
119+
modelIn.isParameterDeref(n) and
120+
nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n))
121+
)
122+
) and
123+
nodeOut.asInstruction() = callOutput(call, modelOut) and
116124
call.getStaticCallTarget() = func and
117125
func.hasTaintFlow(modelIn, modelOut)
118126
)
@@ -126,8 +134,8 @@ predicate modeledInstructionTaintStep(Instruction instrIn, Instruction instrOut)
126134
CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut,
127135
int indexMid, InParameter modelMidIn, OutReturnValue modelOut
128136
|
129-
instrIn = callInput(call, modelIn) and
130-
instrOut = callOutput(call, modelOut) and
137+
nodeIn = callInput(call, modelIn) and
138+
nodeOut.asInstruction() = callOutput(call, modelOut) and
131139
call.getStaticCallTarget() = func and
132140
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
133141
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ class ConversionConstructorModel extends Constructor, TaintFunction {
2121
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
2222
// taint flow from the first constructor argument to the returned object
2323
input.isParameter(0) and
24-
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
24+
(
25+
output.isReturnValue()
26+
or
27+
output.isQualifierObject()
28+
)
2529
}
2630
}
2731

@@ -32,7 +36,11 @@ class CopyConstructorModel extends CopyConstructor, DataFlowFunction {
3236
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
3337
// data flow from the first constructor argument to the returned object
3438
input.isParameter(0) and
35-
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
39+
(
40+
output.isReturnValue()
41+
or
42+
output.isQualifierObject()
43+
)
3644
}
3745
}
3846

@@ -43,7 +51,11 @@ class MoveConstructorModel extends MoveConstructor, DataFlowFunction {
4351
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
4452
// data flow from the first constructor argument to the returned object
4553
input.isParameter(0) and
46-
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
54+
(
55+
output.isReturnValue()
56+
or
57+
output.isQualifierObject()
58+
)
4759
}
4860
}
4961

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ class StdSequenceContainerConstructor extends Constructor, TaintFunction {
3838
input.isParameterDeref(getAValueTypeParameterIndex()) or
3939
input.isParameter(getAnIteratorParameterIndex())
4040
) and
41-
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
41+
(
42+
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
43+
or
44+
output.isQualifierObject()
45+
)
4246
}
4347
}
4448

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ class StdStringConstructor extends Constructor, TaintFunction {
4747
input.isParameterDeref(getAStringParameterIndex()) or
4848
input.isParameter(getAnIteratorParameterIndex())
4949
) and
50-
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
50+
(
51+
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
52+
or
53+
output.isQualifierObject()
54+
)
5155
}
5256
}
5357

@@ -573,7 +577,11 @@ class StdStringStreamConstructor extends Constructor, TaintFunction {
573577
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
574578
// taint flow from any parameter of string type to the returned object
575579
input.isParameterDeref(getAStringParameterIndex()) and
576-
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
580+
(
581+
output.isReturnValue() // TODO: this is only needed for AST data flow, which treats constructors as returning the new object
582+
or
583+
output.isQualifierObject()
584+
)
577585
}
578586
}
579587

cpp/ql/test/library-tests/dataflow/taint-tests/IRTaintTestCommon.qll

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import cpp
2+
import semmle.code.cpp.ir.IR
23
import semmle.code.cpp.ir.dataflow.TaintTracking
34

45
/** Common data flow configuration to be used by tests. */
56
class TestAllocationConfig extends TaintTracking::Configuration {
67
TestAllocationConfig() { this = "TestAllocationConfig" }
78

89
override predicate isSource(DataFlow::Node source) {
9-
source.asExpr().(FunctionCall).getTarget().getName() = "source"
10+
source.(DataFlow::ExprNode).getConvertedExpr().(FunctionCall).getTarget().getName() = "source"
1011
or
1112
source.asParameter().getName().matches("source%")
1213
or
@@ -17,8 +18,20 @@ class TestAllocationConfig extends TaintTracking::Configuration {
1718
override predicate isSink(DataFlow::Node sink) {
1819
exists(FunctionCall call |
1920
call.getTarget().getName() = "sink" and
20-
sink.asExpr() = call.getAnArgument()
21+
sink.(DataFlow::ExprNode).getConvertedExpr() = call.getAnArgument()
22+
or
23+
call.getTarget().getName() = "sink" and
24+
sink.asExpr() = call.getAnArgument() and
25+
sink.(DataFlow::ExprNode).getConvertedExpr() instanceof ReferenceDereferenceExpr
2126
)
27+
or
28+
sink
29+
.asInstruction()
30+
.(ReadSideEffectInstruction)
31+
.getPrimaryInstruction()
32+
.(CallInstruction)
33+
.getStaticCallTarget()
34+
.hasName("sink")
2235
}
2336

2437
override predicate isSanitizer(DataFlow::Node barrier) {

0 commit comments

Comments
 (0)