-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Swift: Query for escaping parameters of unsafe closures #13706
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
base: main
Are you sure you want to change the base?
Changes from 2 commits
1cac879
83a787e
f27522d
db18915
8120c8b
459eea5
f125fa2
5f39a1a
37d69e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
WARNING: Unused variable c (test.ql:51,73-74) | ||
edges | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0] | | ||
| file://:0:0:0:0 | [post] self [field, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | | ||
| file://:0:0:0:0 | [post] self [field, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0] | | ||
| file://:0:0:0:0 | [post] self [field] | withUnsafeBytes.swift:33:9:33:9 | self[return] | | ||
| file://:0:0:0:0 | [post] self [field] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field] | | ||
| file://:0:0:0:0 | value | file://:0:0:0:0 | [post] self [field] | | ||
| file://:0:0:0:0 | value [some:0, some:0, some:0, some:0] | file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | value [some:0, some:0, some:0] | file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | value [some:0, some:0] | file://:0:0:0:0 | [post] self [field, some:0, some:0] | | ||
| file://:0:0:0:0 | value [some:0] | file://:0:0:0:0 | [post] self [field, some:0] | | ||
| withUnsafeBytes.swift:9:25:9:25 | $0 | withUnsafeBytes.swift:10:16:10:16 | $0 | | ||
| withUnsafeBytes.swift:13:34:13:37 | p | withUnsafeBytes.swift:13:97:13:97 | p | | ||
| withUnsafeBytes.swift:15:34:15:37 | p | withUnsafeBytes.swift:15:109:15:109 | p | | ||
| withUnsafeBytes.swift:15:109:15:109 | p | withUnsafeBytes.swift:15:97:15:110 | call to id(pointer:) | | ||
| withUnsafeBytes.swift:15:109:15:109 | p | withUnsafeBytes.swift:28:12:28:21 | pointer | | ||
| withUnsafeBytes.swift:20:28:20:31 | p | withUnsafeBytes.swift:20:68:20:68 | p | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | withUnsafeBytes.swift:20:68:20:68 | p [some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | withUnsafeBytes.swift:33:9:33:9 | value | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0] | | ||
| withUnsafeBytes.swift:28:12:28:21 | pointer | withUnsafeBytes.swift:29:12:29:12 | pointer | | ||
| withUnsafeBytes.swift:28:12:28:21 | pointer | withUnsafeBytes.swift:29:12:29:12 | pointer | | ||
| withUnsafeBytes.swift:33:9:33:9 | value | file://:0:0:0:0 | value | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0, some:0] | file://:0:0:0:0 | value [some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0] | file://:0:0:0:0 | value [some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0] | file://:0:0:0:0 | value [some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0] | file://:0:0:0:0 | value [some:0] | | ||
nodes | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0, some:0] | semmle.label | [post] self [field, some:0, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0, some:0] | semmle.label | [post] self [field, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | [post] self [field, some:0, some:0] | semmle.label | [post] self [field, some:0, some:0] | | ||
| file://:0:0:0:0 | [post] self [field, some:0] | semmle.label | [post] self [field, some:0] | | ||
| file://:0:0:0:0 | [post] self [field] | semmle.label | [post] self [field] | | ||
| file://:0:0:0:0 | value | semmle.label | value | | ||
| file://:0:0:0:0 | value [some:0, some:0, some:0, some:0] | semmle.label | value [some:0, some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | value [some:0, some:0, some:0] | semmle.label | value [some:0, some:0, some:0] | | ||
| file://:0:0:0:0 | value [some:0, some:0] | semmle.label | value [some:0, some:0] | | ||
| file://:0:0:0:0 | value [some:0] | semmle.label | value [some:0] | | ||
| withUnsafeBytes.swift:9:25:9:25 | $0 | semmle.label | $0 | | ||
| withUnsafeBytes.swift:10:16:10:16 | $0 | semmle.label | $0 | | ||
| withUnsafeBytes.swift:13:34:13:37 | p | semmle.label | p | | ||
| withUnsafeBytes.swift:13:97:13:97 | p | semmle.label | p | | ||
| withUnsafeBytes.swift:15:34:15:37 | p | semmle.label | p | | ||
| withUnsafeBytes.swift:15:97:15:110 | call to id(pointer:) | semmle.label | call to id(pointer:) | | ||
| withUnsafeBytes.swift:15:109:15:109 | p | semmle.label | p | | ||
| withUnsafeBytes.swift:20:28:20:31 | p | semmle.label | p | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... | semmle.label | ... = ... | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0, some:0] | semmle.label | ... = ... [field, some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0] | semmle.label | ... = ... [field, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0] | semmle.label | ... = ... [field, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0] | semmle.label | ... = ... [field, some:0] | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... [field] | semmle.label | ... = ... [field] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | semmle.label | p | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | semmle.label | p [some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | semmle.label | p [some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | semmle.label | p [some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | semmle.label | p [some:0] | | ||
| withUnsafeBytes.swift:28:12:28:21 | pointer | semmle.label | pointer | | ||
| withUnsafeBytes.swift:28:12:28:21 | pointer | semmle.label | pointer | | ||
| withUnsafeBytes.swift:29:12:29:12 | pointer | semmle.label | pointer | | ||
| withUnsafeBytes.swift:29:12:29:12 | pointer | semmle.label | pointer | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] | semmle.label | self[return] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] | semmle.label | self[return] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] | semmle.label | self[return] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] | semmle.label | self[return] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] | semmle.label | self[return] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0, some:0, some:0] | semmle.label | self[return] [field, some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0, some:0] | semmle.label | self[return] [field, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0] | semmle.label | self[return] [field, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0] | semmle.label | self[return] [field, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | self[return] [field] | semmle.label | self[return] [field] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value | semmle.label | value | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0, some:0] | semmle.label | value [some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0] | semmle.label | value [some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0] | semmle.label | value [some:0, some:0] | | ||
| withUnsafeBytes.swift:33:9:33:9 | value [some:0] | semmle.label | value [some:0] | | ||
subpaths | ||
| withUnsafeBytes.swift:15:109:15:109 | p | withUnsafeBytes.swift:28:12:28:21 | pointer | withUnsafeBytes.swift:29:12:29:12 | pointer | withUnsafeBytes.swift:15:97:15:110 | call to id(pointer:) | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | withUnsafeBytes.swift:33:9:33:9 | value | withUnsafeBytes.swift:33:9:33:9 | self[return] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p | withUnsafeBytes.swift:33:9:33:9 | value | withUnsafeBytes.swift:33:9:33:9 | self[return] [field] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0, some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0, some:0] | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] | withUnsafeBytes.swift:20:58:20:68 | ... = ... | | ||
| withUnsafeBytes.swift:20:68:20:68 | p [some:0] | withUnsafeBytes.swift:33:9:33:9 | value [some:0] | withUnsafeBytes.swift:33:9:33:9 | self[return] [field, some:0] | withUnsafeBytes.swift:20:58:20:68 | ... = ... [field, some:0] | | ||
#select | ||
| withUnsafeBytes.swift:10:16:10:16 | $0 | withUnsafeBytes.swift:9:25:9:25 | $0 | withUnsafeBytes.swift:10:16:10:16 | $0 | This unsafe parameter may escape its invocation | | ||
| withUnsafeBytes.swift:13:97:13:97 | p | withUnsafeBytes.swift:13:34:13:37 | p | withUnsafeBytes.swift:13:97:13:97 | p | This unsafe parameter may escape its invocation | | ||
| withUnsafeBytes.swift:15:97:15:110 | call to id(pointer:) | withUnsafeBytes.swift:15:34:15:37 | p | withUnsafeBytes.swift:15:97:15:110 | call to id(pointer:) | This unsafe parameter may escape its invocation | | ||
| withUnsafeBytes.swift:20:58:20:68 | ... = ... | withUnsafeBytes.swift:20:28:20:31 | p | withUnsafeBytes.swift:20:58:20:68 | ... = ... | This unsafe parameter may escape its invocation | | ||
| withUnsafeBytes.swift:29:12:29:12 | pointer | withUnsafeBytes.swift:28:12:28:21 | pointer | withUnsafeBytes.swift:29:12:29:12 | pointer | This unsafe parameter may escape its invocation | |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import swift | ||
import codeql.swift.dataflow.DataFlow | ||
import Flow::PathGraph | ||
|
||
module Conf implements DataFlow::StateConfigSig { | ||
class FlowState = Callable; | ||
|
||
predicate isSource(DataFlow::Node node, FlowState state) { | ||
// parameter of closure or function passed to withUnsafeBytes | ||
exists( | ||
CallExpr call | | ||
call.getStaticTarget().hasName("withUnsafeBytes(_:)") and | ||
state = node.(DataFlow::ParameterNode).getDeclaringFunction().getUnderlyingCallable() and | ||
( | ||
// if the declaring callable is a closure expr | ||
state = call.getArgument(0).getExpr() | ||
or | ||
state.(Function).getAnAccess() = call.getArgument(0).getExpr() | ||
) | ||
) | ||
} | ||
|
||
predicate isSink(DataFlow::Node node, FlowState state) { | ||
node.(DataFlow::InoutReturnNode).getParameter().getDeclaringFunction() = state | ||
geoffw0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
or | ||
exists(ReturnStmt stmt | | ||
node.asExpr() = stmt.getResult() and | ||
stmt.getEnclosingCallable() = state | ||
) | ||
} | ||
|
||
predicate isBarrier(DataFlow::Node node) { | ||
none() | ||
} | ||
predicate isBarrierIn(DataFlow::Node node) { | ||
none() | ||
} | ||
predicate isBarrierOut(DataFlow::Node node) { | ||
none() | ||
} | ||
predicate isBarrier(DataFlow::Node node, FlowState state) { | ||
none() | ||
} | ||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() } | ||
predicate isAdditionalFlowStep(DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2) { | ||
none() | ||
} | ||
|
||
int fieldFlowBranchLimit() { result = 2 } | ||
|
||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) { | ||
isSink(node, _) and | ||
c = any(DataFlow::ContentSet set) | ||
|
||
} | ||
} | ||
|
||
module Flow = DataFlow::GlobalWithState<Conf>; | ||
|
||
from Flow::PathNode source, Flow::PathNode sink | ||
where Flow::flowPath(source, sink) | ||
select sink, source, sink, "This unsafe parameter may escape its invocation" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// stubs | ||
|
||
|
||
|
||
// tests | ||
|
||
func arrayTest() { | ||
let ints = [1,2,3] | ||
ints.withUnsafeBytes{ // BAD | ||
return $0 | ||
} | ||
|
||
print(ints.withUnsafeBytes({(p: UnsafeRawBufferPointer) -> UnsafeRawBufferPointer in return p})) // BAD | ||
|
||
print(ints.withUnsafeBytes({(p: UnsafeRawBufferPointer) -> UnsafeRawBufferPointer in return id(pointer: p)})) // BAD | ||
|
||
ints.withUnsafeBytes({(p: UnsafeRawBufferPointer) in print(p)}) // GOOD | ||
|
||
var v = PointerHolder() | ||
ints.withUnsafeBytes({(p: UnsafeRawBufferPointer) in v.field = p}) // BAD | ||
print(v.field) | ||
geoffw0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
ints.withUnsafeBytes(myPrint) // GOOD | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
|
||
myPrint(p: ints.withUnsafeBytes(id)) // BAD | ||
} | ||
|
||
func id<T>(pointer: T) -> T { | ||
return pointer | ||
} | ||
|
||
struct PointerHolder { | ||
var field: UnsafeRawBufferPointer? | ||
} | ||
|
||
func myPrint(p: UnsafeRawBufferPointer) { | ||
print(p) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.