Skip to content

Commit b39bc19

Browse files
committed
C++: Add alert provenance plumbing.
1 parent d2eb436 commit b39bc19

File tree

8 files changed

+133
-110
lines changed

8 files changed

+133
-110
lines changed

cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,10 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no
286286
/** Extra data-flow steps needed for lambda flow analysis. */
287287
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
288288

289+
predicate knownSourceModel(Node source, string model) { none() }
290+
291+
predicate knownSinkModel(Node sink, string model) { none() }
292+
289293
/**
290294
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
291295
* side-effect, resulting in a summary from `p` to itself.

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

Lines changed: 62 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ private module ThisFlow {
516516
*/
517517
cached
518518
predicate localFlowStep(Node nodeFrom, Node nodeTo) {
519-
simpleLocalFlowStep(nodeFrom, nodeTo)
519+
simpleLocalFlowStep(nodeFrom, nodeTo, _)
520520
or
521521
// Field flow is not strictly a "step" but covers the whole function
522522
// transitively. There's no way to get a step-like relation out of the global
@@ -530,64 +530,67 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
530530
* This is the local flow predicate that's used as a building block in global
531531
* data flow. It may have less flow than the `localFlowStep` predicate.
532532
*/
533-
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
534-
// Expr -> Expr
535-
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
536-
or
537-
// Assignment -> LValue post-update node
538-
//
539-
// This is used for assignments whose left-hand side is not a variable
540-
// assignment or a storeStep but is still modeled by other means. It could be
541-
// a call to `operator*` or `operator[]` where taint should flow to the
542-
// post-update node of the qualifier.
543-
exists(AssignExpr assign |
544-
nodeFrom.asExpr() = assign and
545-
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
546-
)
547-
or
548-
// Node -> FlowVar -> VariableAccess
549-
exists(FlowVar var |
550-
(
551-
exprToVarStep(nodeFrom.asExpr(), var)
552-
or
553-
varSourceBaseCase(var, nodeFrom.asParameter())
554-
or
555-
varSourceBaseCase(var, nodeFrom.asUninitialized())
556-
or
557-
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
558-
) and
559-
varToNodeStep(var, nodeTo)
560-
)
561-
or
562-
// Expr -> DefinitionByReferenceNode
563-
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
564-
or
565-
// `this` -> adjacent-`this`
566-
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
567-
or
568-
// post-update-`this` -> following-`this`-ref
569-
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
570-
or
571-
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
572-
// from which there is field flow to `x` via reverse read.
573-
exists(PartialDefinition def, Expr inner, Expr outer |
574-
def.definesExpressions(inner, outer) and
575-
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
576-
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
577-
)
578-
or
579-
// Reverse flow: data that flows from the post-update node of a reference
580-
// returned by a function call, back into the qualifier of that function.
581-
// This allows data to flow 'in' through references returned by a modeled
582-
// function such as `operator[]`.
583-
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
584-
call.getTarget() = f and
585-
inModel.isReturnValueDeref() and
586-
outModel.isQualifierObject() and
587-
f.hasDataFlow(inModel, outModel) and
588-
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
589-
nodeTo.asDefiningArgument() = call.getQualifier()
590-
)
533+
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
534+
(
535+
// Expr -> Expr
536+
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
537+
or
538+
// Assignment -> LValue post-update node
539+
//
540+
// This is used for assignments whose left-hand side is not a variable
541+
// assignment or a storeStep but is still modeled by other means. It could be
542+
// a call to `operator*` or `operator[]` where taint should flow to the
543+
// post-update node of the qualifier.
544+
exists(AssignExpr assign |
545+
nodeFrom.asExpr() = assign and
546+
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
547+
)
548+
or
549+
// Node -> FlowVar -> VariableAccess
550+
exists(FlowVar var |
551+
(
552+
exprToVarStep(nodeFrom.asExpr(), var)
553+
or
554+
varSourceBaseCase(var, nodeFrom.asParameter())
555+
or
556+
varSourceBaseCase(var, nodeFrom.asUninitialized())
557+
or
558+
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
559+
) and
560+
varToNodeStep(var, nodeTo)
561+
)
562+
or
563+
// Expr -> DefinitionByReferenceNode
564+
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
565+
or
566+
// `this` -> adjacent-`this`
567+
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
568+
or
569+
// post-update-`this` -> following-`this`-ref
570+
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
571+
or
572+
// In `f(&x->a)`, this step provides the flow from post-`&` to post-`x->a`,
573+
// from which there is field flow to `x` via reverse read.
574+
exists(PartialDefinition def, Expr inner, Expr outer |
575+
def.definesExpressions(inner, outer) and
576+
inner = nodeTo.(InnerPartialDefinitionNode).getPreUpdateNode().asExpr() and
577+
outer = nodeFrom.(PartialDefinitionNode).getPreUpdateNode().asExpr()
578+
)
579+
or
580+
// Reverse flow: data that flows from the post-update node of a reference
581+
// returned by a function call, back into the qualifier of that function.
582+
// This allows data to flow 'in' through references returned by a modeled
583+
// function such as `operator[]`.
584+
exists(DataFlowFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
585+
call.getTarget() = f and
586+
inModel.isReturnValueDeref() and
587+
outModel.isQualifierObject() and
588+
f.hasDataFlow(inModel, outModel) and
589+
nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() = call and
590+
nodeTo.asDefiningArgument() = call.getQualifier()
591+
)
592+
) and
593+
model = ""
591594
}
592595

