diff --git a/csharp/ql/lib/change-notes/2025-08-29-base-qualifier-dispatch.md b/csharp/ql/lib/change-notes/2025-08-29-base-qualifier-dispatch.md new file mode 100644 index 000000000000..780c88608106 --- /dev/null +++ b/csharp/ql/lib/change-notes/2025-08-29-base-qualifier-dispatch.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* A bug has been fixed in the data flow analysis, which means that flow through calls using the `base` qualifier may now be tracked more accurately. \ No newline at end of file diff --git a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll index 7de6c30eb13c..c61ad0f2a2a9 100644 --- a/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dispatch/Dispatch.qll @@ -270,6 +270,14 @@ private module Internal { hasOverrider(t, c) } + /** + * For `base` expressions, the extractor provides the type of the base + * class instead of the derived class; this predicate provides the latter. + */ + private Type getBaseAdjustedType(BaseAccess base) { + result = base.getEnclosingCallable().getDeclaringType() + } + abstract private class DispatchOverridableCall extends DispatchCallImpl { pragma[noinline] OverridableCallable getAStaticTargetExt() { @@ -360,7 +368,12 @@ private module Internal { private predicate contextArgHasType(DispatchCall ctx, Type t, boolean isExact) { exists(Expr arg, int i | this.relevantContext(ctx, i) and - t = getAPossibleType(arg, isExact) + ( + t = getBaseAdjustedType(arg) and isExact = false + or + not exists(getBaseAdjustedType(arg)) and + t = getAPossibleType(arg, isExact) + ) | ctx.getArgument(i) = arg or @@ -725,9 +738,7 @@ private module Internal { Type getType(boolean isExact) { result = this.getType() and - if - this instanceof ObjectCreation or - this instanceof BaseAccess + if this instanceof ObjectCreation or this instanceof BaseAccess then isExact = true else isExact = false } diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs index 9470b4536dc2..109be4aa9957 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.cs @@ -233,3 +233,33 @@ public interface InterfaceB { void Foo(A2 a, object o, bool cond); } + +public class A3 +{ + public virtual void M1(object o) + { + this.M2(o); + } + + public virtual void M2(object o) + { + Sink(o); // should not have flow + } + + public static void Sink(object o) + { + } +} + +public class A4 : A3 +{ + public override void M2(object o) + { + Sink(o); // should have flow + } + + private void CallM1() + { + base.M1(new object()); + } +} diff --git a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected index dfe1a951b277..955202f6afd3 100644 --- a/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/call-sensitivity/CallSensitivityFlow.expected @@ -59,6 +59,10 @@ edges | CallSensitivityFlow.cs:187:13:187:13 | access to local variable o : Object | CallSensitivityFlow.cs:188:14:188:14 | access to local variable o | provenance | | | CallSensitivityFlow.cs:187:17:187:30 | call to method CallMOut : Object | CallSensitivityFlow.cs:187:13:187:13 | access to local variable o : Object | provenance | | | CallSensitivityFlow.cs:205:40:205:40 | o : Object | CallSensitivityFlow.cs:208:18:208:18 | access to parameter o | provenance | | +| CallSensitivityFlow.cs:239:35:239:35 | o : Object | CallSensitivityFlow.cs:241:17:241:17 | access to parameter o : Object | provenance | | +| CallSensitivityFlow.cs:241:17:241:17 | access to parameter o : Object | CallSensitivityFlow.cs:256:36:256:36 | o : Object | provenance | | +| CallSensitivityFlow.cs:256:36:256:36 | o : Object | CallSensitivityFlow.cs:258:14:258:14 | access to parameter o | provenance | | +| CallSensitivityFlow.cs:263:17:263:28 | object creation of type Object : Object | CallSensitivityFlow.cs:239:35:239:35 | o : Object | provenance | | nodes | CallSensitivityFlow.cs:7:38:7:38 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:11:20:11:20 | access to parameter o : Object | semmle.label | access to parameter o : Object | @@ -132,6 +136,11 @@ nodes | CallSensitivityFlow.cs:188:14:188:14 | access to local variable o | semmle.label | access to local variable o | | CallSensitivityFlow.cs:205:40:205:40 | o : Object | semmle.label | o : Object | | CallSensitivityFlow.cs:208:18:208:18 | access to parameter o | semmle.label | access to parameter o | +| CallSensitivityFlow.cs:239:35:239:35 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:241:17:241:17 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| CallSensitivityFlow.cs:256:36:256:36 | o : Object | semmle.label | o : Object | +| CallSensitivityFlow.cs:258:14:258:14 | access to parameter o | semmle.label | access to parameter o | +| CallSensitivityFlow.cs:263:17:263:28 | object creation of type Object : Object | semmle.label | object creation of type Object : Object | subpaths | CallSensitivityFlow.cs:85:26:85:37 | object creation of type Object : Object | CallSensitivityFlow.cs:7:38:7:38 | o : Object | CallSensitivityFlow.cs:11:20:11:20 | access to parameter o : Object | CallSensitivityFlow.cs:85:14:85:44 | call to method FlowThrough | | CallSensitivityFlow.cs:105:26:105:37 | object creation of type Object : Object | CallSensitivityFlow.cs:7:38:7:38 | o : Object | CallSensitivityFlow.cs:11:20:11:20 | access to parameter o : Object | CallSensitivityFlow.cs:105:14:105:41 | call to method FlowThrough | @@ -156,3 +165,4 @@ subpaths | CallSensitivityFlow.cs:172:37:172:48 | object creation of type Object : Object | CallSensitivityFlow.cs:172:37:172:48 | object creation of type Object : Object | CallSensitivityFlow.cs:188:14:188:14 | access to local variable o | $@ | CallSensitivityFlow.cs:188:14:188:14 | access to local variable o | access to local variable o | | CallSensitivityFlow.cs:182:21:182:32 | object creation of type Object : Object | CallSensitivityFlow.cs:182:21:182:32 | object creation of type Object : Object | CallSensitivityFlow.cs:208:18:208:18 | access to parameter o | $@ | CallSensitivityFlow.cs:208:18:208:18 | access to parameter o | access to parameter o | | CallSensitivityFlow.cs:185:21:185:32 | object creation of type Object : Object | CallSensitivityFlow.cs:185:21:185:32 | object creation of type Object : Object | CallSensitivityFlow.cs:164:14:164:14 | access to parameter o | $@ | CallSensitivityFlow.cs:164:14:164:14 | access to parameter o | access to parameter o | +| CallSensitivityFlow.cs:263:17:263:28 | object creation of type Object : Object | CallSensitivityFlow.cs:263:17:263:28 | object creation of type Object : Object | CallSensitivityFlow.cs:258:14:258:14 | access to parameter o | $@ | CallSensitivityFlow.cs:258:14:258:14 | access to parameter o | access to parameter o |