Skip to content

Commit cc8f8e9

Browse files
authored
SR-7732: Dynamic casting CFError to Error results in a memory leak (#28686)
* SR-7732: Dynamic casting CFError to Error results in a memory leak The special handling for casting CFError/NSError to Swift Error type was using cleanup code that didn't correctly handle this case. This replaces the cleanup code with a more targeted version. Fixes: SR-7732 Fixes: rdar://problem/40423061 * Whitespace fixes * Don't rely on localizable strings to verify test behavior. I've verified this simplified test still leaks with the original code and does not leak with the fixed code. * Don't test against old runtimes that predate this fix * Explicitly test both NSError and CFError
1 parent ee43a79 commit cc8f8e9

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

stdlib/public/runtime/Casting.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,7 +911,9 @@ static bool _dynamicCastToExistential(OpaqueValue *dest,
911911
srcDynamicType,
912912
errorWitness)) {
913913
*destBoxAddr = reinterpret_cast<SwiftError*>(embedded);
914-
maybeDeallocateSource(true);
914+
if (shouldDeallocateSource(true, flags)) {
915+
srcType->vw_destroy(src);
916+
}
915917
return true;
916918
}
917919
#endif

test/stdlib/ErrorBridged.swift

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -684,9 +684,9 @@ ErrorBridgingTests.test("Wrapped NSError identity") {
684684
}
685685

686686
extension Error {
687-
func asNSError() -> NSError {
688-
return self as NSError
689-
}
687+
func asNSError() -> NSError {
688+
return self as NSError
689+
}
690690
}
691691

692692
func unconditionalCast<T>(_ x: Any, to: T.Type) -> T {
@@ -788,4 +788,31 @@ ErrorBridgingTests.test("error-to-NSObject casts") {
788788
}
789789
}
790790

791+
// SR-7732: Casting CFError or NSError to Error results in a memory leak
792+
ErrorBridgingTests.test("NSError-to-Error casts") {
793+
func should_not_leak_nserror() {
794+
let something: Any? = NSError(domain: "Foo", code: 1)
795+
expectTrue(something is Error)
796+
}
797+
798+
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
799+
// TODO: Wrap some leak checking around this
800+
// Until then, this is a helpful debug tool
801+
should_not_leak_nserror()
802+
}
803+
}
804+
805+
ErrorBridgingTests.test("CFError-to-Error casts") {
806+
func should_not_leak_cferror() {
807+
let something: Any? = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainCocoa, 1, [:] as CFDictionary)
808+
expectTrue(something is Error)
809+
}
810+
811+
if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
812+
// TODO: Wrap some leak checking around this
813+
// Until then, this is a helpful debug tool
814+
should_not_leak_cferror()
815+
}
816+
}
817+
791818
runAllTests()

0 commit comments

Comments
 (0)