593596
/**

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ predicate localTaintStep(DataFlow::Node src, DataFlow::Node sink) {
3232
* Holds if the additional step from `src` to `sink` should be included in all
3333
* global taint flow configurations.
3434
*/
35-
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
36-
localAdditionalTaintStep(src, sink)
35+
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, string model) {
36+
localAdditionalTaintStep(src, sink) and model = ""
3737
}
3838

3939
/**

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,10 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { no
10201020
/** Extra data-flow steps needed for lambda flow analysis. */
10211021
predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() }
10221022

1023+
predicate knownSourceModel(Node source, string model) { none() }
1024+
1025+
predicate knownSinkModel(Node sink, string model) { none() }
1026+
10231027
/**
10241028
* Holds if flow is allowed to pass from parameter `p` and back to itself as a
10251029
* side-effect, resulting in a summary from `p` to itself.
@@ -1096,7 +1100,7 @@ private predicate localFlowStepWithSummaries(Node node1, Node node2) {
10961100
or
10971101
readStep(node1, _, node2)
10981102
or
1099-
DataFlowImplCommon::argumentValueFlowsThrough(node1, _, node2)
1103+
DataFlowImplCommon::argumentValueFlowsThrough(node1, _, node2, _)
11001104
}
11011105

11021106
/** Holds if `node` flows to a node that is used in a `SwitchInstruction`. */

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

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,7 +1892,7 @@ private module Cached {
18921892
* (intra-procedural) step.
18931893
*/
18941894
cached
1895-
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
1895+
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo, _) }
18961896

