Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Patch CoreText to avoid crash with a malicious sequence of characters.
Objective-C Logos
branch: master
Failed to load latest commit information.
GlyphPatch.plist
Makefile
README.md
Tweak.mm
Tweak.xm
control
theos

README.md

What's GlyphPatch?

GlyphPatch is a MobileSubstrate tweak which patches a CoreText rendering bug on iOS which makes every app to crash when it encounters a particular sequence of characters.

You can find a debian package file under the releases tab.

Background

Part 1

As explained in the characterRangeCodePath() implementation in Font.cpp, the range from U+0600 through U+109F includes Arabic characters, among others.

If one of our characters is in that range, we override the original function to return Auto instead of Complex, from the CodePath enum.

This makes Font::drawText() to call Font::drawSimpleText() instead of Font::drawComplexText(): this function (implemented in WebKit/Source/WebCore/platform/graphics/FontFastPath.cpp), calls Font::getGlyphsAndAdvancesForSimpleText() to work and advance with the GlyphBuffer.

On the other end, the original return value of the function would have required Font::drawText() to call Font::drawComplexText(), which needs to call Font::getGlyphsAndAdvancesForComplexText().

This last function makes use of a ComplexTextController object: the bug resides in the ComplexTextController::adjustGlyphsAndAdvances() function, which is called in the initialization of ComplexTextController. (ComplexTextController::ComplexTextController(const Font*, const TextRun&, bool, HashSet<const SimpleFontData*>*, bool))

Part 2

Ok, so while the "new approach" described above was a better solution than the first one, it actually made Arabic characters look weird (larger).

That was caused by the fact that the new "rendering path" followed by WebCore was not actually meant for that range of characters and did not include all the details required to rendered that set properly.

The only way this could be avoided was to isolate the malicious character sequence and hijack the rendering path only in that case. This is exactly what I'm doing now.

The following code can be read as follows: "render everything as normal, except if the sequence of characters is the malicious one".

Part 3

In WebCore-1640.27/platform/graphics/mac/ComplexTextController.cpp we find the implementation of void ComplexTextController::adjustGlyphsAndAdvances(), the function we can see in the crash logs related to the malicious sequence bug. The former approach, explained above, relied on avoiding to call adjustGlyphsAndAdvances(), hijacking the original rendering behaviour.

But if we dig deeper into adjustGlyphsAndAdvances(), we get a better understanding of the bug and we also find what's actually causing the memory violation.

Since we're actually speaking of a memory violation, the first thing that came to my mind while looking for an alternative approach (I should have done that from the start) was to look for an "out of bounds" access to an array: in our function, there are 2 main loops through 2 different arrays.

The first one is m_complexTextRuns, but the bounds are obtained just by asking the number of elements to the Vector. The second loop iterates over the characters of the ComplexTextRun& complexTextRun object. The number of characters is given by ComplexTextRun::glyphCount(), which just returns the unsigned m_glyphCount variable. Where is this variable set?

By grepping the WebCore source (available on opensource.apple.com) we find out that m_glyphCount is set in ComplexTextControllerCoreText.mm and it is set as the return value of CTRunGetGlyphCount)(CTRunRef run).

Since the m_glyphCount is unsigned, a negative return value would be treated as a huge number: in fact, using particular sequences of characters, CTRunGetGlyphCount returns -1, which is actually treated as 4294967295. This makes the loop run over the limit of the array.

Hope this makes sense; if anyone notices there is something wrong, just let me know! :)

Something went wrong with that request. Please try again.