Some Mac OS X users of PhantomJS reported that it crashed every one and then. This also affects other related projects such as Poltergeist. A reduced test case revealed that it has something to do with the use of some TrueType fonts. I decided to investigate this issue and learned a few things along the way.
There are bad news and good news. The bad news is that I still don’t fully understand how and why this happens, in particular since I only set so much time during the holidays to track the problem. The good news is that the workaround exists, it is very minimal since all I need to do is to comment out one line of code.
The stack trace of the crash may vary, but one example looks like the following:
#0 0x9254bd47 in objc_msgSend () #1 0x0267b7b0 in ?? () #2 0x9a9b8310 in TCFRetained<cgfont *>::Retain () #3 0x9a947102 in TCGFont::TCGFont () #4 0x9a946dee in TCGFontCache::CopyFont () #5 0x9a946ca8 in TBaseFont::CopyNativeFont () #6 0x9a955338 in CTFontCopyGraphicsFont () #7 0x00bace39 in QCoreTextFontEngine::QCoreTextFontEngine () #8 0x00bae0cb in QCoreTextFontEngineMulti::init () #9 0x00baa530 in QCoreTextFontEngineMulti::QCoreTextFontEngineMulti () #10 0x00ddb247 in QFontDatabase::load () #11 0x00dc3e55 in QFontPrivate::engineForScript () #12 0x00dd22da in QFontMetricsF::descent () #13 0x00692f1b in WebCore::SimpleFontData::platformInit () #14 0x00553cad in WebCore::SimpleFontData::SimpleFontData () #15 0x0023cf2e in WebCore::CSSFontFaceSource::getFontData () #16 0x0023702c in WebCore::CSSFontFace::getFontData () </cgfont>
Note the suspicious CTFontCopyGraphicsFont
. I believe this function should not crash at all, but maybe it just follows the expected behavior of wrong reference counting (again, how this is even possible is beyond my imagination). The mysterious thing, as evidenced from the test case, is that it does not happen all the time. There is an exact sequence (could be one of the many possible others) which lead to the crash. Apparently, PhantomJS is not the only one affected by this. There is an upstream QtWebKit bug which tracks the issue. For Qt 5, the crash will never happen thanks to the use of QRawFont
.
A little bit context about Qt font handling follows (this is obviously an oversimplified version). Everytime a font needs to be handled (e.g. looking up its metrics, drawing a text using that font, etc), a query is made to the font database (QFontDatabase
). This database has a cache mechanism to minimize creating the font data and platform-specific font engine for every possible font combination of family, weight, style, size, etc. The font cache is however invalidated (and thus becomes completely empty) every time a new application-specific font is added or removed. With some TrueType fonts, removing the font data and recreating it again apparently causes the above crash. I thought it was because of a mishap with the reference counting (mismatched of CFRetain and CFRelease), but strangely it does not happen with every single font, only with particular fonts (consistently reproduceable).
My workaround is simply to break that crash sequence, i.e. avoiding the removal of the font from the font database. This leads to some small leak of font data. It is not a disaster since we don’t expect PhantomJS to be used in a long running process. In addition, with some popular sites I have tried with, there are only few cases (like The Verge) where more than 4 custom fonts are being used.
A patch release, PhantomJS 1.8.1, has been rolled out to fix this crash problem. If you are using PhantomJS on OS X, get this release immediately.