18971897
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
18981898
nodeFrom != nodeTo and
@@ -1962,41 +1962,45 @@ private module Cached {
19621962
* data flow. It may have less flow than the `localFlowStep` predicate.
19631963
*/
19641964
cached
1965-
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
1966-
// Post update node -> Node flow
1967-
Ssa::postUpdateFlow(nodeFrom, nodeTo)
1968-
or
1969-
// Def-use/Use-use flow
1970-
Ssa::ssaFlow(nodeFrom, nodeTo)
1971-
or
1972-
// Operand -> Instruction flow
1973-
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
1974-
or
1975-
// Instruction -> Operand flow
1976-
exists(Instruction iFrom, Operand opTo |
1977-
iFrom = nodeFrom.asInstruction() and opTo = nodeTo.asOperand()
1978-
|
1979-
simpleOperandLocalFlowStep(iFrom, opTo) and
1980-
// Omit when the instruction node also represents the operand.
1981-
not iFrom = Ssa::getIRRepresentationOfOperand(opTo)
1982-
)
1983-
or
1984-
// Phi node -> Node flow
1985-
Ssa::fromPhiNode(nodeFrom, nodeTo)
1986-
or
1987-
// Indirect operand -> (indirect) instruction flow
1988-
indirectionOperandFlow(nodeFrom, nodeTo)
1989-
or
1990-
// Indirect instruction -> indirect operand flow
1991-
indirectionInstructionFlow(nodeFrom, nodeTo)
1965+
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
1966+
(
1967+
// Post update node -> Node flow
1968+
Ssa::postUpdateFlow(nodeFrom, nodeTo)
1969+
or
1970+
// Def-use/Use-use flow
1971+
Ssa::ssaFlow(nodeFrom, nodeTo)
1972+
or
1973+
// Operand -> Instruction flow
1974+
simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction())
1975+
or
1976+
// Instruction -> Operand flow
1977+
exists(Instruction iFrom, Operand opTo |
1978+
iFrom = nodeFrom.asInstruction() and opTo = nodeTo.asOperand()
1979+
|
1980+
simpleOperandLocalFlowStep(iFrom, opTo) and
1981+
// Omit when the instruction node also represents the operand.
1982+
not iFrom = Ssa::getIRRepresentationOfOperand(opTo)
1983+
)
1984+
or
1985+
// Phi node -> Node flow
1986+
Ssa::fromPhiNode(nodeFrom, nodeTo)
1987+
or
1988+
// Indirect operand -> (indirect) instruction flow
1989+
indirectionOperandFlow(nodeFrom, nodeTo)
1990+
or
1991+
// Indirect instruction -> indirect operand flow
1992+
indirectionInstructionFlow(nodeFrom, nodeTo)
1993+
) and
1994+
model = ""
19921995
or
19931996
// Flow through modeled functions
1994-
modelFlow(nodeFrom, nodeTo)
1997+
modelFlow(nodeFrom, nodeTo, model)
19951998
or
19961999
// Reverse flow: data that flows from the definition node back into the indirection returned
19972000
// by a function. This allows data to flow 'in' through references returned by a modeled
19982001
// function such as `operator[]`.
1999-
reverseFlow(nodeFrom, nodeTo)
2002+
reverseFlow(nodeFrom, nodeTo) and
2003+
model = ""
20002004
}
20012005

20022006
private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) {
@@ -2011,12 +2015,13 @@ private module Cached {
20112015
opTo.getDef() = iFrom
20122016
}
20132017

2014-
private predicate modelFlow(Node nodeFrom, Node nodeTo) {
2018+
private predicate modelFlow(Node nodeFrom, Node nodeTo, string model) {
20152019
exists(
20162020
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
20172021
|
20182022
call.getStaticCallTarget() = func and
2019-
func.hasDataFlow(modelIn, modelOut)
2023+
func.hasDataFlow(modelIn, modelOut) and
2024+
model = "DataFlowFunction"
20202025
|
20212026
nodeFrom = callInput(call, modelIn) and
20222027
nodeTo = callOutput(call, modelOut)

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ private import PrintIRUtilities
1010
*/
1111
private string getFromFlow(Node node2, int order1, int order2) {
1212
exists(Node node1 |
13-
simpleLocalFlowStep(node1, node2) and
13+
simpleLocalFlowStep(node1, node2, _) and
1414
result = nodeId(node1, order1, order2)
1515
)
1616
}
@@ -20,7 +20,7 @@ private string getFromFlow(Node node2, int order1, int order2) {
2020
*/
2121
private string getToFlow(Node node1, int order1, int order2) {
2222
exists(Node node2 |
23-
simpleLocalFlowStep(node1, node2) and
23+
simpleLocalFlowStep(node1, node2, _) and
2424
result = nodeId(node2, order1, order2)
2525
)
2626
}

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

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ private import SsaInternals as Ssa
1414
predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
1515
DataFlow::localFlowStep(nodeFrom, nodeTo)
1616
or
17-
localAdditionalTaintStep(nodeFrom, nodeTo)
17+
localAdditionalTaintStep(nodeFrom, nodeTo, _)
1818
}
1919

2020
/**
@@ -23,20 +23,23 @@ predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
2323
* different objects.
2424
*/
2525
cached
26-
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
27-
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction())
26+
predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, string model) {
27+
operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) and
28+
model = ""
2829
or
29-
modeledTaintStep(nodeFrom, nodeTo)
30+
modeledTaintStep(nodeFrom, nodeTo, model)
3031
or
3132
// Flow from (the indirection of) an operand of a pointer arithmetic instruction to the
3233
// indirection of the pointer arithmetic instruction. This provides flow from `source`
3334
// in `x[source]` to the result of the associated load instruction.
3435
exists(PointerArithmeticInstruction pai, int indirectionIndex |
3536
nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and
3637
hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1)
37-
)
38+
) and
39+
model = ""
3840
or
39-
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo)
41+
any(Ssa::Indirection ind).isAdditionalTaintStep(nodeFrom, nodeTo) and
42+
model = ""
4043
}
4144

