@@ -14,6 +14,39 @@ import swift
14
14
import codeql.swift.dataflow.DataFlow
15
15
import DataFlow:: PathGraph
16
16
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
+
17
50
/**
18
51
* A configuration for tracking string lengths originating from source that is
19
52
* a `String` or an `NSString` object, to a sink of a different kind that
@@ -38,9 +71,37 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
38
71
node .asExpr ( ) = member and
39
72
flowstate = "NSString"
40
73
)
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
+ )
41
98
}
42
99
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 ) {
44
105
exists (
45
106
AbstractFunctionDecl funcDecl , CallExpr call , string funcName , string paramName , int arg
46
107
|
@@ -76,15 +137,15 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
76
137
c .getName ( ) = className and
77
138
c .getAMember ( ) = funcDecl and
78
139
call .getFunction ( ) .( ApplyExpr ) .getStaticTarget ( ) = funcDecl and
79
- flowstate = "String" // `String` length flowing into ` NSString`
140
+ flowstate = "NSString"
80
141
)
81
142
or
82
143
// arguments to function calls...
83
144
// `NSMakeRange`
84
145
funcName = "NSMakeRange(_:_:)" and
85
146
paramName = [ "loc" , "len" ] and
86
147
call .getStaticTarget ( ) = funcDecl and
87
- flowstate = "String" // `String` length flowing into ` NSString`
148
+ flowstate = "NSString"
88
149
or
89
150
// arguments to method calls...
90
151
(
@@ -109,7 +170,7 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
109
170
paramName = "distance"
110
171
) and
111
172
call .getFunction ( ) .( ApplyExpr ) .getStaticTarget ( ) = funcDecl and
112
- flowstate = "NSString" // `NSString` length flowing into ` String`
173
+ flowstate = "String"
113
174
) and
114
175
// match up `funcName`, `paramName`, `arg`, `node`.
115
176
funcDecl .getName ( ) = funcName and
@@ -118,6 +179,16 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
118
179
)
119
180
}
120
181
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
+
121
192
override predicate isAdditionalFlowStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
122
193
// allow flow through `+`, `-`, `*` etc.
123
194
node2 .asExpr ( ) .( ArithmeticOperation ) .getAnOperand ( ) = node1 .asExpr ( )
@@ -126,15 +197,13 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration {
126
197
127
198
from
128
199
StringLengthConflationConfiguration config , DataFlow:: PathNode source , DataFlow:: PathNode sink ,
129
- string flowstate , string message
200
+ StringLengthConflationFlowState sourceFlowState , StringLengthConflationFlowState sinkFlowstate ,
201
+ string message
130
202
where
131
203
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."
140
209
select sink .getNode ( ) , source , sink , message
0 commit comments