Skip to content

Commit c698128

Browse files
committed
C#: Implement ContentSet
1 parent fbcb449 commit c698128

File tree

7 files changed

+254
-133
lines changed

7 files changed

+254
-133
lines changed

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

+105-73
Original file line numberDiff line numberDiff line change
@@ -908,19 +908,20 @@ private class Argument extends Expr {
908908
*
909909
* `postUpdate` indicates whether the store targets a post-update node.
910910
*/
911-
private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, boolean postUpdate) {
911+
private predicate fieldOrPropertyStore(Expr e, ContentSet c, Expr src, Expr q, boolean postUpdate) {
912912
exists(FieldOrProperty f |
913-
c = f.getContent() and
913+
c = f.getContentSet() and
914914
(
915915
f.isFieldLike() and
916916
f instanceof InstanceFieldOrProperty
917917
or
918918
exists(
919919
FlowSummaryImpl::Private::SummarizedCallableImpl sc,
920-
FlowSummaryImpl::Private::SummaryComponentStack input
920+
FlowSummaryImpl::Private::SummaryComponentStack input, ContentSet readSet
921921
|
922922
sc.propagatesFlow(input, _, _, _) and
923-
input.contains(FlowSummaryImpl::Private::SummaryComponent::content(f.getContent()))
923+
input.contains(FlowSummaryImpl::Private::SummaryComponent::content(readSet)) and
924+
c.getAStoreContent() = readSet.getAReadContent()
924925
)
925926
)
926927
|
@@ -970,28 +971,13 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool
970971
)
971972
}
972973

973-
/** Holds if property `p1` overrides or implements source declaration property `p2`. */
974-
private predicate overridesOrImplementsSourceDecl(Property p1, Property p2) {
975-
p1.getOverridee*().getUnboundDeclaration() = p2
976-
or
977-
p1.getAnUltimateImplementee().getUnboundDeclaration() = p2
978-
}
979-
980974
/**
981975
* Holds if `e2` is an expression that reads field or property `c` from
982-
* expression `e1`. This takes overriding into account for properties written
983-
* from library code.
976+
* expression `e1`.
984977
*/
985-
private predicate fieldOrPropertyRead(Expr e1, Content c, FieldOrPropertyRead e2) {
978+
private predicate fieldOrPropertyRead(Expr e1, ContentSet c, FieldOrPropertyRead e2) {
986979
e1 = e2.getQualifier() and
987-
exists(FieldOrProperty ret | c = ret.getContent() |
988-
ret = e2.getTarget()
989-
or
990-
exists(Property target |
991-
target.getGetter() = e2.(PropertyCall).getARuntimeTarget() and
992-
overridesOrImplementsSourceDecl(target, ret)
993-
)
994-
)
980+
c = e2.getTarget().(FieldOrProperty).getContentSet()
995981
}
996982

997983
/**
@@ -1208,6 +1194,11 @@ private module Cached {
12081194
} or
12091195
TCapturedVariableContent(VariableCapture::CapturedVariable v)
12101196

1197+
cached
1198+
newtype TContentSet =
1199+
TSingletonContent(Content c) { not c instanceof PropertyContent } or
1200+
TPropertyContentSet(Property p) { p.isUnboundDeclaration() }
1201+
12111202
cached
12121203
newtype TContentApprox =
12131204
TFieldApproxContent(string firstChar) { firstChar = approximateFieldContent(_) } or
@@ -2076,10 +2067,10 @@ class FieldOrProperty extends Assignable, Modifiable {
20762067
}
20772068

20782069
/** Gets the content that matches this field or property. */
2079-
Content getContent() {
2080-
result.(FieldContent).getField() = this.getUnboundDeclaration()
2070+
ContentSet getContentSet() {
2071+
result.isField(this.getUnboundDeclaration())
20812072
or
2082-
result.(PropertyContent).getProperty() = this.getUnboundDeclaration()
2073+
result.isProperty(this.getUnboundDeclaration())
20832074
}
20842075
}
20852076

@@ -2209,8 +2200,8 @@ private class StoreStepConfiguration extends ControlFlowReachabilityConfiguratio
22092200
}
22102201

22112202
pragma[nomagic]
2212-
private PropertyContent getResultContent() {
2213-
result.getProperty() = any(SystemThreadingTasksTaskTClass c_).getResultProperty()
2203+
private ContentSet getResultContent() {
2204+
result.isProperty(any(SystemThreadingTasksTaskTClass c_).getResultProperty())
22142205
}
22152206

22162207
private predicate primaryConstructorParameterStore(
@@ -2224,17 +2215,16 @@ private predicate primaryConstructorParameterStore(
22242215
)
22252216
}
22262217

2227-
/**
2228-
* Holds if data can flow from `node1` to `node2` via an assignment to
2229-
* content `c`.
2230-
*/
2231-
predicate storeStep(Node node1, ContentSet c, Node node2) {
2218+
pragma[nomagic]
2219+
private predicate recordParameter(RecordType t, Parameter p, string name) {
2220+
p.getName() = name and p.getCallable().getDeclaringType() = t
2221+
}
2222+
2223+
private predicate storeContentStep(Node node1, Content c, Node node2) {
22322224
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
22332225
hasNodePath(x, node1, node) and
22342226
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
22352227
|
2236-
fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
2237-
or
22382228
arrayStore(_, node1.asExpr(), node.getExpr(), postUpdate) and c instanceof ElementContent
22392229
)
22402230
or
@@ -2255,26 +2245,59 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
22552245
c instanceof ElementContent
22562246
)
22572247
or
2248+
primaryConstructorParameterStore(node1, c, node2)
2249+
or
2250+
exists(Parameter p, DataFlowCallable callable |
2251+
node1 = TExplicitParameterNode(p, callable) and
2252+
node2 = TPrimaryConstructorThisAccessNode(p, true, callable) and
2253+
not recordParameter(_, p, _) and
2254+
c.(PrimaryConstructorParameterContent).getParameter() = p
2255+
)
2256+
or
2257+
VariableCapture::storeStep(node1, c, node2)
2258+
}
2259+
2260+
pragma[nomagic]
2261+
private predicate recordProperty(RecordType t, ContentSet c, string name) {
2262+
exists(Property p |
2263+
c.isProperty(p) and
2264+
p.getName() = name and
2265+
p.getDeclaringType() = t
2266+
)
2267+
}
2268+
2269+
/**
2270+
* Holds if data can flow from `node1` to `node2` via an assignment to
2271+
* content `c`.
2272+
*/
2273+
predicate storeStep(Node node1, ContentSet c, Node node2) {
2274+
exists(Content cont |
2275+
storeContentStep(node1, cont, node2) and
2276+
c.isSingleton(cont)
2277+
)
2278+
or
2279+
exists(StoreStepConfiguration x, ExprNode node, boolean postUpdate |
2280+
hasNodePath(x, node1, node) and
2281+
if postUpdate = true then node = node2.(PostUpdateNode).getPreUpdateNode() else node = node2
2282+
|
2283+
fieldOrPropertyStore(_, c, node1.asExpr(), node.getExpr(), postUpdate)
2284+
)
2285+
or
22582286
exists(Expr e |
22592287
e = node1.asExpr() and
22602288
node2.(AsyncReturnNode).getExpr() = e and
22612289
c = getResultContent()
22622290
)
22632291
or
2264-
primaryConstructorParameterStore(node1, c, node2)
2265-
or
2266-
exists(Parameter p, DataFlowCallable callable |
2292+
exists(Parameter p, DataFlowCallable callable, RecordType t, string name |
22672293
node1 = TExplicitParameterNode(p, callable) and
22682294
node2 = TPrimaryConstructorThisAccessNode(p, true, callable) and
2269-
if p.getCallable().getDeclaringType() instanceof RecordType
2270-
then c.(PropertyContent).getProperty().getName() = p.getName()
2271-
else c.(PrimaryConstructorParameterContent).getParameter() = p
2295+
recordParameter(t, p, name) and
2296+
recordProperty(t, c, name)
22722297
)
22732298
or
22742299
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
22752300
node2.(FlowSummaryNode).getSummaryNode())
2276-
or
2277-
VariableCapture::storeStep(node1, c, node2)
22782301
}
22792302

22802303
private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration {
@@ -2342,14 +2365,8 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
23422365
}
23432366
}
23442367

