Skip to content

Commit 1196d0c

Browse files
committed
C#: Rework SummarizedCallable::clearsContent/2
1 parent ed73d9b commit 1196d0c

File tree

7 files changed

+168
-50
lines changed

7 files changed

+168
-50
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,21 @@ module SummaryComponentStack {
7878

7979
class SummarizedCallable = Impl::Public::SummarizedCallable;
8080

81+
private class SummarizedCallableDefaultClearsContent extends Impl::Public::SummarizedCallable {
82+
SummarizedCallable c;
83+
84+
SummarizedCallableDefaultClearsContent() { this = c }
85+
86+
// By default, we assume that all stores into arguments are definite
87+
override predicate clearsContent(int i, DataFlow::Content content) {
88+
exists(SummaryComponentStack output |
89+
c.propagatesFlow(_, output, _) and
90+
output.drop(_) =
91+
SummaryComponentStack::push(SummaryComponent::content(content),
92+
SummaryComponentStack::argument(i)) and
93+
not content instanceof DataFlow::ElementContent
94+
)
95+
}
96+
}
97+
8198
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ private class RetNodeEx extends NodeEx {
252252
ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) }
253253

254254
ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() }
255+
256+
predicate allowFlowThroughParameter() { allowFlowThroughParameterCached(this.asNode()) }
255257
}
256258

257259
private predicate inBarrier(NodeEx node, Configuration config) {
@@ -725,12 +727,16 @@ private module Stage1 {
725727
/** Holds if flow may return from `callable`. */
726728
pragma[nomagic]
727729
private predicate returnFlowCallableNodeCand(
728-
DataFlowCallable callable, ReturnKindExt kind, Configuration config
730+
DataFlowCallable callable, ReturnKindExt kind, boolean allowFlowThroughParameter,
731+
Configuration config
729732
) {
730733
exists(RetNodeEx ret |
731734
throughFlowNodeCand(ret, config) and
732735
callable = ret.getEnclosingCallable() and
733-
kind = ret.getKind()
736+
kind = ret.getKind() and
737+
if ret.allowFlowThroughParameter()
738+
then allowFlowThroughParameter = true
739+
else allowFlowThroughParameter = false
734740
)
735741
}
736742

@@ -739,13 +745,16 @@ private module Stage1 {
739745
* candidate for the origin of a summary.
740746
*/
741747
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
742-
exists(ReturnKindExt kind |
748+
exists(ReturnKindExt kind, boolean allowFlowThroughParameter |
743749
throughFlowNodeCand(p, config) and
744-
returnFlowCallableNodeCand(c, kind, config) and
750+
returnFlowCallableNodeCand(c, kind, allowFlowThroughParameter, config) and
745751
p.getEnclosingCallable() = c and
746-
exists(ap) and
747-
// we don't expect a parameter to return stored in itself
748-
not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
752+
(
753+
if kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
754+
then allowFlowThroughParameter = true
755+
else any()
756+
) and
757+
exists(ap)
749758
)
750759
}
751760

@@ -1394,8 +1403,11 @@ private module Stage2 {
13941403
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
13951404
kind = ret.getKind() and
13961405
p.getPosition() = pos and
1397-
// we don't expect a parameter to return stored in itself
1398-
not kind.(ParamUpdateReturnKind).getPosition() = pos
1406+
(
1407+
if kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
1408+
then ret.allowFlowThroughParameter()
1409+
else any()
1410+
)
13991411
)
14001412
}
14011413

@@ -2083,8 +2095,11 @@ private module Stage3 {
20832095
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
20842096
kind = ret.getKind() and
20852097
p.getPosition() = pos and
2086-
// we don't expect a parameter to return stored in itself
2087-
not kind.(ParamUpdateReturnKind).getPosition() = pos
2098+
(
2099+
if kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
2100+
then ret.allowFlowThroughParameter()
2101+
else any()
2102+
)
20882103
)
20892104
}
20902105

@@ -2843,8 +2858,11 @@ private module Stage4 {
28432858
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
28442859
kind = ret.getKind() and
28452860
p.getPosition() = pos and
2846-
// we don't expect a parameter to return stored in itself
2847-
not kind.(ParamUpdateReturnKind).getPosition() = pos
2861+
(
2862+
if kind.(ParamUpdateReturnKind).getPosition() = p.getPosition()
2863+
then ret.allowFlowThroughParameter()
2864+
else any()
2865+
)
28482866
)
28492867
}
28502868

