Crash under FontCache::purgeInactiveFontData()
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Apr 2016 19:24:42 +0000 (19:24 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 22 Apr 2016 19:24:42 +0000 (19:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=156822
<rdar://problem/25373970>

Reviewed by Darin Adler.

In some rare cases, the Font constructor would mutate the FontPlatformData
that is being passed in. This is an issue because because our FontCache
uses the FontPlatformData as key for the cached fonts. This could lead to
crashes because the WTFMove() in FontCache::purgeInactiveFontData() would
nullify values in our HashMap but we would then fail to remove them from
the HashMap (because the key did not match). We would then reference the
null font when looping again when doing font->hasOneRef().

This patch marks Font::m_platformData member as const to avoid such issues
in the future and moves the code altering the FontPlatformData from the
Font constructor into the FontPlatformData constructor. The purpose of
that code was to initialize FontPlatformData::m_cgFont in case the CGFont
passed in the constructor was null.

* platform/graphics/Font.h:
* platform/graphics/FontCache.cpp:
(WebCore::FontCache::fontForPlatformData):
(WebCore::FontCache::purgeInactiveFontData):
* platform/graphics/FontPlatformData.cpp:
(WebCore::FontPlatformData::FontPlatformData):
* platform/graphics/FontPlatformData.h:
* platform/graphics/cocoa/FontCocoa.mm:
(WebCore::webFallbackFontFamily): Deleted.
(WebCore::Font::platformInit): Deleted.
* platform/graphics/cocoa/FontPlatformDataCocoa.mm:
(WebCore::webFallbackFontFamily):
(WebCore::FontPlatformData::setFallbackCGFont):
* platform/graphics/win/FontPlatformDataCGWin.cpp:
(WebCore::FontPlatformData::setFallbackCGFont):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@199890 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/Font.h
Source/WebCore/platform/graphics/FontCache.cpp
Source/WebCore/platform/graphics/FontPlatformData.cpp
Source/WebCore/platform/graphics/FontPlatformData.h
Source/WebCore/platform/graphics/cocoa/FontCocoa.mm
Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm
Source/WebCore/platform/graphics/freetype/FontPlatformData.h
Source/WebCore/platform/graphics/freetype/FontPlatformDataFreeType.cpp
Source/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp
Source/WebCore/platform/graphics/win/FontPlatformDataCairoWin.cpp
Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp
Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp

index 2237839..8800859 100644 (file)
@@ -1,5 +1,43 @@
 2016-04-22  Chris Dumez  <cdumez@apple.com>
 
+        Crash under FontCache::purgeInactiveFontData()
+        https://bugs.webkit.org/show_bug.cgi?id=156822
+        <rdar://problem/25373970>
+
+        Reviewed by Darin Adler.
+
+        In some rare cases, the Font constructor would mutate the FontPlatformData
+        that is being passed in. This is an issue because because our FontCache
+        uses the FontPlatformData as key for the cached fonts. This could lead to
+        crashes because the WTFMove() in FontCache::purgeInactiveFontData() would
+        nullify values in our HashMap but we would then fail to remove them from
+        the HashMap (because the key did not match). We would then reference the
+        null font when looping again when doing font->hasOneRef().
+
+        This patch marks Font::m_platformData member as const to avoid such issues
+        in the future and moves the code altering the FontPlatformData from the
+        Font constructor into the FontPlatformData constructor. The purpose of
+        that code was to initialize FontPlatformData::m_cgFont in case the CGFont
+        passed in the constructor was null.
+
+        * platform/graphics/Font.h:
+        * platform/graphics/FontCache.cpp:
+        (WebCore::FontCache::fontForPlatformData):
+        (WebCore::FontCache::purgeInactiveFontData):
+        * platform/graphics/FontPlatformData.cpp:
+        (WebCore::FontPlatformData::FontPlatformData):
+        * platform/graphics/FontPlatformData.h:
+        * platform/graphics/cocoa/FontCocoa.mm:
+        (WebCore::webFallbackFontFamily): Deleted.
+        (WebCore::Font::platformInit): Deleted.
+        * platform/graphics/cocoa/FontPlatformDataCocoa.mm:
+        (WebCore::webFallbackFontFamily):
+        (WebCore::FontPlatformData::setFallbackCGFont):
+        * platform/graphics/win/FontPlatformDataCGWin.cpp:
+        (WebCore::FontPlatformData::setFallbackCGFont):
+
+2016-04-22  Chris Dumez  <cdumez@apple.com>
+
         Support disabling at runtime IndexedDB constructors exposed to workers
         https://bugs.webkit.org/show_bug.cgi?id=156883
 
index 4e92f59..7b8b540 100644 (file)
@@ -229,7 +229,7 @@ private:
     float m_maxCharWidth;
     float m_avgCharWidth;
 
-    FontPlatformData m_platformData;
+    const FontPlatformData m_platformData;
 
     mutable RefPtr<GlyphPage> m_glyphPageZero;
     mutable HashMap<unsigned, RefPtr<GlyphPage>> m_glyphPages;
index 47479e7..9fbc6c7 100644 (file)
@@ -396,6 +396,8 @@ Ref<Font> FontCache::fontForPlatformData(const FontPlatformData& platformData)
     if (addResult.isNewEntry)
         addResult.iterator->value = Font::create(platformData);
 
+    ASSERT(addResult.iterator->value->platformData() == platformData);
+
     return *addResult.iterator->value;
 }
 
@@ -435,8 +437,10 @@ void FontCache::purgeInactiveFontData(unsigned purgeCount)
         // Fonts may ref other fonts so we loop until there are no changes.
         if (fontsToDelete.isEmpty())
             break;
-        for (auto& font : fontsToDelete)
-            cachedFonts().remove(font->platformData());
+        for (auto& font : fontsToDelete) {
+            bool success = cachedFonts().remove(font->platformData());
+            ASSERT_UNUSED(success, success);
+        }
     };
 
     Vector<FontPlatformDataCacheKey> keysToRemove;
