Fatal error: Insufficient space allocated to copy string contents (Emoji related)

I have this crash since iOS 13 update:

// Test. This code crashes even in a playground.
let h = 3, w = 3
// No crash without "📃" symbol!
let fieldStrings: [String] = ["📃", "3", "3", "4", "1", "2", "2", "4", "2"]
let maxLength = fieldStrings.reduce(0) { (maxLength, s) in max(maxLength, s.count) }
print(maxLength) // 1
// No crash without padding!
let paddedFieldStrings = fieldStrings.map { s in
    s.padding(toLength: maxLength, withPad: " ", startingAt: 0)
}
var lines = [String]()
for y in 0..<h {
    var line = ""
    for x in 0..<(w - 1) {
        // CRASH HERE! x = 0, y = 0
        line += paddedFieldStrings[x * h + y] + "  "
    }
    line += paddedFieldStrings[(w - 1) * h + y]
    lines.append(line)
}

U+1F4C3 :page_with_curl:may be replaced with any other emoji and the code still going to crash.

Crash is always reproduced on iOS13.1.2 device and iOS 13.0 simulator, but there is no crash on iOS 12.2 simulator.

It looks like Swift runtime bug. I don't know where to report it so I send a feedback to Apple and created a question on SO. Someone who have experience with https://bugs.swift.org, please to create an issue.

Update:
Try to run this in playground:
print("📃".padding(toLength: 1, withPad: " ", startingAt: 0))

You'll get really large exception, like: 07
Sorry, playgrounds doesn't allow to copy text of exceptions, so I can only provide the screenshot.

1 Like

method padding(toLength:withPad:startingAt:) is defined in Foundation, which suggests it's not a String method, but NSString one (also the fact that startingAt is an Int, not String.Index). NSString works with utf-16 code units, where ":page_with_curl:" has length of 2, not 1. It shouldn't mangle your string anyway, so that's both a bug in your code, and in Foundation

I can reproduce it in Obj-C

        NSString *foo = @"🖼";
        NSString *bar = [foo stringByPaddingToLength:1 withString:@" " startingAtIndex:0];
        NSLog(@"%@", foo); // 2019-10-09 10:10:44.324711+0200 objctest[18415:75824] 🖼
        NSLog(@"%@", bar); // 2019-10-09 10:10:44.325174+0200 objctest[18415:75824] \ud83d

Worth filing a radar ( https://feedbackassistant.apple.com ), but not an issue on bugs.swift.org

I filed it for you as FB7361456

edit: Oops, I missed that you sent a feedback already, and wanted help just with bugs.swift.org

2 Likes
  1. Thanks for filing a radar, more similar issues – more attention to them.

  2. Despite the padding(toLength:withPad:startingAt:) is in Foundation, the call stack on crash shows Swift methods:

    |#1|0x00000001d27a4b98 in _swift_stdlib_reportFatalError ()|
    |#2|0x00000001d26b65c0 in specialized _fatalErrorMessage(_:_:file:line:flags:) ()|
    |#3|0x00000001d26b9bc8 in specialized _copyCollectionToContiguousArray<A>(_:) ()|
    |#4|0x00000001d261cbec in _StringGuts._foreignGrow(_:) ()|
    |#5|0x00000001d261cff0 in _StringGuts.append(_:) ()|
    |#6|0x00000001d26b9fb8 in specialized _StringGuts.append(_:) ()|
    |#7|0x0000000100785010 in specialized static String.+ infix(_:_:) [inlined] ()|
    |#8|0x0000000100785000 in static String.+ infix(_:_:) [inlined] ()|
    |#9|0x0000000100785000 in specialized AppDelegate.application(_:didFinishLaunchingWithOptions:) at ...
    

So I believe that Foundation is partially implemented on Swift (?).

  1. So, my mistake, talking simple, was to assume that count == length? How can I fix it? I suppose somehow change maxLength calculation, or may be convert everything to NSString.
1 Like

I think the crash comes from the + operator function that doesn't expect invalid strings passed to it. In my opinion crashing is an okay thing to do when that happens, maybe the error message isn't optimal. (But that's just my opinion, if you think that's a thing that shouldn't happen, I can help you with making an account for bugs.swift.org and creating an issue there)

Yes. I am not sure if bridging between NSString and String can break anything (If there's anyone reading this that knows how objc bridging works, please reassure me) but I think you can change s.count to either s.utf16.count or s.length and it will work

Yes. Offsets into String.UTF16View are semantically equivalent to offsets into NSString.

Bridging can invalidate indices (String.UTF8View.Index), but not offsets (Int from distance(from:to:), count, etc.).

3 Likes

This sounds like a known bug that @David_Smith fixed recently. David, could this be related?

1 Like

It does sound very similar. If so, I merged the fix to the 5.1 branch recently.

2 Likes