@@ -102,7 +102,67 @@ private predicate summarizedLocalStep(Node nodeFrom, Node nodeTo) {
102
102
}
103
103
104
104
/** Holds if there is a level step from `nodeFrom` to `nodeTo`. */
105
- predicate levelStep ( Node nodeFrom , Node nodeTo ) { summarizedLocalStep ( nodeFrom , nodeTo ) }
105
+ predicate levelStep ( Node nodeFrom , Node nodeTo ) {
106
+ summarizedLocalStep ( nodeFrom , nodeTo )
107
+ or
108
+ localFieldStep ( nodeFrom , nodeTo )
109
+ }
110
+
111
+ /**
112
+ * Gets a method of `mod`, with `instance` indicating if this is an instance method.
113
+ *
114
+ * Does not take inheritance or the various forms of inclusion into account.
115
+ */
116
+ pragma [ nomagic]
117
+ private MethodBase getAMethod ( ModuleBase mod , boolean instance ) {
118
+ not mod instanceof SingletonClass and
119
+ result = mod .getAMethod ( ) and
120
+ if result instanceof SingletonMethod then instance = false else instance = true
121
+ or
122
+ exists ( SingletonClass cls |
123
+ cls .getValue ( ) .( SelfVariableAccess ) .getCfgScope ( ) = mod and
124
+ result = cls .getAMethod ( ) .( Method ) and
125
+ instance = false
126
+ )
127
+ }
128
+
129
+ /**
130
+ * Gets a value flowing into `field` in `mod`, with `instance` indicating if it's
131
+ * a field on an instance of `mod` (as opposed to the module object itself).
132
+ */
133
+ pragma [ nomagic]
134
+ private Node fieldPredecessor ( ModuleBase mod , boolean instance , string field ) {
135
+ exists ( InstanceVariableWriteAccess access , AssignExpr assign |
136
+ access .getReceiver ( ) .getCfgScope ( ) = getAMethod ( mod , instance ) and
137
+ field = access .getVariable ( ) .getName ( ) and
138
+ assign .getLeftOperand ( ) = access and
139
+ result .asExpr ( ) .getExpr ( ) = assign .getRightOperand ( )
140
+ )
141
+ }
142
+
143
+ /**
144
+ * Gets a reference to `field` in `mod`, with `instance` indicating if it's
145
+ * a field on an instance of `mod` (as opposed to the module object itself).
146
+ */
147
+ pragma [ nomagic]
148
+ private Node fieldSuccessor ( ModuleBase mod , boolean instance , string field ) {
149
+ exists ( InstanceVariableReadAccess access |
150
+ access .getReceiver ( ) .getCfgScope ( ) = getAMethod ( mod , instance ) and
151
+ result .asExpr ( ) .getExpr ( ) = access and
152
+ field = access .getVariable ( ) .getName ( )
153
+ )
154
+ }
155
+
156
+ /**
157
+ * Holds if `pred -> succ` should be used a level step, from a field assignment to
158
+ * a read within the same class.
159
+ */
160
+ private predicate localFieldStep ( Node pred , Node succ ) {
161
+ exists ( ModuleBase mod , boolean instance , string field |
162
+ pred = fieldPredecessor ( mod , instance , field ) and
163
+ succ = fieldSuccessor ( mod , instance , field )
164
+ )
165
+ }
106
166
107
167
pragma [ noinline]
108
168
private predicate argumentPositionMatch (
@@ -325,9 +385,18 @@ private predicate hasStoreSummary(
325
385
SummarizedCallable callable , DataFlow:: ContentSet contents , SummaryComponentStack input ,
326
386
SummaryComponentStack output
327
387
) {
328
- callable .propagatesFlow ( input , push ( SummaryComponent:: content ( contents ) , output ) , true ) and
329
388
not isNonLocal ( input .head ( ) ) and
330
- not isNonLocal ( output .head ( ) )
389
+ not isNonLocal ( output .head ( ) ) and
390
+ (
391
+ callable .propagatesFlow ( input , push ( SummaryComponent:: content ( contents ) , output ) , true )
392
+ or
393
+ // Allow the input to start with an arbitrary WithoutContent[X].
394
+ // Since type-tracking only tracks one content deep, and we're about to store into another content,
395
+ // we're already preventing the input from being in a content.
396
+ callable
397
+ .propagatesFlow ( push ( SummaryComponent:: withoutContent ( _) , input ) ,
398
+ push ( SummaryComponent:: content ( contents ) , output ) , true )
399
+ )
331
400
}
332
401
333
402
pragma [ nomagic]
@@ -460,6 +529,9 @@ private predicate dependsOnSummaryComponentStack(
460
529
callable .propagatesFlow ( stack , _, true )
461
530
or
462
531
callable .propagatesFlow ( _, stack , true )
532
+ or
533
+ // include store summaries as they may skip an initial step at the input
534
+ hasStoreSummary ( callable , _, stack , _)
463
535
)
464
536
or
465
537
dependsOnSummaryComponentStackCons ( callable , _, stack )
0 commit comments