index adc5c71..765fed1 100644 (file)
@@ -58,6 +58,8 @@ FontPlatformData::FontPlatformData(CGFontRef cgFont, float size, bool syntheticB
     : FontPlatformData(size, syntheticBold, syntheticOblique, orientation, widthVariant, textRenderingMode)
 {
     m_cgFont = cgFont;
+    if (!m_cgFont)
+        setFallbackCGFont();
 }
 #endif
 
index cc0332f..4e3dc44 100644 (file)
@@ -216,6 +216,9 @@ private:
 #if PLATFORM(WIN)
     void platformDataInit(HFONT, float size, HDC, WCHAR* faceName);
 #endif
+#if USE(CG)
+    void setFallbackCGFont();
+#endif
 
 public:
     bool m_syntheticBold { false };
index 648841a..1cb2764 100644 (file)
@@ -79,13 +79,7 @@ static bool fontHasVerticalGlyphs(CTFontRef ctFont)
     return false;
 }
 
-#if USE(APPKIT)
-static NSString *webFallbackFontFamily(void)
-{
-    static NSString *webFallbackFontFamily = [[[NSFont systemFontOfSize:16.0f] familyName] retain];
-    return webFallbackFontFamily;
-}
-#else
+#if !USE(APPKIT)
 bool fontFamilyShouldNotBeUsedForArabic(CFStringRef fontFamilyName)
 {
     if (!fontFamilyName)
@@ -104,59 +98,6 @@ void Font::platformInit()
 #if USE(APPKIT)
     m_syntheticBoldOffset = m_platformData.m_syntheticBold ? 1.0f : 0.f;
 
-    bool failedSetup = false;
-    if (!platformData().cgFont()) {
-        // Ack! Something very bad happened, like a corrupt font.
-        // Try looking for an alternate 'base' font for this renderer.
-
-        // Special case hack to use "Times New Roman" in place of "Times".
-        // "Times RO" is a common font whose family name is "Times".
-        // It overrides the normal "Times" family font.
-        // It also appears to have a corrupt regular variant.
-        NSString *fallbackFontFamily;
-        if ([[m_platformData.nsFont() familyName] isEqual:@"Times"])
-            fallbackFontFamily = @"Times New Roman";
-        else
-            fallbackFontFamily = webFallbackFontFamily();
-        
-        // Try setting up the alternate font.
-        // This is a last ditch effort to use a substitute font when something has gone wrong.
-#if !ERROR_DISABLED
-        RetainPtr<NSFont> initialFont = m_platformData.nsFont();
-#endif
-        if (m_platformData.font())
-            m_platformData.setNSFont([[NSFontManager sharedFontManager] convertFont:m_platformData.nsFont() toFamily:fallbackFontFamily]);
-        else
-            m_platformData.setNSFont([NSFont fontWithName:fallbackFontFamily size:m_platformData.size()]);
-        if (!platformData().cgFont()) {
-            if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
-                // OK, couldn't setup Times New Roman as an alternate to Times, fallback
-                // on the system font.  If this fails we have no alternative left.
-                m_platformData.setNSFont([[NSFontManager sharedFontManager] convertFont:m_platformData.nsFont() toFamily:webFallbackFontFamily()]);
-                if (!platformData().cgFont()) {
-                    // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
-                    LOG_ERROR("unable to initialize with font %@", initialFont.get());
-                    failedSetup = true;
-                }
-            } else {
-                // We tried the requested font and the system font. No joy. We have to give up.
-                LOG_ERROR("unable to initialize with font %@", initialFont.get());
-                failedSetup = true;
-            }
-        }
-
-        // Report the problem.
-        LOG_ERROR("Corrupt font detected, using %@ in place of %@.",
-            [m_platformData.nsFont() familyName], [initialFont.get() familyName]);
-    }
-
-    // If all else fails, try to set up using the system font.
-    // This is probably because Times and Times New Roman are both unavailable.
-    if (failedSetup) {
-        m_platformData.setNSFont([NSFont systemFontOfSize:[m_platformData.nsFont() pointSize]]);
-        LOG_ERROR("failed to set up font, using system font %s", m_platformData.font());
-    }
-
 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101100
     // Work around <rdar://problem/19433490>
     CGGlyph dummyGlyphs[] = {0, 0};
