Skip to content

Commit d5af71a

Browse files
authored
Merge pull request #16647 from michaelnebel/csharp/idempotentsummarygeneration
C#: Make summary generation idempotent.
2 parents 456c046 + 546b260 commit d5af71a

File tree

13 files changed

+133
-105
lines changed

13 files changed

+133
-105
lines changed

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

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ private import semmle.code.csharp.frameworks.system.Collections
1010
private import semmle.code.csharp.frameworks.system.collections.Generic
1111

1212
/**
13-
* Gets a source declaration of callable `c` that has a body or has
14-
* a flow summary.
13+
* Gets a source declaration of callable `c` that has a body and is
14+
* defined in source.
1515
*/
1616
Callable getCallableForDataFlow(Callable c) {
1717
result = c.getUnboundDeclaration() and
@@ -269,13 +269,19 @@ class NonDelegateDataFlowCall extends DataFlowCall, TNonDelegateCall {
269269
override DataFlowCallable getARuntimeTarget() {
270270
result.asCallable() = getCallableForDataFlow(dc.getADynamicTarget())
271271
or
272-
exists(Callable c, boolean static |
273-
result.asSummarizedCallable() = c and
274-
c = this.getATarget(static)
272+
// Only use summarized callables with generated summaries in case
273+
// we are not able to dispatch to a source declaration.
274+
exists(FlowSummary::SummarizedCallable sc, boolean static |
275+
result.asSummarizedCallable() = sc and
276+
sc = this.getATarget(static) and
277+
not (
278+
sc.applyGeneratedModel() and
279+
dc.getADynamicTarget().getUnboundDeclaration().getFile().fromSource()
280+
)
275281
|
276282
static = false
277283
or
278-
static = true and not c instanceof RuntimeCallable
284+
static = true and not sc instanceof RuntimeCallable
279285
)
280286
}
281287

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -556,9 +556,9 @@ private predicate interpretNeutral(UnboundCallable c, string kind, string proven
556556
private class SummarizedCallableAdapter extends SummarizedCallable {
557557
SummarizedCallableAdapter() {
558558
exists(Provenance provenance | interpretSummary(this, _, _, _, provenance, _) |
559-
not this.hasBody()
559+
not this.fromSource()
560560
or
561-
this.hasBody() and provenance.isManual()
561+
this.fromSource() and provenance.isManual()
562562
)
563563
}
564564

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.cs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -200,31 +200,25 @@ void M2()
200200
void M3()
201201
{
202202
var o1 = new object();
203-
Sink(MixedFlowArgs(o1, null));
203+
Sink(Library.MixedFlowArgs(o1, null));
204204

205205
var o2 = new object();
206-
Sink(MixedFlowArgs(null, o2));
206+
Sink(Library.MixedFlowArgs(null, o2));
207207
}
208208

209209
void M4()
210210
{
211211
var o1 = new object();
212-
Sink(GeneratedFlowWithGeneratedNeutral(o1));
212+
Sink(Library.GeneratedFlowWithGeneratedNeutral(o1));
213213

214214
var o2 = new object();
215-
Sink(GeneratedFlowWithManualNeutral(o2)); // no flow because the modelled method has a manual neutral summary model
215+
Sink(Library.GeneratedFlowWithManualNeutral(o2)); // no flow because the modelled method has a manual neutral summary model
216216
}
217217

218218
object GeneratedFlow(object o) => throw null;
219219

220220
object GeneratedFlowArgs(object o1, object o2) => throw null;
221221

222-
object MixedFlowArgs(object o1, object o2) => throw null;
223-
224-
object GeneratedFlowWithGeneratedNeutral(object o) => throw null;
225-
226-
object GeneratedFlowWithManualNeutral(object o) => throw null;
227-
228222
static void Sink(object o) { }
229223
}
230224

@@ -268,4 +262,33 @@ void M1(MyInlineArray a)
268262

269263
static void Sink(object o) { }
270264
}
265+
266+
public class J
267+
{
268+
public virtual object Prop1 { get; }
269+
270+
public virtual void SetProp1(object o) => throw null;
271+
272+
public virtual object Prop2 { get; }
273+
274+
public virtual void SetProp2(object o) => throw null;
275+
276+
void M1()
277+
{
278+
var j = new object();
279+
SetProp1(j);
280+
// flow as there is a manual summary.
281+
Sink(this.Prop1);
282+
}
283+
284+
void M2()
285+
{
286+
var j = new object();
287+
SetProp2(j);
288+
// no flow as there is only a generated summary and source code is available.
289+
Sink(this.Prop2);
290+
}
291+
292+
static void Sink(object o) { }
293+
}
271294
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
3+
namespace My.Qltest
4+
{
5+
public class Library
6+
{
7+
public static object MixedFlowArgs(object o1, object o2) => throw null;
8+
9+
public static object GeneratedFlowWithGeneratedNeutral(object o) => throw null;
10+
11+
public static object GeneratedFlowWithManualNeutral(object o) => throw null;
12+
13+
public static object StepArgReturnGenerated(object x) => throw null;
14+
15+
public static object StepArgReturnGeneratedIgnored(object x) => throw null;
16+
}
17+
}
Binary file not shown.

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.expected

Lines changed: 46 additions & 34 deletions
Large diffs are not rendered by default.

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ext.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,20 @@ extensions:
2727
- ["My.Qltest", "G", false, "GeneratedFlow", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
2828
- ["My.Qltest", "G", false, "GeneratedFlowArgs", "(System.Object,System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
2929
- ["My.Qltest", "G", false, "GeneratedFlowArgs", "(System.Object,System.Object)", "", "Argument[1]", "ReturnValue", "value", "df-generated"]
30-
- ["My.Qltest", "G", false, "MixedFlowArgs", "(System.Object,System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
31-
- ["My.Qltest", "G", false, "MixedFlowArgs", "(System.Object,System.Object)", "", "Argument[1]", "ReturnValue", "value", "manual"]
32-
- ["My.Qltest", "G", false, "GeneratedFlowWithGeneratedNeutral", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
33-
- ["My.Qltest", "G", false, "GeneratedFlowWithManualNeutral", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
30+
- ["My.Qltest", "Library", false, "MixedFlowArgs", "(System.Object,System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
31+
- ["My.Qltest", "Library", false, "MixedFlowArgs", "(System.Object,System.Object)", "", "Argument[1]", "ReturnValue", "value", "manual"]
32+
- ["My.Qltest", "Library", false, "GeneratedFlowWithGeneratedNeutral", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
33+
- ["My.Qltest", "Library", false, "GeneratedFlowWithManualNeutral", "(System.Object)", "", "Argument[0]", "ReturnValue", "value", "df-generated"]
3434
- ["My.Qltest", "HE", false, "ExtensionMethod", "(My.Qltest.HI)", "", "Argument[0]", "ReturnValue", "value", "manual"]
3535
- ["My.Qltest", "I", false, "GetFirst", "(My.Qltest.MyInlineArray)", "", "Argument[0].Element", "ReturnValue", "value", "manual"]
36+
- ["My.Qltest", "J", false, "get_Prop1", "()", "", "Argument[this]", "ReturnValue", "value", "manual"]
37+
- ["My.Qltest", "J", false, "SetProp1", "(System.Object)", "", "Argument[0]", "Argument[this]", "value", "manual"]
38+
- ["My.Qltest", "J", false, "get_Prop2", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"]
39+
- ["My.Qltest", "J", false, "SetProp2", "(System.Object)", "", "Argument[0]", "Argument[this]", "value", "manual"]
3640
- addsTo:
3741
pack: codeql/csharp-all
3842
extensible: neutralModel
3943
# "namespace", "type", "name", "signature", "kind", "provenance"
4044
data:
41-
- ["My.Qltest", "G", "GeneratedFlowWithGeneratedNeutral", "(System.Object)", "summary", "df-generated"]
42-
- ["My.Qltest", "G", "GeneratedFlowWithManualNeutral", "(System.Object)", "summary", "manual"]
45+
- ["My.Qltest", "Library", "GeneratedFlowWithGeneratedNeutral", "(System.Object)", "summary", "df-generated"]
46+
- ["My.Qltest", "Library", "GeneratedFlowWithManualNeutral", "(System.Object)", "summary", "manual"]

csharp/ql/test/library-tests/dataflow/external-models/ExternalFlow.ql

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,6 @@ module TaintConfig implements DataFlow::ConfigSig {
2020

2121
module Taint = TaintTracking::Global<TaintConfig>;
2222

23-
/**
24-
* Emulate that methods with summaries do not have a body.
25-
* This is relevant for dataflow analysis using summaries with a generated like
26-
* provenance as generated summaries are only applied, if a
27-
* callable does not have a body.
28-
*/
29-
private class MethodsWithGeneratedModels extends Method {
30-
MethodsWithGeneratedModels() {
31-
this.hasFullyQualifiedName("My.Qltest", "G",
32-
["MixedFlowArgs", "GeneratedFlowWithGeneratedNeutral", "GeneratedFlowWithManualNeutral"])
33-
}
34-
35-
override predicate hasBody() { none() }
36-
}
37-
3823
from Taint::PathNode source, Taint::PathNode sink
3924
where Taint::flowPath(source, sink)
4025
select sink, source, sink, "$@", source, source.toString()

csharp/ql/test/library-tests/dataflow/external-models/Steps.cs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ void Foo()
4444
new Sub().StepOverride("string");
4545

4646
object arg4 = new object();
47-
this.StepArgQualGenerated(arg4);
48-
47+
Library.StepArgReturnGenerated(arg4);
48+
4949
object arg5 = new object();
50-
this.StepArgQualGeneratedIgnored(arg5);
50+
Library.StepArgReturnGeneratedIgnored(arg5);
5151
}
5252

5353
object StepArgRes(object x) { return null; }
@@ -56,10 +56,6 @@ void StepArgArg(object @in, object @out) { }
5656

5757
void StepArgQual(object x) { }
5858

59-
void StepArgQualGenerated(object x) { }
60-
61-
void StepArgQualGeneratedIgnored(object x) { }
62-
6359
object StepQualRes() { return null; }
6460

6561
void StepQualArg(object @out) { }

csharp/ql/test/library-tests/dataflow/external-models/steps.expected

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ summaryThroughStep
1111
| Steps.cs:41:29:41:29 | 0 | Steps.cs:41:13:41:30 | call to method StepGeneric | true |
1212
| Steps.cs:42:30:42:34 | false | Steps.cs:42:13:42:35 | call to method StepGeneric2<Boolean> | true |
1313
| Steps.cs:44:36:44:43 | "string" | Steps.cs:44:13:44:44 | call to method StepOverride | true |
14-
| Steps.cs:47:39:47:42 | access to local variable arg4 | Steps.cs:47:13:47:16 | [post] this access | false |
14+
| Steps.cs:47:44:47:47 | access to local variable arg4 | Steps.cs:47:13:47:48 | call to method StepArgReturnGenerated | false |
1515
summaryGetterStep
16-
| Steps.cs:28:13:28:16 | this access | Steps.cs:28:13:28:34 | call to method StepFieldGetter | Steps.cs:67:13:67:17 | field Field |
17-
| Steps.cs:32:13:32:16 | this access | Steps.cs:32:13:32:37 | call to method StepPropertyGetter | Steps.cs:73:13:73:20 | property Property |
16+
| Steps.cs:28:13:28:16 | this access | Steps.cs:28:13:28:34 | call to method StepFieldGetter | Steps.cs:63:13:63:17 | field Field |
17+
| Steps.cs:32:13:32:16 | this access | Steps.cs:32:13:32:37 | call to method StepPropertyGetter | Steps.cs:69:13:69:20 | property Property |
1818
| Steps.cs:36:13:36:16 | this access | Steps.cs:36:13:36:36 | call to method StepElementGetter | file://:0:0:0:0 | element |
1919
summarySetterStep
20-
| Steps.cs:30:34:30:34 | 0 | Steps.cs:30:13:30:16 | [post] this access | Steps.cs:67:13:67:17 | field Field |
21-
| Steps.cs:34:37:34:37 | 0 | Steps.cs:34:13:34:16 | [post] this access | Steps.cs:73:13:73:20 | property Property |
20+
| Steps.cs:30:34:30:34 | 0 | Steps.cs:30:13:30:16 | [post] this access | Steps.cs:63:13:63:17 | field Field |
21+
| Steps.cs:34:37:34:37 | 0 | Steps.cs:34:13:34:16 | [post] this access | Steps.cs:69:13:69:20 | property Property |
2222
| Steps.cs:38:36:38:36 | 0 | Steps.cs:38:13:38:16 | [post] this access | file://:0:0:0:0 | element |

csharp/ql/test/library-tests/dataflow/external-models/steps.ext.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ extensions:
1818
- ["My.Qltest", "C+Generic<T,U>", false, "StepGeneric", "(T)", "", "Argument[0]", "ReturnValue", "value", "manual"]
1919
- ["My.Qltest", "C+Generic<T,U>", false, "StepGeneric2<S>", "(S)", "", "Argument[0]", "ReturnValue", "value", "manual"]
2020
- ["My.Qltest", "C+Base<T>", true, "StepOverride", "(T)", "", "Argument[0]", "ReturnValue", "value", "manual"]
21-
- ["My.Qltest", "C", false, "StepArgQualGenerated", "(System.Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
22-
- ["My.Qltest", "C", false, "StepArgQualGeneratedIgnored", "(System.Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"]
21+
- ["My.Qltest", "Library", false, "StepArgReturnGenerated", "(System.Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
22+
- ["My.Qltest", "Library", false, "StepArgReturnGeneratedIgnored", "(System.Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"]
2323
- addsTo:
2424
pack: codeql/csharp-all
2525
extensible: neutralModel
2626
# "namespace", "type", "name", "signature", "kind", "provenance"
2727
data:
28-
- ["My.Qltest", "C", "StepArgQualGenerated", "(System.Object)", "summary", "df-generated"]
29-
- ["My.Qltest", "C", "StepArgQualGeneratedIgnored", "(System.Object)", "summary", "manual"]
28+
- ["My.Qltest", "Library", "StepArgReturnGenerated", "(System.Object)", "summary", "df-generated"]
29+
- ["My.Qltest", "Library", "StepArgReturnGeneratedIgnored", "(System.Object)", "summary", "manual"]
3030

csharp/ql/test/library-tests/dataflow/external-models/steps.ql

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,6 @@ import semmle.code.csharp.dataflow.FlowSummary
66
import semmle.code.csharp.dataflow.internal.DataFlowDispatch as DataFlowDispatch
77
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
88

9-
/**
10-
* Emulate that methods with summaries do not have a body.
11-
* This is relevant for dataflow analysis using summaries with a generated like
12-
* provenance as generated summaries are only applied, if a
13-
* callable does not have a body.
14-
*/
15-
private class StepArgQualGenerated extends Method {
16-
StepArgQualGenerated() {
17-
exists(string name |
18-
this.hasFullyQualifiedName("My.Qltest", "C", name) and name.matches("StepArgQualGenerated%")
19-
)
20-
}
21-
22-
override predicate hasBody() { none() }
23-
}
24-
259
query predicate summaryThroughStep(
2610
DataFlow::Node node1, DataFlow::Node node2, boolean preservesValue
2711
) {

csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,3 +575,4 @@ public class DImpl : D
575575
public override string Prop { get { return tainted; } }
576576
}
577577
}
578+

0 commit comments

Comments
 (0)