Skip to content

Commit c18c35d

Browse files
committed
Merge branch 'main' into rust-improve-cfg
2 parents 6a5a505 + 2837d25 commit c18c35d

File tree

16 files changed

+239
-31
lines changed

16 files changed

+239
-31
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: breaking
3+
---
4+
* C#: Add support for MaD directly on properties and indexers using *attributes*. Using `Attribute.Getter` or `Attribute.Setter` in the model `ext` field applies the model to the getter or setter for properties and indexers. Prior to this change `Attribute` models unintentionally worked for property setters (if the property is decorated with the matching attribute). That is, a model that uses the `Attribute` feature directly on a property for a property setter needs to be changed to `Attribute.Setter`.

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

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@
2828
* types can be short names or fully qualified names (mixing these two options
2929
* is not allowed within a single signature).
3030
* 6. The `ext` column specifies additional API-graph-like edges. Currently
31-
* there are only two valid values: "" and "Attribute". The empty string has no
32-
* effect. "Attribute" applies if `name` and `signature` were left blank and
33-
* acts by selecting an element that is attributed with the attribute type
34-
* selected by the first 4 columns. This can be another member such as a field,
35-
* property, method, or parameter.
31+
* there are only a few valid values: "", "Attribute", "Attribute.Getter" and "Attribute.Setter".
32+
* The empty string has no effect. "Attribute" applies if `name` and `signature` were left blank
33+
* and acts by selecting an element (except for properties and indexers) that is attributed with
34+
* the attribute type selected by the first 4 columns. This can be another member such as
35+
* a field, method, or parameter. "Attribute.Getter" and "Attribute.Setter" work similar to
36+
* "Attribute", except that they can only be applied to properties and indexers.
37+
* "Attribute.Setter" selects the setter element of a property/indexer and "Attribute.Getter"
38+
* selects the getter.
3639
* 7. The `input` column specifies how data enters the element selected by the
3740
* first 6 columns, and the `output` column specifies how data leaves the
3841
* element selected by the first 6 columns. For sinks, an `input` can be either "",
@@ -96,6 +99,7 @@ private import FlowSummaryImpl::Private::External
9699
private import semmle.code.csharp.commons.QualifiedName
97100
private import semmle.code.csharp.dispatch.OverridableCallable
98101
private import semmle.code.csharp.frameworks.System
102+
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
99103
private import codeql.mad.ModelValidation as SharedModelVal
100104

101105
/**
@@ -194,8 +198,6 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
194198

195199
/** Provides a query predicate to check the MaD models for validation errors. */
196200
module ModelValidation {
197-
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
198-
199201
private predicate getRelevantAccessPath(string path) {
200202
summaryModel(_, _, _, _, _, _, path, _, _, _, _) or
201203
summaryModel(_, _, _, _, _, _, _, path, _, _, _) or
@@ -289,7 +291,7 @@ module ModelValidation {
289291
not signature.regexpMatch("|\\([a-zA-Z0-9_<>\\.\\+\\*,\\[\\]]*\\)") and
290292
result = "Dubious signature \"" + signature + "\" in " + pred + " model."
291293
or
292-
not ext.regexpMatch("|Attribute") and
294+
not ext = ["", "Attribute", "Attribute.Getter", "Attribute.Setter"] and
293295
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
294296
or
295297
invalidProvenance(provenance) and
@@ -406,6 +408,30 @@ Declaration interpretBaseDeclaration(string namespace, string type, string name,
406408
)
407409
}
408410

411+
pragma[inline]
412+
private Declaration interpretExt(Declaration d, ExtPath ext) {
413+
ext = "" and result = d
414+
or
415+
ext.getToken(0) = "Attribute" and
416+
(
417+
not exists(ext.getToken(1)) and
418+
result.(Attributable).getAnAttribute().getType() = d and
419+
not result instanceof Property and
420+
not result instanceof Indexer
421+
or
422+
exists(string accessor | accessor = ext.getToken(1) |
423+
result.(Accessor).getDeclaration().getAnAttribute().getType() = d and
424+
(
425+
result instanceof Getter and
426+
accessor = "Getter"
427+
or
428+
result instanceof Setter and
429+
accessor = "Setter"
430+
)
431+
)
432+
)
433+
}
434+
409435
/** Gets the source/sink/summary/neutral element corresponding to the supplied parameters. */
410436
pragma[nomagic]
411437
Declaration interpretElement(
@@ -425,12 +451,18 @@ Declaration interpretElement(
425451
)
426452
)
427453
|
428-
ext = "" and result = d
429-
or
430-
ext = "Attribute" and result.(Attributable).getAnAttribute().getType() = d
454+
result = interpretExt(d, ext)
431455
)
432456
}
433457

458+
private predicate relevantExt(string ext) {
459+
summaryModel(_, _, _, _, _, ext, _, _, _, _, _) or
460+
sourceModel(_, _, _, _, _, ext, _, _, _, _) or
461+
sinkModel(_, _, _, _, _, ext, _, _, _, _)
462+
}
463+
464+
private class ExtPath = AccessPathSyntax::AccessPath<relevantExt/1>::AccessPath;
465+
434466
private predicate parseSynthField(AccessPathToken c, string name) {
435467
c.getName() = "SyntheticField" and name = c.getAnArgument()
436468
}

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ void Foo()
1212

1313
object fieldWrite = new object();
1414
TaggedField = fieldWrite;
15+
16+
object propertyWrite = new object();
17+
TaggedPropertySetter = propertyWrite;
18+
19+
object indexerWrite = new object();
20+
this[0] = indexerWrite;
1521
}
1622

1723
object SinkMethod()
@@ -34,7 +40,21 @@ void TaggedSinkMethod(object x) { }
3440

3541
[SinkAttribute]
3642
object TaggedField;
43+
44+
[SinkPropertyAttribute]
45+
object TaggedPropertySetter { get; set; }
46+
47+
[SinkIndexerAttribute]
48+
object this[int index]
49+
{
50+
get { return null; }
51+
set { }
52+
}
3753
}
3854

3955
class SinkAttribute : System.Attribute { }
40-
}
56+
57+
class SinkPropertyAttribute : System.Attribute { }
58+
59+
class SinkIndexerAttribute : System.Attribute { }
60+
}

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

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@ void Foo()
1818
x = TaggedSrcField;
1919

2020
x = SrcTwoArg("", "");
21+
22+
x = TaggedSrcPropertyGetter;
23+
x = this[0];
2124
}
2225

23-
[SourceAttribute()]
26+
[SourceAttribute]
2427
void Tagged1(object taggedMethodParam)
2528
{
2629
}
2730

28-
void Tagged2([SourceAttribute()] object taggedSrcParam)
31+
void Tagged2([SourceAttribute] object taggedSrcParam)
2932
{
3033
}
3134

@@ -49,14 +52,20 @@ public override void SrcParam(object p) { }
4952

5053
void SrcArg(object src) { }
5154

52-
[SourceAttribute()]
55+
[SourceAttribute]
5356
object TaggedSrcMethod() { return null; }
5457

55-
[SourceAttribute()]
58+
[SourceAttribute]
5659
object TaggedSrcField;
5760

5861
object SrcTwoArg(string s1, string s2) { return null; }
62+
63+
[SourceAttribute]
64+
object TaggedSrcPropertyGetter { get; }
65+
66+
[SourceAttribute]
67+
object this[int i] => null;
5968
}
6069

6170
class SourceAttribute : System.Attribute { }
62-
}
71+
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ invalidModelRow
44
| Sinks.cs:11:13:11:41 | this access | file-content-store |
55
| Sinks.cs:11:30:11:40 | access to local variable argToTagged | file-content-store |
66
| Sinks.cs:14:27:14:36 | access to local variable fieldWrite | sql-injection |
7-
| Sinks.cs:20:20:20:22 | access to local variable res | js-injection |
8-
| Sinks.cs:27:20:27:25 | access to local variable resTag | html-injection |
7+
| Sinks.cs:17:36:17:48 | access to local variable propertyWrite | sql-injection |
8+
| Sinks.cs:20:23:20:34 | access to local variable indexerWrite | sql-injection |
9+
| Sinks.cs:26:20:26:22 | access to local variable res | js-injection |
10+
| Sinks.cs:33:20:33:25 | access to local variable resTag | html-injection |

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ extensions:
99
- ["My.Qltest", "SinkAttribute", false, "", "", "Attribute", "ReturnValue", "html-injection", "manual"]
1010
- ["My.Qltest", "SinkAttribute", false, "", "", "Attribute", "Argument", "file-content-store", "manual"]
1111
- ["My.Qltest", "SinkAttribute", false, "", "", "Attribute", "", "sql-injection", "manual"]
12+
- ["My.Qltest", "SinkPropertyAttribute", false, "", "", "Attribute.Setter", "Argument[0]", "sql-injection", "manual"]
13+
- ["My.Qltest", "SinkIndexerAttribute", false, "", "", "Attribute.Setter", "Argument[1]", "sql-injection", "manual"]

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ invalidModelRow
99
| Sources.cs:17:17:17:33 | call to method TaggedSrcMethod | local |
1010
| Sources.cs:18:17:18:30 | access to field TaggedSrcField | local |
1111
| Sources.cs:20:17:20:33 | call to method SrcTwoArg | local |
12-
| Sources.cs:24:14:24:20 | this | local |
13-
| Sources.cs:24:29:24:45 | taggedMethodParam | local |
14-
| Sources.cs:28:49:28:62 | taggedSrcParam | local |
15-
| Sources.cs:40:45:40:45 | p | local |
16-
| Sources.cs:47:50:47:50 | p | local |
17-
| Sources.cs:53:16:53:30 | this | local |
12+
| Sources.cs:22:17:22:39 | access to property TaggedSrcPropertyGetter | local |
13+
| Sources.cs:23:17:23:23 | access to indexer | local |
14+
| Sources.cs:27:14:27:20 | this | local |
15+
| Sources.cs:27:29:27:45 | taggedMethodParam | local |
16+
| Sources.cs:31:47:31:60 | taggedSrcParam | local |
17+
| Sources.cs:43:45:43:45 | p | local |
18+
| Sources.cs:50:50:50:50 | p | local |
19+
| Sources.cs:56:16:56:30 | this | local |

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ extensions:
1717
- ["My.Qltest", "SourceAttribute", false, "", "", "Attribute", "ReturnValue", "local", "manual"]
1818
- ["My.Qltest", "SourceAttribute", false, "", "", "Attribute", "Parameter", "local", "manual"]
1919
- ["My.Qltest", "SourceAttribute", false, "", "", "Attribute", "", "local", "manual"]
20-
- ["My.Qltest", "A", false, "SrcTwoArg", "(System.String,System.String)", "", "ReturnValue", "local", "manual"]
20+
- ["My.Qltest", "SourceAttribute", false, "", "", "Attribute.Getter", "ReturnValue", "local", "manual"]
21+
- ["My.Qltest", "A", false, "SrcTwoArg", "(System.String,System.String)", "", "ReturnValue", "local", "manual"]

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,14 @@ private module CaptureInput implements VariableCapture::InputSig<Location> {
147147
}
148148

149149
class Callable extends J::Callable {
150-
predicate isConstructor() { this instanceof Constructor }
150+
predicate isConstructor() {
151+
// InstanceInitializers are called from constructors and are equally likely
152+
// to capture variables for the purpose of field initialization, so we treat
153+
// them as constructors for the heuristic identification of whether to allow
154+
// this-to-this summaries.
155+
this instanceof Constructor or
156+
this instanceof InstanceInitializer
157+
}
151158
}
152159
}
153160

java/ql/test/library-tests/dataflow/capture/B.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,35 @@ void run() {
248248
sink(l.get(0)); // $ hasValueFlow=src
249249
sink(l2.get(0)); // $ hasValueFlow=src
250250
}
251+
252+
void testInstanceInitializer() {
253+
// Tests capture in the instance initializer ("<obinit>")
254+
String s = source("init");
255+
class MyLocal3 {
256+
String f = s;
257+
void run() {
258+
sink(this.f); // $ hasValueFlow=init
259+
}
260+
}
261+
new MyLocal3().run();
262+
}
263+
264+
void testConstructorIndirection() {
265+
// Tests capture in nested constructor call
266+
String s = source("init");
267+
class MyLocal4 {
268+
String f;
269+
MyLocal4() {
270+
this(42);
271+
}
272+
MyLocal4(int i) {
273+
f = s;
274+
}
275+
String get() {
276+
return this.f;
277+
}
278+
}
279+
sink(new MyLocal4().get()); // $ hasValueFlow=init
280+
sink(new MyLocal4(1).get()); // $ hasValueFlow=init
281+
}
251282
}

0 commit comments

Comments
 (0)