@@ -2917,6 +2935,8 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
29172935

29182936
int getParameterPos() { p.isParameterOf(_, result) }
29192937

2938+
ParameterNode getParameterNode() { result = p.asNode() }
2939+
29202940
override string toString() { result = p + ": " + ap }
29212941

29222942
predicate hasLocationInfo(
@@ -3617,7 +3637,11 @@ private predicate paramFlowsThrough(
36173637
ap = mid.getAp() and
36183638
apa = ap.getApprox() and
36193639
pos = sc.getParameterPos() and
3620-
not kind.(ParamUpdateReturnKind).getPosition() = pos
3640+
(
3641+
if kind.(ParamUpdateReturnKind).getPosition() = pos
3642+
then ret.allowFlowThroughParameter()
3643+
else any()
3644+
)
36213645
)
36223646
}
36233647

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplCommon.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,6 +801,9 @@ private module Cached {
801801
exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call))
802802
}
803803

804+
cached
805+
predicate allowFlowThroughParameterCached(Node ret) { allowFlowThroughParameter(ret) }
806+
804807
cached
805808
newtype TCallContext =
806809
TAnyCallContext() or

csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ module Consistency {
175175

176176
query predicate postWithInFlow(Node n, string msg) {
177177
isPostUpdateNode(n) and
178+
not clearsContent(n, _) and
178179
simpleLocalFlowStep(_, n) and
179180
msg = "PostUpdateNode should not be the target of local flow."
180181
}

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

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,18 @@ module LocalFlow {
310310
result = n.(ExplicitParameterNode).getSsaDefinition()
311311
}
312312

313+
/**
314+
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
315+
* involving SSA definition `def`.
316+
*/
317+
predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
318+
exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo |
319+
SsaImpl::adjacentReadPairSameVar(def, cfnFrom, cfnTo) and
320+
nodeTo = TExprNode(cfnTo) and
321+
nodeFrom = TExprNode(cfnFrom)
322+
)
323+
}
324+
313325
/**
314326
* Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving
315327
* SSA definition `def.
@@ -322,14 +334,7 @@ module LocalFlow {
322334
)
323335
or
324336
// Flow from read to next read
325-
exists(ControlFlow::Node cfnFrom, ControlFlow::Node cfnTo |
326-
SsaImpl::adjacentReadPairSameVar(def, cfnFrom, cfnTo) and
327-
nodeTo = TExprNode(cfnTo)
328-
|
329-
nodeFrom = TExprNode(cfnFrom)
330-
or
331-
cfnFrom = nodeFrom.(PostUpdateNode).getPreUpdateNode().getControlFlowNode()
332-
)
337+
localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
333338
or
334339
// Flow into phi node
335340
exists(Ssa::PhiNode phi |
@@ -399,6 +404,12 @@ module LocalFlow {
399404
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
400405
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
401406
or
407+
exists(Ssa::Definition def |
408+
LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and
409+
not FlowSummaryImpl::Private::Steps::summaryClearsContentArg(nodeFrom, _) and
410+
not LocalFlow::usesInstanceField(def)
411+
)
412+
or
402413
LocalFlow::localFlowCapturedVarStep(nodeFrom, nodeTo)
403414
or
404415
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, true)
@@ -716,6 +727,8 @@ private module Cached {
716727
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
717728
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
718729
or
730+
LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo)
731+
or
719732
exists(Ssa::Definition def |
720733
LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) and
721734
LocalFlow::usesInstanceField(def)
@@ -1691,9 +1704,6 @@ predicate clearsContent(Node n, Content c) {
16911704
or
16921705
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
16931706
or
1694-
FlowSummaryImpl::Private::Steps::summaryStoresIntoArg(c, n) and
1695-
not c instanceof ElementContent
1696-
or
16971707
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
16981708
or
16991709
exists(WithExpr we, ObjectInitializer oi, FieldOrProperty f |
@@ -2003,3 +2013,11 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
20032013
preservesValue = false
20042014
)
20052015
}
2016+
2017+
/**
2018+
* Holds if flow is allowed to pass from a parameter `p`, to return
2019+
* node `ret`, and back out to `p`.
2020+
*/
2021+
predicate allowFlowThroughParameter(ReturnNodeExt ret) {
2022+
FlowSummaryImpl::Private::summaryAllowFlowThroughParameter(ret)
2023+
}

0 commit comments

Comments
 (0)