2345-
/**
2346-
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
2347-
*/
2348-
predicate readStep(Node node1, ContentSet c, Node node2) {
2368+
private predicate readContentStep(Node node1, Content c, Node node2) {
23492369
exists(ReadStepConfiguration x |
2350-
hasNodePath(x, node1, node2) and
2351-
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
2352-
or
23532370
hasNodePath(x, node1, node2) and
23542371
arrayRead(node1.asExpr(), node2.asExpr()) and
23552372
c instanceof ElementContent
@@ -2361,10 +2378,6 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
23612378
c instanceof ElementContent
23622379
)
23632380
or
2364-
hasNodePath(x, node1, node2) and
2365-
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
2366-
c = getResultContent()
2367-
or
23682381
node1 =
23692382
any(InstanceParameterAccessPreNode n |
23702383
n.getUnderlyingControlFlowNode() = node2.(ExprNode).getControlFlowNode() and
@@ -2402,10 +2415,41 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
24022415
)
24032416
)
24042417
or
2418+
VariableCapture::readStep(node1, c, node2)
2419+
}
2420+
2421+
/**
2422+
* Holds if data can flow from `node1` to `node2` via a read of content `c`.
2423+
*/
2424+
predicate readStep(Node node1, ContentSet c, Node node2) {
2425+
exists(Content cont |
2426+
readContentStep(node1, cont, node2) and
2427+
c.isSingleton(cont)
2428+
)
2429+
or
2430+
exists(ReadStepConfiguration x | hasNodePath(x, node1, node2) |
2431+
fieldOrPropertyRead(node1.asExpr(), c, node2.asExpr())
2432+
or
2433+
node2.asExpr().(AwaitExpr).getExpr() = node1.asExpr() and
2434+
c = getResultContent()
2435+
)
2436+
or
24052437
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
24062438
node2.(FlowSummaryNode).getSummaryNode())
2439+
}
2440+
2441+
private predicate clearsCont(Node n, Content c) {
2442+
exists(Argument a, Struct s, Field f |
2443+
a = n.(PostUpdateNode).getPreUpdateNode().asExpr() and
2444+
a.getType() = s and
2445+
f = s.getAField() and
2446+
c.(FieldContent).getField() = f.getUnboundDeclaration() and
2447+
not f.isRef()
2448+
)
24072449
or
2408-
VariableCapture::readStep(node1, c, node2)
2450+
n = any(PostUpdateNode n1 | primaryConstructorParameterStore(_, c, n1)).getPreUpdateNode()
2451+
or
2452+
VariableCapture::clearsContent(n, c)
24092453
}
24102454

