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-11040] EXC_BAD_ACCESS in Swift when accessing @optional ObjC properties #53429

Closed
swift-ci opened this issue Jun 28, 2019 · 5 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-11040
Radar None
Original Reporter bnut (JIRA User)
Type Bug
Status Resolved
Resolution Duplicate

Attachment: Download

Environment

{code:bash}$ xcodebuild -version
Xcode 10.2.1
Build version 10E1001
$ swift --version
Apple Swift version 5.0.1 (swiftlang-1001.0.82.4 clang-1001.0.46.5)
Target: x86_64-apple-darwin18.2.0

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: c3b9d6818264022e889eba045206c98b

duplicates:

  • SR-8622 Nonnull Objective-C property that falsely returns nil causes inconsistent Swift behavior

Issue Description:

Swift code can crash at runtime with EXC_BAD_ACCESS when you use a nonnull property as an @optional requirement in ObjC that mistakenly returns nil.

This happens in debug and release builds with unguarded access to that property, and only in release if used in an if let x = obj.objcProperty.

It only seems to occur when the property isn't a bridged type (it doesn't crash with String).

@protocol SomeProtocol
@optional
@property (nonnull) NSString * someString;
@property (nonnull) SomeSwiftType * someObject;
@end
@implementation TheBug (SomeProtocol)
-(NSString *) someString { // Calling this works.
    return nil;
}
-(SomeSwiftType *) someObject { // Calling this crashes.
    return nil;
}
@end

The crash output looks like this:

Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)

See the attached project for a more nuanced example (ViewController.swift). Note that you will have to run on the debug or release schemes to see all effects.

@swift-ci
Copy link
Collaborator Author

Comment by Andrew Bennett (JIRA)

This might be a duplicate of SR-8622. It was determined in that one by @belkadan that:

The cost of checking every nonnull return value was determined to be too high, but maybe we could do it in Debug builds.

Although it seems like it's already being checked for bridged types, and in this particular case it may not be too expensive to treat it as .none rather than .some(nil!).

@swift-ci
Copy link
Collaborator Author

Comment by Ding Ye (JIRA)

I am not sure if we can call it a Swift compiler bug. nonnull Methods/properties returning nil can causes undefined behaviour when used, and thus the inconsistency of Debug/Release modes technically makes sense.

The logic of if let x = obj.objcProperty can be broken down into the following two lines of code:

let tmp: SomeSwiftType? = obj.objcProperty  // Line 1
if let x = tmp {  }   // Line 2

In Line 1, an optional (i.e. enum) variable tmp is allocated. Then, it checks whether the getter of property objcProperty exists. If it exists, it injects .some to the enum tmp and maps value of the .some case to the result of the getter; otherwise, it injects .none to the enum tmp. Note that the injection is performed via inject-enum-addr, which yields undefined behaviour when the data for a mismatches case is stored.

In Line 2, it checks which enum case tmp is injected. If it is .some, it assigns the mapped value to x and then goes into the curly braces.

In your specific case, the inject-enum-addr in Line 1 has undefined behaviour, since the data nil mismatches SomeSwiftType for the .some case. Such undefined behaviour may lead to inconsistent observations under different circumstances (e.g., Release/Debug modes).

Anyway, it would be good if clang could raise warnings at the nil returning points in methods declared nonnull.

@swift-ci
Copy link
Collaborator Author

Comment by Andrew Bennett (JIRA)

Yeah, I don’t mind whether this is fixed at runtime, but because it only happens on release builds and a lot of the code is optimized out it means that it’s very hard to track down why it’s crashing.

We changed it to a required property that is nullable, and at least now ObjC has warnings when you do it wrong (don’t implement it).

The real bug IMO is that the nullability requirements of the ObjC interface are not enforced (or warnings) on the implementation. As you said dingobye (JIRA User). This seems inconsistent and incomplete, and for us lead to a difficult to find bug.

This ticket is probably mislabeled, I’m sorry, but there didn’t seem to be a better way to label it.

@swift-ci
Copy link
Collaborator Author

Comment by Ding Ye (JIRA)

For nonnull interfaces, we can do some improvements in any (or all) of the following three aspects:

[1] It should have been clearly documented somewhere that violating nonnull can introduce undefined behaviour. It makes sense that users themselves are responsible to keep the returned value nonnull (e.g., checking nullability in the ObjC code before returning)., however the consequence of violation should have been clearly presented.

[2] Improve the clang compiler to raise warnings when nil is found as a return value. However, it can only capture constant nil value, since ObjC is not strongly typed.

[3] Enforce the nullability at runtime. For every nonnull method, the compiler can help to synthesize code of nullability check immediately before returning. However, this introduces some overhead.

@belkadan
Copy link
Contributor

I think we should consolidate discussion in SR-8622, since the @optional ends up not figuring in here. These are all good points, though.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
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
Projects
None yet
Development

No branches or pull requests

2 participants