CVE-2018-7999 (C++, published 2018-03-09T19:29Z)
Description: value: 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 v3 base score: 8.8)
Weakness: CWE-476 \u2014 NULL Pointer Dereference
The product dereferences a pointer that it expects to be valid but is NULL.
Repository: https://github.com/silnrsi/graphite
Files changed:
include/graphite2/Font.h
src/GlyphCache.cpp
src/gr_face.cpp
tests/featuremap/CMakeLists.txt
tests/featuremap/featuremaptest.cpp
tests/vm/CMakeLists.txt
Commit message: Deprecate and make ineffective gr_face_dumbRendering


Vulnerable code:
    /** Dumb rendering will be enabled if the graphite tables are invalid */
    Loader(const Face & face, const bool dumb_font);    //return result indicates success. Do not use if failed.
: _glyph_loader(new Loader(face, bool(face_options & gr_face_dumbRendering))),
GlyphCache::Loader::Loader(const Face & face, const bool dumb_font)
    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)
        {
            _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;
        }
        if (silf)   options &= ~gr_face_dumbRendering;
        else if (!(options &  gr_face_dumbRendering))
            return options & gr_face_dumbRendering;
add_test(NAME featuremaptest COMMAND $<TARGET_FILE:featuremaptest> ${testing_SOURCE_DIR}/fonts/tiny.ttf)
    gr_face * face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, gr_face_dumbRendering);
		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);
add_test(vm-test-call-threading vm-test-call ${testing_SOURCE_DIR}/fonts/tiny.ttf 1)
	add_test(vm-test-direct-threading vm-test-direct ${testing_SOURCE_DIR}/fonts/tiny.ttf 1)

Fixed code:
    /** Dumb rendering will be enabled if the graphite tables are invalid. DEPRECATED. */
    Loader(const Face & face);    //return result indicates success. Do not use if failed.
: _glyph_loader(new Loader(face)),
GlyphCache::Loader::Loader(const Face & face)
    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;
    }
    _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;
        if (!silf)
            return false;
add_test(NAME featuremaptest COMMAND $<TARGET_FILE:featuremaptest> ${testing_SOURCE_DIR}/fonts/small.ttf)
    gr_face * face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, 0);
		face = gr_make_face_with_ops(&dummyFace, &face_handle::ops, 0);
		testAssert("fail gracefully on bad table", !face);
add_test(vm-test-call-threading vm-test-call ${testing_SOURCE_DIR}/fonts/small.ttf 1)
	add_test(vm-test-direct-threading vm-test-direct ${testing_SOURCE_DIR}/fonts/small.ttf 1)