Skip to content

Commit ac26371

Browse files
authored
Merge pull request #9909 from geoffw0/stringlengthconflation6
Swift: Understand String.utf8.count etc in the string length conflation CVE query
2 parents 56ee07e + 6cd6f74 commit ac26371

File tree

4 files changed

+159
-75
lines changed

4 files changed

+159
-75
lines changed

swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<overview>
66
<p>Using a length value from an <code>NSString</code> in a <code>String</code>, or a count from a <code>String</code> in an <code>NSString</code>, may cause unexpected behavior including (in some cases) buffer overwrites. This is because certain unicode sequences are represented as one character in a <code>String</code> but as a sequence of multiple characters in an <code>NSString</code>. For example, a 'thumbs up' emoji with a skin tone modifier (&#x1F44D;&#x1F3FF;) is represented as U+1F44D (&#x1F44D;) then the modifier U+1F3FF.</p>
77

8+
<p>This issue can also arise from using the values of <code>String.utf8.count</code>, <code>String.utf16.count</code> or <code>String.unicodeScalars.count</code> in an unsuitable place.</p>
9+
810
</overview>
911
<recommendation>
1012

swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,39 @@ import swift
1414
import codeql.swift.dataflow.DataFlow
1515
import 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

127198
from
128199
StringLengthConflationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
129-
string flowstate, string message
200+
StringLengthConflationFlowState sourceFlowState, StringLengthConflationFlowState sinkFlowstate,
201+
string message
130202
where
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."
140209
select sink.getNode(), source, sink, message

0 commit comments

Comments
 (0)