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

[SR-7732] Dynamic casting CFError to Error results in a memory leak #3701

Closed
CharlesJS opened this issue May 21, 2018 · 5 comments
Closed

[SR-7732] Dynamic casting CFError to Error results in a memory leak #3701

CharlesJS opened this issue May 21, 2018 · 5 comments

Comments

@CharlesJS
Copy link

Previous ID SR-7732
Radar rdar://problem/40423061
Original Reporter @CharlesJS
Type Bug
Status Resolved
Resolution Done

Attachment: Download

Environment

Xcode 9.3

Xcode 9.4 beta 2

Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.37.1)

Apple Swift version 4.1.1 (swiftlang-902.0.53 clang-902.0.39.2)

Swift version 4.2-dev (LLVM 072be1cf17, Clang c7422ffe24, Swift 3843d39297)

Additional Detail from JIRA
Votes 0
Component/s Foundation, Standard Library
Labels Bug, Leak
Assignee @tbkka
Priority Medium

md5: b68a4ce9dd61b2117ff719fbbeb397a6

Issue Description:

If you have an Any? containing a CFError, and you cast that to an Error, your CFError object will leak. If you cast using as? NSError instead of as? Error, the leak does not occur.

Code:

import Foundation

func leak() {
    let something: Any? = CFErrorCreate(kCFAllocatorDefault, "Foo" as CFString, 1, nil)
    
    if let error = something as? Error {
        print("Error: \(error.localizedDescription)")
    }
}

leak()

print("Now run leaks on the process. You will find that the CFError has leaked.")

CFRunLoopRun()

Retain-release history of the CFError according to Instruments:

0    Malloc    +1    1    00:02.329.423    ErrorLeak    leak()
1    Retain    +1    2    00:02.329.615    ErrorLeak    swift::metadataimpl::BufferValueWitnesses<swift::metadataimpl::ValueWitnesses<swift::metadataimpl::ObjCRetainableBox>, 8ul, 8ul, (swift::metadataimpl::FixedPacking)1>::initializeBufferWithCopyOfBuffer(swift::ValueBuffer*, swift::ValueBuffer*, swift::TargetMetadata<swift::InProcess> const*)
2    Retain    +1    3    00:02.330.503    ErrorLeak    swift::metadataimpl::ValueWitnesses<swift::metadataimpl::ObjCRetainableBox>::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*)
3    Retain    +1    4    00:02.330.537    ErrorLeak    protocol witness for Error._getEmbeddedNSError() in conformance NSError
4    Release    -1    3    00:02.330.539    ErrorLeak    _getErrorEmbeddedNSErrorIndirect<A>(_:)
5    Retain    +1    4    00:02.331.733    ErrorLeak    swift::metadataimpl::ValueWitnesses<swift::metadataimpl::ObjCRetainableBox>::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*)
6    Retain    +1    5    00:02.331.766    ErrorLeak    swift::metadataimpl::ValueWitnesses<swift::metadataimpl::ObjCRetainableBox>::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*)
7    Retain    +1    6    00:02.331.767    ErrorLeak    swift::metadataimpl::ValueWitnesses<swift::metadataimpl::ObjCRetainableBox>::initializeWithCopy(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetMetadata<swift::InProcess> const*)
8    Retain    +1    7    00:02.331.801    ErrorLeak    _T0s5ErrorP10FoundationE20localizedDescriptionSSvg
9    Release    -1    6    00:02.331.802    ErrorLeak    _T0s5ErrorP10FoundationE20localizedDescriptionSSvg
10    Release    -1    5    00:02.331.803    ErrorLeak    _T0s5ErrorP10FoundationE20localizedDescriptionSSvg
11    Release    -1    4    00:02.376.758    ErrorLeak    _T0s5ErrorP10FoundationE20localizedDescriptionSSvg
12    Release    -1    3    00:02.376.759    ErrorLeak    leak()
13    Release    -1    2    00:02.377.918    ErrorLeak    leak()
14    Release    -1    1    00:02.377.919    ErrorLeak    _T0ypSgWh0_

Lines 2 through 4 appear to contain the key. Examining the traces for each in Instruments reveals that each of these lines occurs within _getErrorEmbeddedNSErrorIndirect. In line 2, initializeWithCopy retains the error; in line 3, protocol witness for Error._getEmbeddedNSError() retains it again, and then in line 3, it's released by _getErrorEmbeddedNSErrorIndirect. The second retain, however, is never balanced.

I've included a sample project to demonstrate the problem. Run it, then run the leaks command (or examine it in Instruments) to see the leak. Change "as? Error" to "as? NSError" to see the leak go away.

@belkadan
Copy link

Thanks, Charles.

@swift-ci create

@belkadan
Copy link

Confirmed that it's not just an autoreleasepool problem, too (just in case Charles' analysis was incorrect).

Backslash can escape underscores: _like this_.

@CharlesJS
Copy link
Author

Thanks for the info re: underscores.

@tbkka
Copy link
Contributor

tbkka commented Dec 12, 2019

Fixed by PR #28686

apple/swift#28686

@tbkka
Copy link
Contributor

tbkka commented Dec 13, 2019

Also fixed in 5.2 branch in PR #28754

apple/swift#28754

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants