diff --git a/csharp/ql/lib/change-notes/2024-02-21-getonly-properties.md b/csharp/ql/lib/change-notes/2024-02-21-getonly-properties.md new file mode 100644 index 000000000000..3e8940a44e6d --- /dev/null +++ b/csharp/ql/lib/change-notes/2024-02-21-getonly-properties.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* C#: Data flow via get only properties like `public object Obj { get; }` is now captured by the data flow library. diff --git a/csharp/ql/lib/semmle/code/csharp/Property.qll b/csharp/ql/lib/semmle/code/csharp/Property.qll index bb127b48f2a3..bdeb9832d3f1 100644 --- a/csharp/ql/lib/semmle/code/csharp/Property.qll +++ b/csharp/ql/lib/semmle/code/csharp/Property.qll @@ -127,6 +127,13 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper properties(this, _, _, getTypeRef(result), _) } + private predicate isAutoPartial() { + this.fromSource() and + not this.isExtern() and + not this.isAbstract() and + not this.getAnAccessor().hasBody() + } + /** * Holds if this property is automatically implemented. For example, `P1` * on line 2 is automatically implemented, while `P2` on line 5 is not in @@ -147,11 +154,22 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper * code. */ predicate isAutoImplemented() { - this.fromSource() and - this.isReadWrite() and - not this.isExtern() and - not this.isAbstract() and - not this.getAnAccessor().hasBody() + this.isAutoPartial() and + this.isReadWrite() + } + + /** + * Holds if this property is automatically implemented and read-only. For + * example, `P1` on line 2 is automatically implemented and read-only + * ```csharp + * class C { + * public int P1 { get; } + * } + * ``` + */ + predicate isAutoImplementedReadOnly() { + this.isAutoPartial() and + this.isReadOnly() } override Property getUnboundDeclaration() { properties(this, _, _, _, result) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index af2c63540808..0b97a268d5d6 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -1899,6 +1899,8 @@ class FieldOrProperty extends Assignable, Modifiable { ( p.isAutoImplemented() or + p.isAutoImplementedReadOnly() + or p.matchesHandle(any(CIL::TrivialProperty tp)) or p.getDeclaringType() instanceof AnonymousClass diff --git a/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected b/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected index 70faa61bd731..fc211507a1ce 100644 --- a/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/constructors/ConstructorFlow.expected @@ -64,6 +64,20 @@ edges | Constructors.cs:112:25:112:27 | access to local variable o31 : Object | Constructors.cs:112:18:112:28 | object creation of type C3 : C3 [parameter o31param] : Object | provenance | | | Constructors.cs:113:14:113:15 | access to local variable c3 : C3 [parameter o31param] : Object | Constructors.cs:106:32:106:39 | this : C3 [parameter o31param] : Object | provenance | | | Constructors.cs:113:14:113:15 | access to local variable c3 : C3 [parameter o31param] : Object | Constructors.cs:113:14:113:21 | access to property Obj31 | provenance | | +| Constructors.cs:121:26:121:28 | oc1 : Object | Constructors.cs:123:20:123:22 | access to parameter oc1 : Object | provenance | | +| Constructors.cs:121:38:121:40 | oc2 : Object | Constructors.cs:124:20:124:22 | access to parameter oc2 : Object | provenance | | +| Constructors.cs:123:20:123:22 | access to parameter oc1 : Object | Constructors.cs:123:13:123:16 | [post] this access : C4 [property Obj1] : Object | provenance | | +| Constructors.cs:124:20:124:22 | access to parameter oc2 : Object | Constructors.cs:124:13:124:16 | [post] this access : C4 [property Obj2] : Object | provenance | | +| Constructors.cs:130:18:130:34 | call to method Source : Object | Constructors.cs:132:25:132:26 | access to local variable o1 : Object | provenance | | +| Constructors.cs:131:18:131:34 | call to method Source : Object | Constructors.cs:132:29:132:30 | access to local variable o2 : Object | provenance | | +| Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj1] : Object | Constructors.cs:133:14:133:15 | access to local variable c4 : C4 [property Obj1] : Object | provenance | | +| Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj2] : Object | Constructors.cs:134:14:134:15 | access to local variable c4 : C4 [property Obj2] : Object | provenance | | +| Constructors.cs:132:25:132:26 | access to local variable o1 : Object | Constructors.cs:121:26:121:28 | oc1 : Object | provenance | | +| Constructors.cs:132:25:132:26 | access to local variable o1 : Object | Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj1] : Object | provenance | | +| Constructors.cs:132:29:132:30 | access to local variable o2 : Object | Constructors.cs:121:38:121:40 | oc2 : Object | provenance | | +| Constructors.cs:132:29:132:30 | access to local variable o2 : Object | Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj2] : Object | provenance | | +| Constructors.cs:133:14:133:15 | access to local variable c4 : C4 [property Obj1] : Object | Constructors.cs:133:14:133:20 | access to property Obj1 | provenance | | +| Constructors.cs:134:14:134:15 | access to local variable c4 : C4 [property Obj2] : Object | Constructors.cs:134:14:134:20 | access to property Obj2 | provenance | | nodes | Constructors.cs:5:24:5:25 | [post] this access : C_no_ctor [field s1] : Object | semmle.label | [post] this access : C_no_ctor [field s1] : Object | | Constructors.cs:5:29:5:45 | call to method Source : Object | semmle.label | call to method Source : Object | @@ -134,6 +148,22 @@ nodes | Constructors.cs:112:25:112:27 | access to local variable o31 : Object | semmle.label | access to local variable o31 : Object | | Constructors.cs:113:14:113:15 | access to local variable c3 : C3 [parameter o31param] : Object | semmle.label | access to local variable c3 : C3 [parameter o31param] : Object | | Constructors.cs:113:14:113:21 | access to property Obj31 | semmle.label | access to property Obj31 | +| Constructors.cs:121:26:121:28 | oc1 : Object | semmle.label | oc1 : Object | +| Constructors.cs:121:38:121:40 | oc2 : Object | semmle.label | oc2 : Object | +| Constructors.cs:123:13:123:16 | [post] this access : C4 [property Obj1] : Object | semmle.label | [post] this access : C4 [property Obj1] : Object | +| Constructors.cs:123:20:123:22 | access to parameter oc1 : Object | semmle.label | access to parameter oc1 : Object | +| Constructors.cs:124:13:124:16 | [post] this access : C4 [property Obj2] : Object | semmle.label | [post] this access : C4 [property Obj2] : Object | +| Constructors.cs:124:20:124:22 | access to parameter oc2 : Object | semmle.label | access to parameter oc2 : Object | +| Constructors.cs:130:18:130:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Constructors.cs:131:18:131:34 | call to method Source : Object | semmle.label | call to method Source : Object | +| Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj1] : Object | semmle.label | object creation of type C4 : C4 [property Obj1] : Object | +| Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj2] : Object | semmle.label | object creation of type C4 : C4 [property Obj2] : Object | +| Constructors.cs:132:25:132:26 | access to local variable o1 : Object | semmle.label | access to local variable o1 : Object | +| Constructors.cs:132:29:132:30 | access to local variable o2 : Object | semmle.label | access to local variable o2 : Object | +| Constructors.cs:133:14:133:15 | access to local variable c4 : C4 [property Obj1] : Object | semmle.label | access to local variable c4 : C4 [property Obj1] : Object | +| Constructors.cs:133:14:133:20 | access to property Obj1 | semmle.label | access to property Obj1 | +| Constructors.cs:134:14:134:15 | access to local variable c4 : C4 [property Obj2] : Object | semmle.label | access to local variable c4 : C4 [property Obj2] : Object | +| Constructors.cs:134:14:134:20 | access to property Obj2 | semmle.label | access to property Obj2 | subpaths | Constructors.cs:64:37:64:37 | access to parameter o : Object | Constructors.cs:57:54:57:55 | o2 : Object | Constructors.cs:59:13:59:19 | SSA def(o1) : Object | Constructors.cs:64:27:64:34 | SSA def(o22param) : Object | | Constructors.cs:71:25:71:25 | access to local variable o : Object | Constructors.cs:41:26:41:26 | o : Object | Constructors.cs:41:32:41:34 | [post] this access : C1 [field Obj] : Object | Constructors.cs:71:18:71:26 | object creation of type C1 : C1 [field Obj] : Object | @@ -147,6 +177,8 @@ subpaths | Constructors.cs:101:14:101:15 | access to local variable c2 : C2 [parameter o22param] : Object | Constructors.cs:48:32:48:39 | this : C2 [parameter o22param] : Object | Constructors.cs:48:32:48:39 | access to parameter o22param : Object | Constructors.cs:101:14:101:21 | access to property Obj22 | | Constructors.cs:112:25:112:27 | access to local variable o31 : Object | Constructors.cs:104:28:104:35 | o31param : Object | Constructors.cs:104:28:104:35 | o31param : Object | Constructors.cs:112:18:112:28 | object creation of type C3 : C3 [parameter o31param] : Object | | Constructors.cs:113:14:113:15 | access to local variable c3 : C3 [parameter o31param] : Object | Constructors.cs:106:32:106:39 | this : C3 [parameter o31param] : Object | Constructors.cs:106:32:106:39 | access to parameter o31param : Object | Constructors.cs:113:14:113:21 | access to property Obj31 | +| Constructors.cs:132:25:132:26 | access to local variable o1 : Object | Constructors.cs:121:26:121:28 | oc1 : Object | Constructors.cs:123:13:123:16 | [post] this access : C4 [property Obj1] : Object | Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj1] : Object | +| Constructors.cs:132:29:132:30 | access to local variable o2 : Object | Constructors.cs:121:38:121:40 | oc2 : Object | Constructors.cs:124:13:124:16 | [post] this access : C4 [property Obj2] : Object | Constructors.cs:132:18:132:31 | object creation of type C4 : C4 [property Obj2] : Object | #select | Constructors.cs:15:18:15:19 | access to field s1 | Constructors.cs:5:29:5:45 | call to method Source : Object | Constructors.cs:15:18:15:19 | access to field s1 | $@ | Constructors.cs:5:29:5:45 | call to method Source : Object | call to method Source : Object | | Constructors.cs:33:18:33:19 | access to field s1 | Constructors.cs:21:29:21:45 | call to method Source : Object | Constructors.cs:33:18:33:19 | access to field s1 | $@ | Constructors.cs:21:29:21:45 | call to method Source : Object | call to method Source : Object | @@ -157,3 +189,5 @@ subpaths | Constructors.cs:93:14:93:21 | access to property Obj22 | Constructors.cs:91:21:91:37 | call to method Source : Object | Constructors.cs:93:14:93:21 | access to property Obj22 | $@ | Constructors.cs:91:21:91:37 | call to method Source : Object | call to method Source : Object | | Constructors.cs:101:14:101:21 | access to property Obj22 | Constructors.cs:99:21:99:37 | call to method Source : Object | Constructors.cs:101:14:101:21 | access to property Obj22 | $@ | Constructors.cs:99:21:99:37 | call to method Source : Object | call to method Source : Object | | Constructors.cs:113:14:113:21 | access to property Obj31 | Constructors.cs:111:19:111:35 | call to method Source : Object | Constructors.cs:113:14:113:21 | access to property Obj31 | $@ | Constructors.cs:111:19:111:35 | call to method Source : Object | call to method Source : Object | +| Constructors.cs:133:14:133:20 | access to property Obj1 | Constructors.cs:130:18:130:34 | call to method Source : Object | Constructors.cs:133:14:133:20 | access to property Obj1 | $@ | Constructors.cs:130:18:130:34 | call to method Source : Object | call to method Source : Object | +| Constructors.cs:134:14:134:20 | access to property Obj2 | Constructors.cs:131:18:131:34 | call to method Source : Object | Constructors.cs:134:14:134:20 | access to property Obj2 | $@ | Constructors.cs:131:18:131:34 | call to method Source : Object | call to method Source : Object | diff --git a/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs b/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs index f8a75953d086..af83b9896e28 100644 --- a/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs +++ b/csharp/ql/test/library-tests/dataflow/constructors/Constructors.cs @@ -113,6 +113,27 @@ public void M5() Sink(c3.Obj31); // $ hasValueFlow=6 } + public class C4 + { + public object Obj1 { get; init; } + public object Obj2 { get; } + + public C4(object oc1, object oc2) + { + Obj1 = oc1; + Obj2 = oc2; + } + } + + public void M6() + { + var o1 = Source(7); + var o2 = Source(8); + var c4 = new C4(o1, o2); + Sink(c4.Obj1); // $ hasValueFlow=7 + Sink(c4.Obj2); // $ hasValueFlow=8 + } + public static void Sink(object o) { } public static T Source(object source) => throw null;