Skip to content

Commit 031cac9

Browse files
committed
C#: Fix CFG position of property setter calls.
1 parent 711c3ee commit 031cac9

File tree

3 files changed

+74
-24
lines changed

3 files changed

+74
-24
lines changed

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -270,15 +270,17 @@ private module CallGraph {
270270
*
271271
* the constructor call `new Lazy<int>(M2)` includes `M2` as a target.
272272
*/
273-
Callable getARuntimeTarget(Call c, boolean libraryDelegateCall) {
273+
Callable getARuntimeTarget(Call c, ControlFlowNode n, boolean libraryDelegateCall) {
274274
// Non-delegate call: use dispatch library
275275
exists(DispatchCall dc | dc.getCall() = c |
276+
n = dc.getControlFlowNode() and
276277
result = dc.getADynamicTarget().getUnboundDeclaration() and
277278
libraryDelegateCall = false
278279
)
279280
or
280281
// Delegate call: use simple analysis
281-
result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall)
282+
result = SimpleDelegateAnalysis::getARuntimeDelegateTarget(c, libraryDelegateCall) and
283+
n = c.getControlFlowNode()
282284
}
283285

284286
private module SimpleDelegateAnalysis {
@@ -465,7 +467,7 @@ private module CallGraph {
465467

466468
/** Holds if `(c1,c2)` is an edge in the call graph. */
467469
predicate callEdge(Callable c1, Callable c2) {
468-
exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _))
470+
exists(Call c | c.getEnclosingCallable() = c1 and c2 = getARuntimeTarget(c, _, _))
469471
}
470472
}
471473

@@ -599,7 +601,7 @@ private module FieldOrPropsImpl {
599601
private predicate intraInstanceCallEdge(Callable c1, InstanceCallable c2) {
600602
exists(Call c |
601603
c.getEnclosingCallable() = c1 and
602-
c2 = getARuntimeTarget(c, _) and
604+
c2 = getARuntimeTarget(c, _, _) and
603605
c.(QualifiableExpr).targetIsLocalInstance()
604606
)
605607
}
@@ -615,8 +617,7 @@ private module FieldOrPropsImpl {
615617

616618
pragma[noinline]
617619
predicate callAt(BasicBlock bb, int i, Call call) {
618-
bb.getNode(i) = call.getAControlFlowNode() and
619-
getARuntimeTarget(call, _).hasBody()
620+
getARuntimeTarget(call, bb.getNode(i), _).hasBody()
620621
}
621622

622623
/**
@@ -635,7 +636,7 @@ private module FieldOrPropsImpl {
635636
Call call, FieldOrPropSourceVariable fps, FieldOrProp fp, Callable c, boolean fresh
636637
) {
637638
updateCandidate(_, _, fps, call) and
638-
c = getARuntimeTarget(call, _) and
639+
c = getARuntimeTarget(call, _, _) and
639640
fp = fps.getAssignable() and
640641
if c instanceof Constructor then fresh = true else fresh = false
641642
}

csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class DispatchCall extends Internal::TDispatchCall {
2020
/** Gets the underlying expression of this call. */
2121
Expr getCall() { result = Internal::getCall(this) }
2222

23+
/** Gets the control flow node of this call. */
24+
ControlFlowNode getControlFlowNode() { result = Internal::getControlFlowNode(this) }
25+
2326
/** Gets the `i`th argument of this call. */
2427
Expr getArgument(int i) { result = Internal::getArgument(this, i) }
2528

@@ -90,7 +93,12 @@ private module Internal {
9093
not mc.isLateBound() and
9194
not isExtensionAccessorCall(mc)
9295
} or
93-
TDispatchAccessorCall(AccessorCall ac) or
96+
TDispatchAccessorCall(AccessorCall ac, boolean isRead) {
97+
// For compound assignments an AccessorCall can be both a read and a write
98+
ac instanceof AssignableRead and isRead = true
99+
or
100+
ac instanceof AssignableWrite and isRead = false
101+
} or
94102
TDispatchOperatorCall(OperatorCall oc) { not oc.isLateBound() } or
95103
TDispatchReflectionCall(MethodCall mc, string name, Expr object, Expr qualifier, int args) {
96104
isReflectionCall(mc, name, object, qualifier, args)
@@ -117,6 +125,11 @@ private module Internal {
117125
cached
118126
Expr getCall(DispatchCall dc) { result = dc.(DispatchCallImpl).getCall() }
119127

128+
cached
129+
ControlFlowNode getControlFlowNode(DispatchCall dc) {
130+
result = dc.(DispatchCallImpl).getControlFlowNode()
131+
}
132+
120133
cached
121134
Expr getArgument(DispatchCall dc, int i) { result = dc.(DispatchCallImpl).getArgument(i) }
122135

@@ -224,6 +237,9 @@ private module Internal {
224237
/** Gets the underlying expression of this call. */
225238
abstract Expr getCall();
226239

240+
/** Gets the control flow node of this call. */
241+
ControlFlowNode getControlFlowNode() { result = this.getCall().getControlFlowNode() }
242+
227243
/** Gets the `i`th argument of this call. */
228244
abstract Expr getArgument(int i);
229245

@@ -877,13 +893,28 @@ private module Internal {
877893
* into account.
878894
*/
879895
private class DispatchAccessorCall extends DispatchOverridableCall, TDispatchAccessorCall {
880-
override AccessorCall getCall() { this = TDispatchAccessorCall(result) }
896+
private predicate isRead() { this = TDispatchAccessorCall(_, true) }
897+
898+
override ControlFlowNode getControlFlowNode() {
899+
if this.isRead()
900+
then result = this.getCall().getControlFlowNode()
901+
else
902+
exists(AssignableDefinition def |
903+
def.getTargetAccess() = this.getCall() and result = def.getExpr().getControlFlowNode()
904+
)
905+
}
906+
907+
override AccessorCall getCall() { this = TDispatchAccessorCall(result, _) }
881908

882909
override Expr getArgument(int i) { result = this.getCall().getArgument(i) }
883910

884911
override Expr getQualifier() { result = this.getCall().(MemberAccess).getQualifier() }
885912

886-
override Accessor getAStaticTarget() { result = this.getCall().getTarget() }
913+
override Accessor getAStaticTarget() {
914+
if this.isRead()
915+
then result = this.getCall().getReadTarget()
916+
else result = this.getCall().getWriteTarget()
917+
}
887918

888919
override RuntimeAccessor getADynamicTarget() {
889920
result = DispatchOverridableCall.super.getADynamicTarget() and

csharp/ql/lib/semmle/code/csharp/exprs/Call.qll

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,25 @@ class FunctionPointerCall extends DelegateLikeCall, @function_pointer_invocation
633633
* (`EventCall`).
634634
*/
635635
class AccessorCall extends Call, QualifiableExpr, @call_access_expr {
636-
override Accessor getTarget() { none() }
636+
override Accessor getTarget() { result = this.getReadTarget() or result = this.getWriteTarget() }
637+
638+
/**
639+
* Gets the static (compile-time) target of this call, assuming that this is
640+
* an `AssignableRead`.
641+
*
642+
* Note that left-hand sides of compound assignments are both
643+
* `AssignableRead`s and `AssignableWrite`s.
644+
*/
645+
Accessor getReadTarget() { none() }
646+
647+
/**
648+
* Gets the static (compile-time) target of this call, assuming that this is
649+
* an `AssignableWrite`.
650+
*
651+
* Note that left-hand sides of compound assignments are both
652+
* `AssignableRead`s and `AssignableWrite`s.
653+
*/
654+
Accessor getWriteTarget() { none() }
637655

638656
override Expr getArgument(int i) { none() }
639657

@@ -655,12 +673,12 @@ class AccessorCall extends Call, QualifiableExpr, @call_access_expr {
655673
* ```
656674
*/
657675
class PropertyCall extends AccessorCall, PropertyAccessExpr {
658-
override Accessor getTarget() {
659-
exists(PropertyAccess pa, Property p | pa = this and p = this.getProperty() |
660-
pa instanceof AssignableRead and result = p.getGetter()
661-
or
662-
pa instanceof AssignableWrite and result = p.getSetter()
663-
)
676+
override Accessor getReadTarget() {
677+
this instanceof AssignableRead and result = this.getProperty().getGetter()
678+
}
679+
680+
override Accessor getWriteTarget() {
681+
this instanceof AssignableWrite and result = this.getProperty().getSetter()
664682
}
665683

666684
override Expr getArgument(int i) {
@@ -690,12 +708,12 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr {
690708
* ```
691709
*/
692710
class IndexerCall extends AccessorCall, IndexerAccessExpr {
693-
override Accessor getTarget() {
694-
exists(IndexerAccess ia, Indexer i | ia = this and i = this.getIndexer() |
695-
ia instanceof AssignableRead and result = i.getGetter()
696-
or
697-
ia instanceof AssignableWrite and result = i.getSetter()
698-
)
711+
override Accessor getReadTarget() {
712+
this instanceof AssignableRead and result = this.getIndexer().getGetter()
713+
}
714+
715+
override Accessor getWriteTarget() {
716+
this instanceof AssignableWrite and result = this.getIndexer().getSetter()
699717
}
700718

701719
override Expr getArgument(int i) {
@@ -770,7 +788,7 @@ class ExtensionPropertyCall extends PropertyCall {
770788
* ```
771789
*/
772790
class EventCall extends AccessorCall, EventAccessExpr {
773-
override EventAccessor getTarget() {
791+
override EventAccessor getWriteTarget() {
774792
exists(Event e, AddOrRemoveEventExpr aoree |
775793
e = this.getEvent() and
776794
aoree.getLValue() = this

0 commit comments

Comments
 (0)