Xcode downloads client crash reports for string interpolation (_StringGuts.append)

Xcode contains several crash reports downloaded from users of my app. Thread 1 apparently crashes while performing a string interpolation. All the other threads only contain calls to system code.

The String.appendingPathComponent(_:) that appears in the stacktrace is defined as follows:

extension String {

    func appendingPathComponent(_ pathComponent: String) -> String {
        return pathComponent == "" ? self : self == "" || self == "/" ? "\(self)\(pathComponent)" : "\(self)/\(pathComponent)"
    }

}

What could cause such a crash?

Thread 1 Crashed:
0   CoreFoundation                	0x00007ff81566f9df __CFStringEncodeByteStream + 120 (CFStringEncodings.c:692)
1   Foundation                    	0x00007ff8164c95aa -[NSString(NSStringOtherEncodings) getBytes:maxLength:usedLength:encoding:options:range:remainingRange:] + 204 (NSStringEncodings.m:341)
2   libswiftCore.dylib            	0x00007ff822c6c1e0 String.UTF8View._foreignDistance(from:to:) + 208 (StringUTF8View.swift:507)
3   libswiftCore.dylib            	0x00007ff822c56715 _StringGuts.append(_:) + 1445 (StringGutsRangeReplaceable.swift:191)
4   MyApp                           0x00000001010c3c0f String.appendingPathComponent(_:) + 15 (<compiler-generated>:0)

Xcode contains several crash reports downloaded from users of my app.

Please post a full crash report, per the advice in Posting a Crash Report.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks, attached the full crash report.

That’s an interesting crash. Consider the crashing thread backtrace:

Thread 1 Crashed:
0 CoreFoundation     … __CFStringEncodeByteStream + 120 …
1 Foundation         … -[NSString(NSStringOtherEncodings) getBytes:maxLength:usedLength:encoding:options:range:remainingRange:] + 204 …
2 libswiftCore.dylib … String.UTF8View._foreignDistance(from:to:) + 208 …
3 libswiftCore.dylib … _StringGuts.append(_:) + 1445 …
4 MyApp              … String.appendingPathComponent(_:) + 15 …
5 MyApp              … Snapshot.renameFile(_:to:) + 863 (Snapshot+Modify.swift:135)
6 MyApp              … Snapshot.renameFile(_:to:) + 498 (Snapshot+Modify.swift:135)

Frames 6 and 5 are your code. Frame 4 is Swift standard library code that’s been inlined into your program. Frames 3 through 0 are Swift and Foundation implementation stuff.

What’s interesting is frames 6 and 5. There’s actually two oddities:

  • How did thread 1 get started? Normally the top of a thread leads to either Dispatch or pthreads, but in this case there’s neither.

  • What’s up with the symbolication? They both point to the same line of code, line 135 of Snapshot+Modify.swift, which seems unlikely.

The threading question leads directly to this:

Exception Type:        EXC_BAD_ACCESS (SIGBUS)
…

VM Region Info: 0x7000041c9ff8 is in 0x7000041c9000-0x7000041ca000;  bytes after start: 4088  bytes before end: 7
      REGION TYPE                 START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      Stack                 700004041000-7000040c3000 [  520K] rw-/rwx SM=PRV  thread 3
      GAP OF 0x106000 BYTES
--->  STACK GUARD           7000041c9000-7000041ca000 [    4K] ---/rwx SM=NUL  ... for thread 1
      Stack                 7000041ca000-70000424c000 [  520K] rw-/rwx SM=PRV  thread 1

The immediate cause of the crash was you accessing unmapped memory. Note that this address is a stack guard, that is, an unmapped page below the bottom of your stack that’s there to increase the probability of your catching stack overflows. Note that this is for thread 1, which gels with the fact that thread 1 crashed. So, thread 1 ran off the bottom of its stack and crashed.

But how? The stack seems to be half a MiB-ish in size, which is a lot. And you only have 7 frames on your stack.

The fact that the backtrace doesn’t lead back to Dispatch or pthreads suggests that something has gone wrong with the backtracing. So you really have run off the bottom of your stack but the backtrace isn’t showing all the relevant frames.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thank you very much for your insights.

How did thread 1 get started?

