Crash issue related to CTRun and CTRunGetPositions

39 views Asked by At

I encountered a peculiar issue. In a project from many years ago, there is a piece of code like this:

func testFunc() {
 // ......
 let runs = CTLineGetGlyphRuns(finalLine) as! [CTRun]
 if let truncationRun = runs.last {
     var truncationPosition = CGPoint.zero
     CTRunGetPositions(truncationRun, CFRange(location: 0, length: 0), &truncationPosition)
     var truncationAscent: CGFloat = 0, truncationDescent: CGFloat = 0
     let truncationWidth = CTRunGetTypographicBounds(truncationRun, CFRange(location: 0, length: 0), &truncationAscent, &truncationDescent, nil)
     // ......
 }
 // ......

}

When the number of glyphs in truncationRun exceeds a certain value, a crash occurs after the function execution ends. This value varies across different iOS systems – on iOS 13, it is 11; on iOS 15, it is 7; and on iOS 17, it is 8. I do not understand why this happens?

enter image description here

Of course, if space for the trancationPosition control is allocated based on the CTRunGetGlyphCount() method, this code runs normally.

1

There are 1 answers

4
Rob Napier On

I assume your question is "why doesn't this crash when the run is short?" This code should definitely crash in the general case since it tries to write an array of CGPoints into space allocated for a single CGPoint. This corrupts other memory and crashes. Classic buffer overflow.

But as to why in some cases it doesn't crash, it depends heavily on what comes immediately after truncationPosition in memory. The memory you corrupt may not have anything in it. It may have something in it that doesn't "matter." It may be empty due to alignment requirements. The larger the overflow, the more likely you'll run into something important or smash the stack itself.

But the short answer is that writing beyond your buffer is undefined behavior, and the system does not promise it will crash. Often it will, but often it may just do strange things or even "work." What happens can change between OS versions, and even between runs of the same code on the same version.