Commit message: writet1 protection against buffer overflow

git-svn-id: svn://tug.org/texlive/trunk/Build/source@48697 c570f23f-e606-0410-a88d-b1316a301751
CVE: CVE-2018-17407
CWE: CWE-119
Vulnerability description: An issue was discovered in t1_check_unusual_charstring functions in writet1.c files in TeX Live before 2018-09-21. A buffer overflow in the handling of Type 1 fonts allows arbitrary code execution when a malicious font is loaded by one of the vulnerable tools: pdflatex, pdftex, dvips, or luatex.

Project: texlive-source
File: writet1.c

static void pdftex_fail(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    fputs("\nError: module writet1", stderr);
    if (cur_file_name)
        fprintf(stderr, " (file %s)", cur_file_name);
    fputs(": ", stderr);
    vsprintf(print_buf, fmt, args);
    fputs(print_buf, stderr);
    fputs("\n ==> Fatal error occurred, the output PS file is not finished!\n", stderr);
    va_end(args);
    exit(-1);
}

Project: texlive-source
File: writet1.c

static void t1_flush_cs(boolean is_subr)
{
    char *p;
    byte *r, *return_cs = NULL;
    cs_entry *tab, *end_tab, *ptr;
    char *start_line, *line_end;
    int count, size_pos;
    unsigned short cr, cs_len = 0;      /* to avoid warning about uninitialized use of cs_len */
    if (is_subr) {
        start_line = subr_array_start;
        line_end = subr_array_end;
        size_pos = subr_size_pos;
        tab = subr_tab;
        count = subr_max + 1;
        end_tab = subr_tab + count;
    } else {
        start_line = cs_dict_start;
        line_end = cs_dict_end;
        size_pos = cs_size_pos;
        tab = cs_tab;
        end_tab = cs_ptr;
        count = cs_count;
    }
    t1_line_ptr = t1_line_array;
    for (p = start_line; p - start_line < size_pos;)
        *t1_line_ptr++ = *p++;
    while (isdigit((unsigned char)*p))
        p++;
    sprintf(t1_line_ptr, "%u", count);
    strcat(t1_line_ptr, p);
    t1_line_ptr = eol(t1_line_array);
    t1_putline();

    /* create return_cs to replace unused subr's */
    if (is_subr) {
        cr = 4330;
        cs_len = 0;
        /* at this point we have t1_lenIV >= 0;
         * a negative value would be caught in t1_scan_param() */
        return_cs = xtalloc(t1_lenIV + 1, byte);
        for (cs_len = 0, r = return_cs; cs_len < t1_lenIV; cs_len++, r++)
            *r = cencrypt(0x00, &cr);
        *r = cencrypt(CS_RETURN, &cr);
        cs_len++;
    }

    for (ptr = tab; ptr < end_tab; ptr++) {
        if (ptr->used) {
            if (is_subr)
                sprintf(t1_line_array, "dup %lu %u",
                        (unsigned long) (ptr - tab), ptr->cslen);
            else
                sprintf(t1_line_array, "/%s %u", ptr->name, ptr->cslen);
            p = strend(t1_line_array);
            memcpy(p, ptr->data, ptr->len);
            t1_line_ptr = p + ptr->len;
            t1_putline();
        } else {
            /* replace unsused subr's by return_cs */
            if (is_subr) {
                sprintf(t1_line_array, "dup %lu %u%s ",
                        (unsigned long) (ptr - tab), cs_len, cs_token_pair[0]);
                p = strend(t1_line_array);
                memcpy(p, return_cs, cs_len);
                t1_line_ptr = p + cs_len;
                t1_putline();
                sprintf(t1_line_array, " %s", cs_token_pair[1]);
                t1_line_ptr = eol(t1_line_array);
                t1_putline();
            }
        }
        xfree(ptr->data);
        if (ptr->name != notdef)
            xfree(ptr->name);
    }
    sprintf(t1_line_array, "%s", line_end);
    t1_line_ptr = eol(t1_line_array);
    t1_putline();
    if (is_subr)
        xfree(return_cs);
    xfree(tab);
    xfree(start_line);
    xfree(line_end);
}

Project: texlive-source
File: writet1.c

static int t1_getbyte(void)
{
    int c = t1_getchar();
    if (t1_pfa)
        return c;
    if (t1_block_length == 0) {
        if (c != 128)
            pdftex_fail("invalid marker");
        c = t1_getchar();
        if (c == 3) {
            while (!t1_eof())
                t1_getchar();
            return EOF;
        }
        t1_block_length = t1_getchar() & 0xff;
        t1_block_length |= (t1_getchar() & 0xff) << 8;
        t1_block_length |= (t1_getchar() & 0xff) << 16;
        t1_block_length |= (t1_getchar() & 0xff) << 24;
        c = t1_getchar();
    }
    t1_block_length--;
    return c;
}

Project: texlive-source
File: writet1.c

static void t1_mark_glyphs(void)
{
    int i;
    char *charset = extra_charset();
    char *g, *s, *r;
    cs_entry *ptr;
    if (t1_synthetic || embed_all_glyphs(tex_font)) { /* mark everything */
        if (cs_tab != NULL)
            for (ptr = cs_tab; ptr < cs_ptr; ptr++)
                if (ptr->valid)
                    ptr->used = true;
        if (subr_tab != NULL) {
            for (ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
                if (ptr->valid)
                    ptr->used = true;
            subr_max = subr_size - 1;
        }
        return;
    }
    mark_cs(notdef);
    for (i = 0; i < 256; i++)
        if (is_used_char(i)) {
            if (t1_glyph_names[i] == notdef)
                pdftex_warn("character %i is mapped to %s", i, notdef);
            else
                mark_cs(t1_glyph_names[i]);
        }
    if (charset == NULL)
        goto set_subr_max;
    g = s = charset + 1; /* skip the first '/' */
    r = strend(g);
    while (g < r) {
        while (*s != '/' && s < r)
            s++;
        *s = 0; /* terminate g by rewriting '/' to 0 */
        mark_cs(g);
        g = s + 1;
    }
  set_subr_max:
    if (subr_tab != NULL)
        for (subr_max = -1, ptr = subr_tab; ptr - subr_tab < subr_size; ptr++)
            if (ptr->used && ptr - subr_tab > subr_max)
                subr_max = ptr - subr_tab;
}

Project: texlive-source
File: writet1.c

static void t1_outhex(byte b)
{
    static const char *hexdigits = "0123456789ABCDEF";
    t1_putchar(hexdigits[b/16]);
    t1_putchar(hexdigits[b%16]);
    hexline_length += 2;
    end_hexline();
}

Project: texlive-source
File: writet1.c

static void t1_subset_ascii_part(void)
{
    int i, j;
    t1_getline();
    while (!t1_prefix("/Encoding")) {
        t1_scan_param();
        if (!(t1_prefix("/UniqueID")
              && !strncmp(t1_line_array + strlen(t1_line_array) -4, "def", 3)))
            t1_putline();
        t1_getline();
    }
    if (is_reencoded(fm_cur))
        t1_glyph_names = external_enc();
    else
        t1_glyph_names = t1_builtin_enc();
    if (is_included(fm_cur) && is_subsetted(fm_cur)) {
        make_subset_tag(fm_cur, t1_glyph_names);
        update_subset_tag();
    }
    if (t1_encoding == ENC_STANDARD)
        t1_puts("/Encoding StandardEncoding def\n");
    else {
        t1_puts
            ("/Encoding 256 array\n0 1 255 {1 index exch /.notdef put} for\n");
        for (i = 0, j = 0; i < 256; i++) {
            if (is_used_char(i) && t1_glyph_names[i] != notdef) {
                j++;
                t1_printf("dup %i /%s put\n", (int)t1_char(i), t1_glyph_names[i]);
            }
        }
        if (j == 0)
            /* We didn't mark anything for the Encoding array. */
            /* We add "dup 0 /.notdef put" for compatibility   */
            /* with Acrobat 5.0.                               */
            t1_puts("dup 0 /.notdef put\n");
        t1_puts("readonly def\n");
    }
    do {
        t1_getline();
        t1_scan_param();
        if (!t1_prefix("/UniqueID"))    /* ignore UniqueID for subsetted fonts */
            t1_putline();
    } while (t1_in_eexec == 0);
}

Project: texlive-source
File: writet1.c

static void cs_init(void)
{
    cs_ptr = cs_tab = NULL;
    cs_dict_start = cs_dict_end = NULL;
    cs_counter = cs_size = cs_size_pos = 0;
    cs_token_pair = NULL;
    subr_tab = NULL;
    subr_array_start = subr_array_end = NULL;
    subr_max = subr_size = subr_size_pos = 0;
}

Project: texlive-source
File: writet1.c

static void enc_getline(void)
{
    char *p;
    char c;
  restart:
    if (enc_eof())
        normal_error("type 1","unexpected end of file");
    p = enc_line;
    do {
        c = (char) enc_getchar();
        append_char_to_buf(c, p, enc_line, ENC_BUF_SIZE);
    }
    while (c != 10 && !enc_eof());
    append_eol(p, enc_line, ENC_BUF_SIZE);
    if (p - enc_line < 2 || *enc_line == '%')
        goto restart;
}

Project: texlive-source
File: writet1.c

static void t1_init_params(int open_name_prefix)
{
    report_start_file(open_name_prefix,cur_file_name);
    t1_lenIV = 4;
    t1_dr = 55665;
    t1_er = 55665;
    t1_in_eexec = 0;
    t1_cs = false;
    t1_scan = true;
    t1_synthetic = false;
    t1_eexec_encrypt = false;
    t1_block_length = 0;
    t1_check_pfa();
}

Project: texlive-source
File: writet1.c

static void t1_scan_param(PDF pdf)
{
    static const char *lenIV = "/lenIV";
    if (!t1_scan || *t1_line_array != '/')
        return;
    if (t1_prefix(lenIV)) {
        t1_lenIV = (short) t1_scan_num(t1_line_array + strlen(lenIV), 0);
        if (t1_lenIV < 0)
            normal_error("type 1","negative value of lenIV is not supported");
        return;
    }
    t1_scan_keys(pdf);
}