You're right, I didn't notice that the stacktrace looks strange. The code in thread 1 is called from inside NSFileCoordinator.coordinate(readingItemAt:options:writingItemAt:options:error:byAccessor:), which in turn is called inside DispatchQueue.global(qos: .userInitiated).async, with some other of my code before and after each of these calls.

What’s up with the symbolication?

I'm not sure I understand why the double line 135 seems unlikely. I can confirm that in this case it's a recursive method call. Could it be that the program somehow went into infinite recursion, explaining why the thread's origin is cut off?

I can confirm that in this case it's a recursive method call.

OK, thanks for confirming that. Most of the time I see this sort of thing it’s because the backtracer has lost the plot.

Could it be that the program somehow went into infinite recursion

That or just a very deep directory hierarchy (-:

explaining why the thread's origin is cut off?

Normally, an infinite recursion will produce a backtrace with 512-ish frames, which is an arbitrary limit imposed by the crash reporter. I suppose it might be possible that something about this specific backtrace is causing the backtracer to stop early.

You could test this. Set up a relatively deep directory hierarchy and then, in Snapshot.renameFile(_:to:), if you’re at some depth limit, trigger a crash. The crash report should show frames for each level of your recursion. If it only shows two frames you’ve confirmed that you’re hitting a limit in the backtracer.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

You could test this.

I manually caused an infinite recursion at the same location as the other crash report and I got a crash report with 511 frames. Frames numbered 9 to 510 are all the same, and since 510 is the last one, it looks like it's cut off. Otherwise the frames look very similar to the crash report downloaded by Xcode.

Thread 4 Crashed::  Dispatch queue: com.apple.root.user-initiated-qos
0   CoreFoundation                	       0x1837f5084 __CFStringEncodeByteStream + 76
1   Foundation                    	       0x184901558 -[NSString(NSStringOtherEncodings) getBytes:maxLength:usedLength:encoding:options:range:remainingRange:] + 260
2   libswiftCore.dylib            	       0x1931cf194 _NSStringCopyBytes(_:encoding:into:) + 124
3   libswiftCore.dylib            	       0x1931d67f0 _StringGuts._foreignCopyUTF8(into:) + 80
4   libswiftCore.dylib            	       0x1931d7bc8 _StringGuts._foreignConvertedToSmall() + 68
5   libswiftCore.dylib            	       0x1931d7b44 _StringGuts.append(_:) + 1232
6   libswiftCore.dylib            	       0x19316ba80 String.write<A>(to:) + 56
7   libswiftCore.dylib            	       0x19300bef0 DefaultStringInterpolation.appendInterpolation<A>(_:) + 72
8   MyApp                         	       0x1000e4538 String.appendingPathComponent(_:) + 924 (PathAdditions.swift:75)
9   MyApp                         	       0x100119138 Snapshot.renameFile(_:to:) + 696 (Snapshot+Modify.swift:135)
10  MyApp                         	       0x10011915c Snapshot.renameFile(_:to:) + 732 (Snapshot+Modify.swift:135)
...
510  MyApp                        	       0x10011915c Snapshot.renameFile(_:to:) + 732 (Snapshot+Modify.swift:135)

And it's also the same exception type:

Exception Type:        EXC_BAD_ACCESS (SIGBUS)
Exception Codes:       KERN_PROTECTION_FAILURE at 0x0000000170027fd8
Exception Codes:       0x0000000000000002, 0x0000000170027fd8

Termination Reason:    Namespace SIGNAL, Code 10 Bus error: 10
Terminating Process:   exc handler [66786]

VM Region Info: 0x170027fd8 is in 0x170024000-0x170028000;  bytes after start: 16344  bytes before end: 39
      REGION TYPE                    START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      Stack                       16ff9c000-170024000    [  544K] rw-/rwx SM=PRV  thread 3
--->  STACK GUARD                 170024000-170028000    [   16K] ---/rwx SM=NUL  ... for thread 4
      Stack                       170028000-1700b0000    [  544K] rw-/rwx SM=PRV  thread 4

I guess we solved the mystery? Probably the backtrace got cut off along the way between the user and my Xcode instance.

Thank you very much for your help!

Xcode downloads client crash reports for string interpolation (_StringGuts.append)
 
 
Q