@@ -209,97 +209,99 @@ private module Cached {
209209 * value-preserving steps, not taking call contexts into account.
210210 *
211211 * `contentIn` describes the content of `p` that can flow to `node`
212- * (if any).
212+ * (if any), `t2` is the type of the tracked value, and `t1` is the
213+ * type before reading `contentIn` (`= t2` when no content is read).
213214 */
214- predicate parameterValueFlow ( ParameterNode p , Node node , ContentOption contentIn ) {
215- parameterValueFlow0 ( p , node , contentIn ) and
215+ predicate parameterValueFlow (
216+ ParameterNode p , Node node , ContentOption contentIn , DataFlowType t1 , DataFlowType t2
217+ ) {
218+ parameterValueFlow0 ( p , node , contentIn , t1 , t2 ) and
216219 if node instanceof CastingNode
217- then
218- // normal flow through
219- contentIn = TContentNone ( ) and
220- compatibleTypes ( getErasedNodeTypeBound ( p ) , getErasedNodeTypeBound ( node ) )
221- or
222- // getter
223- exists ( Content fIn |
224- contentIn .getContent ( ) = fIn and
225- compatibleTypes ( fIn .getType ( ) , getErasedNodeTypeBound ( node ) )
226- )
220+ then compatibleTypes ( t2 , getErasedNodeTypeBound ( node ) )
227221 else any ( )
228222 }
229223
230224 pragma [ nomagic]
231- private predicate parameterValueFlow0 ( ParameterNode p , Node node , ContentOption contentIn ) {
225+ private predicate parameterValueFlow0 (
226+ ParameterNode p , Node node , ContentOption contentIn , DataFlowType t1 , DataFlowType t2
227+ ) {
232228 p = node and
233229 Cand:: cand ( p , _) and
234- contentIn = TContentNone ( )
230+ contentIn = TContentNone ( ) and
231+ t1 = getErasedNodeTypeBound ( p ) and
232+ t2 = t1
235233 or
236234 // local flow
237235 exists ( Node mid |
238- parameterValueFlow ( p , mid , contentIn ) and
236+ parameterValueFlow ( p , mid , contentIn , t1 , t2 ) and
239237 LocalFlowBigStep:: localFlowBigStep ( mid , node )
240238 )
241239 or
242240 // read
243- exists ( Node mid , Content f |
244- parameterValueFlow ( p , mid , TContentNone ( ) ) and
245- readStep ( mid , f , node ) and
246- contentIn .getContent ( ) = f and
241+ exists ( Node mid |
242+ parameterValueFlow ( p , mid , TContentNone ( ) , _, t1 ) and
243+ readStep ( mid , contentIn .getContent ( ) , node ) and
247244 Cand:: parameterValueFlowReturnCand ( p , _, true ) and
248- compatibleTypes ( getErasedNodeTypeBound ( p ) , f .getContainerType ( ) )
245+ compatibleTypes ( t1 , getErasedNodeTypeBound ( mid ) ) and
246+ t2 = getErasedNodeTypeBound ( node )
249247 )
250248 or
251249 // flow through: no prior read
252- exists ( ArgumentNode arg |
253- parameterValueFlowArg ( p , arg , TContentNone ( ) ) and
254- argumentValueFlowsThrough ( arg , contentIn , node )
250+ exists ( ArgumentNode arg , DataFlowType t0_ , DataFlowType t1_ , DataFlowType t2_ |
251+ parameterValueFlowArg ( p , arg , TContentNone ( ) , _, t0_ ) and
252+ argumentValueFlowsThrough ( arg , contentIn , node , t1_ , t2_ ) and
253+ if contentIn = TContentNone ( )
254+ then t1 = t0_ and t2 = t1
255+ else (
256+ t1 = t1_ and
257+ t2 = t2_
258+ )
255259 )
256260 or
257261 // flow through: no read inside method
258262 exists ( ArgumentNode arg |
259- parameterValueFlowArg ( p , arg , contentIn ) and
260- argumentValueFlowsThrough ( arg , TContentNone ( ) , node )
263+ parameterValueFlowArg ( p , arg , contentIn , t1 , t2 ) and
264+ argumentValueFlowsThrough ( arg , TContentNone ( ) , node , _ , _ )
261265 )
262266 }
263267
264268 pragma [ nomagic]
265269 private predicate parameterValueFlowArg (
266- ParameterNode p , ArgumentNode arg , ContentOption contentIn
270+ ParameterNode p , ArgumentNode arg , ContentOption contentIn , DataFlowType t1 , DataFlowType t2
267271 ) {
268- parameterValueFlow ( p , arg , contentIn ) and
272+ parameterValueFlow ( p , arg , contentIn , t1 , t2 ) and
269273 Cand:: argumentValueFlowsThroughCand ( arg , _, _)
270274 }
271275
272276 pragma [ nomagic]
273277 private predicate argumentValueFlowsThrough0 (
274- DataFlowCall call , ArgumentNode arg , ReturnKind kind , ContentOption contentIn
278+ DataFlowCall call , ArgumentNode arg , ReturnKind kind , ContentOption contentIn ,
279+ DataFlowType t1 , DataFlowType t2
275280 ) {
276281 exists ( ParameterNode param | viableParamArg ( call , param , arg ) |
277- parameterValueFlowReturn ( param , kind , contentIn )
282+ parameterValueFlowReturn ( param , kind , contentIn , t1 , t2 )
278283 )
279284 }
280285
281286 /**
282287 * Holds if `arg` flows to `out` through a call using only value-preserving steps,
283288 * not taking call contexts into account.
284289 *
285- * `contentIn` describes the content of `arg` that can flow to `out` (if any).
290+ * `contentIn` describes the content of `arg` that can flow to `out` (if any),
291+ * `t2` is the type of the tracked value, and `t1` is the type before reading
292+ * `contentIn` (`= t2` when no content is read).
286293 */
287294 pragma [ nomagic]
288- predicate argumentValueFlowsThrough ( ArgumentNode arg , ContentOption contentIn , Node out ) {
295+ predicate argumentValueFlowsThrough (
296+ ArgumentNode arg , ContentOption contentIn , Node out , DataFlowType t1 , DataFlowType t2
297+ ) {
289298 exists ( DataFlowCall call , ReturnKind kind |
290- argumentValueFlowsThrough0 ( call , arg , kind , contentIn ) and
291- out = getAnOutNode ( call , kind )
292- |
293- // normal flow through
294- contentIn = TContentNone ( ) and
295- compatibleTypes ( getErasedNodeTypeBound ( arg ) , getErasedNodeTypeBound ( out ) )
296- or
297- // getter
298- exists ( Content fIn |
299- contentIn .getContent ( ) = fIn and
300- compatibleTypes ( getErasedNodeTypeBound ( arg ) , fIn .getContainerType ( ) ) and
301- compatibleTypes ( fIn .getType ( ) , getErasedNodeTypeBound ( out ) )
302- )
299+ argumentValueFlowsThrough0 ( call , arg , kind , contentIn , t1 , t2 ) and
300+ out = getAnOutNode ( call , kind ) and
301+ compatibleTypes ( t2 , getErasedNodeTypeBound ( out ) ) and
302+ if contentIn = TContentNone ( )
303+ then compatibleTypes ( getErasedNodeTypeBound ( arg ) , getErasedNodeTypeBound ( out ) )
304+ else compatibleTypes ( getErasedNodeTypeBound ( arg ) , t1 )
303305 )
304306 }
305307
@@ -308,13 +310,14 @@ private module Cached {
308310 * callable using only value-preserving steps.
309311 *
310312 * `contentIn` describes the content of `p` that can flow to the return
311- * node (if any).
313+ * node (if any), `t2` is the type of the tracked value, and `t1` is the
314+ * type before reading `contentIn` (`= t2` when no content is read).
312315 */
313316 private predicate parameterValueFlowReturn (
314- ParameterNode p , ReturnKind kind , ContentOption contentIn
317+ ParameterNode p , ReturnKind kind , ContentOption contentIn , DataFlowType t1 , DataFlowType t2
315318 ) {
316319 exists ( ReturnNode ret |
317- parameterValueFlow ( p , ret , contentIn ) and
320+ parameterValueFlow ( p , ret , contentIn , t1 , t2 ) and
318321 kind = ret .getKind ( )
319322 )
320323 }
@@ -329,7 +332,23 @@ private module Cached {
329332 */
330333 cached
331334 predicate parameterValueFlowsToPreUpdate ( ParameterNode p , PostUpdateNode n ) {
332- parameterValueFlow ( p , n .getPreUpdateNode ( ) , TContentNone ( ) )
335+ parameterValueFlow ( p , n .getPreUpdateNode ( ) , TContentNone ( ) , _, _)
336+ }
337+
338+ private predicate store ( Node node1 , Content c , Node node2 , DataFlowType containerType ) {
339+ storeStep ( node1 , c , node2 ) and
340+ readStep ( _, c , _) and
341+ containerType = getErasedNodeTypeBound ( node2 )
342+ or
343+ exists ( Node n1 , Node n2 |
344+ n1 = node1 .( PostUpdateNode ) .getPreUpdateNode ( ) and
345+ n2 = node2 .( PostUpdateNode ) .getPreUpdateNode ( )
346+ |
347+ argumentValueFlowsThrough ( n2 , TContentSome ( c ) , n1 , containerType , _)
348+ or
349+ readStep ( n2 , c , n1 ) and
350+ containerType = getErasedNodeTypeBound ( n2 )
351+ )
333352 }
334353
335354 /**
@@ -340,17 +359,8 @@ private module Cached {
340359 * been stored into, in order to handle cases like `x.f1.f2 = y`.
341360 */
342361 cached
343- predicate store ( Node node1 , Content f , Node node2 ) {
344- storeStep ( node1 , f , node2 ) and readStep ( _, f , _)
345- or
346- exists ( Node n1 , Node n2 |
347- n1 = node1 .( PostUpdateNode ) .getPreUpdateNode ( ) and
348- n2 = node2 .( PostUpdateNode ) .getPreUpdateNode ( )
349- |
350- argumentValueFlowsThrough ( n2 , TContentSome ( f ) , n1 )
351- or
352- readStep ( n2 , f , n1 )
353- )
362+ predicate store ( Node node1 , TypedContent tc , Node node2 ) {
363+ store ( node1 , tc .getContent ( ) , node2 , tc .getContainerType ( ) )
354364 }
355365
356366 import FlowThrough
@@ -397,10 +407,13 @@ private module Cached {
397407 TBooleanNone ( ) or
398408 TBooleanSome ( boolean b ) { b = true or b = false }
399409
410+ cached
411+ newtype TTypedContent = MkTypedContent ( Content c , DataFlowType t ) { store ( _, c , _, t ) }
412+
400413 cached
401414 newtype TAccessPathFront =
402415 TFrontNil ( DataFlowType t ) or
403- TFrontHead ( Content f )
416+ TFrontHead ( TypedContent tc )
404417
405418 cached
406419 newtype TAccessPathFrontOption =
@@ -415,13 +428,17 @@ class CastingNode extends Node {
415428 CastingNode ( ) {
416429 this instanceof ParameterNode or
417430 this instanceof CastNode or
418- this instanceof OutNodeExt
431+ this instanceof OutNodeExt or
432+ // For reads, `x.f`, we want to check that the tracked type after the read (which
433+ // is obtained by popping the head of the access path stack) is compatible with
434+ // the type of `x.f`.
435+ readStep ( _, _, this )
419436 }
420437}
421438
422439newtype TContentOption =
423440 TContentNone ( ) or
424- TContentSome ( Content f )
441+ TContentSome ( Content c )
425442
426443private class ContentOption extends TContentOption {
427444 Content getContent ( ) { this = TContentSome ( result ) }
@@ -692,6 +709,23 @@ class BooleanOption extends TBooleanOption {
692709 }
693710}
694711
712+ /** Content tagged with the type of a containing object. */
713+ class TypedContent extends MkTypedContent {
714+ private Content c ;
715+ private DataFlowType t ;
716+
717+ TypedContent ( ) { this = MkTypedContent ( c , t ) }
718+
719+ /** Gets the content. */
720+ Content getContent ( ) { result = c }
721+
722+ /** Gets the container type. */
723+ DataFlowType getContainerType ( ) { result = t }
724+
725+ /** Gets a textual representation of this content. */
726+ string toString ( ) { result = c .toString ( ) }
727+ }
728+
695729/**
696730 * The front of an access path. This is either a head or a nil.
697731 */
@@ -702,25 +736,29 @@ abstract class AccessPathFront extends TAccessPathFront {
702736
703737 abstract boolean toBoolNonEmpty ( ) ;
704738
705- predicate headUsesContent ( Content f ) { this = TFrontHead ( f ) }
739+ predicate headUsesContent ( TypedContent tc ) { this = TFrontHead ( tc ) }
706740}
707741
708742class AccessPathFrontNil extends AccessPathFront , TFrontNil {
709- override string toString ( ) {
710- exists ( DataFlowType t | this = TFrontNil ( t ) | result = ppReprType ( t ) )
711- }
743+ private DataFlowType t ;
712744
713- override DataFlowType getType ( ) { this = TFrontNil ( result ) }
745+ AccessPathFrontNil ( ) { this = TFrontNil ( t ) }
746+
747+ override string toString ( ) { result = ppReprType ( t ) }
748+
749+ override DataFlowType getType ( ) { result = t }
714750
715751 override boolean toBoolNonEmpty ( ) { result = false }
716752}
717753
718754class AccessPathFrontHead extends AccessPathFront , TFrontHead {
719- override string toString ( ) { exists ( Content f | this = TFrontHead ( f ) | result = f . toString ( ) ) }
755+ private TypedContent tc ;
720756
721- override DataFlowType getType ( ) {
722- exists ( Content head | this = TFrontHead ( head ) | result = head .getContainerType ( ) )
723- }
757+ AccessPathFrontHead ( ) { this = TFrontHead ( tc ) }
758+
759+ override string toString ( ) { result = tc .toString ( ) }
760+
761+ override DataFlowType getType ( ) { result = tc .getContainerType ( ) }
724762
725763 override boolean toBoolNonEmpty ( ) { result = true }
726764}
0 commit comments