CVE-2018-7999 (published 2018-03-09T19:29Z)

In libgraphite2 in graphite2 1.3.11, a NULL pointer dereference vulnerability was found in Segment.cpp during a dumbRendering operation, which may allow attackers to cause a denial of service or possibly have unspecified other impact via a crafted .ttf file.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-476
cwe_name: NULL Pointer Dereference
description: The product dereferences a pointer that it expects to be valid but is NULL.
extended_description: 
            
            
            
         
         

Fix information:
repo_url: https://github.com/silnrsi/graphite
commit_details: repo_url: https://github.com/silnrsi/graphite
author: Martin Hosken
author_date: 2018-03-06 12:42:16+07:00
msg: Deprecate and make ineffective gr_face_dumbRendering
repository: repo_url: https://github.com/silnrsi/graphite
repo_name: silnrsi/graphite
description: Graphite is a "smart font" system developed specifically to handle the complexities of lesser-known languages of the world.
repo_language: C++
owner: silnrsi
file_changes: filename: Font.h
change_type: ModificationType.MODIFY
diff: @@ -56,7 +56,7 @@ GR2_API void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix);
 enum gr_face_options {
     /** No preload, no cmap caching, fail if the graphite tables are invalid */
     gr_face_default = 0,
-    /** Dumb rendering will be enabled if the graphite tables are invalid */
+    /** Dumb rendering will be enabled if the graphite tables are invalid. DEPRECATED. */
     gr_face_dumbRendering = 1,
     /** preload glyphs at construction time */
     gr_face_preloadGlyphs = 2,

programming_language: C
filename: GlyphCache.cpp
change_type: ModificationType.MODIFY
diff: @@ -84,7 +84,7 @@ const SlantBox SlantBox::empty = {0,0,0,0};
 class GlyphCache::Loader
 {
 public:
-    Loader(const Face & face, const bool dumb_font);    //return result indicates success. Do not use if failed.
+    Loader(const Face & face);    //return result indicates success. Do not use if failed.
 
     operator bool () const throw();
     unsigned short int units_per_em() const throw();
@@ -115,7 +115,7 @@ private:
 
 
 GlyphCache::GlyphCache(const Face & face, const uint32 face_options)
-: _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))),
+: _glyph_loader(new Loader(face)),
   _glyphs(_glyph_loader && *_glyph_loader && _glyph_loader->num_glyphs()
         ? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
   _boxes(_glyph_loader && _glyph_loader->has_boxes() && _glyph_loader->num_glyphs()
@@ -239,7 +239,7 @@ const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const      //result m
 
 
 
-GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
+GlyphCache::Loader::Loader(const Face & face)
 : _head(face, Tag::head),
   _hhea(face, Tag::hhea),
   _hmtx(face, Tag::hmtx),
@@ -265,52 +265,49 @@ GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
         return;
     }
 
-    if (!dumb_font)
+    if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL
+        || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL
+        || m_pGloc.size() < 8)
     {
-        if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL
-            || (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL
-            || m_pGloc.size() < 8)
-        {
-            _head = Face::Table();
-            return;
-        }
-        const byte    * p = m_pGloc;
-        int       version = be::read<uint32>(p);
-        const uint16    flags = be::read<uint16>(p);
-        _num_attrs = be::read<uint16>(p);
-        // We can accurately calculate the number of attributed glyphs by
-        //  subtracting the length of the attribids array (numAttribs long if present)
-        //  and dividing by either 2 or 4 depending on shor or lonf format
-        _long_fmt              = flags & 1;
-        int tmpnumgattrs       = (m_pGloc.size()
-                                   - (p - m_pGloc)
-                                   - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
-                                       / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
-
-        if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
-            || _num_attrs == 0 || _num_attrs > 0x3000  // is this hard limit appropriate?
-            || _num_glyphs_graphics > tmpnumgattrs
-            || m_pGlat.size() < 4)
-        {
-            _head = Face::Table();
-            return;
-        }
+        _head = Face::Table();
+        return;
+    }
+    const byte    * p = m_pGloc;
+    int       version = be::read<uint32>(p);
+    const uint16    flags = be::read<uint16>(p);
+    _num_attrs = be::read<uint16>(p);
+    // We can accurately calculate the number of attributed glyphs by
+    //  subtracting the length of the attribids array (numAttribs long if present)
+    //  and dividing by either 2 or 4 depending on shor or lonf format
+    _long_fmt              = flags & 1;
+    int tmpnumgattrs       = (m_pGloc.size()
+                               - (p - m_pGloc)
+                               - sizeof(uint16)*(flags & 0x2 ? _num_attrs : 0))
+                                   / (_long_fmt ? sizeof(uint32) : sizeof(uint16)) - 1;
+
+    if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
+        || _num_attrs == 0 || _num_attrs > 0x3000  // is this hard limit appropriate?
+        || _num_glyphs_graphics > tmpnumgattrs
+        || m_pGlat.size() < 4)
+    {
+        _head = Face::Table();
+        return;
+    }
 
