@@ -7,6 +7,7 @@ private import codeql.swift.dataflow.Ssa
77private import codeql.swift.controlflow.BasicBlocks
88private import codeql.swift.dataflow.FlowSummary as FlowSummary
99private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
10+ private import codeql.swift.dataflow.ExternalFlow
1011private import codeql.swift.frameworks.StandardLibrary.PointerTypes
1112private import codeql.swift.frameworks.StandardLibrary.Array
1213private import codeql.swift.frameworks.StandardLibrary.Dictionary
@@ -184,142 +185,145 @@ private module Cached {
184185 nodeTo = getParameterDefNode ( nodeFrom .asParameter ( ) )
185186 }
186187
187- private predicate localFlowStepCommon ( Node nodeFrom , Node nodeTo ) {
188- exists ( Ssa:: Definition def |
189- // Step from assignment RHS to def
190- def .( Ssa:: WriteDefinition ) .assigns ( nodeFrom .getCfgNode ( ) ) and
191- nodeTo .asDefinition ( ) = def
188+ private predicate localFlowStepCommon ( Node nodeFrom , Node nodeTo , string model ) {
189+ (
190+ exists ( Ssa:: Definition def |
191+ // Step from assignment RHS to def
192+ def .( Ssa:: WriteDefinition ) .assigns ( nodeFrom .getCfgNode ( ) ) and
193+ nodeTo .asDefinition ( ) = def
194+ or
195+ // step from def to first read
196+ nodeFrom .asDefinition ( ) = def and
197+ nodeTo .getCfgNode ( ) = def .getAFirstRead ( ) and
198+ (
199+ nodeTo instanceof InoutReturnNodeImpl
200+ implies
201+ nodeTo .( InoutReturnNodeImpl ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
202+ )
203+ or
204+ // use-use flow
205+ localSsaFlowStepUseUse ( def , nodeFrom , nodeTo )
206+ or
207+ localSsaFlowStepUseUse ( def , nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) , nodeTo )
208+ or
209+ // step from previous read to Phi node
210+ localFlowSsaInput ( nodeFrom , def , nodeTo .asDefinition ( ) )
211+ )
192212 or
193- // step from def to first read
194- nodeFrom .asDefinition ( ) = def and
195- nodeTo .getCfgNode ( ) = def .getAFirstRead ( ) and
196- (
197- nodeTo instanceof InoutReturnNodeImpl
198- implies
199- nodeTo .( InoutReturnNodeImpl ) .getParameter ( ) = def .getSourceVariable ( ) .asVarDecl ( )
213+ localFlowSsaParamInput ( nodeFrom , nodeTo )
214+ or
215+ // flow through `&` (inout argument)
216+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( InOutExpr ) .getSubExpr ( )
217+ or
218+ // reverse flow through `&` (inout argument)
219+ nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) .( InOutExpr ) .getSubExpr ( ) =
220+ nodeTo .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( )
221+ or
222+ // flow through `try!` and similar constructs
223+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( AnyTryExpr ) .getSubExpr ( )
224+ or
225+ // flow through `!`
226+ // note: there's a case in `readStep` that handles when the source is the
227+ // `OptionalSomeContentSet` within the RHS. This case is for when the
228+ // `Optional` itself is tainted (which it usually shouldn't be, but
229+ // retaining this case increases robustness of flow).
230+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( ForceValueExpr ) .getSubExpr ( )
231+ or
232+ // read of an optional .some member via `let x: T = y: T?` pattern matching
233+ // note: similar to `ForceValueExpr` this is ideally a content `readStep` but
234+ // in practice we sometimes have taint on the optional itself.
235+ nodeTo .asPattern ( ) = nodeFrom .asPattern ( ) .( OptionalSomePattern ) .getSubPattern ( )
236+ or
237+ // flow through `?` and `?.`
238+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( BindOptionalExpr ) .getSubExpr ( )
239+ or
240+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( OptionalEvaluationExpr ) .getSubExpr ( )
241+ or
242+ // flow through unary `+` (which does nothing)
243+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( UnaryPlusExpr ) .getOperand ( )
244+ or
245+ // flow through varargs expansions (that wrap an `ArrayExpr` where varargs enter a call)
246+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( VarargExpansionExpr ) .getSubExpr ( )
247+ or
248+ // flow through nil-coalescing operator `??`
249+ exists ( BinaryExpr nco |
250+ nco .getOperator ( ) .( FreeFunction ) .getName ( ) = "??(_:_:)" and
251+ nodeTo .asExpr ( ) = nco
252+ |
253+ // value argument
254+ nodeFrom .asExpr ( ) = nco .getAnOperand ( )
255+ or
256+ // unpack closure (the second argument is an `AutoClosureExpr` argument)
257+ nodeFrom .asExpr ( ) = nco .getAnOperand ( ) .( AutoClosureExpr ) .getExpr ( )
200258 )
201259 or
202- // use-use flow
203- localSsaFlowStepUseUse ( def , nodeFrom , nodeTo )
260+ // flow through ternary operator `? :`
261+ exists ( IfExpr ie |
262+ nodeTo .asExpr ( ) = ie and
263+ nodeFrom .asExpr ( ) = ie .getBranch ( _)
264+ )
204265 or
205- localSsaFlowStepUseUse ( def , nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) , nodeTo )
266+ // flow through OpenExistentialExpr (compiler generated expression wrapper)
267+ nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( OpenExistentialExpr ) .getSubExpr ( )
206268 or
207- // step from previous read to Phi node
208- localFlowSsaInput ( nodeFrom , def , nodeTo .asDefinition ( ) )
209- )
210- or
211- localFlowSsaParamInput ( nodeFrom , nodeTo )
212- or
213- // flow through `&` (inout argument)
214- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( InOutExpr ) .getSubExpr ( )
215- or
216- // reverse flow through `&` (inout argument)
217- nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) .( InOutExpr ) .getSubExpr ( ) =
218- nodeTo .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( )
219- or
220- // flow through `try!` and similar constructs
221- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( AnyTryExpr ) .getSubExpr ( )
222- or
223- // flow through `!`
224- // note: there's a case in `readStep` that handles when the source is the
225- // `OptionalSomeContentSet` within the RHS. This case is for when the
226- // `Optional` itself is tainted (which it usually shouldn't be, but
227- // retaining this case increases robustness of flow).
228- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( ForceValueExpr ) .getSubExpr ( )
229- or
230- // read of an optional .some member via `let x: T = y: T?` pattern matching
231- // note: similar to `ForceValueExpr` this is ideally a content `readStep` but
232- // in practice we sometimes have taint on the optional itself.
233- nodeTo .asPattern ( ) = nodeFrom .asPattern ( ) .( OptionalSomePattern ) .getSubPattern ( )
234- or
235- // flow through `?` and `?.`
236- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( BindOptionalExpr ) .getSubExpr ( )
237- or
238- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( OptionalEvaluationExpr ) .getSubExpr ( )
239- or
240- // flow through unary `+` (which does nothing)
241- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( UnaryPlusExpr ) .getOperand ( )
242- or
243- // flow through varargs expansions (that wrap an `ArrayExpr` where varargs enter a call)
244- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( VarargExpansionExpr ) .getSubExpr ( )
245- or
246- // flow through nil-coalescing operator `??`
247- exists ( BinaryExpr nco |
248- nco .getOperator ( ) .( FreeFunction ) .getName ( ) = "??(_:_:)" and
249- nodeTo .asExpr ( ) = nco
250- |
251- // value argument
252- nodeFrom .asExpr ( ) = nco .getAnOperand ( )
269+ // flow from Expr to Pattern
270+ exists ( Expr e , Pattern p |
271+ nodeFrom .asExpr ( ) = e and
272+ nodeTo .asPattern ( ) = p and
273+ p .getImmediateMatchingExpr ( ) = e
274+ )
253275 or
254- // unpack closure (the second argument is an `AutoClosureExpr` argument)
255- nodeFrom .asExpr ( ) = nco .getAnOperand ( ) .( AutoClosureExpr ) .getExpr ( )
256- )
257- or
258- // flow through ternary operator `? :`
259- exists ( IfExpr ie |
260- nodeTo .asExpr ( ) = ie and
261- nodeFrom .asExpr ( ) = ie .getBranch ( _)
262- )
263- or
264- // flow through OpenExistentialExpr (compiler generated expression wrapper)
265- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( OpenExistentialExpr ) .getSubExpr ( )
266- or
267- // flow from Expr to Pattern
268- exists ( Expr e , Pattern p |
269- nodeFrom .asExpr ( ) = e and
270- nodeTo .asPattern ( ) = p and
271- p .getImmediateMatchingExpr ( ) = e
272- )
273- or
274- // flow from Pattern to an identity-preserving sub-Pattern:
275- nodeTo .asPattern ( ) =
276- [
277- nodeFrom .asPattern ( ) .( IsPattern ) .getSubPattern ( ) ,
278- nodeFrom .asPattern ( ) .( TypedPattern ) .getSubPattern ( )
279- ]
280- or
281- // Flow from the last component in a key path chain to
282- // the return node for the key path.
283- exists ( KeyPathExpr keyPath |
284- nodeFrom .( KeyPathComponentNodeImpl ) .getComponent ( ) =
285- keyPath .getComponent ( keyPath .getNumberOfComponents ( ) - 1 ) and
286- nodeTo .( KeyPathReturnNodeImpl ) .getKeyPathExpr ( ) = keyPath
287- )
288- or
289- exists ( KeyPathExpr keyPath |
290- nodeTo .( KeyPathComponentPostUpdateNode ) .getComponent ( ) =
291- keyPath .getComponent ( keyPath .getNumberOfComponents ( ) - 1 ) and
292- nodeFrom .( KeyPathReturnPostUpdateNode ) .getKeyPathExpr ( ) = keyPath
293- )
294- or
295- // Flow to the result of a keypath assignment
296- exists ( KeyPathApplicationExpr apply , AssignExpr assign |
297- apply = assign .getDest ( ) and
298- nodeTo .asExpr ( ) = apply and
299- nodeFrom .asExpr ( ) = assign .getSource ( )
300- )
276+ // flow from Pattern to an identity-preserving sub-Pattern:
277+ nodeTo .asPattern ( ) =
278+ [
279+ nodeFrom .asPattern ( ) .( IsPattern ) .getSubPattern ( ) ,
280+ nodeFrom .asPattern ( ) .( TypedPattern ) .getSubPattern ( )
281+ ]
282+ or
283+ // Flow from the last component in a key path chain to
284+ // the return node for the key path.
285+ exists ( KeyPathExpr keyPath |
286+ nodeFrom .( KeyPathComponentNodeImpl ) .getComponent ( ) =
287+ keyPath .getComponent ( keyPath .getNumberOfComponents ( ) - 1 ) and
288+ nodeTo .( KeyPathReturnNodeImpl ) .getKeyPathExpr ( ) = keyPath
289+ )
290+ or
291+ exists ( KeyPathExpr keyPath |
292+ nodeTo .( KeyPathComponentPostUpdateNode ) .getComponent ( ) =
293+ keyPath .getComponent ( keyPath .getNumberOfComponents ( ) - 1 ) and
294+ nodeFrom .( KeyPathReturnPostUpdateNode ) .getKeyPathExpr ( ) = keyPath
295+ )
296+ or
297+ // Flow to the result of a keypath assignment
298+ exists ( KeyPathApplicationExpr apply , AssignExpr assign |
299+ apply = assign .getDest ( ) and
300+ nodeTo .asExpr ( ) = apply and
301+ nodeFrom .asExpr ( ) = assign .getSource ( )
302+ )
303+ or
304+ // flow step according to the closure capture library
305+ captureValueStep ( nodeFrom , nodeTo )
306+ ) and
307+ model = ""
301308 or
302309 // flow through a flow summary (extension of `SummaryModelCsv`)
303310 FlowSummaryImpl:: Private:: Steps:: summaryLocalStep ( nodeFrom .( FlowSummaryNode ) .getSummaryNode ( ) ,
304- nodeTo .( FlowSummaryNode ) .getSummaryNode ( ) , true )
305- or
306- // flow step according to the closure capture library
307- captureValueStep ( nodeFrom , nodeTo )
311+ nodeTo .( FlowSummaryNode ) .getSummaryNode ( ) , true , model )
308312 }
309313
310314 /**
311315 * This is the local flow predicate that is used as a building block in global
312316 * data flow.
313317 */
314318 cached
315- predicate simpleLocalFlowStep ( Node nodeFrom , Node nodeTo ) {
316- localFlowStepCommon ( nodeFrom , nodeTo )
319+ predicate simpleLocalFlowStep ( Node nodeFrom , Node nodeTo , string model ) {
320+ localFlowStepCommon ( nodeFrom , nodeTo , model )
317321 }
318322
319323 /** This is the local flow predicate that is exposed. */
320324 cached
321325 predicate localFlowStepImpl ( Node nodeFrom , Node nodeTo ) {
322- localFlowStepCommon ( nodeFrom , nodeTo ) or
326+ localFlowStepCommon ( nodeFrom , nodeTo , _ ) or
323327 FlowSummaryImpl:: Private:: Steps:: summaryThroughStepValue ( nodeFrom , nodeTo , _)
324328 }
325329
@@ -1396,6 +1400,10 @@ predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
13961400/** Extra data-flow steps needed for lambda flow analysis. */
13971401predicate additionalLambdaFlowStep ( Node nodeFrom , Node nodeTo , boolean preservesValue ) { none ( ) }
13981402
1403+ predicate knownSourceModel ( Node source , string model ) { sourceNode ( source , _, model ) }
1404+
1405+ predicate knownSinkModel ( Node sink , string model ) { sinkNode ( sink , _, model ) }
1406+
13991407/**
14001408 * Holds if flow is allowed to pass from parameter `p` and back to itself as a
14011409 * side-effect, resulting in a summary from `p` to itself.
0 commit comments