Swift: Standardize the sources, sinks etc.

This commit is contained in:
Geoffrey White
2023-03-20 16:54:00 +00:00
parent dfcad7fa84
commit 4c0d02a87d
3 changed files with 176 additions and 122 deletions

View File

@@ -7,13 +7,14 @@ import swift
import codeql.swift.dataflow.DataFlow
/**
* A flow state for encoding types of Swift string encoding.
* A type of Swift string encoding. This class is used as a flow state for
* the string length conflation taint tracking configuration.
*/
class StringLengthConflationFlowState extends string {
string equivClass;
class StringType extends string {
string singular;
string equivClass;
StringLengthConflationFlowState() {
StringType() {
this = "String" and singular = "a String" and equivClass = "String"
or
this = "NSString" and singular = "an NSString" and equivClass = "NSString"
@@ -38,3 +39,159 @@ class StringLengthConflationFlowState extends string {
*/
string getSingular() { result = singular }
}
/**
* A dataflow source for string length conflation vulnerabilities. That is,
* a `DataFlow::Node` where a string length is generated.
*/
abstract class StringLengthConflationSource extends DataFlow::Node {
/**
* Gets the `StringType` for this source.
*/
abstract StringType getStringType();
}
/**
* A dataflow sink for string length conflation vulnerabilities. That is,
* a `DataFlow::Node` where a string length is used.
*/
abstract class StringLengthConflationSink extends DataFlow::Node {
/**
* Gets the correct `StringType` for this sink.
*/
abstract StringType getCorrectStringType();
}
abstract class StringLengthConflationSanitizer extends DataFlow::Node { }
/**
* A unit class for adding additional taint steps.
*/
class StringLengthConflationAdditionalTaintStep extends Unit {
/**
* Holds if the step from `node1` to `node2` should be considered a taint
* step for paths related to string length conflation vulnerabilities.
*/
abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
}
private class DefaultStringLengthConflationSource extends StringLengthConflationSource {
StringType stringType;
DefaultStringLengthConflationSource() {
exists(MemberRefExpr memberRef, string className, string varName |
memberRef.getBase().getType().(NominalType).getABaseType*().getName() = className and
memberRef.getMember().(VarDecl).getName() = varName and
this.asExpr() = memberRef and
(
// result of a call to `String.count`
className = "String" and
varName = "count" and
stringType = "String"
or
// result of a call to `NSString.length`
className = ["NSString", "NSMutableString"] and
varName = "length" and
stringType = "NSString"
or
// result of a call to `String.utf8.count`
className = "String.UTF8View" and
varName = "count" and
stringType = "String.utf8"
or
// result of a call to `String.utf16.count`
className = "String.UTF16View" and
varName = "count" and
stringType = "String.utf16"
or
// result of a call to `String.unicodeScalars.count`
className = "String.UnicodeScalarView" and
varName = "count" and
stringType = "String.unicodeScalars"
)
)
}
override StringType getStringType() { result = stringType }
}
private class DefaultStringLengthConflationSink extends StringLengthConflationSink {
StringType correctStringType;
DefaultStringLengthConflationSink() {
exists(AbstractFunctionDecl funcDecl, CallExpr call, string funcName, int arg |
(
// arguments to method calls...
exists(string className, ClassOrStructDecl c |
(
// `NSRange.init`
className = "NSRange" and
funcName = "init(location:length:)" and
arg = [0, 1]
or
// `NSString.character`
className = ["NSString", "NSMutableString"] and
funcName = "character(at:)" and
arg = 0
or
// `NSString.character`
className = ["NSString", "NSMutableString"] and
funcName = "substring(from:)" and
arg = 0
or
// `NSString.character`
className = ["NSString", "NSMutableString"] and
funcName = "substring(to:)" and
arg = 0
or
// `NSMutableString.insert`
className = "NSMutableString" and
funcName = "insert(_:at:)" and
arg = 1
) and
c.getName() = className and
c.getABaseTypeDecl*().(ClassOrStructDecl).getAMember() = funcDecl and
call.getStaticTarget() = funcDecl and
correctStringType = "NSString"
)
or
// arguments to function calls...
// `NSMakeRange`
funcName = "NSMakeRange(_:_:)" and
arg = [0, 1] and
call.getStaticTarget() = funcDecl and
correctStringType = "NSString"
or
// arguments to method calls...
(
// `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast`
funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and
arg = 0
or
// `String.prefix`, `String.suffix`
funcName = ["prefix(_:)", "suffix(_:)"] and
arg = 0
or
// `String.Index.init`
funcName = "init(encodedOffset:)" and
arg = 0
or
// `String.index`
funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and
arg = [0, 1]
or
// `String.formIndex`
funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and
arg = [0, 1]
) and
call.getStaticTarget() = funcDecl and
correctStringType = "String"
) and
// match up `funcName`, `arg`, `node`.
funcDecl.getName() = funcName and
call.getArgument(arg).getExpr() = this.asExpr()
)
}
override StringType getCorrectStringType() { result = correctStringType }
}

View File

@@ -17,125 +17,23 @@ class StringLengthConflationConfiguration extends TaintTracking::Configuration {
StringLengthConflationConfiguration() { this = "StringLengthConflationConfiguration" }
override predicate isSource(DataFlow::Node node, string flowstate) {
exists(MemberRefExpr memberRef, string className, string varName |
memberRef.getBase().getType().(NominalType).getABaseType*().getName() = className and
memberRef.getMember().(VarDecl).getName() = varName and
node.asExpr() = memberRef and
(
// result of a call to `String.count`
className = "String" and
varName = "count" and
flowstate = "String"
or
// result of a call to `NSString.length`
className = ["NSString", "NSMutableString"] and
varName = "length" and
flowstate = "NSString"
or
// result of a call to `String.utf8.count`
className = "String.UTF8View" and
varName = "count" and
flowstate = "String.utf8"
or
// result of a call to `String.utf16.count`
className = "String.UTF16View" and
varName = "count" and
flowstate = "String.utf16"
or
// result of a call to `String.unicodeScalars.count`
className = "String.UnicodeScalarView" and
varName = "count" and
flowstate = "String.unicodeScalars"
)
)
}
/**
* Holds if `node` is a sink and `flowstate` is the *correct* flow state for
* that sink. We actually want to report incorrect flow states.
*/
predicate isSinkImpl(DataFlow::Node node, string flowstate) {
exists(AbstractFunctionDecl funcDecl, CallExpr call, string funcName, int arg |
(
// arguments to method calls...
exists(string className, ClassOrStructDecl c |
(
// `NSRange.init`
className = "NSRange" and
funcName = "init(location:length:)" and
arg = [0, 1]
or
// `NSString.character`
className = ["NSString", "NSMutableString"] and
funcName = "character(at:)" and
arg = 0
or
// `NSString.character`
className = ["NSString", "NSMutableString"] and
funcName = "substring(from:)" and
arg = 0
or
// `NSString.character`
className = ["NSString", "NSMutableString"] and
funcName = "substring(to:)" and
arg = 0
or
// `NSMutableString.insert`
className = "NSMutableString" and
funcName = "insert(_:at:)" and
arg = 1
) and
c.getName() = className and
c.getABaseTypeDecl*().(ClassOrStructDecl).getAMember() = funcDecl and
call.getStaticTarget() = funcDecl and
flowstate = "NSString"
)
or
// arguments to function calls...
// `NSMakeRange`
funcName = "NSMakeRange(_:_:)" and
arg = [0, 1] and
call.getStaticTarget() = funcDecl and
flowstate = "NSString"
or
// arguments to method calls...
(
// `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast`
funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and
arg = 0
or
// `String.prefix`, `String.suffix`
funcName = ["prefix(_:)", "suffix(_:)"] and
arg = 0
or
// `String.Index.init`
funcName = "init(encodedOffset:)" and
arg = 0
or
// `String.index`
funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and
arg = [0, 1]
or
// `String.formIndex`
funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and
arg = [0, 1]
) and
call.getStaticTarget() = funcDecl and
flowstate = "String"
) and
// match up `funcName`, `arg`, `node`.
funcDecl.getName() = funcName and
call.getArgument(arg).getExpr() = node.asExpr()
)
flowstate = node.(StringLengthConflationSource).getStringType()
}
override predicate isSink(DataFlow::Node node, string flowstate) {
// Permit any *incorrect* flowstate, as those are the results the query
// should report.
exists(string correctFlowState |
isSinkImpl(node, correctFlowState) and
flowstate.(StringLengthConflationFlowState).getEquivClass() !=
correctFlowState.(StringLengthConflationFlowState).getEquivClass()
correctFlowState = node.(StringLengthConflationSink).getCorrectStringType() and
flowstate.(StringType).getEquivClass() != correctFlowState.(StringType).getEquivClass()
)
}
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof StringLengthConflationSanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
any(StringLengthConflationAdditionalTaintStep s).step(nodeFrom, nodeTo)
}
}

View File

@@ -17,13 +17,12 @@ import DataFlow::PathGraph
from
StringLengthConflationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
StringLengthConflationFlowState sourceFlowState, StringLengthConflationFlowState sinkFlowstate,
string message
StringType sourceType, StringType sinkType, string message
where
config.hasFlowPath(source, sink) and
config.isSource(source.getNode(), sourceFlowState) and
config.isSinkImpl(sink.getNode(), sinkFlowstate) and
config.isSource(source.getNode(), sourceType) and
sinkType = sink.getNode().(StringLengthConflationSink).getCorrectStringType() and
message =
"This " + sourceFlowState + " length is used in " + sinkFlowstate.getSingular() +
"This " + sourceType + " length is used in " + sinkType.getSingular() +
", but it may not be equivalent."
select sink.getNode(), source, sink, message