-        _num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
-        p = m_pGlat;
-        version = be::read<uint32>(p);
-        if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8))       // reject Glat tables that are too new
-        {
-            _head = Face::Table();
-            return;
-        }
-        else if (version >= 0x00030000)
-        {
-            unsigned int glatflags = be::read<uint32>(p);
-            _has_boxes = glatflags & 1;
-            // delete this once the compiler is fixed
-            _has_boxes = true;
-        }
+    _num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
+    p = m_pGlat;
+    version = be::read<uint32>(p);
+    if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8))       // reject Glat tables that are too new
+    {
+        _head = Face::Table();
+        return;
+    }
+    else if (version >= 0x00030000)
+    {
+        unsigned int glatflags = be::read<uint32>(p);
+        _has_boxes = glatflags & 1;
+        // delete this once the compiler is fixed
+        _has_boxes = true;
     }
 }
 

programming_language: C++
filename: gr_face.cpp
change_type: ModificationType.MODIFY
diff: @@ -47,8 +47,7 @@ namespace
         telemetry::category _misc_cat(face.tele.misc);
 #endif
         Face::Table silf(face, Tag::Silf, 0x00050000);
-        if (silf)   options &= ~gr_face_dumbRendering;
-        else if (!(options &  gr_face_dumbRendering))
+        if (!silf)
             return false;
 
         if (!face.readGlyphs(options))
@@ -74,7 +73,7 @@ namespace
                 return true;
         }
         else
-            return options & gr_face_dumbRendering;
+            return false;
     }
 }
 

programming_language: C++
filename: CMakeLists.txt
change_type: ModificationType.MODIFY
diff: @@ -20,7 +20,7 @@ if (GRAPHITE2_ASAN)
 endif (GRAPHITE2_ASAN)
 target_link_libraries(featuremaptest graphite2 graphite2-base graphite2-segcache graphite2-base)
 
-add_test(NAME featuremaptest COMMAND $<TARGET_FILE:featuremaptest> ${testing_SOURCE_DIR}/fonts/tiny.ttf)
+add_test(NAME featuremaptest COMMAND $<TARGET_FILE:featuremaptest> ${testing_SOURCE_DIR}/fonts/small.ttf)
 set_tests_properties(featuremaptest PROPERTIES TIMEOUT 3)
 if (GRAPHITE2_ASAN)
     set_property(TEST featuremaptest APPEND PROPERTY ENVIRONMENT "ASAN_SYMBOLIZER_PATH=${ASAN_SYMBOLIZER}")

programming_language: Shell
filename: featuremaptest.cpp
change_type: ModificationType.MODIFY
diff: @@ -243,7 +243,7 @@ template <class T> void testFeatTable(const T & table, const char * testName)
 {
     FeatureMap testFeatureMap;
     dummyFace.replace_table(TtfUtil::Tag::Feat, &table, sizeof(T));
-    gr_face * face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, gr_face_dumbRendering);
+    gr_face * face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, 0);
     if (!face) throw std::runtime_error("failed to load font");
     bool readStatus = testFeatureMap.readFeats(*face);
     testAssert("readFeats", readStatus);
@@ -285,9 +285,8 @@ int main(int argc, char * argv[])
 		// test a bad settings offset stradling the end of the table
 		FeatureMap testFeatureMap;
 		dummyFace.replace_table(TtfUtil::Tag::Feat, &testBadOffset, sizeof testBadOffset);
-		face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, gr_face_dumbRendering);
-		bool readStatus = testFeatureMap.readFeats(*face);
-		testAssert("fail gracefully on bad table", !readStatus);
+		face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, 0);
+		testAssert("fail gracefully on bad table", !face);
 	}
 	catch (std::exception & e)
 	{

programming_language: C++
filename: CMakeLists.txt
change_type: ModificationType.MODIFY
diff: @@ -41,7 +41,7 @@ if  (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 	endif ("${CMAKE_BUILD_TYPE}" STREQUAL "Release")
 endif  (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 
-add_test(vm-test-call-threading vm-test-call ${testing_SOURCE_DIR}/fonts/tiny.ttf 1)
+add_test(vm-test-call-threading vm-test-call ${testing_SOURCE_DIR}/fonts/small.ttf 1)
 set_tests_properties(vm-test-call-threading PROPERTIES
         PASS_REGULAR_EXPRESSION "simple program size:    14 bytes.*result of program: 42"
         FAIL_REGULAR_EXPRESSION "program terminated early;stack not empty")
@@ -51,7 +51,7 @@ if (GRAPHITE2_ASAN)
 endif (GRAPHITE2_ASAN)
 
 if  (${CMAKE_COMPILER_IS_GNUCXX})
-	add_test(vm-test-direct-threading vm-test-direct ${testing_SOURCE_DIR}/fonts/tiny.ttf 1)
+	add_test(vm-test-direct-threading vm-test-direct ${testing_SOURCE_DIR}/fonts/small.ttf 1)
 	set_tests_properties(vm-test-direct-threading PROPERTIES
 			PASS_REGULAR_EXPRESSION "simple program size:    14 bytes.*result of program: 42"
 			FAIL_REGULAR_EXPRESSION "program terminated early;stack not empty")

programming_language: Shell