Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swift: Query for escaping parameters of unsafe closures #13706

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

rdmarsh2
Copy link
Contributor

No description provided.

@github-actions github-actions bot added the Swift label Jul 10, 2023
Copy link
Contributor

@geoffw0 geoffw0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good early results, this looks promising.

ints.withUnsafeBytes({(p: UnsafeRawBufferPointer) in v.field = p}) // BAD
print(v.field)

ints.withUnsafeBytes(myPrint) // GOOD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another variant here would be calling a member function that stores the pointer argument in the object it's called on for later use. The effect is similar to the v.field = p case.


predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
isSink(node, _) and
c = any(DataFlow::ContentSet set)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a cartesian product - might be a performance issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is (and I hit the performance issue on a real database). I've shrunk it by narrowing the sinks.


from Flow::PathNode source, Flow::PathNode sink
where Flow::flowPath(source, sink)
select sink, source, sink, "This unsafe parameter may escape its invocation"

Check warning

Code scanning / CodeQL

Alert message style violation

Alert message should end with a full stop.
@github-actions
Copy link
Contributor

QHelp previews:

swift/ql/src/queries/Security/CWE-825/UnsafePointerEscapesClosure.qhelp

Unsafe pointer escapes closure

Certain functions take a closure and pass a temporary pointer into it. If this pointer escapes from the closure and is used outside it, memory corruption may occur.

Recommendation

Do not use temporary pointers outside the closure they are passed to.

Example

In the first example below, the pointer is returned from the closure, potentially leading to memory corruption. In the second example, all work with the pointer is done inside the closure, and it is not returned.

func bad() {
    let ints = [1,2,3]
    let bytes = ints.withUnsafeBytes{
        return $0
    }
    print(bytes)
}

func good() {
    let ints = [1,2,3]
    let bytes = ints.withUnsafeBytes{
        print($0)
    }
}

References

)
}

additional predicate isUnsafePointerFunction(Function f) {
exists(CallExpr call |
isUnsafePointerCall(call) and
f.getAnAccess() = call.getArgument(0).getExpr()
f.getAnAccess() = call.getAnArgument().getExpr()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe that this can match arguments other than the intended closure? If it's possible to pass a closure or function to another argument that is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might need to revisit this if there's a case where the function takes multiple closures, but everything I've modeled only takes one closure argument.

ints.withUnsafeBytes({(p: UnsafeRawBufferPointer) in
v.field = p
return 1
}) // BAD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the issue with implicit returns? Surely we will see them in real world code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that we were picking up the implicit return of the assignment, but not the field being assigned. This is currently not detected... I think there's some missing flow for closure captures.

* @kind path-problem
* @id swift/unsafe-pointer-escapes-closure
* @tags security
* external/cwe/cwe-825
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs precision, severity and security-severity tags. I guess we want to test with a near-final version before choosing a precision.


<references>
<li><a href="https://developer.apple.com/documentation/swift/array/withunsafebytes(_:)">withUnsafeBytes</a></li>
</references>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants