@@ -14,6 +14,39 @@ import swift
1414import codeql.swift.dataflow.DataFlow
1515import DataFlow:: PathGraph
1616
17+ /**
18+ * A flow state for this query, which is a type of Swift string encoding.
19+ */
20+ class StringLengthConflationFlowState extends string {
21+ string equivClass ;
22+ string singular ;
23+
24+ StringLengthConflationFlowState ( ) {
25+ this = "String" and singular = "a String" and equivClass = "String"
26+ or
27+ this = "NSString" and singular = "an NSString" and equivClass = "NSString"
28+ or
29+ this = "String.utf8" and singular = "a String.utf8" and equivClass = "String.utf8"
30+ or
31+ this = "String.utf16" and singular = "a String.utf16" and equivClass = "NSString"
32+ or
33+ this = "String.unicodeScalars" and
34+ singular = "a String.unicodeScalars" and
35+ equivClass = "String.unicodeScalars"
36+ }
37+
38+ /**
39+ * Gets the equivalence class for this flow state. If these are equal,
40+ * they should be treated as equivalent.
41+ */
42+ string getEquivClass ( ) { result = equivClass }
43+
44+ /**
45+ * Gets text for the singular form of this flow state.
46+ */
47+ string getSingular ( ) { result = singular }
48+ }
49+
1750/**
1851 * A configuration for tracking string lengths originating from source that is
1952 * a `String` or an `NSString` object, to a sink of a different kind that
@@ -38,9 +71,37 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
3871 node .asExpr ( ) = member and
3972 flowstate = "NSString"
4073 )
74+ or
75+ // result of a call to `String.utf8.count`
76+ exists ( MemberRefExpr member |
77+ member .getBaseExpr ( ) .getType ( ) .getName ( ) = "String.UTF8View" and
78+ member .getMember ( ) .( VarDecl ) .getName ( ) = "count" and
79+ node .asExpr ( ) = member and
80+ flowstate = "String.utf8"
81+ )
82+ or
83+ // result of a call to `String.utf16.count`
84+ exists ( MemberRefExpr member |
85+ member .getBaseExpr ( ) .getType ( ) .getName ( ) = "String.UTF16View" and
86+ member .getMember ( ) .( VarDecl ) .getName ( ) = "count" and
87+ node .asExpr ( ) = member and
88+ flowstate = "String.utf16"
89+ )
90+ or
91+ // result of a call to `String.unicodeScalars.count`
92+ exists ( MemberRefExpr member |
93+ member .getBaseExpr ( ) .getType ( ) .getName ( ) = "String.UnicodeScalarView" and
94+ member .getMember ( ) .( VarDecl ) .getName ( ) = "count" and
95+ node .asExpr ( ) = member and
96+ flowstate = "String.unicodeScalars"
97+ )
4198 }
4299
43- override predicate isSink ( DataFlow:: Node node , string flowstate ) {
100+ /**
101+ * Holds if `node` is a sink and `flowstate` is the *correct* flow state for
102+ * that sink. We actually want to report incorrect flow states.
103+ */
104+ predicate isSinkImpl ( DataFlow:: Node node , string flowstate ) {
44105 exists (
45106 AbstractFunctionDecl funcDecl , CallExpr call , string funcName , string paramName , int arg
46107 |
@@ -76,15 +137,15 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
76137 c .getName ( ) = className and
77138 c .getAMember ( ) = funcDecl and
78139 call .getFunction ( ) .( ApplyExpr ) .getStaticTarget ( ) = funcDecl and
79- flowstate = "String" // `String` length flowing into ` NSString`
140+ flowstate = "NSString"
80141 )
81142 or
82143 // arguments to function calls...
83144 // `NSMakeRange`
84145 funcName = "NSMakeRange(_:_:)" and
85146 paramName = [ "loc" , "len" ] and
86147 call .getStaticTarget ( ) = funcDecl and
87- flowstate = "String" // `String` length flowing into ` NSString`
148+ flowstate = "NSString"
88149 or
89150 // arguments to method calls...
90151 (
@@ -109,7 +170,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
109170 paramName = "distance"
110171 ) and
111172 call .getFunction ( ) .( ApplyExpr ) .getStaticTarget ( ) = funcDecl and
112- flowstate = "NSString" // `NSString` length flowing into ` String`
173+ flowstate = "String"
113174 ) and
114175 // match up `funcName`, `paramName`, `arg`, `node`.
115176 funcDecl .getName ( ) = funcName and
@@ -118,6 +179,16 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
118179 )
119180 }
120181
182+ override predicate isSink ( DataFlow:: Node node , string flowstate ) {
183+ // Permit any *incorrect* flowstate, as those are the results the query
184+ // should report.
185+ exists ( string correctFlowState |
186+ isSinkImpl ( node , correctFlowState ) and
187+ flowstate .( StringLengthConflationFlowState ) .getEquivClass ( ) !=
188+ correctFlowState .( StringLengthConflationFlowState ) .getEquivClass ( )
189+ )
190+ }
191+
121192 override predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
122193 // allow flow through `+`, `-`, `*` etc.
123194 node2 .asExpr ( ) .( ArithmeticOperation ) .getAnOperand ( ) = node1 .asExpr ( )
@@ -126,15 +197,13 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
126197
127198from
128199 StringLengthConflationConfiguration config , DataFlow:: PathNode source , DataFlow:: PathNode sink ,
129- string flowstate , string message
200+ StringLengthConflationFlowState sourceFlowState , StringLengthConflationFlowState sinkFlowstate ,
201+ string message
130202where
131203 config .hasFlowPath ( source , sink ) and
132- config .isSink ( sink .getNode ( ) , flowstate ) and
133- (
134- flowstate = "String" and
135- message = "This String length is used in an NSString, but it may not be equivalent."
136- or
137- flowstate = "NSString" and
138- message = "This NSString length is used in a String, but it may not be equivalent."
139- )
204+ config .isSource ( source .getNode ( ) , sourceFlowState ) and
205+ config .isSinkImpl ( sink .getNode ( ) , sinkFlowstate ) and
206+ message =
207+ "This " + sourceFlowState + " length is used in " + sinkFlowstate .getSingular ( ) +
208+ ", but it may not be equivalent."
140209select sink .getNode ( ) , source , sink , message
0 commit comments