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-10554] Crash: Bound function reference to protocol existential has nil self when invoked #52954

Open
JaviSoto opened this issue Apr 25, 2019 · 15 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software regression run-time crash Bug → crash: Swift code crashed during execution swift 5.0

Comments

@JaviSoto
Copy link
Contributor

Previous ID SR-10554
Radar rdar://problem/51276413
Original Reporter @JaviSoto
Type Bug

Attachment: Download

Environment

Xcode 10.2

Swift 5

iPhone OS 12.2 (16E227)

Additional Detail from JIRA
Votes 1
Component/s Compiler
Labels Bug, 5.0Regression, RunTimeCrash
Assignee None
Priority Medium

md5: 5c4ddc7a5ab66026df6b9cbe7c230868

Issue Description:

As best as we can figure, the crash we're seeing indicates that self is nil inside a method. The method in question was captured as a bound method from a protocol existential and then invoked later. We can't actually reproduce this ourselves though.

I'm fairly certain that this may be a Swift bug because this crash started occurring on the first release of the Twitch app that we submitted with Xcode 10.2 and the Swift 5 compiler (in Swift 5 mode). The previous few releases were built with Xcode 10.1 and Swift 4.2 and didn't exhibit this crash on the same version of the Apollo-ios library.

While it has affected only a small percentage of users on that release, it has become our #1 crash by a factor of 2.

I have attached an Xcode crash log. The crash happens in this line of this file: https://github.com/apollographql/apollo-ios/blob/0928061a06147bf973a921d424a4d871c7c30bf0/Sources/Apollo/InMemoryNormalizedCache.swift#L9 (Both Fabric and Xcode/iTunes Connect crash symbolication agree).

@belkadan
Copy link
Contributor

The high bit is set there, too, so I'm not sure that conclusion is correct. I wonder if it's related to some of the enums-with-one-payload issues that we've seen (cc aschwaighofer@apple.com (JIRA User)).

@lilyball
Copy link
Mannequin

lilyball mannequin commented Apr 26, 2019

Oh huh you're right the high bit is indeed set, it's 0x8000000000000010. Interestingly, the Crashlytics crash shows the invalid address as 0x0000000000000000. While we did notice the Apple crash ended in 10 instead of 00 we didn't notice the high bit had flipped too. I'm going to just chalk this up to yet another Crashlytics bug and assume the Apple log is correct.

@JaviSoto
Copy link
Contributor Author

@belkadan or aschwaighofer@apple.com (JIRA User) do you have another Jira for that enums issue you referenced?

As an update, users of our app hit this about 1000 times per day.

@aschwaighofer
Copy link
Member

@JaviSoto
Copy link
Contributor Author

Great, thank you!

@JaviSoto
Copy link
Contributor Author

Looking at the source code and that other Jira, I don't see how it can be related, but maybe I'm missing something. It seems like that issue was related to single-case enums, but there are no enums in this code?

@belkadan
Copy link
Contributor

We're not sure if it's related. There's not really enough information in this bug to be actionable.

@lilyball
Copy link
Mannequin

lilyball mannequin commented May 29, 2019

How can we gather more information? All we have right now is the crash log, the line it occurs on, and the fact that it didn't happen with the Swift 4.2 compiler.

@jckarter
Copy link
Member

By "bound protocol function", do you mean something like the case in https://bugs.swift.org/browse/SR-75 ? It could be that you're somehow "lucky" enough that your case miscompiles rather than crashing somewhere inside the compiler.

@lilyball
Copy link
Mannequin

lilyball mannequin commented May 30, 2019

Not quite. The code is accessing a bound function like that, except on a protocol existential, not on a protocol type. It's something like

protocol NormalizedCache {
    func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]>
    …
}

class ReadTransaction {
    let cache: NormalizedCache

    lazy var loader: DataLoader<CacheKey, Record?> = DataLoader(cache.loadRecords)
    …
}

class DataLoader<Key, Value> {
    let loader: ([Key]) -> Promise<[Value]>

    init(_ loader: @escaping ([Key]) -> Promise<[Value]>) {
        self.loader = loader
    }
    …
}

So cache is a protocol existential of type NormalizedCache and the bound function cache.loadRecords is retrieved and passed to another type, which stores it and then later invokes it (with self.loader(keys)).

The concrete NormalizedCache type involved here is something called InMemoryNormalizedCache, and the crash occurs inside its implementation of loadRecords at the first spot where the self value is accessed. So AFAICT the bound function cache.loadRecords is acting pretty similarly to something like

@belkadan
Copy link
Contributor

If you've got it narrowed down to that, you've got a potential workaround: use an explicit closure. If that makes it go away, it might be a problem with bound functions on existentials; if it doesn't, it could be a problem with lazy.

@lilyball
Copy link
Mannequin

lilyball mannequin commented May 30, 2019

The crash isn't in our code, it's in a third-party library that we depend on. And unfortunately the authors of that library are rather nonresponsive to issues so I have very little hope that we'd be able to convince them to change that to a closure.

If we knew how to reproduce this crash locally I'd test that as a temporary patch, but I don't think any of us have ever seen this crash ourselves, we've only gotten crash logs from users.

@belkadan
Copy link
Contributor

Got it. :-(

@JaviSoto
Copy link
Contributor Author

JaviSoto commented Jun 5, 2019

We manually patched the dependency code to try out that work-around in our latest release, but that did not fix the crash.

We changed from:

fileprivate lazy var loader: DataLoader<CacheKey, Record?> = DataLoader(self.cache.loadRecords)

to:

fileprivate lazy var loader: DataLoader<CacheKey, Record?> = DataLoader({ [cache] keys in return cache.loadRecords(forKeys: keys) })

@lilyball
Copy link
Mannequin

lilyball mannequin commented Jul 12, 2019

This has now skyrocketed to being our number 1 crasher.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software regression run-time crash Bug → crash: Swift code crashed during execution swift 5.0
Projects
None yet
Development

No branches or pull requests

5 participants