24112455
/**
@@ -2414,6 +2458,11 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
24142458
* in `x.f = newValue`.
24152459
*/
24162460
predicate clearsContent(Node n, ContentSet c) {
2461+
exists(Content cont |
2462+
clearsCont(n, cont) and
2463+
c.isSingleton(cont)
2464+
)
2465+
or
24172466
fieldOrPropertyStore(_, c, _, n.asExpr(), true)
24182467
or
24192468
fieldOrPropertyStore(_, c, _, n.(ObjectInitializerNode).getInitializer(), false)
@@ -2424,20 +2473,8 @@ predicate clearsContent(Node n, ContentSet c) {
24242473
oi = we.getInitializer() and
24252474
n.asExpr() = oi and
24262475
f = oi.getAMemberInitializer().getInitializedMember() and
2427-
c = f.getContent()
2428-
)
2429-
or
2430-
exists(Argument a, Struct s, Field f |
2431-
a = n.(PostUpdateNode).getPreUpdateNode().asExpr() and
2432-
a.getType() = s and
2433-
f = s.getAField() and
2434-
c.(FieldContent).getField() = f.getUnboundDeclaration() and
2435-
not f.isRef()
2476+
c = f.getContentSet()
24362477
)
2437-
or
2438-
n = any(PostUpdateNode n1 | primaryConstructorParameterStore(_, c, n1)).getPreUpdateNode()
2439-
or
2440-
VariableCapture::clearsContent(n, c)
24412478
}
24422479

24432480
/**
@@ -2447,7 +2484,7 @@ predicate clearsContent(Node n, ContentSet c) {
24472484
predicate expectsContent(Node n, ContentSet c) {
24482485
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
24492486
or
2450-
n.asExpr() instanceof SpreadElementExpr and c instanceof ElementContent
2487+
n.asExpr() instanceof SpreadElementExpr and c.isElement()
24512488
}
24522489

24532490
class NodeRegion instanceof ControlFlow::BasicBlock {
@@ -3048,8 +3085,3 @@ abstract class SyntheticField extends string {
30483085
/** Gets the type of this synthetic field. */
30493086
Type getType() { result instanceof ObjectType }
30503087
}
3051-
3052-
/**
3053-
* Holds if the the content `c` is a container.
3054-
*/
3055-
predicate containerContent(DataFlow::Content c) { c instanceof DataFlow::ElementContent }

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

+67-5
Original file line numberDiff line numberDiff line change
@@ -267,22 +267,84 @@ class CapturedVariableContent extends Content, TCapturedVariableContent {
267267
override Location getLocation() { result = v.getLocation() }
268268
}
269269

270+
/** Holds if property `p1` overrides or implements source declaration property `p2`. */
271+
private predicate overridesOrImplementsSourceDecl(Property p1, Property p2) {
272+
p1.getOverridee*().getUnboundDeclaration() = p2
273+
or
274+
p1.getAnUltimateImplementee().getUnboundDeclaration() = p2
275+
}
276+
270277
/**
271278
* An entity that represents a set of `Content`s.
272279
*
273280
* The set may be interpreted differently depending on whether it is
274281
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
275282
*/
276-
class ContentSet instanceof Content {
283+
class ContentSet extends TContentSet {
284+
/** Holds if this content set is the singleton `{c}`. */
285+
predicate isSingleton(Content c) { this = TSingletonContent(c) }
286+
287+
/**
288+
* Holds if this content set represents the property `p`.
289+
*
290+
*
291+
* For `getAReadContent`, this set represents all properties that may
292+
* (reflexively and transitively) override/implement `p` (or vice versa).
293+
*/
294+
predicate isProperty(Property p) { this = TPropertyContentSet(p) }
295+
296+
/** Holds if this content set represent the field `f`. */
297+
predicate isField(Field f) { this.isSingleton(TFieldContent(f)) }
298+
299+
/** Holds if this content set represents an element in a collection. */
300+
predicate isElement() { this.isSingleton(TElementContent()) }
301+
277302
/** Gets a content that may be stored into when storing into this set. */
278-
Content getAStoreContent() { result = this }
303+
Content getAStoreContent() {
304+
this.isSingleton(result)
305+
or
306+
this.isProperty(result.(PropertyContent).getProperty())
307+
}
279308

280309
/** Gets a content that may be read from when reading from this set. */
281-
Content getAReadContent() { result = this }
310+
Content getAReadContent() {
311+
this.isSingleton(result)
312+
or
313+
exists(Property p1, Property p2 |
314+
this.isProperty(p1) and
315+
p2 = result.(PropertyContent).getProperty()
316+
|
317+
p1 = p2
318+
or
319+
overridesOrImplementsSourceDecl(p2, p1)
320+
or
321+
overridesOrImplementsSourceDecl(p1, p2)
322+
)
323+
}
282324

283325
/** Gets a textual representation of this content set. */
284-
string toString() { result = super.toString() }
326+
string toString() {
327+
exists(Content c |
328+
this.isSingleton(c) and
329+
result = c.toString()
330+
)
331+
or
332+
exists(Property p |
333+
this.isProperty(p) and
334+
result = "property " + p.getName()
335+
)
336+
}
285337

286338
/** Gets the location of this content set. */
287-
Location getLocation() { result = super.getLocation() }
339+
Location getLocation() {
340+
exists(Content c |
341+
this.isSingleton(c) and
342+
result = c.getLocation()
343+
)
344+
or
345+
exists(Property p |
346+
this.isProperty(p) and
347+
result = p.getLocation()
348+
)
349+
}
288350
}

0 commit comments

Comments
 (0)