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-7493] EXC_BAD_ACCESS of NSError via caught error.localizedDescription, only in Archive builds! #50036

Closed
swift-ci opened this issue Apr 21, 2018 · 10 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself optimized only Flag: An issue whose reproduction requires optimized compilation

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-7493
Radar rdar://problem/39810532
Original Reporter natevw (JIRA User)
Type Bug
Status Resolved
Resolution Done

Attachment: Download

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

md5: b1e28c85954878764a8d6b92b44b49d5

Issue Description:

I have a vexing issue with my production builds under Xcode 9.3 (9E145).

In a certain catch blocks, calls to `error.localizedString` crash in Archive builds:

* thread #​1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x18)
 frame #​0: 0x000000010012fee4 MyTarget`(extension in Foundation):Swift.Error.localizedDescription.getter : Swift.String + 100
MyTarget`(extension in Foundation):Swift.Error.localizedDescription.getter : Swift.String:
-> 0x10012fee4 <+100>: callq *0x18(%rsi)
 0x10012fee7 <+103>: movq %rax, %r12
 0x10012feea <+106>: movq -0x38(%rbp), %rax
 0x10012feee <+110>: movq 0x8(%rax), %r13

When I run (Debug configuration) from Xcode there is no issue, nor even when I edit the scheme to use Release configuration for the Run action.

When I "Archive" the product the issue is encountered — even when turning off Swift Compiler and LLVM optimization settings!

I took some time to try parse through the differences in swiftc compiler and clang linker commands between the two builds. There didn't appear any swiftc difference, only an extra bit about "-Xlinker -final_output -Xlinker MyTarget" in the clang linker call.

Unfortunately I have not been able to reduce this in a sample project but after updating to Xcode 9.3 it is happening reliably for this target's Archive builds.

@bob-wilson
Copy link

Strange! I have no ideas offhand. If you can share your full project, one option would be to file an Apple bug report (radar) and attach the project so we can try to reproduce the issue. Otherwise, unless someone happens to recognize this as a known issue, I'm not sure we can help you.

@swift-ci
Copy link
Collaborator Author

Comment by Nathan Vander Wilt (JIRA)

Thanks Bob. After a bit more wrangling it turns out this is really simple to reproduce, fortunately!

The issue stems from the error instance. I was trying unsuccessfully to reproduce with a Swift error, but it turns out the error was bubbling up from lower down, presumably an NSError.

1. Put this in main.swift of a new Xcode 9.3 (9E145)'s macOS "Command Line Tool" project template:

import Foundation
do {
  _ = try Data(contentsOf: URL(fileURLWithPath: "/tmp/not/exist/file/missing"))
} catch {
  NSLog("About to access `error.localizedDescription`")
  print(error.localizedDescription)
  NSLog("Done. No crash.")
}

2. Product > Run — all three lines will be output, finishing with "Done. No crash." as expected

3. Product > Archive — run the executable inside the Archive directly from terminal or via lldb. It will log "About to access" and then… Segmentation fault: 11

@swift-ci
Copy link
Collaborator Author

Comment by Nathan Vander Wilt (JIRA)

Tested against Xcode 9.2 / Swift 4.0 and same behavior:

  • Build and Run (Debug): fine

  • Build for Profiling (Release): fine

  • Archive: segfaults whenever an NSError is logged

Again the only non-path difference I've been able to spot so far is that in the Archive build some extra

-Xlinker -final_output

flags are seen in the build log.

@swift-ci
Copy link
Collaborator Author

Comment by Nathan Vander Wilt (JIRA)

Attached a sample (back in Xcode 9.3 again) project.

This is a significant blocker for my client, since our packaging process is based around Archive builds:

Everything works fine when testing, but randomly "explodes" when deployed!

@bob-wilson
Copy link

@swift-ci create

@bob-wilson
Copy link

Thanks, Nathan. We'll investigate.

@swift-ci
Copy link
Collaborator Author

swift-ci commented May 4, 2018

Comment by Simon Corsin (JIRA)

I also encountered the same exact issue. On release and debug through Xcode it works, after archiving it crashes. A workaround that works for me natevw (JIRA User) is to explicitly mark the error as NSError:

do {
  // some code that can throw
} catch let error as NSError {
  let errorMessage = error.localizedDescription
}

If I don't it will crash at runtime when calling localizedDescription. The underlying error in that case is an NSError that comes from Objective-C code

@swift-ci
Copy link
Collaborator Author

swift-ci commented May 4, 2018

Comment by Nathan Vander Wilt (JIRA)

Thanks for the confirmation Simon!

I'm still hoping for more of a "build settings"–level workaround but if worse comes to worse we might have to add something like that to all our catch blocks for the time being.

@swift-ci
Copy link
Collaborator Author

Comment by Nathan Vander Wilt (JIRA)

Figured out the difference! Archive build is calling /usr/bin/strip, which the Release build doesn't do. Confirmed that if I build a Debug/Release build and then call strip on the resulting product it exhibits the same behavior.

Thus a workaround for this issue is to set STRIP_INSTALLED_PRODUCT = NO (i.e. in Build Settings, in the Deployment section, set "Strip Linked Product" to No). I see there are other settings "Strip Style" and "Strip Swift Symbols" that may provide a more fine-grained workaround but haven't experimented with those yet.

UPDATE

Turning off STRIP_SWIFT_SYMBOLS has no effect, however setting STRIP_STYLE to either 'non-global' or 'debugging' (anything but the default 'all') does prevent the segfault without having to turn STRIP_INSTALLED_PRODUCT off entirely.

UPDATE #2

Leaving all other Deployment configuration alone and setting "STRIPFLAGS = -u" also avoids the segfault. This is documented in `man 1 strip` as meaning:

> Save all undefined symbols. This is intended for use with relocatable objects to save symbols referred to by external relocation entries.

A few other promising options (e.g. -r to "Save all symbols referenced dynamically") only caused the strip command to become upset.

It might be possible to specifically whitelist a few symbols using -s to pass a list file. But I'm neither sure what the symbol for the Error/NSError localizedDescription would be nor whether that symbol is the only problematic one or merely the first noticed.

@mikeash
Copy link
Contributor

mikeash commented May 18, 2018

This has been fixed in #16677 I changed the symbol lookup mechanism to add a bit of indirection and marked the new indirect symbols as dynamically referenced, which preserves them when stripping.

To work around it until you're able to use that, you can find the symbols to whitelist in ErrorObject.mm. They are:

  • _$SSo10CFErrorRefas5Error10FoundationWa

  • _$SSo8NSObjectCs8Hashable10ObjectiveCWa

  • _$S10Foundation24_getErrorDefaultUserInfoyyXlSgxs0C0RzlF

  • _$S10Foundation21_bridgeNSErrorToError_3outSbSo0C0C_SpyxGtAA021_ObjectiveCBridgeableE0RzlF

  • _$S10Foundation26_ObjectiveCBridgeableErrorMp

(Depending on how far you go back, there may be a different prefix on the names. You can check your unstripped binary with nm -g and search for So10CFErrorRefas5Error10FoundationWa to verify that.)

Or, of course, skipping the strip phase is a simple way to get it working for now.

@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 optimized only Flag: An issue whose reproduction requires optimized compilation
Projects
None yet
Development

No branches or pull requests

3 participants