Commit message: None
CVE: CVE-2017-9610
CWE: CWE-125
Vulnerability description: The xps_load_sfnt_name function in xps/xpsfont.c in Artifex Ghostscript GhostXPS 9.21 allows remote attackers to cause a denial of service (heap-based buffer over-read and application crash) or possibly have unspecified other impact via a crafted document.

Project: ghostscript
File: xpsfont.c

xps_decode_font_char(xps_font_t *font, int code)
{
    int gid = xps_decode_font_char_imp(font, code);
    if (gid == 0)
        return code;
    return gid;
}

Project: ghostscript
File: xpsfont.c

xps_decode_font_char_imp(xps_font_t *font, int code)
{
    byte *table;

    /* no cmap selected: return identity */
    if (font->cmapsubtable <= 0)
        return code;

    table = font->data + font->cmapsubtable;

    switch (u16(table))
    {
        case 0: /* Apple standard 1-to-1 mapping. */
            {
                int i, length = u16(&table[2]) - 6;

                if (length < 0 || length > 256)
                    return gs_error_invalidfont;

                for (i=0;i<length;i++) {
                    if (table[6 + i] == code)
                        return i;
                }
            }
            return 0;
        case 4: /* Microsoft/Adobe segmented mapping. */
            {
                int segCount2 = u16(table + 6);
                byte *endCount = table + 14;
                byte *startCount = endCount + segCount2 + 2;
                byte *idDelta = startCount + segCount2;
                byte *idRangeOffset = idDelta + segCount2;
                int i2;

                if (segCount2 < 3 || segCount2 > 65535)
                    return gs_error_invalidfont;

                for (i2 = 0; i2 < segCount2 - 3; i2 += 2)
                {
                    int delta = s16(idDelta + i2), roff = s16(idRangeOffset + i2);
                    int start = u16(startCount + i2);
                    int end = u16(endCount + i2);
                    int glyph, i;

                    if (end < start)
                        return gs_error_invalidfont;

                    for (i=start;i<=end;i++) {
                        if (roff == 0) {
                            glyph = (i + delta) & 0xffff;
                        } else {
                            glyph = u16(idRangeOffset + i2 + roff + ((i - start) << 1));
                        }
                        if (glyph == code) {
                            return i;
                        }
                    }
                }
            }
            return 0;
        case 6: /* Single interval lookup. */
            {
                int ch, i, length = u16(&table[8]);
                int firstCode = u16(&table[6]);

                if (length < 0 || length > 65535)
                    return gs_error_invalidfont;

                for (i=0;i<length;i++) {
                    ch = u16(&table[10 + (i * 2)]);
                    if (ch == code)
                        return (firstCode + i);
                }
            }
            return 0;
        case 10: /* Trimmed array (like 6) */
            {
                unsigned int ch, i, length = u32(&table[20]);
                int firstCode = u32(&table[16]);
                for (i=0;i<length;i++) {
                    ch = u16(&table[10 + (i * 2)]);
                    if (ch == code)
                        return (firstCode + i);
                }
            }
            return 0;
        case 12: /* Segmented coverage. (like 4) */
            {
                unsigned int nGroups = u32(&table[12]);
                int Group;

                for (Group=0;Group<nGroups;Group++)
                {
                    int startCharCode = u32(&table[16 + (Group * 12)]);
                    int endCharCode = u32(&table[16 + (Group * 12) + 4]);
                    int startGlyphCode = u32(&table[16 + (Group * 12) + 8]);

                    if (code >= startGlyphCode && code <= (startGlyphCode + (endCharCode - startCharCode))) {
                        return startGlyphCode + (code - startCharCode);
                    }
                }
            }
            return 0;
        case 2: /* High-byte mapping through table. */
        case 8: /* Mixed 16-bit and 32-bit coverage (like 2) */
        default:
            gs_warn1("unknown cmap format: %d\n", u16(table));
            return 0;
    }
    return 0;
}

Project: ghostscript
File: xpsfont.c

xps_encode_font_char_imp(xps_font_t *font, int code)
{
    byte *table;

    /* no cmap selected: return identity */
    if (font->cmapsubtable <= 0)
        return code;

    table = font->data + font->cmapsubtable;

    switch (u16(table))
    {
    case 0: /* Apple standard 1-to-1 mapping. */
        return table[code + 6];

    case 4: /* Microsoft/Adobe segmented mapping. */
        {
            int segCount2 = u16(table + 6);
            byte *endCount = table + 14;
            byte *startCount = endCount + segCount2 + 2;
            byte *idDelta = startCount + segCount2;
            byte *idRangeOffset = idDelta + segCount2;
            int i2;

            for (i2 = 0; i2 < segCount2 - 3; i2 += 2)
            {
                int delta, roff;
                int start = u16(startCount + i2);
                int glyph;

                if ( code < start )
                    return 0;
                if ( code > u16(endCount + i2) )
                    continue;
                delta = s16(idDelta + i2);
                roff = s16(idRangeOffset + i2);
                if ( roff == 0 )
                {
                    return ( code + delta ) & 0xffff; /* mod 65536 */
                    return 0;
                }
                glyph = u16(idRangeOffset + i2 + roff + ((code - start) << 1));
                return (glyph == 0 ? 0 : glyph + delta);
            }

            /*
             * The TrueType documentation says that the last range is
             * always supposed to end with 0xffff, so this shouldn't
             * happen; however, in some real fonts, it does.
             */
            return 0;
        }

    case 6: /* Single interval lookup. */
        {
            int firstCode = u16(table + 6);
            int entryCount = u16(table + 8);
            if ( code < firstCode || code >= firstCode + entryCount )
                return 0;
            return u16(table + 10 + ((code - firstCode) << 1));
        }

    case 10: /* Trimmed array (like 6) */
        {
            int startCharCode = u32(table + 12);
            int numChars = u32(table + 16);
            if ( code < startCharCode || code >= startCharCode + numChars )
                return 0;
            return u32(table + 20 + (code - startCharCode) * 4);
        }

    case 12: /* Segmented coverage. (like 4) */
        {
            int nGroups = u32(table + 12);
            byte *group = table + 16;
            int i;

            for (i = 0; i < nGroups; i++)
            {
                int startCharCode = u32(group + 0);
                int endCharCode = u32(group + 4);
                int startGlyphID = u32(group + 8);
                if ( code < startCharCode )
                    return 0;
                if ( code <= endCharCode )
                    return startGlyphID + (code - startCharCode);
                group += 12;
            }

            return 0;
        }

    case 2: /* High-byte mapping through table. */
    case 8: /* Mixed 16-bit and 32-bit coverage (like 2) */
    default:
        gs_warn1("unknown cmap format: %d\n", u16(table));
        return 0;
    }

    return 0;
}

Project: ghostscript
File: xpsfont.c

xps_measure_font_glyph(xps_context_t *ctx, xps_font_t *font, int gid, xps_glyph_metrics_t *mtx)
{

    int head, format, loca, glyf;
    int ofs, len;
    int idx, i, n;
    int hadv, vadv, vorg;
    int vtop, ymax, desc;
    int scale;

    /* some insane defaults */

    scale = 1000; /* units-per-em */
    hadv = 500;
    vadv = -1000;
    vorg = 1000;

    /*
     * Horizontal metrics are easy.
     */

    ofs = xps_find_sfnt_table(font, "hhea", &len);
    if (ofs < 0 || len < 2 * 18)
    {
        gs_warn("hhea table is too short");
        return;
    }

    vorg = s16(font->data + ofs + 4); /* ascender is default vorg */
    desc = s16(font->data + ofs + 6); /* descender */
    if (desc < 0)
        desc = -desc;
    n = u16(font->data + ofs + 17 * 2);

    ofs = xps_find_sfnt_table(font, "hmtx", &len);
    if (ofs < 0)
    {
        gs_warn("cannot find hmtx table");
        return;
    }

    idx = gid;
    if (idx > n - 1)
        idx = n - 1;

    hadv = u16(font->data + ofs + idx * 4);
    vadv = 0;

    /*
     * Vertical metrics are hairy (with missing tables).
     */

    head = xps_find_sfnt_table(font, "head", &len);
    if (head > 0)
    {
        scale = u16(font->data + head + 18); /* units per em */
    }

    ofs = xps_find_sfnt_table(font, "OS/2", &len);
    if (ofs > 0 && len > 70)
    {
        vorg = s16(font->data + ofs + 68); /* sTypoAscender */
        desc = s16(font->data + ofs + 70); /* sTypoDescender */
        if (desc < 0)
            desc = -desc;
    }

    ofs = xps_find_sfnt_table(font, "vhea", &len);
    if (ofs > 0 && len >= 2 * 18)
    {
        n = u16(font->data + ofs + 17 * 2);

        ofs = xps_find_sfnt_table(font, "vmtx", &len);
        if (ofs < 0)
        {
            gs_warn("cannot find vmtx table");
            return;
        }

        idx = gid;
        if (idx > n - 1)
            idx = n - 1;

        vadv = u16(font->data + ofs + idx * 4);
        vtop = u16(font->data + ofs + idx * 4 + 2);

        glyf = xps_find_sfnt_table(font, "glyf", &len);
        loca = xps_find_sfnt_table(font, "loca", &len);
        if (head > 0 && glyf > 0 && loca > 0)
        {
            format = u16(font->data + head + 50); /* indexToLocaFormat */

            if (format == 0)
                ofs = u16(font->data + loca + gid * 2) * 2;
            else
                ofs = u32(font->data + loca + gid * 4);

            ymax = u16(font->data + glyf + ofs + 8); /* yMax */

            vorg = ymax + vtop;
        }
    }

    ofs = xps_find_sfnt_table(font, "VORG", &len);
    if (ofs > 0)
    {
        vorg = u16(font->data + ofs + 6);
        n = u16(font->data + ofs + 6);
        for (i = 0; i < n; i++)
        {
            if (u16(font->data + ofs + 8 + 4 * i) == gid)
            {
                vorg = s16(font->data + ofs + 8 + 4 * i + 2);
                break;
            }
        }
    }

    if (vadv == 0)
        vadv = vorg + desc;

    mtx->hadv = hadv / (float) scale;
    mtx->vadv = vadv / (float) scale;
    mtx->vorg = vorg / (float) scale;
}