index 3a87042..2852060 100644 (file)
@@ -58,6 +58,77 @@ FontPlatformData::~FontPlatformData()
 {
 }
 
+#if USE(APPKIT)
+
+static NSString *webFallbackFontFamily(void)
+{
+    static NSString *webFallbackFontFamily = [[[NSFont systemFontOfSize:16.0f] familyName] retain];
+    return webFallbackFontFamily;
+}
+
+void FontPlatformData::setFallbackCGFont()
+{
+    if (cgFont())
+        return;
+
+    // Ack! Something very bad happened, like a corrupt font.
+    // Try looking for an alternate 'base' font for this renderer.
+
+    // Special case hack to use "Times New Roman" in place of "Times".
+    // "Times RO" is a common font whose family name is "Times".
+    // It overrides the normal "Times" family font.
+    // It also appears to have a corrupt regular variant.
+    NSString *fallbackFontFamily;
+    if ([[nsFont() familyName] isEqual:@"Times"])
+        fallbackFontFamily = @"Times New Roman";
+    else
+        fallbackFontFamily = webFallbackFontFamily();
+
+    // Try setting up the alternate font.
+    // This is a last ditch effort to use a substitute font when something has gone wrong.
+#if !ERROR_DISABLED
+    RetainPtr<NSFont> initialFont = nsFont();
+#endif
+    if (font())
+        setNSFont([[NSFontManager sharedFontManager] convertFont:nsFont() toFamily:fallbackFontFamily]);
+    else
+        setNSFont([NSFont fontWithName:fallbackFontFamily size:size()]);
+
+    if (cgFont())
+        return;
+
+    if ([fallbackFontFamily isEqual:@"Times New Roman"]) {
+        // OK, couldn't setup Times New Roman as an alternate to Times, fallback
+        // on the system font. If this fails we have no alternative left.
+        setNSFont([[NSFontManager sharedFontManager] convertFont:nsFont() toFamily:webFallbackFontFamily()]);
+        if (cgFont())
+            return;
+
+        // We tried, Times, Times New Roman, and the system font. No joy. We have to give up.
+        LOG_ERROR("unable to initialize with font %@", initialFont.get());
+    } else {
+        // We tried the requested font and the system font. No joy. We have to give up.
+        LOG_ERROR("unable to initialize with font %@", initialFont.get());
+    }
+
+    // Report the problem.
+    LOG_ERROR("Corrupt font detected, using %@ in place of %@.", [nsFont() familyName], [initialFont.get() familyName]);
+
+    // If all else fails, try to set up using the system font.
+    // This is probably because Times and Times New Roman are both unavailable.
+    ASSERT(!cgFont());
+    setNSFont([NSFont systemFontOfSize:[nsFont() pointSize]]);
+    LOG_ERROR("failed to set up font, using system font %s", font());
+}
+
+#else
+
+void FontPlatformData::setFallbackCGFont()
+{
+}
+
+#endif
+
 void FontPlatformData::platformDataInit(const FontPlatformData& f)
 {
     m_font = f.m_font;
index 128ef57..c9b95e6 100644 (file)
@@ -72,7 +72,7 @@ public:
 
     HarfBuzzFace* harfBuzzFace() const;
 
-    bool isFixedPitch();
+    bool isFixedPitch() const;
     float size() const { return m_size; }
     void setSize(float size) { m_size = size; }
     bool syntheticBold() const { return m_syntheticBold; }
index 4b51139..b9cbfb8 100644 (file)
@@ -273,7 +273,7 @@ HarfBuzzFace* FontPlatformData::harfBuzzFace() const
     return m_harfBuzzFace.get();
 }
 
-bool FontPlatformData::isFixedPitch()
+bool FontPlatformData::isFixedPitch() const
 {
     return m_fixedWidth;
 }
index 9772c9c..919686c 100644 (file)
@@ -113,6 +113,8 @@ void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR*
     LOGFONT logfont;
     GetObject(font, sizeof(logfont), &logfont);
     m_cgFont = adoptCF(CGFontCreateWithPlatformFont(&logfont));
+    if (!m_useGDI)
+        m_isSystemFont = !wcscmp(faceName, L"Lucida Grande");
 }
 
 FontPlatformData::FontPlatformData(GDIObject<HFONT> hfont, CGFontRef font, float size, bool bold, bool oblique, bool useGDI)
@@ -155,4 +157,8 @@ bool FontPlatformData::platformIsEqual(const FontPlatformData& other) const
         && m_useGDI == other.m_useGDI;
 }
 
+void FontPlatformData::setFallbackCGFont()
+{
+}
+
 }
index 89fc26f..c91db1b 100644 (file)
@@ -54,6 +54,9 @@ void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR*
 
     m_scaledFont = cairo_scaled_font_create(fontFace, &sizeMatrix, &ctm, fontOptions);
     cairo_font_face_destroy(fontFace);
+
+    if (!m_useGDI && m_size)
+        m_isSystemFont = !wcscmp(faceName, L"Lucida Grande");
 }
 
 FontPlatformData::FontPlatformData(GDIObject<HFONT> font, cairo_font_face_t* fontFace, float size, bool bold, bool oblique)
index fa595e5..d408fa7 100644 (file)
@@ -86,7 +86,6 @@ void Font::platformInit()
         int faceLength = GetTextFace(dc, 0, 0);
         Vector<WCHAR> faceName(faceLength);
         GetTextFace(dc, faceLength, faceName.data());
-        m_platformData.setIsSystemFont(!wcscmp(faceName.data(), L"Lucida Grande"));
         SelectObject(dc, oldFont);
 
         fAscent = ascentConsideringMacAscentHack(faceName.data(), fAscent, fDescent);
index e027ff5..f795a5f 100644 (file)
@@ -46,7 +46,6 @@ void Font::platformInit()
     m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f;
     m_scriptCache = 0;
     m_scriptFontProperties = 0;
-    m_platformData.setIsSystemFont(false);
 
     if (m_platformData.useGDI())
        return initGDIFont();
@@ -55,7 +54,6 @@ void Font::platformInit()
         m_fontMetrics.reset();
         m_avgCharWidth = 0;
         m_maxCharWidth = 0;
-        m_platformData.setIsSystemFont(false);
         m_scriptCache = 0;
         m_scriptFontProperties = 0;
         return;
@@ -87,11 +85,6 @@ void Font::platformInit()
     }
     float xHeight = ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts.
 
-    int faceLength = ::GetTextFace(dc, 0, 0);
-    Vector<WCHAR> faceName(faceLength);
-    ::GetTextFace(dc, faceLength, faceName.data());
-    m_platformData.setIsSystemFont(!wcscmp(faceName.data(), L"Lucida Grande"));
-
     m_fontMetrics.setAscent(ascent);
     m_fontMetrics.setDescent(descent);
     m_fontMetrics.setLineGap(lineGap);