4245
/**
@@ -113,8 +116,8 @@ predicate localExprTaint(Expr e1, Expr e2) {
113116
* Holds if the additional step from `src` to `sink` should be included in all
114117
* global taint flow configurations.
115118
*/
116-
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
117-
localAdditionalTaintStep(src, sink)
119+
predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink, string model) {
120+
localAdditionalTaintStep(src, sink, model)
118121
}
119122

120123
/**
@@ -134,7 +137,7 @@ predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
134137
* Holds if taint can flow from `nodeIn` to `nodeOut` through a call to a
135138
* modeled function.
136139
*/
137-
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
140+
predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string model) {
138141
// Normal taint steps
139142
exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut |
140143
call.getStaticCallTarget() = func and
@@ -143,7 +146,8 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
143146
nodeIn = callInput(call, modelIn) and nodeOut = callOutput(call, modelOut)
144147
or
145148
exists(int d | nodeIn = callInput(call, modelIn, d) and nodeOut = callOutput(call, modelOut, d))
146-
)
149+
) and
150+
model = "TaintFunction"
147151
or
148152
// Taint flow from one argument to another and data flow from an argument to a
149153
// return value. This happens in functions like `strcat` and `memcpy`. We
@@ -160,7 +164,8 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
160164
func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and
161165
func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and
162166
modelMidOut.isParameterDeref(indexMid) and
163-
modelMidIn.isParameter(indexMid)
167+
modelMidIn.isParameter(indexMid) and
168+
model = "TaintFunction"
164169
)
165170
or
166171
// Taint flow from a pointer argument to an output, when the model specifies flow from the deref
@@ -173,9 +178,11 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) {
173178
indirectArgument.hasAddressOperandAndIndirectionIndex(nodeIn.asOperand(), _) and
174179
call.getStaticCallTarget() = func and
175180
(
176-
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
181+
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) and
182+
model = "DataFlowFunction"
177183
or
178-
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
184+
func.(TaintFunction).hasTaintFlow(modelIn, modelOut) and
185+
model = "TaintFunction"
179186
) and
180187
nodeOut = callOutput(call, modelOut)
181188
)

cpp/ql/src/Security/CWE/CWE-611/XXE.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module XxeConfig implements DataFlow::StateConfigSig {
3535
) {
3636
// create additional flow steps for `XxeFlowStateTransformer`s
3737
state2 = node2.asIndirectExpr().(XxeFlowStateTransformer).transform(state1) and
38-
DataFlow::simpleLocalFlowStep(node1, node2)
38+
DataFlow::simpleLocalFlowStep(node1, node2, _)
3939
}
4040

4141
predicate isBarrier(DataFlow::Node node, FlowState flowstate) {

0 commit comments

Comments
 (0)